summaryrefslogtreecommitdiffstats
path: root/dom/storage/LocalStorageManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/storage/LocalStorageManager.cpp')
-rw-r--r--dom/storage/LocalStorageManager.cpp450
1 files changed, 450 insertions, 0 deletions
diff --git a/dom/storage/LocalStorageManager.cpp b/dom/storage/LocalStorageManager.cpp
new file mode 100644
index 0000000000..1a9f566c42
--- /dev/null
+++ b/dom/storage/LocalStorageManager.cpp
@@ -0,0 +1,450 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+#include "LocalStorageManager.h"
+#include "LocalStorage.h"
+#include "StorageDBThread.h"
+#include "StorageIPC.h"
+#include "StorageUtils.h"
+
+#include "nsIEffectiveTLDService.h"
+
+#include "nsPIDOMWindow.h"
+#include "nsNetUtil.h"
+#include "nsNetCID.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+#include "nsIObserverService.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/dom/LocalStorageCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace StorageUtils;
+
+LocalStorageManager* LocalStorageManager::sSelf = nullptr;
+
+// static
+uint32_t LocalStorageManager::GetOriginQuota() {
+ return StaticPrefs::dom_storage_default_quota() * 1024; // pref is in kBs
+}
+
+// static
+uint32_t LocalStorageManager::GetSiteQuota() {
+ return std::max(StaticPrefs::dom_storage_default_quota(),
+ StaticPrefs::dom_storage_default_site_quota()) *
+ 1024; // pref is in kBs
+}
+
+NS_IMPL_ISUPPORTS(LocalStorageManager, nsIDOMStorageManager,
+ nsILocalStorageManager)
+
+LocalStorageManager::LocalStorageManager() : mCaches(8) {
+ MOZ_ASSERT(!NextGenLocalStorageEnabled());
+
+ StorageObserver* observer = StorageObserver::Self();
+ NS_ASSERTION(
+ observer,
+ "No StorageObserver, cannot observe private data delete notifications!");
+
+ if (observer) {
+ observer->AddSink(this);
+ }
+
+ NS_ASSERTION(!sSelf,
+ "Somebody is trying to "
+ "do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
+ sSelf = this;
+
+ if (!XRE_IsParentProcess()) {
+ // Do this only on the child process. The thread IPC bridge
+ // is also used to communicate chrome observer notifications.
+ // Note: must be called after we set sSelf
+ for (const uint32_t id : {0, 1}) {
+ StorageDBChild::GetOrCreate(id);
+ }
+ }
+}
+
+LocalStorageManager::~LocalStorageManager() {
+ StorageObserver* observer = StorageObserver::Self();
+ if (observer) {
+ observer->RemoveSink(this);
+ }
+
+ sSelf = nullptr;
+}
+
+// static
+nsAutoCString LocalStorageManager::CreateOrigin(
+ const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) {
+ // Note: some hard-coded sqlite statements are dependent on the format this
+ // method returns. Changing this without updating those sqlite statements
+ // will cause malfunction.
+
+ nsAutoCString scope;
+ scope.Append(aOriginSuffix);
+ scope.Append(':');
+ scope.Append(aOriginNoSuffix);
+ return scope;
+}
+
+LocalStorageCache* LocalStorageManager::GetCache(
+ const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) {
+ CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
+ LocalStorageCacheHashKey* entry = table->GetEntry(aOriginNoSuffix);
+ if (!entry) {
+ return nullptr;
+ }
+
+ return entry->cache();
+}
+
+already_AddRefed<StorageUsage> LocalStorageManager::GetOriginUsage(
+ const nsACString& aOriginNoSuffix, const uint32_t aPrivateBrowsingId) {
+ RefPtr<StorageUsage> usage;
+ if (mUsages.Get(aOriginNoSuffix, &usage)) {
+ return usage.forget();
+ }
+
+ usage = new StorageUsage(aOriginNoSuffix);
+
+ StorageDBChild* storageChild =
+ StorageDBChild::GetOrCreate(aPrivateBrowsingId);
+ if (storageChild) {
+ storageChild->AsyncGetUsage(usage);
+ }
+
+ mUsages.Put(aOriginNoSuffix, usage);
+
+ return usage.forget();
+}
+
+already_AddRefed<LocalStorageCache> LocalStorageManager::PutCache(
+ const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix,
+ const nsACString& aQuotaKey, nsIPrincipal* aPrincipal) {
+ CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
+ LocalStorageCacheHashKey* entry = table->PutEntry(aOriginNoSuffix);
+ RefPtr<LocalStorageCache> cache = entry->cache();
+
+ // Lifetime handled by the cache, do persist
+ cache->Init(this, true, aPrincipal, aQuotaKey);
+ return cache.forget();
+}
+
+void LocalStorageManager::DropCache(LocalStorageCache* aCache) {
+ if (!NS_IsMainThread()) {
+ NS_WARNING(
+ "StorageManager::DropCache called on a non-main thread, shutting "
+ "down?");
+ }
+
+ CacheOriginHashtable* table = mCaches.LookupOrAdd(aCache->OriginSuffix());
+ table->RemoveEntry(aCache->OriginNoSuffix());
+}
+
+nsresult LocalStorageManager::GetStorageInternal(
+ CreateMode aCreateMode, mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal,
+ nsIPrincipal* aStoragePrincipal, const nsAString& aDocumentURI,
+ bool aPrivate, Storage** aRetval) {
+ nsAutoCString originAttrSuffix;
+ nsAutoCString originKey;
+ nsAutoCString quotaKey;
+
+ aStoragePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
+
+ nsresult rv = aStoragePrincipal->GetStorageOriginKey(originKey);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ rv = aStoragePrincipal->GetLocalStorageQuotaKey(quotaKey);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ RefPtr<LocalStorageCache> cache = GetCache(originAttrSuffix, originKey);
+
+ // Get or create a cache for the given scope
+ if (!cache) {
+ if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
+ *aRetval = nullptr;
+ return NS_OK;
+ }
+
+ if (aCreateMode == CreateMode::CreateIfShouldPreload) {
+ const uint32_t privateBrowsingId =
+ aStoragePrincipal->GetPrivateBrowsingId();
+
+ // This is a demand to just preload the cache, if the scope has
+ // no data stored, bypass creation and preload of the cache.
+ StorageDBChild* db = StorageDBChild::Get(privateBrowsingId);
+ if (db) {
+ if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(
+ originAttrSuffix, originKey))) {
+ return NS_OK;
+ }
+ } else {
+ if (originKey.EqualsLiteral("knalb.:about")) {
+ return NS_OK;
+ }
+ }
+ }
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ ::mozilla::ipc::PBackgroundChild* backgroundActor =
+ ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
+ if (NS_WARN_IF(!backgroundActor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ::mozilla::ipc::PrincipalInfo principalInfo;
+ rv = mozilla::ipc::PrincipalToPrincipalInfo(aStoragePrincipal,
+ &principalInfo);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ uint32_t privateBrowsingId;
+ rv = aStoragePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+#endif
+
+ // There is always a single instance of a cache per scope
+ // in a single instance of a DOM storage manager.
+ cache = PutCache(originAttrSuffix, originKey, quotaKey, aStoragePrincipal);
+
+#if !defined(MOZ_WIDGET_ANDROID)
+ LocalStorageCacheChild* actor = new LocalStorageCacheChild(cache);
+
+ MOZ_ALWAYS_TRUE(
+ backgroundActor->SendPBackgroundLocalStorageCacheConstructor(
+ actor, principalInfo, originKey, privateBrowsingId));
+
+ cache->SetActor(actor);
+#endif
+ }
+
+ if (aRetval) {
+ nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
+
+ RefPtr<Storage> storage =
+ new LocalStorage(inner, this, cache, aDocumentURI, aPrincipal,
+ aStoragePrincipal, aPrivate);
+ storage.forget(aRetval);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LocalStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
+ nsIPrincipal* aStoragePrincipal,
+ Storage** aRetval) {
+ return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
+ aPrincipal, aStoragePrincipal, u""_ns, false,
+ aRetval);
+}
+
+NS_IMETHODIMP
+LocalStorageManager::CreateStorage(mozIDOMWindow* aWindow,
+ nsIPrincipal* aPrincipal,
+ nsIPrincipal* aStoragePrincipal,
+ const nsAString& aDocumentURI, bool aPrivate,
+ Storage** aRetval) {
+ return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
+ aStoragePrincipal, aDocumentURI, aPrivate, aRetval);
+}
+
+NS_IMETHODIMP
+LocalStorageManager::GetStorage(mozIDOMWindow* aWindow,
+ nsIPrincipal* aPrincipal,
+ nsIPrincipal* aStoragePrincipal, bool aPrivate,
+ Storage** aRetval) {
+ return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
+ aPrincipal, aStoragePrincipal, u""_ns, aPrivate,
+ aRetval);
+}
+
+NS_IMETHODIMP
+LocalStorageManager::CloneStorage(Storage* aStorage) {
+ // Cloning is supported only for sessionStorage
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LocalStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
+ bool* aRetval) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(aStorage);
+ MOZ_ASSERT(aRetval);
+
+ // Only used by sessionStorage.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LocalStorageManager::GetNextGenLocalStorageEnabled(bool* aResult) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aResult);
+
+ *aResult = NextGenLocalStorageEnabled();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LocalStorageManager::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
+ Promise** _retval) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(_retval);
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LocalStorageManager::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
+ Promise** _retval) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(_retval);
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void LocalStorageManager::ClearCaches(uint32_t aUnloadFlags,
+ const OriginAttributesPattern& aPattern,
+ const nsACString& aOriginScope) {
+ for (auto iter1 = mCaches.Iter(); !iter1.Done(); iter1.Next()) {
+ OriginAttributes 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.UserData();
+
+ for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
+ LocalStorageCache* cache = iter2.Get()->cache();
+
+ if (aOriginScope.IsEmpty() ||
+ StringBeginsWith(cache->OriginNoSuffix(), aOriginScope)) {
+ cache->UnloadItems(aUnloadFlags);
+ }
+ }
+ }
+}
+
+nsresult LocalStorageManager::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")) {
+ ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns);
+ return NS_OK;
+ }
+
+ // Clear everything, caches + database
+ if (!strcmp(aTopic, "extension:purge-localStorage-caches")) {
+ ClearCaches(LocalStorageCache::kUnloadComplete, pattern, aOriginScope);
+ return NS_OK;
+ }
+
+ if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
+ // This is only meant for SessionStorageManager.
+ return NS_OK;
+ }
+
+ // Clear from caches everything that has been stored
+ // while in session-only mode
+ if (!strcmp(aTopic, "session-only-cleared")) {
+ ClearCaches(LocalStorageCache::kUnloadSession, pattern, aOriginScope);
+ return NS_OK;
+ }
+
+ // Clear all private-browsing caches
+ if (!strcmp(aTopic, "private-browsing-data-cleared")) {
+ ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns);
+ return NS_OK;
+ }
+
+ // Clear localStorage data beloging to an origin pattern
+ if (!strcmp(aTopic, "origin-attr-pattern-cleared")) {
+ ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns);
+ return NS_OK;
+ }
+
+ if (!strcmp(aTopic, "profile-change")) {
+ // For case caches are still referenced - clear them completely
+ ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns);
+ mCaches.Clear();
+ return NS_OK;
+ }
+
+#ifdef DOM_STORAGE_TESTS
+ if (!strcmp(aTopic, "test-reload")) {
+ // This immediately completely reloads all caches from the database.
+ ClearCaches(LocalStorageCache::kTestReload, pattern, ""_ns);
+ 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;
+}
+
+// static
+LocalStorageManager* LocalStorageManager::Self() {
+ MOZ_ASSERT(!NextGenLocalStorageEnabled());
+
+ return sSelf;
+}
+
+LocalStorageManager* LocalStorageManager::Ensure() {
+ MOZ_ASSERT(!NextGenLocalStorageEnabled());
+
+ if (sSelf) {
+ return sSelf;
+ }
+
+ // Cause sSelf to be populated.
+ nsCOMPtr<nsIDOMStorageManager> initializer =
+ do_GetService("@mozilla.org/dom/localStorage-manager;1");
+ MOZ_ASSERT(sSelf, "Didn't initialize?");
+
+ return sSelf;
+}
+
+} // namespace dom
+} // namespace mozilla