Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/storage/LocalStorageCache.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 "LocalStorageCache.h"
8
9
#include "Storage.h"
10
#include "StorageDBThread.h"
11
#include "StorageIPC.h"
12
#include "StorageUtils.h"
13
#include "LocalStorageManager.h"
14
15
#include "nsAutoPtr.h"
16
#include "nsDOMString.h"
17
#include "nsXULAppAPI.h"
18
#include "mozilla/Unused.h"
19
#include "nsProxyRelease.h"
20
#include "nsThreadUtils.h"
21
22
namespace mozilla {
23
namespace dom {
24
25
#define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
26
27
namespace {
28
29
const uint32_t kDefaultSet = 0;
30
const uint32_t kPrivateSet = 1;
31
const uint32_t kSessionSet = 2;
32
33
inline uint32_t
34
GetDataSetIndex(bool aPrivate, bool aSessionOnly)
35
0
{
36
0
  if (aPrivate) {
37
0
    return kPrivateSet;
38
0
  }
39
0
40
0
  if (aSessionOnly) {
41
0
    return kSessionSet;
42
0
  }
43
0
44
0
  return kDefaultSet;
45
0
}
46
47
inline uint32_t
48
GetDataSetIndex(const LocalStorage* aStorage)
49
0
{
50
0
  return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
51
0
}
52
53
} // namespace
54
55
// LocalStorageCacheBridge
56
57
NS_IMPL_ADDREF(LocalStorageCacheBridge)
58
59
// Since there is no consumer of return value of Release, we can turn this
60
// method to void to make implementation of asynchronous
61
// LocalStorageCache::Release much simpler.
62
NS_IMETHODIMP_(void) LocalStorageCacheBridge::Release(void)
63
0
{
64
0
  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
65
0
  nsrefcnt count = --mRefCnt;
66
0
  NS_LOG_RELEASE(this, count, "LocalStorageCacheBridge");
67
0
  if (0 == count) {
68
0
    mRefCnt = 1; /* stabilize */
69
0
    /* enable this to find non-threadsafe destructors: */
70
0
    /* NS_ASSERT_OWNINGTHREAD(_class); */
71
0
    delete (this);
72
0
  }
73
0
}
74
75
// LocalStorageCache
76
77
LocalStorageCache::LocalStorageCache(const nsACString* aOriginNoSuffix)
78
  : mActor(nullptr)
79
  , mOriginNoSuffix(*aOriginNoSuffix)
80
  , mMonitor("LocalStorageCache")
81
  , mLoaded(false)
82
  , mLoadResult(NS_OK)
83
  , mInitialized(false)
84
  , mPersistent(false)
85
  , mSessionOnlyDataSetActive(false)
86
  , mPreloadTelemetryRecorded(false)
87
0
{
88
0
  MOZ_COUNT_CTOR(LocalStorageCache);
89
0
}
90
91
LocalStorageCache::~LocalStorageCache()
92
0
{
93
0
  if (mActor) {
94
0
    mActor->SendDeleteMeInternal();
95
0
    MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
96
0
  }
97
0
98
0
  if (mManager) {
99
0
    mManager->DropCache(this);
100
0
  }
101
0
102
0
  MOZ_COUNT_DTOR(LocalStorageCache);
103
0
}
104
105
void
106
LocalStorageCache::SetActor(LocalStorageCacheChild* aActor)
107
0
{
108
0
  AssertIsOnOwningThread();
109
0
  MOZ_ASSERT(aActor);
110
0
  MOZ_ASSERT(!mActor);
111
0
112
0
  mActor = aActor;
113
0
}
114
115
NS_IMETHODIMP_(void)
116
LocalStorageCache::Release(void)
117
0
{
118
0
  // We must actually release on the main thread since the cache removes it
119
0
  // self from the manager's hash table.  And we don't want to lock access to
120
0
  // that hash table.
121
0
  if (NS_IsMainThread()) {
122
0
    LocalStorageCacheBridge::Release();
123
0
    return;
124
0
  }
125
0
126
0
  RefPtr<nsRunnableMethod<LocalStorageCacheBridge, void, false>> event =
127
0
    NewNonOwningRunnableMethod("dom::LocalStorageCacheBridge::Release",
128
0
                               static_cast<LocalStorageCacheBridge*>(this),
129
0
                               &LocalStorageCacheBridge::Release);
130
0
131
0
  nsresult rv = NS_DispatchToMainThread(event);
132
0
  if (NS_FAILED(rv)) {
133
0
    NS_WARNING("LocalStorageCache::Release() on a non-main thread");
134
0
    LocalStorageCacheBridge::Release();
135
0
  }
136
0
}
137
138
void
139
LocalStorageCache::Init(LocalStorageManager* aManager,
140
                   bool aPersistent,
141
                   nsIPrincipal* aPrincipal,
142
                   const nsACString& aQuotaOriginScope)
143
0
{
144
0
  if (mInitialized) {
145
0
    return;
146
0
  }
147
0
148
0
  mInitialized = true;
149
0
  aPrincipal->OriginAttributesRef().CreateSuffix(mOriginSuffix);
150
0
  mPersistent = aPersistent;
151
0
  if (aQuotaOriginScope.IsEmpty()) {
152
0
    mQuotaOriginScope = Origin();
153
0
  } else {
154
0
    mQuotaOriginScope = aQuotaOriginScope;
155
0
  }
156
0
157
0
  if (mPersistent) {
158
0
    mManager = aManager;
159
0
    Preload();
160
0
  }
161
0
162
0
  // Check the quota string has (or has not) the identical origin suffix as
163
0
  // this storage cache is bound to.
164
0
  MOZ_ASSERT(StringBeginsWith(mQuotaOriginScope, mOriginSuffix));
165
0
  MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope,
166
0
                                                         NS_LITERAL_CSTRING("^")));
167
0
168
0
  mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
169
0
}
170
171
void
172
LocalStorageCache::NotifyObservers(const LocalStorage* aStorage,
173
                                   const nsString& aKey,
174
                                   const nsString& aOldValue,
175
                                   const nsString& aNewValue)
176
0
{
177
0
  AssertIsOnOwningThread();
178
0
  MOZ_ASSERT(aStorage);
179
0
180
0
  if (!mActor) {
181
0
    return;
182
0
  }
183
0
184
0
  // We want to send a message to the parent in order to broadcast the
185
0
  // StorageEvent correctly to any child process.
186
0
187
0
  Unused << mActor->SendNotify(aStorage->DocumentURI(),
188
0
                               aKey,
189
0
                               aOldValue,
190
0
                               aNewValue);
191
0
}
192
193
inline bool
194
LocalStorageCache::Persist(const LocalStorage* aStorage) const
195
0
{
196
0
  return mPersistent &&
197
0
         !aStorage->IsSessionOnly() &&
198
0
         !aStorage->IsPrivate();
199
0
}
200
201
const nsCString
202
LocalStorageCache::Origin() const
203
0
{
204
0
  return LocalStorageManager::CreateOrigin(mOriginSuffix, mOriginNoSuffix);
205
0
}
206
207
LocalStorageCache::Data&
208
LocalStorageCache::DataSet(const LocalStorage* aStorage)
209
0
{
210
0
  uint32_t index = GetDataSetIndex(aStorage);
211
0
212
0
  if (index == kSessionSet && !mSessionOnlyDataSetActive) {
213
0
    // Session only data set is demanded but not filled with
214
0
    // current data set, copy to session only set now.
215
0
216
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
217
0
218
0
    Data& defaultSet = mData[kDefaultSet];
219
0
    Data& sessionSet = mData[kSessionSet];
220
0
221
0
    for (auto iter = defaultSet.mKeys.Iter(); !iter.Done(); iter.Next()) {
222
0
      sessionSet.mKeys.Put(iter.Key(), iter.UserData());
223
0
    }
224
0
225
0
    mSessionOnlyDataSetActive = true;
226
0
227
0
    // This updates sessionSet.mOriginQuotaUsage and also updates global usage
228
0
    // for all session only data
229
0
    ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
230
0
  }
231
0
232
0
  return mData[index];
233
0
}
234
235
bool
236
LocalStorageCache::ProcessUsageDelta(const LocalStorage* aStorage,
237
                                     int64_t aDelta,
238
                                     const MutationSource aSource)
239
0
{
240
0
  return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta, aSource);
241
0
}
242
243
bool
244
LocalStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex,
245
                                     const int64_t aDelta,
246
                                     const MutationSource aSource)
247
0
{
248
0
  // Check limit per this origin
249
0
  Data& data = mData[aGetDataSetIndex];
250
0
  uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
251
0
  if (aSource == ContentMutation &&
252
0
      aDelta > 0 && newOriginUsage > LocalStorageManager::GetQuota()) {
253
0
    return false;
254
0
  }
255
0
256
0
  // Now check eTLD+1 limit
257
0
  if (mUsage &&
258
0
      !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta, aSource)) {
259
0
    return false;
260
0
  }
261
0
262
0
  // Update size in our data set
263
0
  data.mOriginQuotaUsage = newOriginUsage;
264
0
  return true;
265
0
}
266
267
void
268
LocalStorageCache::Preload()
269
0
{
270
0
  if (mLoaded || !mPersistent) {
271
0
    return;
272
0
  }
273
0
274
0
  StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
275
0
  if (!storageChild) {
276
0
    mLoaded = true;
277
0
    mLoadResult = NS_ERROR_FAILURE;
278
0
    return;
279
0
  }
280
0
281
0
  storageChild->AsyncPreload(this);
282
0
}
283
284
void
285
LocalStorageCache::WaitForPreload(Telemetry::HistogramID aTelemetryID)
286
0
{
287
0
  if (!mPersistent) {
288
0
    return;
289
0
  }
290
0
291
0
  bool loaded = mLoaded;
292
0
293
0
  // Telemetry of rates of pending preloads
294
0
  if (!mPreloadTelemetryRecorded) {
295
0
    mPreloadTelemetryRecorded = true;
296
0
    Telemetry::Accumulate(
297
0
      Telemetry::LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS,
298
0
      !loaded);
299
0
  }
300
0
301
0
  if (loaded) {
302
0
    return;
303
0
  }
304
0
305
0
  // Measure which operation blocks and for how long
306
0
  Telemetry::RuntimeAutoTimer timer(aTelemetryID);
307
0
308
0
  // If preload already started (i.e. we got some first data, but not all)
309
0
  // SyncPreload will just wait for it to finish rather then synchronously
310
0
  // read from the database.  It seems to me more optimal.
311
0
312
0
  // TODO place for A/B testing (force main thread load vs. let preload finish)
313
0
314
0
  // No need to check sDatabase for being non-null since preload is either
315
0
  // done before we've shut the DB down or when the DB could not start,
316
0
  // preload has not even be started.
317
0
  StorageDBChild::Get()->SyncPreload(this);
318
0
}
319
320
nsresult
321
LocalStorageCache::GetLength(const LocalStorage* aStorage, uint32_t* aRetval)
322
0
{
323
0
  if (Persist(aStorage)) {
324
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
325
0
    if (NS_FAILED(mLoadResult)) {
326
0
      return mLoadResult;
327
0
    }
328
0
  }
329
0
330
0
  *aRetval = DataSet(aStorage).mKeys.Count();
331
0
  return NS_OK;
332
0
}
333
334
nsresult
335
LocalStorageCache::GetKey(const LocalStorage* aStorage, uint32_t aIndex,
336
                           nsAString& aRetval)
337
0
{
338
0
  // XXX: This does a linear search for the key at index, which would
339
0
  // suck if there's a large numer of indexes. Do we care? If so,
340
0
  // maybe we need to have a lazily populated key array here or
341
0
  // something?
342
0
  if (Persist(aStorage)) {
343
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
344
0
    if (NS_FAILED(mLoadResult)) {
345
0
      return mLoadResult;
346
0
    }
347
0
  }
348
0
349
0
  aRetval.SetIsVoid(true);
350
0
  for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) {
351
0
    if (aIndex == 0) {
352
0
      aRetval = iter.Key();
353
0
      break;
354
0
    }
355
0
    aIndex--;
356
0
  }
357
0
358
0
  return NS_OK;
359
0
}
360
361
void
362
LocalStorageCache::GetKeys(const LocalStorage* aStorage,
363
                           nsTArray<nsString>& aKeys)
364
0
{
365
0
  if (Persist(aStorage)) {
366
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
367
0
  }
368
0
369
0
  if (NS_FAILED(mLoadResult)) {
370
0
    return;
371
0
  }
372
0
373
0
  for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) {
374
0
    aKeys.AppendElement(iter.Key());
375
0
  }
376
0
}
377
378
nsresult
379
LocalStorageCache::GetItem(const LocalStorage* aStorage, const nsAString& aKey,
380
                           nsAString& aRetval)
381
0
{
382
0
  if (Persist(aStorage)) {
383
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
384
0
    if (NS_FAILED(mLoadResult)) {
385
0
      return mLoadResult;
386
0
    }
387
0
  }
388
0
389
0
  // not using AutoString since we don't want to copy buffer to result
390
0
  nsString value;
391
0
  if (!DataSet(aStorage).mKeys.Get(aKey, &value)) {
392
0
    SetDOMStringToNull(value);
393
0
  }
394
0
395
0
  aRetval = value;
396
0
397
0
  return NS_OK;
398
0
}
399
400
nsresult
401
LocalStorageCache::SetItem(const LocalStorage* aStorage, const nsAString& aKey,
402
                           const nsString& aValue, nsString& aOld,
403
                           const MutationSource aSource)
404
0
{
405
0
  // Size of the cache that will change after this action.
406
0
  int64_t delta = 0;
407
0
408
0
  if (Persist(aStorage)) {
409
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
410
0
    if (NS_FAILED(mLoadResult)) {
411
0
      return mLoadResult;
412
0
    }
413
0
  }
414
0
415
0
  Data& data = DataSet(aStorage);
416
0
  if (!data.mKeys.Get(aKey, &aOld)) {
417
0
    SetDOMStringToNull(aOld);
418
0
419
0
    // We only consider key size if the key doesn't exist before.
420
0
    delta += static_cast<int64_t>(aKey.Length());
421
0
  }
422
0
423
0
  delta += static_cast<int64_t>(aValue.Length()) -
424
0
           static_cast<int64_t>(aOld.Length());
425
0
426
0
  if (!ProcessUsageDelta(aStorage, delta, aSource)) {
427
0
    return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
428
0
  }
429
0
430
0
  if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
431
0
    return NS_SUCCESS_DOM_NO_OPERATION;
432
0
  }
433
0
434
0
  data.mKeys.Put(aKey, aValue);
435
0
436
0
  if (aSource != ContentMutation) {
437
0
    return NS_OK;
438
0
  }
439
0
440
0
#if !defined(MOZ_WIDGET_ANDROID)
441
0
  NotifyObservers(aStorage, nsString(aKey), aOld, aValue);
442
0
#endif
443
0
444
0
  if (Persist(aStorage)) {
445
0
    StorageDBChild* storageChild = StorageDBChild::Get();
446
0
    if (!storageChild) {
447
0
      NS_ERROR("Writing to localStorage after the database has been shut down"
448
0
               ", data lose!");
449
0
      return NS_ERROR_NOT_INITIALIZED;
450
0
    }
451
0
452
0
    if (DOMStringIsNull(aOld)) {
453
0
      return storageChild->AsyncAddItem(this, aKey, aValue);
454
0
    }
455
0
456
0
    return storageChild->AsyncUpdateItem(this, aKey, aValue);
457
0
  }
458
0
459
0
  return NS_OK;
460
0
}
461
462
nsresult
463
LocalStorageCache::RemoveItem(const LocalStorage* aStorage,
464
                              const nsAString& aKey,
465
                              nsString& aOld, const MutationSource aSource)
466
0
{
467
0
  if (Persist(aStorage)) {
468
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
469
0
    if (NS_FAILED(mLoadResult)) {
470
0
      return mLoadResult;
471
0
    }
472
0
  }
473
0
474
0
  Data& data = DataSet(aStorage);
475
0
  if (!data.mKeys.Get(aKey, &aOld)) {
476
0
    SetDOMStringToNull(aOld);
477
0
    return NS_SUCCESS_DOM_NO_OPERATION;
478
0
  }
479
0
480
0
  // Recalculate the cached data size
481
0
  const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
482
0
                          static_cast<int64_t>(aKey.Length()));
483
0
  Unused << ProcessUsageDelta(aStorage, delta, aSource);
484
0
  data.mKeys.Remove(aKey);
485
0
486
0
  if (aSource != ContentMutation) {
487
0
    return NS_OK;
488
0
  }
489
0
490
0
#if !defined(MOZ_WIDGET_ANDROID)
491
0
  NotifyObservers(aStorage, nsString(aKey), aOld, VoidString());
492
0
#endif
493
0
494
0
  if (Persist(aStorage)) {
495
0
    StorageDBChild* storageChild = StorageDBChild::Get();
496
0
    if (!storageChild) {
497
0
      NS_ERROR("Writing to localStorage after the database has been shut down"
498
0
               ", data lose!");
499
0
      return NS_ERROR_NOT_INITIALIZED;
500
0
    }
501
0
502
0
    return storageChild->AsyncRemoveItem(this, aKey);
503
0
  }
504
0
505
0
  return NS_OK;
506
0
}
507
508
nsresult
509
LocalStorageCache::Clear(const LocalStorage* aStorage,
510
                         const MutationSource aSource)
511
0
{
512
0
  bool refresh = false;
513
0
  if (Persist(aStorage)) {
514
0
    // We need to preload all data (know the size) before we can proceeed
515
0
    // to correctly decrease cached usage number.
516
0
    // XXX as in case of unload, this is not technically needed now, but
517
0
    // after super-scope quota introduction we have to do this.  Get telemetry
518
0
    // right now.
519
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS);
520
0
    if (NS_FAILED(mLoadResult)) {
521
0
      // When we failed to load data from the database, force delete of the
522
0
      // scope data and make use of the storage possible again.
523
0
      refresh = true;
524
0
      mLoadResult = NS_OK;
525
0
    }
526
0
  }
527
0
528
0
  Data& data = DataSet(aStorage);
529
0
  bool hadData = !!data.mKeys.Count();
530
0
531
0
  if (hadData) {
532
0
    Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage, aSource);
533
0
    data.mKeys.Clear();
534
0
  }
535
0
536
0
  if (aSource != ContentMutation) {
537
0
    return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
538
0
  }
539
0
540
0
#if !defined(MOZ_WIDGET_ANDROID)
541
0
  if (hadData) {
542
0
    NotifyObservers(aStorage, VoidString(), VoidString(), VoidString());
543
0
  }
544
0
#endif
545
0
546
0
  if (Persist(aStorage) && (refresh || hadData)) {
547
0
    StorageDBChild* storageChild = StorageDBChild::Get();
548
0
    if (!storageChild) {
549
0
      NS_ERROR("Writing to localStorage after the database has been shut down"
550
0
               ", data lose!");
551
0
      return NS_ERROR_NOT_INITIALIZED;
552
0
    }
553
0
554
0
    return storageChild->AsyncClear(this);
555
0
  }
556
0
557
0
  return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
558
0
}
559
560
int64_t
561
LocalStorageCache::GetOriginQuotaUsage(const LocalStorage* aStorage) const
562
0
{
563
0
  return mData[GetDataSetIndex(aStorage)].mOriginQuotaUsage;
564
0
}
565
566
void
567
LocalStorageCache::UnloadItems(uint32_t aUnloadFlags)
568
0
{
569
0
  if (aUnloadFlags & kUnloadDefault) {
570
0
    // Must wait for preload to pass correct usage to ProcessUsageDelta
571
0
    // XXX this is not technically needed right now since there is just
572
0
    // per-origin isolated quota handling, but when we introduce super-
573
0
    // -scope quotas, we have to do this.  Better to start getting
574
0
    // telemetry right now.
575
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
576
0
577
0
    mData[kDefaultSet].mKeys.Clear();
578
0
    ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
579
0
  }
580
0
581
0
  if (aUnloadFlags & kUnloadPrivate) {
582
0
    mData[kPrivateSet].mKeys.Clear();
583
0
    ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
584
0
  }
585
0
586
0
  if (aUnloadFlags & kUnloadSession) {
587
0
    mData[kSessionSet].mKeys.Clear();
588
0
    ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
589
0
    mSessionOnlyDataSetActive = false;
590
0
  }
591
0
592
0
#ifdef DOM_STORAGE_TESTS
593
0
  if (aUnloadFlags & kTestReload) {
594
0
    WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
595
0
596
0
    mData[kDefaultSet].mKeys.Clear();
597
0
    mLoaded = false; // This is only used in testing code
598
0
    Preload();
599
0
  }
600
0
#endif
601
0
}
602
603
// LocalStorageCacheBridge
604
605
uint32_t
606
LocalStorageCache::LoadedCount()
607
0
{
608
0
  MonitorAutoLock monitor(mMonitor);
609
0
  Data& data = mData[kDefaultSet];
610
0
  return data.mKeys.Count();
611
0
}
612
613
bool
614
LocalStorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
615
0
{
616
0
  MonitorAutoLock monitor(mMonitor);
617
0
  if (mLoaded) {
618
0
    return false;
619
0
  }
620
0
621
0
  Data& data = mData[kDefaultSet];
622
0
  if (data.mKeys.Get(aKey, nullptr)) {
623
0
    return true; // don't stop, just don't override
624
0
  }
625
0
626
0
  data.mKeys.Put(aKey, aValue);
627
0
  data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
628
0
  return true;
629
0
}
630
631
void
632
LocalStorageCache::LoadDone(nsresult aRv)
633
0
{
634
0
  MonitorAutoLock monitor(mMonitor);
635
0
  mLoadResult = aRv;
636
0
  mLoaded = true;
637
0
  monitor.Notify();
638
0
}
639
640
void
641
LocalStorageCache::LoadWait()
642
0
{
643
0
  MonitorAutoLock monitor(mMonitor);
644
0
  while (!mLoaded) {
645
0
    monitor.Wait();
646
0
  }
647
0
}
648
649
// StorageUsage
650
651
StorageUsage::StorageUsage(const nsACString& aOriginScope)
652
  : mOriginScope(aOriginScope)
653
0
{
654
0
  mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
655
0
}
656
657
namespace {
658
659
class LoadUsageRunnable : public Runnable
660
{
661
public:
662
  LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
663
    : Runnable("dom::LoadUsageRunnable")
664
    , mTarget(aUsage)
665
    , mDelta(aDelta)
666
0
  {}
667
668
private:
669
  int64_t* mTarget;
670
  int64_t mDelta;
671
672
0
  NS_IMETHOD Run() override { *mTarget = mDelta; return NS_OK; }
673
};
674
675
} // namespace
676
677
void
678
StorageUsage::LoadUsage(const int64_t aUsage)
679
0
{
680
0
  // Using kDefaultSet index since it is the index for the persitent data
681
0
  // stored in the database we have just loaded usage for.
682
0
  if (!NS_IsMainThread()) {
683
0
    // In single process scenario we get this call from the DB thread
684
0
    RefPtr<LoadUsageRunnable> r =
685
0
      new LoadUsageRunnable(mUsage + kDefaultSet, aUsage);
686
0
    NS_DispatchToMainThread(r);
687
0
  } else {
688
0
    // On a child process we get this on the main thread already
689
0
    mUsage[kDefaultSet] += aUsage;
690
0
  }
691
0
}
692
693
bool
694
StorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,
695
  const int64_t aDelta, const LocalStorageCache::MutationSource aSource)
696
0
{
697
0
  MOZ_ASSERT(NS_IsMainThread());
698
0
699
0
  int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
700
0
  if (aSource == LocalStorageCache::ContentMutation &&
701
0
      aDelta > 0 && newUsage > LocalStorageManager::GetQuota()) {
702
0
    return false;
703
0
  }
704
0
705
0
  mUsage[aDataSetIndex] = newUsage;
706
0
  return true;
707
0
}
708
709
} // namespace dom
710
} // namespace mozilla