Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / dom / storage / LocalStorageManager.cpp
blob51ff3e4d0cec9b79f1e4072bbbaf24317f2f3977
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"
17 #include "nsNetCID.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;
34 // static
35 uint32_t LocalStorageManager::GetOriginQuota() {
36 return StaticPrefs::dom_storage_default_quota() * 1024; // pref is in kBs
39 // static
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();
53 NS_ASSERTION(
54 observer,
55 "No StorageObserver, cannot observe private data delete notifications!");
57 if (observer) {
58 observer->AddSink(this);
61 NS_ASSERTION(!sSelf,
62 "Somebody is trying to "
63 "do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
64 sSelf = this;
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();
78 if (observer) {
79 observer->RemoveSink(this);
82 sSelf = nullptr;
85 // static
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.
92 nsAutoCString scope;
93 scope.Append(aOriginSuffix);
94 scope.Append(':');
95 scope.Append(aOriginNoSuffix);
96 return scope;
99 LocalStorageCache* LocalStorageManager::GetCache(
100 const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) {
101 CacheOriginHashtable* table = mCaches.GetOrInsertNew(aOriginSuffix);
102 LocalStorageCacheHashKey* entry = table->GetEntry(aOriginNoSuffix);
103 if (!entry) {
104 return nullptr;
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);
117 if (storageChild) {
118 storageChild->AsyncGetUsage(usage);
121 return usage;
122 }));
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()) {
139 NS_WARNING(
140 "StorageManager::DropCache called on a non-main thread, shutting "
141 "down?");
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
171 if (!cache) {
172 if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
173 *aRetval = nullptr;
174 return NS_OK;
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);
184 if (db) {
185 if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(
186 originAttrSuffix, originKey))) {
187 return NS_OK;
189 } else {
190 if (originKey.EqualsLiteral("knalb.:about")) {
191 return NS_OK;
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,
205 &principalInfo);
206 if (NS_WARN_IF(NS_FAILED(rv))) {
207 return rv;
210 uint32_t privateBrowsingId;
211 rv = aStoragePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
212 if (NS_WARN_IF(NS_FAILED(rv))) {
213 return rv;
216 if (!backgroundActor->CanSend()) {
217 return NS_ERROR_FAILURE;
219 #endif
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);
228 MOZ_ALWAYS_TRUE(
229 backgroundActor->SendPBackgroundLocalStorageCacheConstructor(
230 actor, principalInfo, originKey, privateBrowsingId));
232 cache->SetActor(actor);
233 #endif
236 if (aRetval) {
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);
245 return NS_OK;
248 NS_IMETHODIMP
249 LocalStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
250 nsIPrincipal* aStoragePrincipal,
251 Storage** aRetval) {
252 return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
253 aPrincipal, aStoragePrincipal, u""_ns, false,
254 aRetval);
257 NS_IMETHODIMP
258 LocalStorageManager::CreateStorage(mozIDOMWindow* aWindow,
259 nsIPrincipal* aPrincipal,
260 nsIPrincipal* aStoragePrincipal,
261 const nsAString& aDocumentURI, bool aPrivate,
262 Storage** aRetval) {
263 return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
264 aStoragePrincipal, aDocumentURI, aPrivate, aRetval);
267 NS_IMETHODIMP
268 LocalStorageManager::GetStorage(mozIDOMWindow* aWindow,
269 nsIPrincipal* aPrincipal,
270 nsIPrincipal* aStoragePrincipal, bool aPrivate,
271 Storage** aRetval) {
272 return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
273 aPrincipal, aStoragePrincipal, u""_ns, aPrivate,
274 aRetval);
277 NS_IMETHODIMP
278 LocalStorageManager::CloneStorage(Storage* aStorage) {
279 // Cloning is supported only for sessionStorage
280 return NS_ERROR_NOT_IMPLEMENTED;
283 NS_IMETHODIMP
284 LocalStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
285 bool* aRetval) {
286 MOZ_ASSERT(NS_IsMainThread());
287 MOZ_ASSERT(aPrincipal);
288 MOZ_ASSERT(aStorage);
289 MOZ_ASSERT(aRetval);
291 // Only used by sessionStorage.
292 return NS_ERROR_NOT_IMPLEMENTED;
295 NS_IMETHODIMP
296 LocalStorageManager::GetNextGenLocalStorageEnabled(bool* aResult) {
297 MOZ_ASSERT(NS_IsMainThread());
298 MOZ_ASSERT(aResult);
300 *aResult = NextGenLocalStorageEnabled();
301 return NS_OK;
304 NS_IMETHODIMP
305 LocalStorageManager::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
306 Promise** _retval) {
307 MOZ_ASSERT(NS_IsMainThread());
308 MOZ_ASSERT(aPrincipal);
309 MOZ_ASSERT(_retval);
311 return NS_ERROR_NOT_IMPLEMENTED;
314 NS_IMETHODIMP
315 LocalStorageManager::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
316 Promise** _retval) {
317 MOZ_ASSERT(NS_IsMainThread());
318 MOZ_ASSERT(aPrincipal);
319 MOZ_ASSERT(_retval);
321 return NS_ERROR_NOT_IMPLEMENTED;
324 NS_IMETHODIMP
325 LocalStorageManager::GetState(nsIPrincipal* aPrincipal, JSContext* aContext,
326 Promise** _retval) {
327 MOZ_ASSERT(NS_IsMainThread());
328 MOZ_ASSERT(aPrincipal);
329 MOZ_ASSERT(_retval);
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) {
338 OriginAttributes oa;
339 DebugOnly<bool> rv = oa.PopulateFromSuffix(cacheEntry.GetKey());
340 MOZ_ASSERT(rv);
341 if (!aPattern.Matches(oa)) {
342 // This table doesn't match the given origin attributes pattern
343 continue;
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);
371 return NS_OK;
374 // Clear everything, caches + database
375 if (!strcmp(aTopic, "extension:purge-localStorage-caches")) {
376 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, aOriginScope);
377 return NS_OK;
380 if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
381 // This is only meant for SessionStorageManager.
382 return NS_OK;
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);
389 return NS_OK;
392 // Clear all private-browsing caches
393 if (!strcmp(aTopic, "private-browsing-data-cleared")) {
394 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns);
395 return NS_OK;
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);
402 return NS_OK;
405 if (!strcmp(aTopic, "profile-change")) {
406 // For case caches are still referenced - clear them completely
407 ClearCaches(LocalStorageCache::kUnloadComplete, pattern, ""_ns);
408 mCaches.Clear();
409 return NS_OK;
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);
416 return NS_OK;
419 if (!strcmp(aTopic, "test-flushed")) {
420 if (!XRE_IsParentProcess()) {
421 nsCOMPtr<nsIObserverService> obs =
422 mozilla::services::GetObserverService();
423 if (obs) {
424 obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
428 return NS_OK;
430 #endif
432 NS_ERROR("Unexpected topic");
433 return NS_ERROR_UNEXPECTED;
436 // static
437 LocalStorageManager* LocalStorageManager::Self() {
438 MOZ_ASSERT(!NextGenLocalStorageEnabled());
440 return sSelf;
443 LocalStorageManager* LocalStorageManager::Ensure() {
444 MOZ_ASSERT(!NextGenLocalStorageEnabled());
446 if (sSelf) {
447 return sSelf;
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?");
455 return sSelf;
458 } // namespace mozilla::dom