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 "DOMStorageIPC.h"
8 
9 #include "DOMStorageManager.h"
10 
11 #include "mozilla/dom/ContentChild.h"
12 #include "mozilla/dom/ContentParent.h"
13 #include "mozilla/Unused.h"
14 #include "nsIDiskSpaceWatcher.h"
15 #include "nsThreadUtils.h"
16 
17 namespace mozilla {
18 namespace dom {
19 
20 // ----------------------------------------------------------------------------
21 // Child
22 // ----------------------------------------------------------------------------
23 
24 NS_IMPL_ADDREF(DOMStorageDBChild)
25 
NS_IMETHODIMP_(MozExternalRefCountType)26 NS_IMETHODIMP_(MozExternalRefCountType) DOMStorageDBChild::Release(void)
27 {
28   NS_PRECONDITION(0 != mRefCnt, "dup release");
29   nsrefcnt count = --mRefCnt;
30   NS_LOG_RELEASE(this, count, "DOMStorageDBChild");
31   if (count == 1 && mIPCOpen) {
32     Send__delete__(this);
33     return 0;
34   }
35   if (count == 0) {
36     mRefCnt = 1;
37     delete this;
38     return 0;
39   }
40   return count;
41 }
42 
43 void
AddIPDLReference()44 DOMStorageDBChild::AddIPDLReference()
45 {
46   MOZ_ASSERT(!mIPCOpen, "Attempting to retain multiple IPDL references");
47   mIPCOpen = true;
48   AddRef();
49 }
50 
51 void
ReleaseIPDLReference()52 DOMStorageDBChild::ReleaseIPDLReference()
53 {
54   MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference");
55   mIPCOpen = false;
56   Release();
57 }
58 
DOMStorageDBChild(DOMLocalStorageManager * aManager)59 DOMStorageDBChild::DOMStorageDBChild(DOMLocalStorageManager* aManager)
60   : mManager(aManager)
61   , mStatus(NS_OK)
62   , mIPCOpen(false)
63 {
64 }
65 
~DOMStorageDBChild()66 DOMStorageDBChild::~DOMStorageDBChild()
67 {
68 }
69 
70 nsTHashtable<nsCStringHashKey>&
OriginsHavingData()71 DOMStorageDBChild::OriginsHavingData()
72 {
73   if (!mOriginsHavingData) {
74     mOriginsHavingData = new nsTHashtable<nsCStringHashKey>;
75   }
76 
77   return *mOriginsHavingData;
78 }
79 
80 nsresult
Init()81 DOMStorageDBChild::Init()
82 {
83   ContentChild* child = ContentChild::GetSingleton();
84   AddIPDLReference();
85   child->SendPStorageConstructor(this);
86   return NS_OK;
87 }
88 
89 nsresult
Shutdown()90 DOMStorageDBChild::Shutdown()
91 {
92   // There is nothing to do here, IPC will release automatically and
93   // the actual thread running on the parent process will also stop
94   // automatically in profile-before-change topic observer.
95   return NS_OK;
96 }
97 
98 void
AsyncPreload(DOMStorageCacheBridge * aCache,bool aPriority)99 DOMStorageDBChild::AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority)
100 {
101   if (mIPCOpen) {
102     // Adding ref to cache for the time of preload.  This ensures a reference to
103     // to the cache and that all keys will load into this cache object.
104     mLoadingCaches.PutEntry(aCache);
105     SendAsyncPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(), aPriority);
106   } else {
107     // No IPC, no love.  But the LoadDone call is expected.
108     aCache->LoadDone(NS_ERROR_UNEXPECTED);
109   }
110 }
111 
112 void
AsyncGetUsage(DOMStorageUsageBridge * aUsage)113 DOMStorageDBChild::AsyncGetUsage(DOMStorageUsageBridge* aUsage)
114 {
115   if (mIPCOpen) {
116     SendAsyncGetUsage(aUsage->OriginScope());
117   }
118 }
119 
120 void
SyncPreload(DOMStorageCacheBridge * aCache,bool aForceSync)121 DOMStorageDBChild::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
122 {
123   if (NS_FAILED(mStatus)) {
124     aCache->LoadDone(mStatus);
125     return;
126   }
127 
128   if (!mIPCOpen) {
129     aCache->LoadDone(NS_ERROR_UNEXPECTED);
130     return;
131   }
132 
133   // There is no way to put the child process to a wait state to receive all
134   // incoming async responses from the parent, hence we have to do a sync preload
135   // instead.  We are smart though, we only demand keys that are left to load in
136   // case the async preload has already loaded some keys.
137   InfallibleTArray<nsString> keys, values;
138   nsresult rv;
139   SendPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(), aCache->LoadedCount(),
140               &keys, &values, &rv);
141 
142   for (uint32_t i = 0; i < keys.Length(); ++i) {
143     aCache->LoadItem(keys[i], values[i]);
144   }
145 
146   aCache->LoadDone(rv);
147 }
148 
149 nsresult
AsyncAddItem(DOMStorageCacheBridge * aCache,const nsAString & aKey,const nsAString & aValue)150 DOMStorageDBChild::AsyncAddItem(DOMStorageCacheBridge* aCache,
151                                 const nsAString& aKey,
152                                 const nsAString& aValue)
153 {
154   if (NS_FAILED(mStatus) || !mIPCOpen) {
155     return mStatus;
156   }
157 
158   SendAsyncAddItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
159                    nsString(aKey), nsString(aValue));
160   OriginsHavingData().PutEntry(aCache->Origin());
161   return NS_OK;
162 }
163 
164 nsresult
AsyncUpdateItem(DOMStorageCacheBridge * aCache,const nsAString & aKey,const nsAString & aValue)165 DOMStorageDBChild::AsyncUpdateItem(DOMStorageCacheBridge* aCache,
166                                    const nsAString& aKey,
167                                    const nsAString& aValue)
168 {
169   if (NS_FAILED(mStatus) || !mIPCOpen) {
170     return mStatus;
171   }
172 
173   SendAsyncUpdateItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
174                       nsString(aKey), nsString(aValue));
175   OriginsHavingData().PutEntry(aCache->Origin());
176   return NS_OK;
177 }
178 
179 nsresult
AsyncRemoveItem(DOMStorageCacheBridge * aCache,const nsAString & aKey)180 DOMStorageDBChild::AsyncRemoveItem(DOMStorageCacheBridge* aCache,
181                                    const nsAString& aKey)
182 {
183   if (NS_FAILED(mStatus) || !mIPCOpen) {
184     return mStatus;
185   }
186 
187   SendAsyncRemoveItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
188                       nsString(aKey));
189   return NS_OK;
190 }
191 
192 nsresult
AsyncClear(DOMStorageCacheBridge * aCache)193 DOMStorageDBChild::AsyncClear(DOMStorageCacheBridge* aCache)
194 {
195   if (NS_FAILED(mStatus) || !mIPCOpen) {
196     return mStatus;
197   }
198 
199   SendAsyncClear(aCache->OriginSuffix(), aCache->OriginNoSuffix());
200   OriginsHavingData().RemoveEntry(aCache->Origin());
201   return NS_OK;
202 }
203 
204 bool
ShouldPreloadOrigin(const nsACString & aOrigin)205 DOMStorageDBChild::ShouldPreloadOrigin(const nsACString& aOrigin)
206 {
207   // Return true if we didn't receive the origins list yet.
208   // I tend to rather preserve a bit of early-after-start performance
209   // than a bit of memory here.
210   return !mOriginsHavingData || mOriginsHavingData->Contains(aOrigin);
211 }
212 
213 bool
RecvObserve(const nsCString & aTopic,const nsString & aOriginAttributesPattern,const nsCString & aOriginScope)214 DOMStorageDBChild::RecvObserve(const nsCString& aTopic,
215                                const nsString& aOriginAttributesPattern,
216                                const nsCString& aOriginScope)
217 {
218   DOMStorageObserver::Self()->Notify(
219     aTopic.get(), aOriginAttributesPattern, aOriginScope);
220   return true;
221 }
222 
223 bool
RecvOriginsHavingData(nsTArray<nsCString> && aOrigins)224 DOMStorageDBChild::RecvOriginsHavingData(nsTArray<nsCString>&& aOrigins)
225 {
226   for (uint32_t i = 0; i < aOrigins.Length(); ++i) {
227     OriginsHavingData().PutEntry(aOrigins[i]);
228   }
229 
230   return true;
231 }
232 
233 bool
RecvLoadItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey,const nsString & aValue)234 DOMStorageDBChild::RecvLoadItem(const nsCString& aOriginSuffix,
235                                 const nsCString& aOriginNoSuffix,
236                                 const nsString& aKey,
237                                 const nsString& aValue)
238 {
239   DOMStorageCache* aCache = mManager->GetCache(aOriginSuffix, aOriginNoSuffix);
240   if (aCache) {
241     aCache->LoadItem(aKey, aValue);
242   }
243 
244   return true;
245 }
246 
247 bool
RecvLoadDone(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsresult & aRv)248 DOMStorageDBChild::RecvLoadDone(const nsCString& aOriginSuffix,
249                                 const nsCString& aOriginNoSuffix,
250                                 const nsresult& aRv)
251 {
252   DOMStorageCache* aCache = mManager->GetCache(aOriginSuffix, aOriginNoSuffix);
253   if (aCache) {
254     aCache->LoadDone(aRv);
255 
256     // Just drop reference to this cache now since the load is done.
257     mLoadingCaches.RemoveEntry(static_cast<DOMStorageCacheBridge*>(aCache));
258   }
259 
260   return true;
261 }
262 
263 bool
RecvLoadUsage(const nsCString & aOriginNoSuffix,const int64_t & aUsage)264 DOMStorageDBChild::RecvLoadUsage(const nsCString& aOriginNoSuffix, const int64_t& aUsage)
265 {
266   RefPtr<DOMStorageUsageBridge> scopeUsage = mManager->GetOriginUsage(aOriginNoSuffix);
267   scopeUsage->LoadUsage(aUsage);
268   return true;
269 }
270 
271 bool
RecvError(const nsresult & aRv)272 DOMStorageDBChild::RecvError(const nsresult& aRv)
273 {
274   mStatus = aRv;
275   return true;
276 }
277 
278 // ----------------------------------------------------------------------------
279 // Parent
280 // ----------------------------------------------------------------------------
281 
282 NS_IMPL_ADDREF(DOMStorageDBParent)
NS_IMPL_RELEASE(DOMStorageDBParent)283 NS_IMPL_RELEASE(DOMStorageDBParent)
284 
285 void
286 DOMStorageDBParent::AddIPDLReference()
287 {
288   MOZ_ASSERT(!mIPCOpen, "Attempting to retain multiple IPDL references");
289   mIPCOpen = true;
290   AddRef();
291 }
292 
293 void
ReleaseIPDLReference()294 DOMStorageDBParent::ReleaseIPDLReference()
295 {
296   MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference");
297   mIPCOpen = false;
298   Release();
299 }
300 
301 namespace {
302 
303 class SendInitialChildDataRunnable : public Runnable
304 {
305 public:
SendInitialChildDataRunnable(DOMStorageDBParent * aParent)306   explicit SendInitialChildDataRunnable(DOMStorageDBParent* aParent)
307     : mParent(aParent)
308   {}
309 
310 private:
Run()311   NS_IMETHOD Run() override
312   {
313     if (!mParent->IPCOpen()) {
314       return NS_OK;
315     }
316 
317     DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
318     if (db) {
319       InfallibleTArray<nsCString> scopes;
320       db->GetOriginsHavingData(&scopes);
321       mozilla::Unused << mParent->SendOriginsHavingData(scopes);
322     }
323 
324     // We need to check if the device is in a low disk space situation, so
325     // we can forbid in that case any write in localStorage.
326     nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcher =
327       do_GetService("@mozilla.org/toolkit/disk-space-watcher;1");
328     if (!diskSpaceWatcher) {
329       return NS_OK;
330     }
331 
332     bool lowDiskSpace = false;
333     diskSpaceWatcher->GetIsDiskFull(&lowDiskSpace);
334 
335     if (lowDiskSpace) {
336       mozilla::Unused << mParent->SendObserve(
337         nsDependentCString("low-disk-space"), EmptyString(), EmptyCString());
338     }
339 
340     return NS_OK;
341   }
342 
343   RefPtr<DOMStorageDBParent> mParent;
344 };
345 
346 } // namespace
347 
DOMStorageDBParent()348 DOMStorageDBParent::DOMStorageDBParent()
349 : mIPCOpen(false)
350 {
351   DOMStorageObserver* observer = DOMStorageObserver::Self();
352   if (observer) {
353     observer->AddSink(this);
354   }
355 
356   // We are always open by IPC only
357   AddIPDLReference();
358 
359   // Cannot send directly from here since the channel
360   // is not completely built at this moment.
361   RefPtr<SendInitialChildDataRunnable> r =
362     new SendInitialChildDataRunnable(this);
363   NS_DispatchToCurrentThread(r);
364 }
365 
~DOMStorageDBParent()366 DOMStorageDBParent::~DOMStorageDBParent()
367 {
368   DOMStorageObserver* observer = DOMStorageObserver::Self();
369   if (observer) {
370     observer->RemoveSink(this);
371   }
372 }
373 
374 DOMStorageDBParent::CacheParentBridge*
NewCache(const nsACString & aOriginSuffix,const nsACString & aOriginNoSuffix)375 DOMStorageDBParent::NewCache(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix)
376 {
377   return new CacheParentBridge(this, aOriginSuffix, aOriginNoSuffix);
378 }
379 
380 void
ActorDestroy(ActorDestroyReason aWhy)381 DOMStorageDBParent::ActorDestroy(ActorDestroyReason aWhy)
382 {
383   // Implement me! Bug 1005169
384 }
385 
386 bool
RecvAsyncPreload(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const bool & aPriority)387 DOMStorageDBParent::RecvAsyncPreload(const nsCString& aOriginSuffix,
388                                      const nsCString& aOriginNoSuffix,
389                                      const bool& aPriority)
390 {
391   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
392   if (!db) {
393     return false;
394   }
395 
396   db->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix), aPriority);
397   return true;
398 }
399 
400 bool
RecvAsyncGetUsage(const nsCString & aOriginNoSuffix)401 DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aOriginNoSuffix)
402 {
403   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
404   if (!db) {
405     return false;
406   }
407 
408   // The object releases it self in LoadUsage method
409   RefPtr<UsageParentBridge> usage = new UsageParentBridge(this, aOriginNoSuffix);
410   db->AsyncGetUsage(usage);
411   return true;
412 }
413 
414 namespace {
415 
416 // We need another implementation of DOMStorageCacheBridge to do
417 // synchronous IPC preload.  This class just receives Load* notifications
418 // and fills the returning arguments of RecvPreload with the database
419 // values for us.
420 class SyncLoadCacheHelper : public DOMStorageCacheBridge
421 {
422 public:
SyncLoadCacheHelper(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,uint32_t aAlreadyLoadedCount,InfallibleTArray<nsString> * aKeys,InfallibleTArray<nsString> * aValues,nsresult * rv)423   SyncLoadCacheHelper(const nsCString& aOriginSuffix,
424                       const nsCString& aOriginNoSuffix,
425                       uint32_t aAlreadyLoadedCount,
426                       InfallibleTArray<nsString>* aKeys,
427                       InfallibleTArray<nsString>* aValues,
428                       nsresult* rv)
429   : mMonitor("DOM Storage SyncLoad IPC")
430   , mSuffix(aOriginSuffix)
431   , mOrigin(aOriginNoSuffix)
432   , mKeys(aKeys)
433   , mValues(aValues)
434   , mRv(rv)
435   , mLoaded(false)
436   , mLoadedCount(aAlreadyLoadedCount)
437   {
438     // Precaution
439     *mRv = NS_ERROR_UNEXPECTED;
440   }
441 
Origin() const442   virtual const nsCString Origin() const
443   {
444     return DOMStorageManager::CreateOrigin(mSuffix, mOrigin);
445   }
OriginNoSuffix() const446   virtual const nsCString& OriginNoSuffix() const { return mOrigin; }
OriginSuffix() const447   virtual const nsCString& OriginSuffix() const { return mSuffix; }
Loaded()448   virtual bool Loaded() { return mLoaded; }
LoadedCount()449   virtual uint32_t LoadedCount() { return mLoadedCount; }
LoadItem(const nsAString & aKey,const nsString & aValue)450   virtual bool LoadItem(const nsAString& aKey, const nsString& aValue)
451   {
452     // Called on the aCache background thread
453     MOZ_ASSERT(!mLoaded);
454     if (mLoaded) {
455       return false;
456     }
457 
458     ++mLoadedCount;
459     mKeys->AppendElement(aKey);
460     mValues->AppendElement(aValue);
461     return true;
462   }
463 
LoadDone(nsresult aRv)464   virtual void LoadDone(nsresult aRv)
465   {
466     // Called on the aCache background thread
467     MonitorAutoLock monitor(mMonitor);
468     MOZ_ASSERT(!mLoaded && mRv);
469     mLoaded = true;
470     if (mRv) {
471       *mRv = aRv;
472       mRv = nullptr;
473     }
474     monitor.Notify();
475   }
476 
LoadWait()477   virtual void LoadWait()
478   {
479     // Called on the main thread, exits after LoadDone() call
480     MonitorAutoLock monitor(mMonitor);
481     while (!mLoaded) {
482       monitor.Wait();
483     }
484   }
485 
486 private:
487   Monitor mMonitor;
488   nsCString mSuffix, mOrigin;
489   InfallibleTArray<nsString>* mKeys;
490   InfallibleTArray<nsString>* mValues;
491   nsresult* mRv;
492   bool mLoaded;
493   uint32_t mLoadedCount;
494 };
495 
496 } // namespace
497 
498 bool
RecvPreload(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const uint32_t & aAlreadyLoadedCount,InfallibleTArray<nsString> * aKeys,InfallibleTArray<nsString> * aValues,nsresult * aRv)499 DOMStorageDBParent::RecvPreload(const nsCString& aOriginSuffix,
500                                 const nsCString& aOriginNoSuffix,
501                                 const uint32_t& aAlreadyLoadedCount,
502                                 InfallibleTArray<nsString>* aKeys,
503                                 InfallibleTArray<nsString>* aValues,
504                                 nsresult* aRv)
505 {
506   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
507   if (!db) {
508     return false;
509   }
510 
511   RefPtr<SyncLoadCacheHelper> cache(
512     new SyncLoadCacheHelper(aOriginSuffix, aOriginNoSuffix, aAlreadyLoadedCount, aKeys, aValues, aRv));
513 
514   db->SyncPreload(cache, true);
515   return true;
516 }
517 
518 bool
RecvAsyncAddItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey,const nsString & aValue)519 DOMStorageDBParent::RecvAsyncAddItem(const nsCString& aOriginSuffix,
520                                      const nsCString& aOriginNoSuffix,
521                                      const nsString& aKey,
522                                      const nsString& aValue)
523 {
524   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
525   if (!db) {
526     return false;
527   }
528 
529   nsresult rv = db->AsyncAddItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
530   if (NS_FAILED(rv) && mIPCOpen) {
531     mozilla::Unused << SendError(rv);
532   }
533 
534   return true;
535 }
536 
537 bool
RecvAsyncUpdateItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey,const nsString & aValue)538 DOMStorageDBParent::RecvAsyncUpdateItem(const nsCString& aOriginSuffix,
539                                         const nsCString& aOriginNoSuffix,
540                                         const nsString& aKey,
541                                         const nsString& aValue)
542 {
543   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
544   if (!db) {
545     return false;
546   }
547 
548   nsresult rv = db->AsyncUpdateItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
549   if (NS_FAILED(rv) && mIPCOpen) {
550     mozilla::Unused << SendError(rv);
551   }
552 
553   return true;
554 }
555 
556 bool
RecvAsyncRemoveItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey)557 DOMStorageDBParent::RecvAsyncRemoveItem(const nsCString& aOriginSuffix,
558                                         const nsCString& aOriginNoSuffix,
559                                         const nsString& aKey)
560 {
561   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
562   if (!db) {
563     return false;
564   }
565 
566   nsresult rv = db->AsyncRemoveItem(NewCache(aOriginSuffix, aOriginNoSuffix), aKey);
567   if (NS_FAILED(rv) && mIPCOpen) {
568     mozilla::Unused << SendError(rv);
569   }
570 
571   return true;
572 }
573 
574 bool
RecvAsyncClear(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix)575 DOMStorageDBParent::RecvAsyncClear(const nsCString& aOriginSuffix,
576                                    const nsCString& aOriginNoSuffix)
577 {
578   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
579   if (!db) {
580     return false;
581   }
582 
583   nsresult rv = db->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
584   if (NS_FAILED(rv) && mIPCOpen) {
585     mozilla::Unused << SendError(rv);
586   }
587 
588   return true;
589 }
590 
591 bool
RecvAsyncFlush()592 DOMStorageDBParent::RecvAsyncFlush()
593 {
594   DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
595   if (!db) {
596     return false;
597   }
598 
599   db->AsyncFlush();
600   return true;
601 }
602 
603 // DOMStorageObserverSink
604 
605 nsresult
Observe(const char * aTopic,const nsAString & aOriginAttributesPattern,const nsACString & aOriginScope)606 DOMStorageDBParent::Observe(const char* aTopic,
607                             const nsAString& aOriginAttributesPattern,
608                             const nsACString& aOriginScope)
609 {
610   if (mIPCOpen) {
611       mozilla::Unused << SendObserve(nsDependentCString(aTopic),
612                                      nsString(aOriginAttributesPattern),
613                                      nsCString(aOriginScope));
614   }
615 
616   return NS_OK;
617 }
618 
619 namespace {
620 
621 // Results must be sent back on the main thread
622 class LoadRunnable : public Runnable
623 {
624 public:
625   enum TaskType {
626     loadItem,
627     loadDone
628   };
629 
LoadRunnable(DOMStorageDBParent * aParent,TaskType aType,const nsACString & aOriginSuffix,const nsACString & aOriginNoSuffix,const nsAString & aKey=EmptyString (),const nsAString & aValue=EmptyString ())630   LoadRunnable(DOMStorageDBParent* aParent,
631                TaskType aType,
632                const nsACString& aOriginSuffix,
633                const nsACString& aOriginNoSuffix,
634                const nsAString& aKey = EmptyString(),
635                const nsAString& aValue = EmptyString())
636   : mParent(aParent)
637   , mType(aType)
638   , mSuffix(aOriginSuffix)
639   , mOrigin(aOriginNoSuffix)
640   , mKey(aKey)
641   , mValue(aValue)
642   { }
643 
LoadRunnable(DOMStorageDBParent * aParent,TaskType aType,const nsACString & aOriginSuffix,const nsACString & aOriginNoSuffix,nsresult aRv)644   LoadRunnable(DOMStorageDBParent* aParent,
645                TaskType aType,
646                const nsACString& aOriginSuffix,
647                const nsACString& aOriginNoSuffix,
648                nsresult aRv)
649   : mParent(aParent)
650   , mType(aType)
651   , mSuffix(aOriginSuffix)
652   , mOrigin(aOriginNoSuffix)
653   , mRv(aRv)
654   { }
655 
656 private:
657   RefPtr<DOMStorageDBParent> mParent;
658   TaskType mType;
659   nsCString mSuffix, mOrigin;
660   nsString mKey;
661   nsString mValue;
662   nsresult mRv;
663 
Run()664   NS_IMETHOD Run() override
665   {
666     if (!mParent->IPCOpen()) {
667       return NS_OK;
668     }
669 
670     switch (mType)
671     {
672     case loadItem:
673       mozilla::Unused << mParent->SendLoadItem(mSuffix, mOrigin, mKey, mValue);
674       break;
675     case loadDone:
676       mozilla::Unused << mParent->SendLoadDone(mSuffix, mOrigin, mRv);
677       break;
678     }
679 
680     return NS_OK;
681   }
682 };
683 
684 } // namespace
685 
686 // DOMStorageDBParent::CacheParentBridge
687 
688 const nsCString
Origin() const689 DOMStorageDBParent::CacheParentBridge::Origin() const
690 {
691   return DOMStorageManager::CreateOrigin(mOriginSuffix, mOriginNoSuffix);
692 }
693 
694 bool
LoadItem(const nsAString & aKey,const nsString & aValue)695 DOMStorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey, const nsString& aValue)
696 {
697   if (mLoaded) {
698     return false;
699   }
700 
701   ++mLoadedCount;
702 
703   RefPtr<LoadRunnable> r =
704     new LoadRunnable(mParent, LoadRunnable::loadItem, mOriginSuffix, mOriginNoSuffix, aKey, aValue);
705   NS_DispatchToMainThread(r);
706   return true;
707 }
708 
709 void
LoadDone(nsresult aRv)710 DOMStorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
711 {
712   // Prevent send of duplicate LoadDone.
713   if (mLoaded) {
714     return;
715   }
716 
717   mLoaded = true;
718 
719   RefPtr<LoadRunnable> r =
720     new LoadRunnable(mParent, LoadRunnable::loadDone, mOriginSuffix, mOriginNoSuffix, aRv);
721   NS_DispatchToMainThread(r);
722 }
723 
724 void
LoadWait()725 DOMStorageDBParent::CacheParentBridge::LoadWait()
726 {
727   // Should never be called on this implementation
728   MOZ_ASSERT(false);
729 }
730 
731 // DOMStorageDBParent::UsageParentBridge
732 
733 namespace {
734 
735 class UsageRunnable : public Runnable
736 {
737 public:
UsageRunnable(DOMStorageDBParent * aParent,const nsACString & aOriginScope,const int64_t & aUsage)738   UsageRunnable(DOMStorageDBParent* aParent, const nsACString& aOriginScope, const int64_t& aUsage)
739   : mParent(aParent)
740   , mOriginScope(aOriginScope)
741   , mUsage(aUsage)
742   {}
743 
744 private:
Run()745   NS_IMETHOD Run() override
746   {
747     if (!mParent->IPCOpen()) {
748       return NS_OK;
749     }
750 
751     mozilla::Unused << mParent->SendLoadUsage(mOriginScope, mUsage);
752     return NS_OK;
753   }
754 
755   RefPtr<DOMStorageDBParent> mParent;
756   nsCString mOriginScope;
757   int64_t mUsage;
758 };
759 
760 } // namespace
761 
762 void
LoadUsage(const int64_t aUsage)763 DOMStorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
764 {
765   RefPtr<UsageRunnable> r = new UsageRunnable(mParent, mOriginScope, aUsage);
766   NS_DispatchToMainThread(r);
767 }
768 
769 } // namespace dom
770 } // namespace mozilla
771