Source code

Revision control

Other Tools

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/. */
6
7
#include "LocalStorageManager.h"
8
#include "LocalStorage.h"
9
#include "StorageDBThread.h"
10
#include "StorageUtils.h"
11
12
#include "nsIEffectiveTLDService.h"
13
14
#include "nsNetUtil.h"
15
#include "nsNetCID.h"
16
#include "nsPrintfCString.h"
17
#include "nsXULAppAPI.h"
18
#include "nsThreadUtils.h"
19
#include "nsIObserverService.h"
20
#include "mozilla/Services.h"
21
#include "mozilla/StaticPrefs_dom.h"
22
#include "mozilla/dom/LocalStorageCommon.h"
23
24
namespace mozilla {
25
namespace dom {
26
27
using namespace StorageUtils;
28
29
LocalStorageManager* LocalStorageManager::sSelf = nullptr;
30
31
// static
32
uint32_t LocalStorageManager::GetQuota() {
33
return StaticPrefs::dom_storage_default_quota() * 1024; // pref is in kBs
34
}
35
36
NS_IMPL_ISUPPORTS(LocalStorageManager, nsIDOMStorageManager,
37
nsILocalStorageManager)
38
39
LocalStorageManager::LocalStorageManager() : mCaches(8) {
40
MOZ_ASSERT(!NextGenLocalStorageEnabled());
41
42
StorageObserver* observer = StorageObserver::Self();
43
NS_ASSERTION(
44
observer,
45
"No StorageObserver, cannot observe private data delete notifications!");
46
47
if (observer) {
48
observer->AddSink(this);
49
}
50
51
NS_ASSERTION(!sSelf,
52
"Somebody is trying to "
53
"do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
54
sSelf = this;
55
56
if (!XRE_IsParentProcess()) {
57
// Do this only on the child process. The thread IPC bridge
58
// is also used to communicate chrome observer notifications.
59
// Note: must be called after we set sSelf
60
StorageDBChild::GetOrCreate();
61
}
62
}
63
64
LocalStorageManager::~LocalStorageManager() {
65
StorageObserver* observer = StorageObserver::Self();
66
if (observer) {
67
observer->RemoveSink(this);
68
}
69
70
sSelf = nullptr;
71
}
72
73
// static
74
nsAutoCString LocalStorageManager::CreateOrigin(
75
const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) {
76
// Note: some hard-coded sqlite statements are dependent on the format this
77
// method returns. Changing this without updating those sqlite statements
78
// will cause malfunction.
79
80
nsAutoCString scope;
81
scope.Append(aOriginSuffix);
82
scope.Append(':');
83
scope.Append(aOriginNoSuffix);
84
return scope;
85
}
86
87
LocalStorageCache* LocalStorageManager::GetCache(
88
const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) {
89
CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
90
LocalStorageCacheHashKey* entry = table->GetEntry(aOriginNoSuffix);
91
if (!entry) {
92
return nullptr;
93
}
94
95
return entry->cache();
96
}
97
98
already_AddRefed<StorageUsage> LocalStorageManager::GetOriginUsage(
99
const nsACString& aOriginNoSuffix) {
100
RefPtr<StorageUsage> usage;
101
if (mUsages.Get(aOriginNoSuffix, &usage)) {
102
return usage.forget();
103
}
104
105
usage = new StorageUsage(aOriginNoSuffix);
106
107
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
108
if (storageChild) {
109
storageChild->AsyncGetUsage(usage);
110
}
111
112
mUsages.Put(aOriginNoSuffix, usage);
113
114
return usage.forget();
115
}
116
117
already_AddRefed<LocalStorageCache> LocalStorageManager::PutCache(
118
const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix,
119
nsIPrincipal* aPrincipal) {
120
CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
121
LocalStorageCacheHashKey* entry = table->PutEntry(aOriginNoSuffix);
122
RefPtr<LocalStorageCache> cache = entry->cache();
123
124
nsAutoCString quotaOrigin;
125
aPrincipal->GetLocalStorageQuotaKey(quotaOrigin);
126
127
// Lifetime handled by the cache, do persist
128
cache->Init(this, true, aPrincipal, quotaOrigin);
129
return cache.forget();
130
}
131
132
void LocalStorageManager::DropCache(LocalStorageCache* aCache) {
133
if (!NS_IsMainThread()) {
134
NS_WARNING(
135
"StorageManager::DropCache called on a non-main thread, shutting "
136
"down?");
137
}
138
139
CacheOriginHashtable* table = mCaches.LookupOrAdd(aCache->OriginSuffix());
140
table->RemoveEntry(aCache->OriginNoSuffix());
141
}
142
143
nsresult LocalStorageManager::GetStorageInternal(
144
CreateMode aCreateMode, mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal,
145
nsIPrincipal* aStoragePrincipal, const nsAString& aDocumentURI,
146
bool aPrivate, Storage** aRetval) {
147
nsAutoCString originAttrSuffix;
148
nsAutoCString originKey;
149
150
nsresult rv = aStoragePrincipal->GetStorageOriginKey(originKey);
151
aStoragePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
152
if (NS_FAILED(rv)) {
153
return NS_ERROR_NOT_AVAILABLE;
154
}
155
156
RefPtr<LocalStorageCache> cache = GetCache(originAttrSuffix, originKey);
157
158
// Get or create a cache for the given scope
159
if (!cache) {
160
if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
161
*aRetval = nullptr;
162
return NS_OK;
163
}
164
165
if (aCreateMode == CreateMode::CreateIfShouldPreload) {
166
// This is a demand to just preload the cache, if the scope has
167
// no data stored, bypass creation and preload of the cache.
168
StorageDBChild* db = StorageDBChild::Get();
169
if (db) {
170
if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(
171
originAttrSuffix, originKey))) {
172
return NS_OK;
173
}
174
} else {
175
if (originKey.EqualsLiteral("knalb.:about")) {
176
return NS_OK;
177
}
178
}
179
}
180
181
#if !defined(MOZ_WIDGET_ANDROID)
182
PBackgroundChild* backgroundActor =
183
BackgroundChild::GetOrCreateForCurrentThread();
184
if (NS_WARN_IF(!backgroundActor)) {
185
return NS_ERROR_FAILURE;
186
}
187
188
PrincipalInfo principalInfo;
189
rv = mozilla::ipc::PrincipalToPrincipalInfo(aStoragePrincipal,
190
&principalInfo);
191
if (NS_WARN_IF(NS_FAILED(rv))) {
192
return rv;
193
}
194
195
uint32_t privateBrowsingId;
196
rv = aPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
197
if (NS_WARN_IF(NS_FAILED(rv))) {
198
return rv;
199
}
200
#endif
201
202
// There is always a single instance of a cache per scope
203
// in a single instance of a DOM storage manager.
204
cache = PutCache(originAttrSuffix, originKey, aStoragePrincipal);
205
206
#if !defined(MOZ_WIDGET_ANDROID)
207
LocalStorageCacheChild* actor = new LocalStorageCacheChild(cache);
208
209
MOZ_ALWAYS_TRUE(
210
backgroundActor->SendPBackgroundLocalStorageCacheConstructor(
211
actor, principalInfo, originKey, privateBrowsingId));
212
213
cache->SetActor(actor);
214
#endif
215
}
216
217
if (aRetval) {
218
nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
219
220
RefPtr<Storage> storage =
221
new LocalStorage(inner, this, cache, aDocumentURI, aPrincipal,
222
aStoragePrincipal, aPrivate);
223
storage.forget(aRetval);
224
}
225
226
return NS_OK;
227
}
228
229
NS_IMETHODIMP
230
LocalStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
231
nsIPrincipal* aStoragePrincipal,
232
Storage** aRetval) {
233
return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
234
aPrincipal, aStoragePrincipal, EmptyString(), false,
235
aRetval);
236
}
237
238
NS_IMETHODIMP
239
LocalStorageManager::CreateStorage(mozIDOMWindow* aWindow,
240
nsIPrincipal* aPrincipal,
241
nsIPrincipal* aStoragePrincipal,
242
const nsAString& aDocumentURI, bool aPrivate,
243
Storage** aRetval) {
244
return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
245
aStoragePrincipal, aDocumentURI, aPrivate, aRetval);
246
}
247
248
NS_IMETHODIMP
249
LocalStorageManager::GetStorage(mozIDOMWindow* aWindow,
250
nsIPrincipal* aPrincipal,
251
nsIPrincipal* aStoragePrincipal, bool aPrivate,
252
Storage** aRetval) {
253
return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
254
aPrincipal, aStoragePrincipal, EmptyString(),
255
aPrivate, aRetval);
256
}
257
258
NS_IMETHODIMP
259
LocalStorageManager::CloneStorage(Storage* aStorage) {
260
// Cloning is supported only for sessionStorage
261
return NS_ERROR_NOT_IMPLEMENTED;
262
}
263
264
NS_IMETHODIMP
265
LocalStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
266
bool* aRetval) {
267
MOZ_ASSERT(NS_IsMainThread());
268
MOZ_ASSERT(aPrincipal);
269
MOZ_ASSERT(aStorage);
270
MOZ_ASSERT(aRetval);
271
272
// Only used by sessionStorage.
273
return NS_ERROR_NOT_IMPLEMENTED;
274
}
275
276
NS_IMETHODIMP
277
LocalStorageManager::GetNextGenLocalStorageEnabled(bool* aResult) {
278
MOZ_ASSERT(NS_IsMainThread());
279
MOZ_ASSERT(aResult);
280
281
*aResult = NextGenLocalStorageEnabled();
282
return NS_OK;
283
}
284
285
NS_IMETHODIMP
286
LocalStorageManager::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
287
nsISupports** _retval) {
288
MOZ_ASSERT(NS_IsMainThread());
289
MOZ_ASSERT(aPrincipal);
290
MOZ_ASSERT(_retval);
291
292
return NS_ERROR_NOT_IMPLEMENTED;
293
}
294
295
NS_IMETHODIMP
296
LocalStorageManager::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
297
nsISupports** _retval) {
298
MOZ_ASSERT(NS_IsMainThread());
299
MOZ_ASSERT(aPrincipal);
300
MOZ_ASSERT(_retval);
301
302
return NS_ERROR_NOT_IMPLEMENTED;
303
}
304
305
void LocalStorageManager::ClearCaches(uint32_t aUnloadFlags,
306
const OriginAttributesPattern& aPattern,
307
const nsACString& aOriginScope) {
308
for (auto iter1 = mCaches.Iter(); !iter1.Done(); iter1.Next()) {
309
OriginAttributes oa;
310
DebugOnly<bool> rv = oa.PopulateFromSuffix(iter1.Key());
311
MOZ_ASSERT(rv);
312
if (!aPattern.Matches(oa)) {
313
// This table doesn't match the given origin attributes pattern
314
continue;
315
}
316
317
CacheOriginHashtable* table = iter1.UserData();
318
319
for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
320
LocalStorageCache* cache = iter2.Get()->cache();
321
322
if (aOriginScope.IsEmpty() ||
323
StringBeginsWith(cache->OriginNoSuffix(), aOriginScope)) {
324
cache->UnloadItems(aUnloadFlags);
325
}
326
}
327
}
328
}
329
330
nsresult LocalStorageManager::Observe(const char* aTopic,
331
const nsAString& aOriginAttributesPattern,
332
const nsACString& aOriginScope) {
333
OriginAttributesPattern pattern;
334
if (!pattern.Init(aOriginAttributesPattern)) {
335
NS_ERROR("Cannot parse origin attributes pattern");
336
return NS_ERROR_FAILURE;
337
}
338
339
// Clear everything, caches + database
340
if (!strcmp(aTopic, "cookie-cleared")) {
341
ClearCaches(LocalStorageCache::kUnloadComplete, pattern, EmptyCString());
342
return NS_OK;
343
}
344
345
// Clear everything, caches + database
346
if (!strcmp(aTopic, "extension:purge-localStorage-caches")) {
347
ClearCaches(LocalStorageCache::kUnloadComplete, pattern, aOriginScope);
348
return NS_OK;
349
}
350
351
if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
352
// This is only meant for SessionStorageManager.
353
return NS_OK;
354
}
355
356
// Clear from caches everything that has been stored
357
// while in session-only mode
358
if (!strcmp(aTopic, "session-only-cleared")) {
359
ClearCaches(LocalStorageCache::kUnloadSession, pattern, aOriginScope);
360
return NS_OK;
361
}
362
363
// Clear all private-browsing caches
364
if (!strcmp(aTopic, "private-browsing-data-cleared")) {
365
ClearCaches(LocalStorageCache::kUnloadPrivate, pattern, EmptyCString());
366
return NS_OK;
367
}
368
369
// Clear localStorage data beloging to an origin pattern
370
if (!strcmp(aTopic, "origin-attr-pattern-cleared")) {
371
ClearCaches(LocalStorageCache::kUnloadComplete, pattern, EmptyCString());
372
return NS_OK;
373
}
374
375
if (!strcmp(aTopic, "profile-change")) {
376
// For case caches are still referenced - clear them completely
377
ClearCaches(LocalStorageCache::kUnloadComplete, pattern, EmptyCString());
378
mCaches.Clear();
379
return NS_OK;
380
}
381
382
#ifdef DOM_STORAGE_TESTS
383
if (!strcmp(aTopic, "test-reload")) {
384
// This immediately completely reloads all caches from the database.
385
ClearCaches(LocalStorageCache::kTestReload, pattern, EmptyCString());
386
return NS_OK;
387
}
388
389
if (!strcmp(aTopic, "test-flushed")) {
390
if (!XRE_IsParentProcess()) {
391
nsCOMPtr<nsIObserverService> obs =
392
mozilla::services::GetObserverService();
393
if (obs) {
394
obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
395
}
396
}
397
398
return NS_OK;
399
}
400
#endif
401
402
NS_ERROR("Unexpected topic");
403
return NS_ERROR_UNEXPECTED;
404
}
405
406
// static
407
LocalStorageManager* LocalStorageManager::Self() {
408
MOZ_ASSERT(!NextGenLocalStorageEnabled());
409
410
return sSelf;
411
}
412
413
LocalStorageManager* LocalStorageManager::Ensure() {
414
MOZ_ASSERT(!NextGenLocalStorageEnabled());
415
416
if (sSelf) {
417
return sSelf;
418
}
419
420
// Cause sSelf to be populated.
421
nsCOMPtr<nsIDOMStorageManager> initializer =
422
do_GetService("@mozilla.org/dom/localStorage-manager;1");
423
MOZ_ASSERT(sSelf, "Didn't initialize?");
424
425
return sSelf;
426
}
427
428
} // namespace dom
429
} // namespace mozilla