michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "DOMStorageManager.h" michael@0: #include "DOMStorage.h" michael@0: #include "DOMStorageDBThread.h" michael@0: michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIEffectiveTLDService.h" michael@0: michael@0: #include "nsNetUtil.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIObserverService.h" michael@0: #include "mozIThirdPartyUtil.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: // Only allow relatively small amounts of data since performance of michael@0: // the synchronous IO is very bad. michael@0: // We are enforcing simple per-origin quota only. michael@0: #define DEFAULT_QUOTA_LIMIT (5 * 1024) michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: namespace { // anon michael@0: michael@0: int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT; michael@0: michael@0: } // anon michael@0: michael@0: DOMLocalStorageManager* michael@0: DOMLocalStorageManager::sSelf = nullptr; michael@0: michael@0: // static michael@0: uint32_t michael@0: DOMStorageManager::GetQuota() michael@0: { michael@0: static bool preferencesInitialized = false; michael@0: if (!preferencesInitialized) { michael@0: mozilla::Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota", michael@0: DEFAULT_QUOTA_LIMIT); michael@0: preferencesInitialized = true; michael@0: } michael@0: michael@0: return gQuotaLimit * 1024; // pref is in kBs michael@0: } michael@0: michael@0: void michael@0: ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult) michael@0: { michael@0: nsACString::const_iterator sourceBegin, sourceEnd; michael@0: aSource.BeginReading(sourceBegin); michael@0: aSource.EndReading(sourceEnd); michael@0: michael@0: aResult.SetLength(aSource.Length()); michael@0: nsACString::iterator destEnd; michael@0: aResult.EndWriting(destEnd); michael@0: michael@0: while (sourceBegin != sourceEnd) { michael@0: *(--destEnd) = *sourceBegin; michael@0: ++sourceBegin; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: CreateReversedDomain(const nsACString& aAsciiDomain, michael@0: nsACString& aKey) michael@0: { michael@0: if (aAsciiDomain.IsEmpty()) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: ReverseString(aAsciiDomain, aKey); michael@0: michael@0: aKey.AppendLiteral("."); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal) michael@0: { michael@0: if (!aSubjectPrincipal) { michael@0: return true; michael@0: } michael@0: michael@0: if (!aObjectPrincipal) { michael@0: return false; michael@0: } michael@0: michael@0: return aSubjectPrincipal->Equals(aObjectPrincipal); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(DOMStorageManager, michael@0: nsIDOMStorageManager) michael@0: michael@0: DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType) michael@0: : mCaches(10) michael@0: , mType(aType) michael@0: , mLowDiskSpace(false) michael@0: { michael@0: DOMStorageObserver* observer = DOMStorageObserver::Self(); michael@0: NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!"); michael@0: michael@0: if (observer) { michael@0: observer->AddSink(this); michael@0: } michael@0: } michael@0: michael@0: DOMStorageManager::~DOMStorageManager() michael@0: { michael@0: DOMStorageObserver* observer = DOMStorageObserver::Self(); michael@0: if (observer) { michael@0: observer->RemoveSink(this); michael@0: } michael@0: } michael@0: michael@0: namespace { // anon michael@0: michael@0: nsresult michael@0: AppendFirstPartyToKey(nsIURI* aFirstPartyIsolationURI, nsACString& aKey) michael@0: { michael@0: if (aFirstPartyIsolationURI) { michael@0: nsCOMPtr thirdPartyUtil = michael@0: do_GetService(THIRDPARTYUTIL_CONTRACTID); michael@0: if (!thirdPartyUtil) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsAutoCString firstPartyHost; michael@0: nsresult rv = thirdPartyUtil->GetFirstPartyHostForIsolation(aFirstPartyIsolationURI, michael@0: firstPartyHost); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aKey.AppendLiteral("&"); michael@0: aKey.Append(firstPartyHost); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CreateScopeKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal, michael@0: nsACString& aKey) michael@0: { michael@0: nsCOMPtr uri; michael@0: nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!uri) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsAutoCString domainScope; michael@0: rv = uri->GetAsciiHost(domainScope); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (domainScope.IsEmpty()) { michael@0: // For the file:/// protocol use the exact directory as domain. michael@0: bool isScheme = false; michael@0: if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) { michael@0: nsCOMPtr url = do_QueryInterface(uri, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = url->GetDirectory(domainScope); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: nsAutoCString key; michael@0: michael@0: rv = CreateReversedDomain(domainScope, key); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoCString scheme; michael@0: rv = uri->GetScheme(scheme); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: key.Append(NS_LITERAL_CSTRING(":") + scheme); michael@0: michael@0: int32_t port = NS_GetRealPort(uri); michael@0: if (port != -1) { michael@0: key.Append(nsPrintfCString(":%d", port)); michael@0: } michael@0: michael@0: bool unknownAppId; michael@0: rv = aPrincipal->GetUnknownAppId(&unknownAppId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!unknownAppId) { michael@0: uint32_t appId; michael@0: rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isInBrowserElement; michael@0: rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) { michael@0: aKey.Assign(key); michael@0: } else { michael@0: aKey.Truncate(); michael@0: aKey.AppendInt(appId); michael@0: aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ? michael@0: NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) + michael@0: NS_LITERAL_CSTRING(":") + key); michael@0: } michael@0: } michael@0: michael@0: // Isolate scope keys to the URL bar domain by appending &firstPartyHost michael@0: // if available. michael@0: return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey); michael@0: } michael@0: michael@0: nsresult michael@0: CreateQuotaDBKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal, michael@0: nsACString& aKey) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsAutoCString subdomainsDBKey; michael@0: nsCOMPtr eTLDService(do_GetService( michael@0: NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr uri; michael@0: rv = aPrincipal->GetURI(getter_AddRefs(uri)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsAutoCString eTLDplusOne; michael@0: rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne); michael@0: if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) { michael@0: // XXX bug 357323 - what to do for localhost/file exactly? michael@0: rv = uri->GetAsciiHost(eTLDplusOne); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: CreateReversedDomain(eTLDplusOne, subdomainsDBKey); michael@0: michael@0: bool unknownAppId; michael@0: rv = aPrincipal->GetUnknownAppId(&unknownAppId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!unknownAppId) { michael@0: uint32_t appId; michael@0: rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool isInBrowserElement; michael@0: rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) { michael@0: aKey.Assign(subdomainsDBKey); michael@0: } else { michael@0: aKey.Truncate(); michael@0: aKey.AppendInt(appId); michael@0: aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ? michael@0: NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) + michael@0: NS_LITERAL_CSTRING(":") + subdomainsDBKey); michael@0: } michael@0: } michael@0: michael@0: // Isolate scope keys to the URL bar domain by appending &firstPartyHost michael@0: // if available. michael@0: return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey); michael@0: } michael@0: michael@0: } // anon michael@0: michael@0: DOMStorageCache* michael@0: DOMStorageManager::GetCache(const nsACString& aScope) const michael@0: { michael@0: DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope); michael@0: if (!entry) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return entry->cache(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: DOMStorageManager::GetScopeUsage(const nsACString& aScope) michael@0: { michael@0: nsRefPtr usage; michael@0: if (mUsages.Get(aScope, &usage)) { michael@0: return usage.forget(); michael@0: } michael@0: michael@0: usage = new DOMStorageUsage(aScope); michael@0: michael@0: if (mType == LocalStorage) { michael@0: DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); michael@0: if (db) { michael@0: db->AsyncGetUsage(usage); michael@0: } michael@0: } michael@0: michael@0: mUsages.Put(aScope, usage); michael@0: michael@0: return usage.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: DOMStorageManager::PutCache(const nsACString& aScope, michael@0: nsIURI* aFirstPartyIsolationURI, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope); michael@0: nsRefPtr cache = entry->cache(); michael@0: michael@0: nsAutoCString quotaScope; michael@0: CreateQuotaDBKey(aFirstPartyIsolationURI, aPrincipal, quotaScope); michael@0: michael@0: // To avoid ever persisting session storage to disk, initialize LocalStorage michael@0: // like SessionStorage. michael@0: switch (mType) { michael@0: case SessionStorage: michael@0: case LocalStorage: michael@0: // Lifetime handled by the manager, don't persist michael@0: entry->HardRef(); michael@0: cache->Init(this, false, aFirstPartyIsolationURI, aPrincipal, quotaScope); michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSERT(false); michael@0: } michael@0: michael@0: return cache.forget(); michael@0: } michael@0: michael@0: void michael@0: DOMStorageManager::DropCache(DOMStorageCache* aCache) michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?"); michael@0: } michael@0: michael@0: mCaches.RemoveEntry(aCache->Scope()); michael@0: } michael@0: michael@0: nsresult michael@0: DOMStorageManager::GetStorageInternal(bool aCreate, michael@0: nsIURI* aFirstPartyIsolationURI, michael@0: nsIPrincipal* aPrincipal, michael@0: const nsAString& aDocumentURI, michael@0: bool aPrivate, michael@0: nsIDOMStorage** aRetval) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsAutoCString scope; michael@0: rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope); michael@0: if (NS_FAILED(rv)) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsRefPtr cache = GetCache(scope); michael@0: michael@0: // Get or create a cache for the given scope michael@0: if (!cache) { michael@0: if (!aCreate) { michael@0: *aRetval = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!aRetval) { michael@0: // This is demand to just preload the cache, if the scope has michael@0: // no data stored, bypass creation and preload of the cache. michael@0: DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); michael@0: if (db) { michael@0: if (!db->ShouldPreloadScope(scope)) { michael@0: return NS_OK; michael@0: } michael@0: } else { michael@0: if (scope.Equals(NS_LITERAL_CSTRING("knalb.:about"))) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // There is always a single instance of a cache per scope michael@0: // in a single instance of a DOM storage manager. michael@0: cache = PutCache(scope, aFirstPartyIsolationURI, aPrincipal); michael@0: } else if (mType == SessionStorage) { michael@0: if (!cache->CheckPrincipal(aPrincipal)) { michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: } michael@0: michael@0: if (aRetval) { michael@0: *aRetval = new DOMStorage(this, cache, aDocumentURI, aPrincipal, aPrivate); michael@0: NS_ADDREF(*aRetval); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal) michael@0: { michael@0: return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), michael@0: false, nullptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::PrecacheStorageForFirstParty(nsIURI* aFirstPartyIsolationURI, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, EmptyString(), michael@0: false, nullptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal, michael@0: const nsAString& aDocumentURI, michael@0: bool aPrivate, michael@0: nsIDOMStorage** aRetval) michael@0: { michael@0: return GetStorageInternal(true, nullptr, aPrincipal, aDocumentURI, michael@0: aPrivate, aRetval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::CreateStorageForFirstParty(nsIURI* aFirstPartyIsolationURI, michael@0: nsIPrincipal* aPrincipal, michael@0: const nsAString& aDocumentURI, michael@0: bool aPrivate, michael@0: nsIDOMStorage** aRetval) michael@0: { michael@0: return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, aDocumentURI, michael@0: aPrivate, aRetval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal, michael@0: bool aPrivate, michael@0: nsIDOMStorage** aRetval) michael@0: { michael@0: return GetStorageInternal(false, nullptr, aPrincipal, EmptyString(), michael@0: aPrivate, aRetval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::GetStorageForFirstParty(nsIURI* aFirstPartyIsolationURI, michael@0: nsIPrincipal* aPrincipal, michael@0: bool aPrivate, michael@0: nsIDOMStorage** aRetval) michael@0: { michael@0: return GetStorageInternal(false, aFirstPartyIsolationURI, aPrincipal, michael@0: EmptyString(), aPrivate, aRetval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage) michael@0: { michael@0: if (mType != SessionStorage) { michael@0: // Cloning is supported only for sessionStorage michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsCOMPtr pstorage = do_QueryInterface(aStorage); michael@0: if (!pstorage) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: const DOMStorageCache* origCache = pstorage->GetCache(); michael@0: michael@0: DOMStorageCache* existingCache = GetCache(origCache->Scope()); michael@0: if (existingCache) { michael@0: // Do not replace an existing sessionStorage. michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // Since this manager is sessionStorage manager, PutCache hard references michael@0: // the cache in our hashtable. michael@0: nsRefPtr newCache = PutCache(origCache->Scope(), michael@0: origCache->FirstPartyIsolationURI(), michael@0: origCache->Principal()); michael@0: michael@0: newCache->CloneFrom(origCache); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal, michael@0: nsIDOMStorage* aStorage, michael@0: bool* aRetval) michael@0: { michael@0: return CheckStorageForFirstParty(nullptr, aPrincipal, aStorage, aRetval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::CheckStorageForFirstParty(nsIURI* aFirstPartyIsolationURI, michael@0: nsIPrincipal* aPrincipal, michael@0: nsIDOMStorage* aStorage, michael@0: bool* aRetval) michael@0: { michael@0: nsCOMPtr pstorage = do_QueryInterface(aStorage); michael@0: if (!pstorage) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: *aRetval = false; michael@0: michael@0: if (!aPrincipal) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsAutoCString scope; michael@0: nsresult rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: DOMStorageCache* cache = GetCache(scope); michael@0: if (cache != pstorage->GetCache()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!pstorage->PrincipalEquals(aPrincipal)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: *aRetval = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Obsolete nsIDOMStorageManager methods michael@0: michael@0: NS_IMETHODIMP michael@0: DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal, michael@0: const nsAString& aDocumentURI, michael@0: bool aPrivate, michael@0: nsIDOMStorage** aRetval) michael@0: { michael@0: if (mType != LocalStorage) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval); michael@0: } michael@0: michael@0: namespace { // anon michael@0: michael@0: class ClearCacheEnumeratorData michael@0: { michael@0: public: michael@0: ClearCacheEnumeratorData(uint32_t aFlags) michael@0: : mUnloadFlags(aFlags) michael@0: {} michael@0: michael@0: uint32_t mUnloadFlags; michael@0: nsCString mKeyPrefix; michael@0: }; michael@0: michael@0: } // anon michael@0: michael@0: PLDHashOperator michael@0: DOMStorageManager::ClearCacheEnumerator(DOMStorageCacheHashKey* aEntry, void* aClosure) michael@0: { michael@0: DOMStorageCache* cache = aEntry->cache(); michael@0: nsCString& key = const_cast(cache->Scope()); michael@0: michael@0: ClearCacheEnumeratorData* data = static_cast(aClosure); michael@0: michael@0: if (data->mKeyPrefix.IsEmpty() || StringBeginsWith(key, data->mKeyPrefix)) { michael@0: cache->UnloadItems(data->mUnloadFlags); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsresult michael@0: DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix) michael@0: { michael@0: // Clear everything, caches + database michael@0: if (!strcmp(aTopic, "cookie-cleared")) { michael@0: ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete); michael@0: mCaches.EnumerateEntries(ClearCacheEnumerator, &data); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Clear from caches everything that has been stored michael@0: // while in session-only mode michael@0: if (!strcmp(aTopic, "session-only-cleared")) { michael@0: ClearCacheEnumeratorData data(DOMStorageCache::kUnloadSession); michael@0: data.mKeyPrefix = aScopePrefix; michael@0: mCaches.EnumerateEntries(ClearCacheEnumerator, &data); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Clear everything (including so and pb data) from caches and database michael@0: // for the gived domain and subdomains. michael@0: if (!strcmp(aTopic, "domain-data-cleared")) { michael@0: ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete); michael@0: data.mKeyPrefix = aScopePrefix; michael@0: mCaches.EnumerateEntries(ClearCacheEnumerator, &data); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Clear all private-browsing caches michael@0: if (!strcmp(aTopic, "private-browsing-data-cleared")) { michael@0: ClearCacheEnumeratorData data(DOMStorageCache::kUnloadPrivate); michael@0: mCaches.EnumerateEntries(ClearCacheEnumerator, &data); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Clear localStorage data beloging to an app. michael@0: if (!strcmp(aTopic, "app-data-cleared")) { michael@0: michael@0: // sessionStorage is expected to stay michael@0: if (mType == SessionStorage) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete); michael@0: data.mKeyPrefix = aScopePrefix; michael@0: mCaches.EnumerateEntries(ClearCacheEnumerator, &data); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp(aTopic, "profile-change")) { michael@0: // For case caches are still referenced - clear them completely michael@0: ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete); michael@0: mCaches.EnumerateEntries(ClearCacheEnumerator, &data); michael@0: michael@0: mCaches.Clear(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp(aTopic, "low-disk-space")) { michael@0: if (mType == LocalStorage) { michael@0: mLowDiskSpace = true; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp(aTopic, "no-low-disk-space")) { michael@0: if (mType == LocalStorage) { michael@0: mLowDiskSpace = false; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef DOM_STORAGE_TESTS michael@0: if (!strcmp(aTopic, "test-reload")) { michael@0: if (mType != LocalStorage) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // This immediately completely reloads all caches from the database. michael@0: ClearCacheEnumeratorData data(DOMStorageCache::kTestReload); michael@0: mCaches.EnumerateEntries(ClearCacheEnumerator, &data); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp(aTopic, "test-flushed")) { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: NS_ERROR("Unexpected topic"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // DOMLocalStorageManager michael@0: michael@0: DOMLocalStorageManager::DOMLocalStorageManager() michael@0: : DOMStorageManager(LocalStorage) michael@0: { michael@0: NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\""); michael@0: sSelf = this; michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: // Do this only on the child process. The thread IPC bridge michael@0: // is also used to communicate chrome observer notifications. michael@0: // Note: must be called after we set sSelf michael@0: DOMStorageCache::StartDatabase(); michael@0: } michael@0: } michael@0: michael@0: DOMLocalStorageManager::~DOMLocalStorageManager() michael@0: { michael@0: sSelf = nullptr; michael@0: } michael@0: michael@0: // DOMSessionStorageManager michael@0: michael@0: DOMSessionStorageManager::DOMSessionStorageManager() michael@0: : DOMStorageManager(SessionStorage) michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: // Do this only on the child process. The thread IPC bridge michael@0: // is also used to communicate chrome observer notifications. michael@0: DOMStorageCache::StartDatabase(); michael@0: } michael@0: } michael@0: michael@0: } // ::dom michael@0: } // ::mozilla