Coverage Report

Created: 2018-09-25 14:53

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