/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Default permissions are read from a URL - this is the preference we read // to find that URL. If not set, don't use any default permissions.
constexpr char kDefaultsUrlPrefName[] = "permissions.manager.defaultsUrl";
// A special value for a permission ID that indicates the ID was loaded as // a default value. These will never be written to the database, but may // be overridden with an explicit permission (including UNKNOWN_ACTION)
constexpr int64_t cIDPermissionIsDefault = -1;
// NOTE: an empty string can be passed as aType - if it is this function will // return "false" unconditionally. bool HasDefaultPref(const nsACString& aType) { // A list of permissions that can have a fallback default permission // set under the permissions.default.* pref. staticconst nsLiteralCString kPermissionsWithDefaults[] = { "camera"_ns, "microphone"_ns, "geo"_ns, "desktop-notification"_ns, "shortcuts"_ns, "screen-wake-lock"_ns};
if (!aType.IsEmpty()) { for (constauto& perm : kPermissionsWithDefaults) { if (perm.Equals(aType)) { returntrue;
}
}
}
returnfalse;
}
// These permissions are special permissions which must be transmitted to the // content process before documents with their principals have loaded within // that process. // // Permissions which are in this list are considered to have a "" permission // key, even if their principal would not normally have that key. staticconst nsLiteralCString kPreloadPermissions[] = { // This permission is preloaded to support properly blocking service worker // interception when a user has disabled storage for a specific site. Once // service worker interception moves to the parent process this should be // removed. See bug 1428130. "cookie"_ns, "https-only-load-insecure"_ns};
// NOTE: nullptr can be passed as aType - if it is this function will return // "false" unconditionally. bool IsPreloadPermission(const nsACString& aType) { if (!aType.IsEmpty()) { for (constauto& perm : kPreloadPermissions) { if (perm.Equals(aType)) { returntrue;
}
}
}
returnfalse;
}
// Array of permission types which should not be isolated by origin attributes, // for user context and private browsing. // Keep this array in sync with 'STRIPPED_PERMS' in // 'test_permmanager_oa_strip.js' // Currently only preloaded permissions are supported. // This is because perms are sent to the content process in bulk by perm key. // Non-preloaded, but OA stripped permissions would not be accessible by sites // in private browsing / non-default user context. static constexpr std::array<nsLiteralCString, 2> kStripOAPermissions = {
{"cookie"_ns, "https-only-load-insecure"_ns}};
bool IsOAForceStripPermission(const nsACString& aType) { if (aType.IsEmpty()) { returnfalse;
} for (constauto& perm : kStripOAPermissions) { if (perm.Equals(aType)) { returntrue;
}
} returnfalse;
}
// Array of permission prefixes which should be isolated only by site. // These site-scoped permissions are stored under their site's principal. // GetAllForPrincipal also needs to look for these especially. static constexpr std::array<nsLiteralCString, 3> kSiteScopedPermissions = {
{"3rdPartyStorage^"_ns, "AllowStorageAccessRequest^"_ns, "3rdPartyFrameStorage^"_ns}};
// Array of permission type prefixes which have a secondary key encoded in the // permission type. These permissions will not be stored in-process with the // secondary key, but updates to them will cause "perm-changed" notifications on // processes for that key. static constexpr std::array<nsLiteralCString, 3> kSecondaryKeyedPermissions = {
{"3rdPartyStorage^"_ns, "AllowStorageAccessRequest^"_ns, "3rdPartyFrameStorage^"_ns}};
nsresult GetOriginFromPrincipal(nsIPrincipal* aPrincipal, bool aForceStripOA,
nsACString& aOrigin) {
nsresult rv = aPrincipal->GetOriginNoSuffix(aOrigin); // The principal may belong to the about:blank content viewer, so this can be // expected to fail. if (NS_FAILED(rv)) { return rv;
}
// Returns the site of the principal, including OA, given a principal.
nsresult GetSiteFromPrincipal(nsIPrincipal* aPrincipal, bool aForceStripOA,
nsACString& aSite) {
nsCOMPtr<nsIURI> uri = aPrincipal->GetURI();
nsCOMPtr<nsIEffectiveTLDService> etld =
mozilla::components::EffectiveTLD::Service();
NS_ENSURE_TRUE(etld, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
nsresult rv = etld->GetSite(uri, aSite);
// The principal may belong to the about:blank content viewer, so this can be // expected to fail. if (NS_FAILED(rv)) {
rv = aPrincipal->GetOrigin(aSite);
NS_ENSURE_SUCCESS(rv, rv); return NS_OK;
}
nsCString GetNextSubDomainForHost(const nsACString& aHost) {
nsCString subDomain;
nsCOMPtr<nsIEffectiveTLDService> etld =
mozilla::components::EffectiveTLD::Service();
nsresult rv = etld->GetNextSubDomain(aHost, subDomain); // We can fail if there is no more subdomain or if the host can't have a // subdomain. if (NS_FAILED(rv)) { return""_ns;
}
return subDomain;
}
// This function produces a nsIURI which is identical to the current // nsIURI, except that it has one less subdomain segment. It returns // `nullptr` if there are no more segments to remove.
already_AddRefed<nsIURI> GetNextSubDomainURI(nsIURI* aURI) {
nsAutoCString host;
nsresult rv = aURI->GetHost(host); if (NS_FAILED(rv)) { return nullptr;
}
nsCString domain = GetNextSubDomainForHost(host); if (domain.IsEmpty()) { return nullptr;
}
nsresult UpgradeHostToOriginAndInsert( const nsACString& aHost, const nsCString& aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime, int64_t aModificationTime,
std::function<nsresult(const nsACString& aOrigin, const nsCString& aType,
uint32_t aPermission, uint32_t aExpireType,
int64_t aExpireTime, int64_t aModificationTime)>&&
aCallback) { if (aHost.EqualsLiteral("")) { // We no longer support the magic host <file>
NS_WARNING( "The magic host is no longer supported. " "It is being removed from the permissions database."); return NS_OK;
}
// First, we check to see if the host is a valid URI. If it is, it can be // imported directly
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost); if (NS_SUCCEEDED(rv)) { // It was previously possible to insert useless entries to your permissions // database for URIs which have a null principal. This acts as a cleanup, // getting rid of these useless database entries if (uri->SchemeIs("moz-nullprincipal")) {
NS_WARNING("A moz-nullprincipal: permission is being discarded."); return NS_OK;
}
// The user may use this host at non-standard ports or protocols, we can use // their history to guess what ports and protocols we want to add permissions // for. We find every URI which they have visited with this host (or a // subdomain of this host), and try to add it as a principal. bool foundHistory = false;
if (histSrv) {
nsCOMPtr<nsINavHistoryQuery> histQuery;
rv = histSrv->GetNewQuery(getter_AddRefs(histQuery));
NS_ENSURE_SUCCESS(rv, rv);
// Get the eTLD+1 of the domain
nsAutoCString eTLD1;
nsCOMPtr<nsIEffectiveTLDService> etld =
mozilla::components::EffectiveTLD::Service();
rv = etld->GetBaseDomainFromHost(aHost, 0, eTLD1);
if (NS_FAILED(rv)) { // If the lookup on the tldService for the base domain for the host // failed, that means that we just want to directly use the host as the // host name for the lookup.
eTLD1 = aHost;
}
// We want to only find history items for this particular eTLD+1, and // subdomains
rv = histQuery->SetDomain(eTLD1);
NS_ENSURE_SUCCESS(rv, rv);
// We want to get the URIs for every item in the user's history with the // given host
rv =
histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
NS_ENSURE_SUCCESS(rv, rv);
// We only search history, because searching both bookmarks and history // is not supported, and history tends to be more comprehensive.
rv = histQueryOpts->SetQueryType(
nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
NS_ENSURE_SUCCESS(rv, rv);
// We include hidden URIs (such as those visited via iFrames) as they may // have permissions too
rv = histQueryOpts->SetIncludeHidden(true);
NS_ENSURE_SUCCESS(rv, rv);
nsTHashSet<nsCString> insertedOrigins; for (uint32_t i = 0; i < childCount; i++) {
nsCOMPtr<nsINavHistoryResultNode> child;
histResultContainer->GetChild(i, getter_AddRefs(child)); if (NS_WARN_IF(NS_FAILED(rv))) continue;
uint32_t type;
rv = child->GetType(&type); if (NS_WARN_IF(NS_FAILED(rv)) ||
type != nsINavHistoryResultNode::RESULT_TYPE_URI) {
NS_WARNING( "Unexpected non-RESULT_TYPE_URI node in " "UpgradeHostToOriginAndInsert()"); continue;
}
nsAutoCString uriSpec;
rv = child->GetUri(uriSpec); if (NS_WARN_IF(NS_FAILED(rv))) continue;
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), uriSpec); if (NS_WARN_IF(NS_FAILED(rv))) continue;
// Use the provided host - this URI may be for a subdomain, rather than // the host we care about.
rv = NS_MutateURI(uri).SetHost(aHost).Finalize(uri); if (NS_WARN_IF(NS_FAILED(rv))) continue;
// We now have a URI which we can make a nsIPrincipal out of
nsCOMPtr<nsIPrincipal> principal;
rv = GetPrincipal(uri, getter_AddRefs(principal)); if (NS_WARN_IF(NS_FAILED(rv))) continue;
nsAutoCString origin;
rv = GetOriginFromPrincipal(principal, IsOAForceStripPermission(aType),
origin); if (NS_WARN_IF(NS_FAILED(rv))) continue;
// Ensure that we don't insert the same origin repeatedly if (insertedOrigins.Contains(origin)) { continue;
}
// If we didn't find any origins for this host in the poermissions database, // we can insert the default http:// and https:// permissions into the // database. This has a relatively high likelihood of applying the permission // to the correct origin. if (!foundHistory) {
nsAutoCString hostSegment;
nsCOMPtr<nsIPrincipal> principal;
nsAutoCString origin;
// If this is an ipv6 URI, we need to surround it in '[', ']' before trying // to parse it as a URI. if (aHost.FindChar(':') != -1) {
hostSegment.AssignLiteral("[");
hostSegment.Append(aHost);
hostSegment.AppendLiteral("]");
} else {
hostSegment.Assign(aHost);
}
// We only want to persist permissions which don't have session or policy // expiration. bool IsPersistentExpire(uint32_t aExpire, const nsACString& aType) { bool res = (aExpire != nsIPermissionManager::EXPIRE_SESSION &&
aExpire != nsIPermissionManager::EXPIRE_POLICY); return res;
}
PermissionManager::~PermissionManager() { // NOTE: Make sure to reject each of the promises in mPermissionKeyPromiseMap // before destroying. for (constauto& promise : mPermissionKeyPromiseMap.Values()) { if (promise) {
promise->Reject(NS_ERROR_FAILURE, __func__);
}
}
mPermissionKeyPromiseMap.Clear();
if (mThread) {
mThread->Shutdown();
mThread = nullptr;
}
}
// static
already_AddRefed<PermissionManager> PermissionManager::GetInstance() { // The lazy initialization could race.
StaticMutexAutoLock lock(sCreationMutex);
if (sInstanceDead) { return nullptr;
}
if (sInstanceHolder) {
RefPtr<PermissionManager> ret(sInstanceHolder); return ret.forget();
}
auto permManager = MakeRefPtr<PermissionManager>(); if (NS_SUCCEEDED(permManager->Init())) { // Note that this does an extra AddRef on purpose to keep us alive // until shutdown.
sInstanceHolder = permManager.get(); return permManager.forget();
}
sInstanceDead = true; return nullptr;
}
nsresult PermissionManager::Init() { // If we are already shutting down, do not permit a creation. // This must match the phase in GetAsyncShutdownBarrier. if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) { return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
// If the 'permissions.memory_only' pref is set to true, then don't write any // permission settings to disk, but keep them in a memory-only database.
mMemoryOnlyDB = Preferences::GetBool("permissions.memory_only", false);
if (IsChildProcess()) { // Stop here; we don't need the DB in the child process. Instead we will be // sent permissions as we need them by our parent process.
mState = eReady;
// We use ClearOnShutdown on the content process only because on the parent // process we need to block the shutdown for the final closeDB() call.
ClearOnShutdown(&sInstanceHolder); return NS_OK;
}
auto readyIfFailed = MakeScopeExit([&]() { // ignore failure here, since it's non-fatal (we can run fine without // persistent storage - e.g. if there's no profile). // XXX should we tell the user about this?
mState = eReady;
});
if (!mPermissionsFile) {
nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR,
getter_AddRefs(mPermissionsFile)); if (NS_FAILED(rv)) {
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(mPermissionsFile)); if (NS_FAILED(rv)) { return;
}
}
// This extra runnable calls EnsureReadCompleted to finialize the // initialization. If there is something blocked by the monitor, it will // be NOP.
NS_DispatchToMainThread(
NS_NewRunnableFunction("PermissionManager::InitDB-MainThread",
[self] { self->EnsureReadCompleted(); }));
// delete corrupted permissions.sqlite and try again
rv = mPermissionsFile->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
LogToConsole(u"Corrupted permissions.sqlite has been removed."_ns);
rv = OpenDatabase(mPermissionsFile);
NS_ENSURE_SUCCESS(rv, rv);
LogToConsole(u"OpenDatabase to permissions.sqlite is successful!"_ns);
}
if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
bool ready;
data->mDBConn->GetConnectionReady(&ready); if (!ready) {
LogToConsole(nsLiteralString(
u"Fail to get connection to permissions.sqlite! Try again!"));
// delete and try again
rv = mPermissionsFile->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
LogToConsole(u"Defective permissions.sqlite has been removed."_ns);
rv = OpenDatabase(mPermissionsFile);
NS_ENSURE_SUCCESS(rv, rv);
LogToConsole(u"OpenDatabase to permissions.sqlite is successful!"_ns);
data->mDBConn->GetConnectionReady(&ready); if (!ready) return NS_ERROR_UNEXPECTED;
}
bool tableExists = false;
data->mDBConn->TableExists("moz_perms"_ns, &tableExists); if (!tableExists) {
data->mDBConn->TableExists("moz_hosts"_ns, &tableExists);
} if (!tableExists) {
rv = CreateTable();
NS_ENSURE_SUCCESS(rv, rv);
} else { // table already exists; check the schema version before reading
int32_t dbSchemaVersion;
rv = data->mDBConn->GetSchemaVersion(&dbSchemaVersion);
NS_ENSURE_SUCCESS(rv, rv);
switch (dbSchemaVersion) { // upgrading. // every time you increment the database schema, you need to // implement the upgrading code from the previous version to the // new one. fall through to current version
case 1: { // previous non-expiry version of database. Upgrade it by adding // the expiration columns
rv = data->mDBConn->ExecuteSimpleSQL( "ALTER TABLE moz_hosts ADD expireType INTEGER"_ns);
NS_ENSURE_SUCCESS(rv, rv);
// fall through to the next upgrade
[[fallthrough]];
// TODO: we want to make default version as version 2 in order to // fix bug 784875. case 0: case 2: { // Add appId/isInBrowserElement fields.
rv = data->mDBConn->ExecuteSimpleSQL( "ALTER TABLE moz_hosts ADD appId INTEGER"_ns);
NS_ENSURE_SUCCESS(rv, rv);
// fall through to the next upgrade
[[fallthrough]];
// Version 3->4 is the creation of the modificationTime field. case 3: {
rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( "ALTER TABLE moz_hosts ADD modificationTime INTEGER"));
NS_ENSURE_SUCCESS(rv, rv);
// We leave the modificationTime at zero for all existing records; // using now() would mean, eg, that doing "remove all from the // last hour" within the first hour after migration would remove // all permissions.
// fall through to the next upgrade
[[fallthrough]];
// In version 5, host appId, and isInBrowserElement were merged into // a single origin entry // // In version 6, the tables were renamed for backwards compatability // reasons with version 4 and earlier. // // In version 7, a bug in the migration used for version 4->5 was // discovered which could have triggered data-loss. Because of that, // all users with a version 4, 5, or 6 database will be re-migrated // from the backup database. (bug 1186034). This migration bug is // not present after bug 1185340, and the re-migration ensures that // all users have the fix. case 5: // This branch could also be reached via dbSchemaVersion == 3, in // which case we want to fall through to the dbSchemaVersion == 4 // case. The easiest way to do that is to perform this extra check // here to make sure that we didn't get here via a fallthrough // from v3 if (dbSchemaVersion == 5) { // In version 5, the backup database is named moz_hosts_v4. We // perform the version 5->6 migration to get the tables to have // consistent naming conventions.
// Version 5->6 is the renaming of moz_hosts to moz_perms, and // moz_hosts_v4 to moz_hosts (bug 1185343) // // In version 5, we performed the modifications to the // permissions database in place, this meant that if you // upgraded to a version which used V5, and then downgraded to a // version which used v4 or earlier, the fallback path would // drop the table, and your permissions data would be lost. This // migration undoes that mistake, by restoring the old moz_hosts // table (if it was present), and instead using the new table // moz_perms for the new permissions schema. // // NOTE: If you downgrade, store new permissions, and then // upgrade again, these new permissions won't be migrated or // reflected in the updated database. This migration only occurs // once, as if moz_perms exists, it will skip creating it. In // addition, permissions added after the migration will not be // visible in previous versions of firefox.
bool permsTableExists = false;
data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists); if (!permsTableExists) { // Move the upgraded database to moz_perms
rv = data->mDBConn->ExecuteSimpleSQL( "ALTER TABLE moz_hosts RENAME TO moz_perms"_ns);
NS_ENSURE_SUCCESS(rv, rv);
} else {
NS_WARNING( "moz_hosts was not renamed to moz_perms, " "as a moz_perms table already exists");
// In the situation where a moz_perms table already exists, // but the schema is lower than 6, a migration has already // previously occured to V6, but a downgrade has caused the // moz_hosts table to be dropped. This should only occur in // the case of a downgrade to a V5 database, which was only // present in a few day's nightlies. As that version was // likely used only on a temporary basis, we assume that the // database from the previous V6 has the permissions which the // user actually wants to use. We have to get rid of moz_hosts // such that moz_hosts_v4 can be moved into its place if it // exists.
rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_hosts"_ns);
NS_ENSURE_SUCCESS(rv, rv);
}
// Rename moz_hosts_v4 back to it's original location, if it // exists bool v4TableExists = false;
data->mDBConn->TableExists("moz_hosts_v4"_ns, &v4TableExists); if (v4TableExists) {
rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( "ALTER TABLE moz_hosts_v4 RENAME TO moz_hosts"));
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to the next upgrade
[[fallthrough]];
// At this point, the version 5 table has been migrated to a version // 6 table We are guaranteed to have at least one of moz_hosts and // moz_perms. If we have moz_hosts, we will migrate moz_hosts into // moz_perms (even if we already have a moz_perms, as we need a // re-migration due to bug 1186034). // // After this migration, we are guaranteed to have both a moz_hosts // (for backwards compatability), and a moz_perms table. The // moz_hosts table will have a v4 schema, and the moz_perms table // will have a v6 schema. case 4: case 6: { bool hostsTableExists = false;
data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists); if (hostsTableExists) { // Both versions 4 and 6 have a version 4 formatted hosts table // named moz_hosts. We can migrate this table to our version 7 // table moz_perms. If moz_perms is present, then we can use it // as a basis for comparison.
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
MigrationEntry entry;
// Read in the old row
rv = stmt->GetUTF8String(0, entry.mHost); if (NS_WARN_IF(NS_FAILED(rv))) { continue;
}
rv = stmt->GetUTF8String(1, entry.mType); if (NS_WARN_IF(NS_FAILED(rv))) { continue;
}
// We don't drop the moz_hosts table such that it is available // for backwards-compatability and for future migrations in case // of migration errors in the current code. Create a marker // empty table which will indicate that the moz_hosts table is // intended to act as a backup. If this table is not present, // then the moz_hosts table was created as a random empty table.
rv = data->mDBConn->ExecuteSimpleSQL(
nsLiteralCString("CREATE TABLE moz_hosts_is_backup (dummy " "INTEGER PRIMARY KEY)"));
NS_ENSURE_SUCCESS(rv, rv);
bool permsTableExists = false;
data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists); if (permsTableExists) { // The user already had a moz_perms table, and we are // performing a re-migration. We count the rows in the old // table for telemetry, and then back up their old database as // moz_perms_v6
nsCOMPtr<mozIStorageStatement> countStmt;
rv = data->mDBConn->CreateStatement( "SELECT COUNT(*) FROM moz_perms"_ns, getter_AddRefs(countStmt)); bool hasResult = false; if (NS_FAILED(rv) ||
NS_FAILED(countStmt->ExecuteStep(&hasResult)) || !hasResult) {
NS_WARNING("Could not count the rows in moz_perms");
}
// Back up the old moz_perms database as moz_perms_v6 before // we move the new table into its position
rv = data->mDBConn->ExecuteSimpleSQL(nsLiteralCString( "ALTER TABLE moz_perms RENAME TO moz_perms_v6"));
NS_ENSURE_SUCCESS(rv, rv);
}
rv = data->mDBConn->CommitTransaction();
NS_ENSURE_SUCCESS(rv, rv);
} else { // We don't have a moz_hosts table, so we create one for // downgrading purposes. This table is empty.
rv = data->mDBConn->ExecuteSimpleSQL(
nsLiteralCString("CREATE TABLE moz_hosts (" " id INTEGER PRIMARY KEY" ",host TEXT" ",type TEXT" ",permission INTEGER" ",expireType INTEGER" ",expireTime INTEGER" ",modificationTime INTEGER" ",appId INTEGER" ",isInBrowserElement INTEGER" ")"));
NS_ENSURE_SUCCESS(rv, rv);
// We are guaranteed to have a moz_perms table at this point.
}
#ifdef DEBUG
{ // At this point, both the moz_hosts and moz_perms tables should // exist bool hostsTableExists = false; bool permsTableExists = false;
data->mDBConn->TableExists("moz_hosts"_ns, &hostsTableExists);
data->mDBConn->TableExists("moz_perms"_ns, &permsTableExists);
MOZ_ASSERT(hostsTableExists && permsTableExists);
} #endif
// fall through to the next upgrade
[[fallthrough]];
// The version 7-8 migration is the re-migration of localhost and // ip-address entries due to errors in the previous version 7 // migration which caused localhost and ip-address entries to be // incorrectly discarded. The version 7 migration logic has been // corrected, and thus this logic only needs to execute if the user // is currently on version 7. case 7: { // This migration will be relatively expensive as we need to // perform database lookups for each origin which we want to // insert. Fortunately, it shouldn't be too expensive as we only // want to insert a small number of entries created for localhost // or IP addresses.
// We only want to perform the re-migration if moz_hosts is a // backup bool hostsIsBackupExists = false;
data->mDBConn->TableExists("moz_hosts_is_backup"_ns,
&hostsIsBackupExists);
// Only perform this migration if the original schema version was // 7, and the moz_hosts table is a backup. if (dbSchemaVersion == 7 && hostsIsBackupExists) {
nsCOMPtr<mozIStorageStatement> stmt;
rv = data->mDBConn->CreateStatement(
nsLiteralCString( "SELECT host, type, permission, expireType, " "expireTime, " "modificationTime, isInBrowserElement FROM moz_hosts"),
getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> idStmt;
rv = data->mDBConn->CreateStatement( "SELECT MAX(id) FROM moz_hosts"_ns, getter_AddRefs(idStmt));
int64_t id = 0; bool hasResult = false; if (NS_SUCCEEDED(rv) &&
NS_SUCCEEDED(idStmt->ExecuteStep(&hasResult)) && hasResult) {
id = idStmt->AsInt32(0) + 1;
}
// Read in the old row
rv = stmt->GetUTF8String(0, entry.mHost); if (NS_WARN_IF(NS_FAILED(rv))) { continue;
}
nsAutoCString eTLD1;
rv = etld->GetBaseDomainFromHost(entry.mHost, 0, eTLD1); if (NS_SUCCEEDED(rv)) { // We only care about entries which the tldService can't // handle continue;
}
rv = stmt->GetUTF8String(1, entry.mType); if (NS_WARN_IF(NS_FAILED(rv))) { continue;
}
// Even if we didn't perform the migration, we want to bump the // schema version to 8.
rv = data->mDBConn->SetSchemaVersion(8);
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to the next upgrade
[[fallthrough]];
// The version 8-9 migration removes the unnecessary backup // moz-hosts database contents. as the data no longer needs to be // migrated case 8: { // We only want to clear out the old table if it is a backup. If // it isn't a backup, we don't need to touch it. bool hostsIsBackupExists = false;
data->mDBConn->TableExists("moz_hosts_is_backup"_ns,
&hostsIsBackupExists); if (hostsIsBackupExists) { // Delete everything from the backup, we want to keep around the // table so that you can still downgrade and not break things, // but we don't need to keep the rows around.
rv = data->mDBConn->ExecuteSimpleSQL("DELETE FROM moz_hosts"_ns);
NS_ENSURE_SUCCESS(rv, rv);
// The table is no longer a backup, so get rid of it.
rv = data->mDBConn->ExecuteSimpleSQL( "DROP TABLE moz_hosts_is_backup"_ns);
NS_ENSURE_SUCCESS(rv, rv);
}
// fall through to the next upgrade
[[fallthrough]];
case 11: { // Migrate 3rdPartyStorage keys to a site scope
rv = data->mDBConn->BeginTransaction();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> updateStmt;
rv = data->mDBConn->CreateStatement(
nsLiteralCString("UPDATE moz_perms SET origin = ?2 WHERE id = ?1"),
getter_AddRefs(updateStmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> deleteStmt;
rv = data->mDBConn->CreateStatement(
nsLiteralCString("DELETE FROM moz_perms WHERE id = ?1"),
getter_AddRefs(deleteStmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> selectStmt;
rv = data->mDBConn->CreateStatement(
nsLiteralCString("SELECT id, origin, type FROM moz_perms WHERE " " SUBSTR(type, 0, 17) == \"3rdPartyStorage^\""),
getter_AddRefs(selectStmt));
NS_ENSURE_SUCCESS(rv, rv);
// fall through to the next upgrade
[[fallthrough]];
// current version. case HOSTS_SCHEMA_VERSION: break;
// downgrading. // if columns have been added to the table, we can still use the // ones we understand safely. if columns have been deleted or // altered, just blow away the table and start from scratch! if you // change the way a column is interpreted, make sure you also change // its name so this check will catch it. default: { // check if all the expected columns exist
nsCOMPtr<mozIStorageStatement> stmt;
rv = data->mDBConn->CreateStatement(
nsLiteralCString("SELECT origin, type, permission, " "expireType, expireTime, " "modificationTime FROM moz_perms"),
getter_AddRefs(stmt)); if (NS_SUCCEEDED(rv)) break;
// our columns aren't there - drop the table!
rv = data->mDBConn->ExecuteSimpleSQL("DROP TABLE moz_perms"_ns);
NS_ENSURE_SUCCESS(rv, rv);
// sets the schema version and creates the moz_perms table.
nsresult PermissionManager::CreateTable() {
MOZ_ASSERT(!NS_IsMainThread()); auto data = mThreadBoundData.Access();
// set the schema version, before creating the table
nsresult rv = data->mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); if (NS_FAILED(rv)) return rv;
// create the table // SQL also lives in automation.py.in. If you change this SQL change that // one too
rv = data->mDBConn->ExecuteSimpleSQL(
nsLiteralCString("CREATE TABLE moz_perms (" " id INTEGER PRIMARY KEY" ",origin TEXT" ",type TEXT" ",permission INTEGER" ",expireType INTEGER" ",expireTime INTEGER" ",modificationTime INTEGER" ")")); if (NS_FAILED(rv)) return rv;
// We also create a legacy V4 table, for backwards compatability, // and to ensure that downgrades don't trigger a schema version change. return data->mDBConn->ExecuteSimpleSQL(
nsLiteralCString("CREATE TABLE moz_hosts (" " id INTEGER PRIMARY KEY" ",host TEXT" ",type TEXT" ",permission INTEGER" ",expireType INTEGER" ",expireTime INTEGER" ",modificationTime INTEGER" ",isInBrowserElement INTEGER" ")"));
}
// Returns whether the given combination of expire type and expire time are // expired. Note that EXPIRE_SESSION only honors expireTime if it is nonzero. bool PermissionManager::HasExpired(uint32_t aExpireType, int64_t aExpireTime) { return (aExpireType == nsIPermissionManager::EXPIRE_TIME ||
(aExpireType == nsIPermissionManager::EXPIRE_SESSION &&
aExpireTime != 0)) &&
aExpireTime <= EXPIRY_NOW;
}
bool isValidPermissionPrincipal = false;
nsresult rv = ShouldHandlePrincipalForPermission(aPrincipal,
isValidPermissionPrincipal);
NS_ENSURE_SUCCESS(rv, rv); if (!isValidPermissionPrincipal) { // return early if the principal is invalid for permissions return rv;
}
DefaultEntry entry;
{ // Lock for mDefaultEntriesForImport
MonitorAutoLock lock(mMonitor);
// Try to update existing entry in mDefaultEntriesForImport, which will // later be used to restore the default permissions when permissions are // cleared bool updatedExistingEntry = false;
nsTArray<DefaultEntry>::iterator defaultEntry =
mDefaultEntriesForImport.begin(); while (defaultEntry != mDefaultEntriesForImport.end()) { if (defaultEntry->mType == aType && defaultEntry->mOrigin == origin) {
defaultEntry->mPermission = aPermission;
entry = *defaultEntry; if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
mDefaultEntriesForImport.RemoveElementAt(defaultEntry);
}
updatedExistingEntry = true; break;
}
++defaultEntry;
}
// Or add a new entry if there wasn't already one and we aren't deleting the // default permission if (!updatedExistingEntry) {
entry.mOrigin = origin;
entry.mPermission = aPermission;
entry.mType = aType; if (aPermission != nsIPermissionManager::UNKNOWN_ACTION) {
mDefaultEntriesForImport.AppendElement(entry);
}
}
}
// So far, we have only updated mDefaultEntriesForImport for later recovery. // Now, we actually need to import this change into the permission manager. return ImportDefaultEntry(entry);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.