void DOMStorageManager::ClearCaches(uint32_t aUnloadFlags, const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { for (auto iter1 = mCaches.Iter(); !iter1.Done(); iter1.Next()) { PrincipalOriginAttributes oa; DebugOnly<bool> rv = oa.PopulateFromSuffix(iter1.Key()); MOZ_ASSERT(rv); if (!aPattern.Matches(oa)) { // This table doesn't match the given origin attributes pattern continue; } CacheOriginHashtable* table = iter1.Data(); for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) { DOMStorageCache* cache = iter2.Get()->cache(); if (aOriginScope.IsEmpty() || StringBeginsWith(cache->OriginNoSuffix(), aOriginScope)) { cache->UnloadItems(aUnloadFlags); } } } }
void SessionStorageManager::ClearStorages(ClearStorageType aType, const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { for (auto iter1 = mOATable.Iter(); !iter1.Done(); iter1.Next()) { OriginAttributes oa; DebugOnly<bool> ok = oa.PopulateFromSuffix(iter1.Key()); MOZ_ASSERT(ok); if (!aPattern.Matches(oa)) { // This table doesn't match the given origin attributes pattern continue; } OriginKeyHashTable* table = iter1.Data(); for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) { if (aOriginScope.IsEmpty() || StringBeginsWith(iter2.Key(), aOriginScope)) { if (aType == eAll) { iter2.Data()->Clear(SessionStorageCache::eDefaultSetType, false); iter2.Data()->Clear(SessionStorageCache::eSessionSetType, false); } else { MOZ_ASSERT(aType == eSessionOnly); iter2.Data()->Clear(SessionStorageCache::eSessionSetType, false); } } } } }
NS_IMETHODIMP nsHttpAuthCache::OriginClearObserver::Observe(nsISupports *subject, const char * topic, const char16_t * data_unicode) { NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE); OriginAttributesPattern pattern; if (!pattern.Init(nsDependentString(data_unicode))) { NS_ERROR("Cannot parse origin attributes pattern"); return NS_ERROR_FAILURE; } mOwner->ClearOriginData(pattern); return NS_OK; }
nsresult SessionStorageManager::Observe(const char* aTopic, const nsAString& aOriginAttributesPattern, const nsACString& aOriginScope) { OriginAttributesPattern pattern; if (!pattern.Init(aOriginAttributesPattern)) { NS_ERROR("Cannot parse origin attributes pattern"); return NS_ERROR_FAILURE; } // Clear everything, caches + database if (!strcmp(aTopic, "cookie-cleared")) { ClearStorages(eAll, pattern, EmptyCString()); return NS_OK; } // Clear from caches everything that has been stored // while in session-only mode if (!strcmp(aTopic, "session-only-cleared")) { ClearStorages(eSessionOnly, pattern, aOriginScope); return NS_OK; } // Clear everything (including so and pb data) from caches and database // for the gived domain and subdomains. if (!strcmp(aTopic, "domain-data-cleared")) { ClearStorages(eAll, pattern, aOriginScope); return NS_OK; } if (!strcmp(aTopic, "profile-change")) { // For case caches are still referenced - clear them completely ClearStorages(eAll, pattern, EmptyCString()); mOATable.Clear(); return NS_OK; } return NS_OK; }
nsresult DOMStorageManager::Observe(const char* aTopic, const nsAString& aOriginAttributesPattern, const nsACString& aOriginScope) { OriginAttributesPattern pattern; pattern.Init(aOriginAttributesPattern); // Clear everything, caches + database if (!strcmp(aTopic, "cookie-cleared")) { ClearCaches(DOMStorageCache::kUnloadComplete, pattern, EmptyCString()); return NS_OK; } // Clear from caches everything that has been stored // while in session-only mode if (!strcmp(aTopic, "session-only-cleared")) { ClearCaches(DOMStorageCache::kUnloadSession, pattern, aOriginScope); return NS_OK; } // Clear everything (including so and pb data) from caches and database // for the gived domain and subdomains. if (!strcmp(aTopic, "domain-data-cleared")) { ClearCaches(DOMStorageCache::kUnloadComplete, pattern, aOriginScope); return NS_OK; } // Clear all private-browsing caches if (!strcmp(aTopic, "private-browsing-data-cleared")) { ClearCaches(DOMStorageCache::kUnloadPrivate, pattern, EmptyCString()); return NS_OK; } // Clear localStorage data beloging to an origin pattern if (!strcmp(aTopic, "origin-attr-pattern-cleared")) { // sessionStorage is expected to stay if (mType == SessionStorage) { return NS_OK; } ClearCaches(DOMStorageCache::kUnloadComplete, pattern, EmptyCString()); return NS_OK; } if (!strcmp(aTopic, "profile-change")) { // For case caches are still referenced - clear them completely ClearCaches(DOMStorageCache::kUnloadComplete, pattern, EmptyCString()); mCaches.Clear(); return NS_OK; } if (!strcmp(aTopic, "low-disk-space")) { if (mType == LocalStorage) { mLowDiskSpace = true; } return NS_OK; } if (!strcmp(aTopic, "no-low-disk-space")) { if (mType == LocalStorage) { mLowDiskSpace = false; } return NS_OK; } #ifdef DOM_STORAGE_TESTS if (!strcmp(aTopic, "test-reload")) { if (mType != LocalStorage) { return NS_OK; } // This immediately completely reloads all caches from the database. ClearCaches(DOMStorageCache::kTestReload, pattern, EmptyCString()); return NS_OK; } if (!strcmp(aTopic, "test-flushed")) { if (!XRE_IsParentProcess()) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (obs) { obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr); } } return NS_OK; } #endif NS_ERROR("Unexpected topic"); return NS_ERROR_UNEXPECTED; }
NS_IMETHODIMP DOMStorageObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { nsresult rv; // Start the thread that opens the database. if (!strcmp(aTopic, kStartupTopic)) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); obs->RemoveObserver(this, kStartupTopic); mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID); if (!mDBThreadStartDelayTimer) { return NS_ERROR_UNEXPECTED; } mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay); return NS_OK; } // Timer callback used to start the database a short timer after startup if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject); if (!timer) { return NS_ERROR_UNEXPECTED; } if (timer == mDBThreadStartDelayTimer) { mDBThreadStartDelayTimer = nullptr; DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); } return NS_OK; } // Clear everything, caches + database if (!strcmp(aTopic, "cookie-changed")) { if (!NS_LITERAL_STRING("cleared").Equals(aData)) { return NS_OK; } DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearAll(); Notify("cookie-cleared"); return NS_OK; } // Clear from caches everything that has been stored // while in session-only mode if (!strcmp(aTopic, "perm-changed")) { // Check for cookie permission change nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject)); if (!perm) { return NS_OK; } nsAutoCString type; perm->GetType(type); if (type != NS_LITERAL_CSTRING("cookie")) { return NS_OK; } uint32_t cap = 0; perm->GetCapability(&cap); if (!(cap & nsICookiePermission::ACCESS_SESSION) || !NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) { return NS_OK; } nsCOMPtr<nsIPrincipal> principal; perm->GetPrincipal(getter_AddRefs(principal)); if (!principal) { return NS_OK; } nsAutoCString originSuffix; BasePrincipal::Cast(principal)->OriginAttributesRef().CreateSuffix(originSuffix); nsCOMPtr<nsIURI> origin; principal->GetURI(getter_AddRefs(origin)); if (!origin) { return NS_OK; } nsAutoCString host; origin->GetHost(host); if (host.IsEmpty()) { return NS_OK; } nsAutoCString originScope; rv = CreateReversedDomain(host, originScope); NS_ENSURE_SUCCESS(rv, rv); Notify("session-only-cleared", NS_ConvertUTF8toUTF16(originSuffix), originScope); return NS_OK; } // Clear everything (including so and pb data) from caches and database // for the gived domain and subdomains. if (!strcmp(aTopic, "browser:purge-domain-data")) { // Convert the domain name to the ACE format nsAutoCString aceDomain; nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID); if (converter) { rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain); NS_ENSURE_SUCCESS(rv, rv); } else { // In case the IDN service is not available, this is the best we can come up with! rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(aData), esc_OnlyNonASCII | esc_AlwaysCopy, aceDomain, fallible); NS_ENSURE_SUCCESS(rv, rv); } nsAutoCString originScope; rv = CreateReversedDomain(aceDomain, originScope); NS_ENSURE_SUCCESS(rv, rv); DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearMatchingOrigin(originScope); Notify("domain-data-cleared", EmptyString(), originScope); return NS_OK; } // Clear all private-browsing caches if (!strcmp(aTopic, "last-pb-context-exited")) { Notify("private-browsing-data-cleared"); return NS_OK; } // Clear data of the origins whose prefixes will match the suffix. if (!strcmp(aTopic, "clear-origin-attributes-data")) { OriginAttributesPattern pattern; if (!pattern.Init(nsDependentString(aData))) { NS_ERROR("Cannot parse origin attributes pattern"); return NS_ERROR_FAILURE; } DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearMatchingOriginAttributes(pattern); Notify("origin-attr-pattern-cleared", nsDependentString(aData)); return NS_OK; } if (!strcmp(aTopic, "profile-after-change")) { Notify("profile-change"); return NS_OK; } if (!strcmp(aTopic, "profile-before-change") || !strcmp(aTopic, "xpcom-shutdown")) { rv = DOMStorageCache::StopDatabase(); if (NS_FAILED(rv)) { NS_WARNING("Error while stopping DOMStorage DB background thread"); } return NS_OK; } if (!strcmp(aTopic, "disk-space-watcher")) { if (NS_LITERAL_STRING("full").Equals(aData)) { Notify("low-disk-space"); } else if (NS_LITERAL_STRING("free").Equals(aData)) { Notify("no-low-disk-space"); } return NS_OK; } #ifdef DOM_STORAGE_TESTS if (!strcmp(aTopic, "domstorage-test-flush-force")) { DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); if (db) { db->AsyncFlush(); } return NS_OK; } if (!strcmp(aTopic, "domstorage-test-flushed")) { // Only used to propagate to IPC children Notify("test-flushed"); return NS_OK; } if (!strcmp(aTopic, "domstorage-test-reload")) { Notify("test-reload"); return NS_OK; } #endif NS_ERROR("Unexpected topic"); return NS_ERROR_UNEXPECTED; }