1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "LocalStorageManager.h"
8 #include "LocalStorage.h"
9 #include "StorageDBThread.h"
10 #include "StorageIPC.h"
11 #include "StorageUtils.h"
13 #include "nsIEffectiveTLDService.h"
15 #include "nsPIDOMWindow.h"
16 #include "nsNetUtil.h"
18 #include "nsPrintfCString.h"
19 #include "nsXULAppAPI.h"
20 #include "nsThreadUtils.h"
21 #include "nsIObserverService.h"
22 #include "mozilla/ipc/BackgroundChild.h"
23 #include "mozilla/ipc/PBackgroundChild.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/StaticPrefs_dom.h"
26 #include "mozilla/dom/LocalStorageCommon.h"
28 namespace mozilla::dom
{
30 using namespace StorageUtils
;
32 LocalStorageManager
* LocalStorageManager::sSelf
= nullptr;
35 uint32_t LocalStorageManager::GetOriginQuota() {
36 return StaticPrefs::dom_storage_default_quota() * 1024; // pref is in kBs
40 uint32_t LocalStorageManager::GetSiteQuota() {
41 return std::max(StaticPrefs::dom_storage_default_quota(),
42 StaticPrefs::dom_storage_default_site_quota()) *
43 1024; // pref is in kBs
46 NS_IMPL_ISUPPORTS(LocalStorageManager
, nsIDOMStorageManager
,
47 nsILocalStorageManager
)
49 LocalStorageManager::LocalStorageManager() : mCaches(8) {
50 MOZ_ASSERT(!NextGenLocalStorageEnabled());
52 StorageObserver
* observer
= StorageObserver::Self();
55 "No StorageObserver, cannot observe private data delete notifications!");
58 observer
->AddSink(this);
62 "Somebody is trying to "
63 "do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
66 if (!XRE_IsParentProcess()) {
67 // Do this only on the child process. The thread IPC bridge
68 // is also used to communicate chrome observer notifications.
69 // Note: must be called after we set sSelf
70 for (const uint32_t id
: {0, 1}) {
71 StorageDBChild::GetOrCreate(id
);
76 LocalStorageManager::~LocalStorageManager() {
77 StorageObserver
* observer
= StorageObserver::Self();
79 observer
->RemoveSink(this);
86 nsAutoCString
LocalStorageManager::CreateOrigin(
87 const nsACString
& aOriginSuffix
, const nsACString
& aOriginNoSuffix
) {
88 // Note: some hard-coded sqlite statements are dependent on the format this
89 // method returns. Changing this without updating those sqlite statements
90 // will cause malfunction.
93 scope
.Append(aOriginSuffix
);
95 scope
.Append(aOriginNoSuffix
);
99 LocalStorageCache
* LocalStorageManager::GetCache(
100 const nsACString
& aOriginSuffix
, const nsACString
& aOriginNoSuffix
) {
101 CacheOriginHashtable
* table
= mCaches
.GetOrInsertNew(aOriginSuffix
);
102 LocalStorageCacheHashKey
* entry
= table
->GetEntry(aOriginNoSuffix
);
107 return entry
->cache();
110 already_AddRefed
<StorageUsage
> LocalStorageManager::GetOriginUsage(
111 const nsACString
& aOriginNoSuffix
, const uint32_t aPrivateBrowsingId
) {
112 return do_AddRef(mUsages
.LookupOrInsertWith(aOriginNoSuffix
, [&] {
113 auto usage
= MakeRefPtr
<StorageUsage
>(aOriginNoSuffix
);
115 StorageDBChild
* storageChild
=
116 StorageDBChild::GetOrCreate(aPrivateBrowsingId
);
118 storageChild
->AsyncGetUsage(usage
);
125 already_AddRefed
<LocalStorageCache
> LocalStorageManager::PutCache(
126 const nsACString
& aOriginSuffix
, const nsACString
& aOriginNoSuffix
,
127 const nsACString
& aQuotaKey
, nsIPrincipal
* aPrincipal
) {
128 CacheOriginHashtable
* table
= mCaches
.GetOrInsertNew(aOriginSuffix
);
129 LocalStorageCacheHashKey
* entry
= table
->PutEntry(aOriginNoSuffix
);
130 RefPtr
<LocalStorageCache
> cache
= entry
->cache();
132 // Lifetime handled by the cache, do persist
133 cache
->Init(this, true, aPrincipal
, aQuotaKey
);
134 return cache
.forget();
137 void LocalStorageManager::DropCache(LocalStorageCache
* aCache
) {
138 if (!NS_IsMainThread()) {
140 "StorageManager::DropCache called on a non-main thread, shutting "
144 CacheOriginHashtable
* table
= mCaches
.GetOrInsertNew(aCache
->OriginSuffix());
145 table
->RemoveEntry(aCache
->OriginNoSuffix());
148 nsresult
LocalStorageManager::GetStorageInternal(
149 CreateMode aCreateMode
, mozIDOMWindow
* aWindow
, nsIPrincipal
* aPrincipal
,
150 nsIPrincipal
* aStoragePrincipal
, const nsAString
& aDocumentURI
,
151 bool aPrivate
, Storage
** aRetval
) {
152 nsAutoCString originAttrSuffix
;
153 nsAutoCString originKey
;
154 nsAutoCString quotaKey
;
156 aStoragePrincipal
->OriginAttributesRef().CreateSuffix(originAttrSuffix
);
158 nsresult rv
= aStoragePrincipal
->GetStorageOriginKey(originKey
);
159 if (NS_WARN_IF(NS_FAILED(rv
))) {
160 return NS_ERROR_NOT_AVAILABLE
;
163 rv
= aStoragePrincipal
->GetLocalStorageQuotaKey(quotaKey
);
164 if (NS_WARN_IF(NS_FAILED(rv
))) {
165 return NS_ERROR_NOT_AVAILABLE
;
168 RefPtr
<LocalStorageCache
> cache
= GetCache(originAttrSuffix
, originKey
);
170 // Get or create a cache for the given scope
172 if (aCreateMode
== CreateMode::UseIfExistsNeverCreate
) {
177 if (aCreateMode
== CreateMode::CreateIfShouldPreload
) {
178 const uint32_t privateBrowsingId
=
179 aStoragePrincipal
->GetPrivateBrowsingId();
181 // This is a demand to just preload the cache, if the scope has
182 // no data stored, bypass creation and preload of the cache.
183 StorageDBChild
* db
= StorageDBChild::Get(privateBrowsingId
);
185 if (!db
->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(
186 originAttrSuffix
, originKey
))) {
190 if (originKey
.EqualsLiteral("knalb.:about")) {
196 #if !defined(MOZ_WIDGET_ANDROID)
197 ::mozilla::ipc::PBackgroundChild
* backgroundActor
=
198 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
199 if (NS_WARN_IF(!backgroundActor
)) {
200 return NS_ERROR_FAILURE
;
203 ::mozilla::ipc::PrincipalInfo principalInfo
;
204 rv
= mozilla::ipc::PrincipalToPrincipalInfo(aStoragePrincipal
,
206 if (NS_WARN_IF(NS_FAILED(rv
))) {
210 uint32_t privateBrowsingId
;
211 rv
= aStoragePrincipal
->GetPrivateBrowsingId(&privateBrowsingId
);
212 if (NS_WARN_IF(NS_FAILED(rv
))) {
216 if (!backgroundActor
->CanSend()) {
217 return NS_ERROR_FAILURE
;
221 // There is always a single instance of a cache per scope
222 // in a single instance of a DOM storage manager.
223 cache
= PutCache(originAttrSuffix
, originKey
, quotaKey
, aStoragePrincipal
);
225 #if !defined(MOZ_WIDGET_ANDROID)
226 LocalStorageCacheChild
* actor
= new LocalStorageCacheChild(cache
);
229 backgroundActor
->SendPBackgroundLocalStorageCacheConstructor(
230 actor
, principalInfo
, originKey
, privateBrowsingId
));
232 cache
->SetActor(actor
);
237 nsCOMPtr
<nsPIDOMWindowInner
> inner
= nsPIDOMWindowInner::From(aWindow
);
239 RefPtr
<Storage
> storage
=
240 new LocalStorage(inner
, this, cache
, aDocumentURI
, aPrincipal
,
241 aStoragePrincipal
, aPrivate
);
242 storage
.forget(aRetval
);
249 LocalStorageManager::PrecacheStorage(nsIPrincipal
* aPrincipal
,
250 nsIPrincipal
* aStoragePrincipal
,
252 return GetStorageInternal(CreateMode::CreateIfShouldPreload
, nullptr,
253 aPrincipal
, aStoragePrincipal
, u
""_ns
, false,
258 LocalStorageManager::CreateStorage(mozIDOMWindow
* aWindow
,
259 nsIPrincipal
* aPrincipal
,
260 nsIPrincipal
* aStoragePrincipal
,
261 const nsAString
& aDocumentURI
, bool aPrivate
,
263 return GetStorageInternal(CreateMode::CreateAlways
, aWindow
, aPrincipal
,
264 aStoragePrincipal
, aDocumentURI
, aPrivate
, aRetval
);
268 LocalStorageManager::GetStorage(mozIDOMWindow
* aWindow
,
269 nsIPrincipal
* aPrincipal
,
270 nsIPrincipal
* aStoragePrincipal
, bool aPrivate
,
272 return GetStorageInternal(CreateMode::UseIfExistsNeverCreate
, aWindow
,
273 aPrincipal
, aStoragePrincipal
, u
""_ns
, aPrivate
,
278 LocalStorageManager::CloneStorage(Storage
* aStorage
) {
279 // Cloning is supported only for sessionStorage
280 return NS_ERROR_NOT_IMPLEMENTED
;
284 LocalStorageManager::CheckStorage(nsIPrincipal
* aPrincipal
, Storage
* aStorage
,
286 MOZ_ASSERT(NS_IsMainThread());
287 MOZ_ASSERT(aPrincipal
);
288 MOZ_ASSERT(aStorage
);
291 // Only used by sessionStorage.
292 return NS_ERROR_NOT_IMPLEMENTED
;
296 LocalStorageManager::GetNextGenLocalStorageEnabled(bool* aResult
) {
297 MOZ_ASSERT(NS_IsMainThread());
300 *aResult
= NextGenLocalStorageEnabled();
305 LocalStorageManager::Preload(nsIPrincipal
* aPrincipal
, JSContext
* aContext
,
307 MOZ_ASSERT(NS_IsMainThread());
308 MOZ_ASSERT(aPrincipal
);
311 return NS_ERROR_NOT_IMPLEMENTED
;
315 LocalStorageManager::IsPreloaded(nsIPrincipal
* aPrincipal
, JSContext
* aContext
,
317 MOZ_ASSERT(NS_IsMainThread());
318 MOZ_ASSERT(aPrincipal
);
321 return NS_ERROR_NOT_IMPLEMENTED
;
325 LocalStorageManager::GetState(nsIPrincipal
* aPrincipal
, JSContext
* aContext
,
327 MOZ_ASSERT(NS_IsMainThread());
328 MOZ_ASSERT(aPrincipal
);
331 return NS_ERROR_NOT_IMPLEMENTED
;
334 void LocalStorageManager::ClearCaches(uint32_t aUnloadFlags
,
335 const OriginAttributesPattern
& aPattern
,
336 const nsACString
& aOriginScope
) {
337 for (const auto& cacheEntry
: mCaches
) {
339 DebugOnly
<bool> rv
= oa
.PopulateFromSuffix(cacheEntry
.GetKey());
341 if (!aPattern
.Matches(oa
)) {
342 // This table doesn't match the given origin attributes pattern
346 CacheOriginHashtable
* table
= cacheEntry
.GetWeak();
348 for (auto iter2
= table
->Iter(); !iter2
.Done(); iter2
.Next()) {
349 LocalStorageCache
* cache
= iter2
.Get()->cache();
351 if (aOriginScope
.IsEmpty() ||
352 StringBeginsWith(cache
->OriginNoSuffix(), aOriginScope
)) {
353 cache
->UnloadItems(aUnloadFlags
);
359 nsresult
LocalStorageManager::Observe(const char* aTopic
,
360 const nsAString
& aOriginAttributesPattern
,
361 const nsACString
& aOriginScope
) {
362 OriginAttributesPattern pattern
;
363 if (!pattern
.Init(aOriginAttributesPattern
)) {
364 NS_ERROR("Cannot parse origin attributes pattern");
365 return NS_ERROR_FAILURE
;
368 // Clear everything, caches + database
369 if (!strcmp(aTopic
, "cookie-cleared")) {
370 ClearCaches(LocalStorageCache::kUnloadComplete
, pattern
, ""_ns
);
374 // Clear everything, caches + database
375 if (!strcmp(aTopic
, "extension:purge-localStorage-caches")) {
376 ClearCaches(LocalStorageCache::kUnloadComplete
, pattern
, aOriginScope
);
380 if (!strcmp(aTopic
, "browser:purge-sessionStorage")) {
381 // This is only meant for SessionStorageManager.
385 // Clear from caches everything that has been stored
386 // while in session-only mode
387 if (!strcmp(aTopic
, "session-only-cleared")) {
388 ClearCaches(LocalStorageCache::kUnloadSession
, pattern
, aOriginScope
);
392 // Clear all private-browsing caches
393 if (!strcmp(aTopic
, "private-browsing-data-cleared")) {
394 ClearCaches(LocalStorageCache::kUnloadComplete
, pattern
, ""_ns
);
398 // Clear localStorage data belonging to an origin pattern
399 if (!strcmp(aTopic
, "clear-origin-attributes-data") ||
400 !strcmp(aTopic
, "dom-storage:clear-origin-attributes-data")) {
401 ClearCaches(LocalStorageCache::kUnloadComplete
, pattern
, ""_ns
);
405 if (!strcmp(aTopic
, "profile-change")) {
406 // For case caches are still referenced - clear them completely
407 ClearCaches(LocalStorageCache::kUnloadComplete
, pattern
, ""_ns
);
412 #ifdef DOM_STORAGE_TESTS
413 if (!strcmp(aTopic
, "test-reload")) {
414 // This immediately completely reloads all caches from the database.
415 ClearCaches(LocalStorageCache::kTestReload
, pattern
, ""_ns
);
419 if (!strcmp(aTopic
, "test-flushed")) {
420 if (!XRE_IsParentProcess()) {
421 nsCOMPtr
<nsIObserverService
> obs
=
422 mozilla::services::GetObserverService();
424 obs
->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
432 NS_ERROR("Unexpected topic");
433 return NS_ERROR_UNEXPECTED
;
437 LocalStorageManager
* LocalStorageManager::Self() {
438 MOZ_ASSERT(!NextGenLocalStorageEnabled());
443 LocalStorageManager
* LocalStorageManager::Ensure() {
444 MOZ_ASSERT(!NextGenLocalStorageEnabled());
450 // Cause sSelf to be populated.
451 nsCOMPtr
<nsIDOMStorageManager
> initializer
=
452 do_GetService("@mozilla.org/dom/localStorage-manager;1");
453 MOZ_ASSERT(sSelf
, "Didn't initialize?");
458 } // namespace mozilla::dom