nsresult DOMStorageManager::GetStorageInternal(bool aCreate, nsIDOMWindow* aWindow, nsIPrincipal* aPrincipal, const nsAString& aDocumentURI, bool aPrivate, nsIDOMStorage** aRetval) { nsresult rv; nsAutoCString scope; rv = CreateScopeKey(aPrincipal, scope); if (NS_FAILED(rv)) { return NS_ERROR_NOT_AVAILABLE; } RefPtr<DOMStorageCache> cache = GetCache(scope); // Get or create a cache for the given scope if (!cache) { if (!aCreate) { *aRetval = nullptr; return NS_OK; } if (!aRetval) { // This is demand to just preload the cache, if the scope has // no data stored, bypass creation and preload of the cache. DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); if (db) { if (!db->ShouldPreloadScope(scope)) { return NS_OK; } } else { if (scope.EqualsLiteral("knalb.:about")) { return NS_OK; } } } // There is always a single instance of a cache per scope // in a single instance of a DOM storage manager. cache = PutCache(scope, aPrincipal); } else if (mType == SessionStorage) { if (!cache->CheckPrincipal(aPrincipal)) { return NS_ERROR_DOM_SECURITY_ERR; } } if (aRetval) { nsCOMPtr<nsIDOMStorage> storage = new DOMStorage( aWindow, this, cache, aDocumentURI, aPrincipal, aPrivate); storage.forget(aRetval); } return NS_OK; }
bool DOMStorageDBParent::RecvAsyncFlush() { DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); if (!db) { return false; } db->AsyncFlush(); return true; }
bool DOMStorageDBParent::RecvAsyncPreload(const nsCString& aScope, const bool& aPriority) { DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); if (!db) { return false; } db->AsyncPreload(NewCache(aScope), aPriority); return true; }
bool DOMStorageDBChild::RecvLoadUsage(const nsCString& aScope, const int64_t& aUsage) { DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); if (!db) { return false; } DOMStorageUsageBridge* scopeUsage = db->GetScopeUsage(aScope); scopeUsage->LoadUsage(aUsage); return true; }
bool DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aScope) { DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); if (!db) { return false; } // The object releases it self in LoadUsage method nsRefPtr<UsageParentBridge> usage = new UsageParentBridge(this, aScope); db->AsyncGetUsage(usage); return true; }
bool DOMStorageDBParent::RecvAsyncClear(const nsCString& aScope) { DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); if (!db) { return false; } nsresult rv = db->AsyncClear(NewCache(aScope)); if (NS_FAILED(rv) && mIPCOpen) { mozilla::unused << SendError(rv); } return true; }
bool DOMStorageDBParent::RecvPreload(const nsCString& aScope, const uint32_t& aAlreadyLoadedCount, InfallibleTArray<nsString>* aKeys, InfallibleTArray<nsString>* aValues, nsresult* aRv) { DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); if (!db) { return false; } nsRefPtr<SyncLoadCacheHelper> cache( new SyncLoadCacheHelper(aScope, aAlreadyLoadedCount, aKeys, aValues, aRv)); db->SyncPreload(cache, true); return true; }
already_AddRefed<DOMStorageUsage> DOMStorageManager::GetScopeUsage(const nsACString& aScope) { RefPtr<DOMStorageUsage> usage; if (mUsages.Get(aScope, &usage)) { return usage.forget(); } usage = new DOMStorageUsage(aScope); if (mType == LocalStorage) { DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); if (db) { db->AsyncGetUsage(usage); } } mUsages.Put(aScope, usage); return usage.forget(); }
NS_IMETHODIMP DOMStorageObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* 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; } nsAutoCString host; perm->GetHost(host); if (host.IsEmpty()) { return NS_OK; } nsAutoCString scope; rv = CreateReversedDomain(host, scope); NS_ENSURE_SUCCESS(rv, rv); Notify("session-only-cleared", scope); 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! NS_EscapeURL(NS_ConvertUTF16toUTF8(aData), esc_OnlyNonASCII | esc_AlwaysCopy, aceDomain); } nsAutoCString scopePrefix; rv = CreateReversedDomain(aceDomain, scopePrefix); NS_ENSURE_SUCCESS(rv, rv); DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); db->AsyncClearMatchingScope(scopePrefix); Notify("domain-data-cleared", scopePrefix); 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 beloging to an app. if (!strcmp(aTopic, "webapps-clear-data")) { nsCOMPtr<mozIApplicationClearPrivateDataParams> params = do_QueryInterface(aSubject); if (!params) { NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); return NS_ERROR_UNEXPECTED; } uint32_t appId; bool browserOnly; rv = params->GetAppId(&appId); NS_ENSURE_SUCCESS(rv, rv); rv = params->GetBrowserOnly(&browserOnly); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID); DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); nsAutoCString scope; scope.AppendInt(appId); scope.Append(NS_LITERAL_CSTRING(":t:")); db->AsyncClearMatchingScope(scope); Notify("app-data-cleared", scope); if (!browserOnly) { scope.Truncate(); scope.AppendInt(appId); scope.Append(NS_LITERAL_CSTRING(":f:")); db->AsyncClearMatchingScope(scope); Notify("app-data-cleared", scope); } 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; }
nsresult DOMStorageManager::GetStorageInternal(bool aCreate, mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal, const nsAString& aDocumentURI, bool aPrivate, nsIDOMStorage** aRetval) { nsresult rv; nsAutoCString originAttrSuffix; BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(originAttrSuffix); nsAutoCString originKey; rv = AppendOriginNoSuffix(aPrincipal, originKey); if (NS_FAILED(rv)) { return NS_ERROR_NOT_AVAILABLE; } RefPtr<DOMStorageCache> cache = GetCache(originAttrSuffix, originKey); // Get or create a cache for the given scope if (!cache) { if (!aCreate) { *aRetval = nullptr; return NS_OK; } if (!aRetval) { // This is a demand to just preload the cache, if the scope has // no data stored, bypass creation and preload of the cache. DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); if (db) { if (!db->ShouldPreloadOrigin(DOMStorageManager::CreateOrigin(originAttrSuffix, originKey))) { return NS_OK; } } else { if (originKey.EqualsLiteral("knalb.:about")) { return NS_OK; } } } // There is always a single instance of a cache per scope // in a single instance of a DOM storage manager. cache = PutCache(originAttrSuffix, originKey, aPrincipal); } else if (mType == SessionStorage) { if (!cache->CheckPrincipal(aPrincipal)) { return NS_ERROR_DOM_SECURITY_ERR; } } if (aRetval) { nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow); nsCOMPtr<nsIDOMStorage> storage = new DOMStorage( inner, this, cache, aDocumentURI, aPrincipal, aPrivate); storage.forget(aRetval); } return NS_OK; }
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; }