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 "StorageIPC.h"
8 
9 #include "StorageCommon.h"
10 #include "StorageUtils.h"
11 #include "LocalStorageManager.h"
12 #include "SessionStorageObserver.h"
13 #include "SessionStorageManager.h"
14 #include "SessionStorageCache.h"
15 
16 #include "mozilla/dom/LocalStorageCommon.h"
17 #include "mozilla/ipc/BackgroundChild.h"
18 #include "mozilla/ipc/BackgroundParent.h"
19 #include "mozilla/ipc/PBackgroundChild.h"
20 #include "mozilla/ipc/PBackgroundParent.h"
21 #include "mozilla/dom/ContentParent.h"
22 #include "mozilla/Unused.h"
23 #include "nsCOMPtr.h"
24 #include "nsIPrincipal.h"
25 #include "nsThreadUtils.h"
26 
27 namespace mozilla {
28 namespace dom {
29 
30 namespace {
31 
32 typedef nsClassHashtable<nsCStringHashKey, nsTArray<LocalStorageCacheParent*>>
33     LocalStorageCacheParentHashtable;
34 
35 StaticAutoPtr<LocalStorageCacheParentHashtable> gLocalStorageCacheParents;
36 
37 StorageDBChild* sStorageChild[kPrivateBrowsingIdCount] = {nullptr, nullptr};
38 
39 // False until we shut the storage child down.
40 bool sStorageChildDown[kPrivateBrowsingIdCount] = {false, false};
41 
42 }  // namespace
43 
LocalStorageCacheChild(LocalStorageCache * aCache)44 LocalStorageCacheChild::LocalStorageCacheChild(LocalStorageCache* aCache)
45     : mCache(aCache) {
46   AssertIsOnOwningThread();
47   MOZ_ASSERT(aCache);
48   aCache->AssertIsOnOwningThread();
49 
50   MOZ_COUNT_CTOR(LocalStorageCacheChild);
51 }
52 
~LocalStorageCacheChild()53 LocalStorageCacheChild::~LocalStorageCacheChild() {
54   AssertIsOnOwningThread();
55 
56   MOZ_COUNT_DTOR(LocalStorageCacheChild);
57 }
58 
SendDeleteMeInternal()59 void LocalStorageCacheChild::SendDeleteMeInternal() {
60   AssertIsOnOwningThread();
61 
62   if (mCache) {
63     mCache->ClearActor();
64     mCache = nullptr;
65 
66     MOZ_ALWAYS_TRUE(PBackgroundLocalStorageCacheChild::SendDeleteMe());
67   }
68 }
69 
ActorDestroy(ActorDestroyReason aWhy)70 void LocalStorageCacheChild::ActorDestroy(ActorDestroyReason aWhy) {
71   AssertIsOnOwningThread();
72 
73   if (mCache) {
74     mCache->ClearActor();
75     mCache = nullptr;
76   }
77 }
78 
RecvObserve(const PrincipalInfo & aPrincipalInfo,const PrincipalInfo & aCachePrincipalInfo,const uint32_t & aPrivateBrowsingId,const nsString & aDocumentURI,const nsString & aKey,const nsString & aOldValue,const nsString & aNewValue)79 mozilla::ipc::IPCResult LocalStorageCacheChild::RecvObserve(
80     const PrincipalInfo& aPrincipalInfo,
81     const PrincipalInfo& aCachePrincipalInfo,
82     const uint32_t& aPrivateBrowsingId, const nsString& aDocumentURI,
83     const nsString& aKey, const nsString& aOldValue,
84     const nsString& aNewValue) {
85   AssertIsOnOwningThread();
86 
87   auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo);
88   if (NS_WARN_IF(principalOrErr.isErr())) {
89     return IPC_FAIL_NO_REASON(this);
90   }
91 
92   auto cachePrincipalOrErr = PrincipalInfoToPrincipal(aCachePrincipalInfo);
93   if (NS_WARN_IF(cachePrincipalOrErr.isErr())) {
94     return IPC_FAIL_NO_REASON(this);
95   }
96 
97   nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
98   nsCOMPtr<nsIPrincipal> cachePrincipal = cachePrincipalOrErr.unwrap();
99 
100   if (StorageUtils::PrincipalsEqual(principal, cachePrincipal)) {
101     Storage::NotifyChange(/* aStorage */ nullptr, principal, aKey, aOldValue,
102                           aNewValue,
103                           /* aStorageType */ u"localStorage", aDocumentURI,
104                           /* aIsPrivate */ !!aPrivateBrowsingId,
105                           /* aImmediateDispatch */ true);
106   }
107 
108   return IPC_OK();
109 }
110 
111 // ----------------------------------------------------------------------------
112 // Child
113 // ----------------------------------------------------------------------------
114 
115 class StorageDBChild::ShutdownObserver final : public nsIObserver {
116   // Expected to be only 0 or 1.
117   const uint32_t mPrivateBrowsingId;
118 
119  public:
ShutdownObserver(const uint32_t aPrivateBrowsingId)120   explicit ShutdownObserver(const uint32_t aPrivateBrowsingId)
121       : mPrivateBrowsingId(aPrivateBrowsingId) {
122     MOZ_ASSERT(NS_IsMainThread());
123     MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount);
124   }
125 
126   NS_DECL_ISUPPORTS
127   NS_DECL_NSIOBSERVER
128 
129  private:
~ShutdownObserver()130   ~ShutdownObserver() { MOZ_ASSERT(NS_IsMainThread()); }
131 };
132 
AddIPDLReference()133 void StorageDBChild::AddIPDLReference() {
134   MOZ_ASSERT(!mIPCOpen, "Attempting to retain multiple IPDL references");
135   mIPCOpen = true;
136   AddRef();
137 }
138 
ReleaseIPDLReference()139 void StorageDBChild::ReleaseIPDLReference() {
140   MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference");
141   mIPCOpen = false;
142   Release();
143 }
144 
StorageDBChild(LocalStorageManager * aManager,const uint32_t aPrivateBrowsingId)145 StorageDBChild::StorageDBChild(LocalStorageManager* aManager,
146                                const uint32_t aPrivateBrowsingId)
147     : mManager(aManager),
148       mPrivateBrowsingId(aPrivateBrowsingId),
149       mStatus(NS_OK),
150       mIPCOpen(false) {
151   MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount);
152   MOZ_ASSERT(!NextGenLocalStorageEnabled());
153 }
154 
155 StorageDBChild::~StorageDBChild() = default;
156 
157 // static
Get(const uint32_t aPrivateBrowsingId)158 StorageDBChild* StorageDBChild::Get(const uint32_t aPrivateBrowsingId) {
159   MOZ_ASSERT(NS_IsMainThread());
160   MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount);
161   MOZ_ASSERT(!NextGenLocalStorageEnabled());
162 
163   return sStorageChild[aPrivateBrowsingId];
164 }
165 
166 // static
GetOrCreate(const uint32_t aPrivateBrowsingId)167 StorageDBChild* StorageDBChild::GetOrCreate(const uint32_t aPrivateBrowsingId) {
168   MOZ_ASSERT(NS_IsMainThread());
169   MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount);
170   MOZ_ASSERT(!NextGenLocalStorageEnabled());
171 
172   StorageDBChild*& storageChild = sStorageChild[aPrivateBrowsingId];
173   if (storageChild || sStorageChildDown[aPrivateBrowsingId]) {
174     // When sStorageChildDown is at true, sStorageChild is null.
175     // Checking sStorageChildDown flag here prevents reinitialization of
176     // the storage child after shutdown.
177     return storageChild;
178   }
179 
180   // Use LocalStorageManager::Ensure in case we're called from
181   // DOMSessionStorageManager's initializer and we haven't yet initialized the
182   // local storage manager.
183   RefPtr<StorageDBChild> newStorageChild =
184       new StorageDBChild(LocalStorageManager::Ensure(), aPrivateBrowsingId);
185 
186   nsresult rv = newStorageChild->Init();
187   if (NS_WARN_IF(NS_FAILED(rv))) {
188     return nullptr;
189   }
190 
191   newStorageChild.forget(&storageChild);
192 
193   return storageChild;
194 }
195 
OriginsHavingData()196 nsTHashSet<nsCString>& StorageDBChild::OriginsHavingData() {
197   if (!mOriginsHavingData) {
198     mOriginsHavingData = MakeUnique<nsTHashSet<nsCString>>();
199   }
200 
201   return *mOriginsHavingData;
202 }
203 
Init()204 nsresult StorageDBChild::Init() {
205   MOZ_ASSERT(NS_IsMainThread());
206 
207   ::mozilla::ipc::PBackgroundChild* actor =
208       ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
209   if (NS_WARN_IF(!actor)) {
210     return NS_ERROR_FAILURE;
211   }
212 
213   nsString profilePath;
214   if (XRE_IsParentProcess() && mPrivateBrowsingId == 0) {
215     nsresult rv = StorageDBThread::GetProfilePath(profilePath);
216     if (NS_WARN_IF(NS_FAILED(rv))) {
217       return rv;
218     }
219   }
220 
221   AddIPDLReference();
222 
223   actor->SendPBackgroundStorageConstructor(this, profilePath,
224                                            mPrivateBrowsingId);
225 
226   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
227   MOZ_ASSERT(observerService);
228 
229   nsCOMPtr<nsIObserver> observer = new ShutdownObserver(mPrivateBrowsingId);
230 
231   MOZ_ALWAYS_SUCCEEDS(
232       observerService->AddObserver(observer, "xpcom-shutdown", false));
233 
234   return NS_OK;
235 }
236 
Shutdown()237 nsresult StorageDBChild::Shutdown() {
238   // There is nothing to do here, IPC will release automatically and
239   // the actual thread running on the parent process will also stop
240   // automatically in profile-before-change topic observer.
241   return NS_OK;
242 }
243 
AsyncPreload(LocalStorageCacheBridge * aCache,bool aPriority)244 void StorageDBChild::AsyncPreload(LocalStorageCacheBridge* aCache,
245                                   bool aPriority) {
246   if (mIPCOpen) {
247     // Adding ref to cache for the time of preload.  This ensures a reference to
248     // to the cache and that all keys will load into this cache object.
249     mLoadingCaches.Insert(aCache);
250     SendAsyncPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
251                      aPriority);
252   } else {
253     // No IPC, no love.  But the LoadDone call is expected.
254     aCache->LoadDone(NS_ERROR_UNEXPECTED);
255   }
256 }
257 
AsyncGetUsage(StorageUsageBridge * aUsage)258 void StorageDBChild::AsyncGetUsage(StorageUsageBridge* aUsage) {
259   if (mIPCOpen) {
260     SendAsyncGetUsage(aUsage->OriginScope());
261   }
262 }
263 
SyncPreload(LocalStorageCacheBridge * aCache,bool aForceSync)264 void StorageDBChild::SyncPreload(LocalStorageCacheBridge* aCache,
265                                  bool aForceSync) {
266   if (NS_FAILED(mStatus)) {
267     aCache->LoadDone(mStatus);
268     return;
269   }
270 
271   if (!mIPCOpen) {
272     aCache->LoadDone(NS_ERROR_UNEXPECTED);
273     return;
274   }
275 
276   // There is no way to put the child process to a wait state to receive all
277   // incoming async responses from the parent, hence we have to do a sync
278   // preload instead.  We are smart though, we only demand keys that are left to
279   // load in case the async preload has already loaded some keys.
280   nsTArray<nsString> keys, values;
281   nsresult rv;
282   SendPreload(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
283               aCache->LoadedCount(), &keys, &values, &rv);
284 
285   for (uint32_t i = 0; i < keys.Length(); ++i) {
286     aCache->LoadItem(keys[i], values[i]);
287   }
288 
289   aCache->LoadDone(rv);
290 }
291 
AsyncAddItem(LocalStorageCacheBridge * aCache,const nsAString & aKey,const nsAString & aValue)292 nsresult StorageDBChild::AsyncAddItem(LocalStorageCacheBridge* aCache,
293                                       const nsAString& aKey,
294                                       const nsAString& aValue) {
295   if (NS_FAILED(mStatus) || !mIPCOpen) {
296     return mStatus;
297   }
298 
299   SendAsyncAddItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
300                    nsString(aKey), nsString(aValue));
301   OriginsHavingData().Insert(aCache->Origin());
302   return NS_OK;
303 }
304 
AsyncUpdateItem(LocalStorageCacheBridge * aCache,const nsAString & aKey,const nsAString & aValue)305 nsresult StorageDBChild::AsyncUpdateItem(LocalStorageCacheBridge* aCache,
306                                          const nsAString& aKey,
307                                          const nsAString& aValue) {
308   if (NS_FAILED(mStatus) || !mIPCOpen) {
309     return mStatus;
310   }
311 
312   SendAsyncUpdateItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
313                       nsString(aKey), nsString(aValue));
314   OriginsHavingData().Insert(aCache->Origin());
315   return NS_OK;
316 }
317 
AsyncRemoveItem(LocalStorageCacheBridge * aCache,const nsAString & aKey)318 nsresult StorageDBChild::AsyncRemoveItem(LocalStorageCacheBridge* aCache,
319                                          const nsAString& aKey) {
320   if (NS_FAILED(mStatus) || !mIPCOpen) {
321     return mStatus;
322   }
323 
324   SendAsyncRemoveItem(aCache->OriginSuffix(), aCache->OriginNoSuffix(),
325                       nsString(aKey));
326   return NS_OK;
327 }
328 
AsyncClear(LocalStorageCacheBridge * aCache)329 nsresult StorageDBChild::AsyncClear(LocalStorageCacheBridge* aCache) {
330   if (NS_FAILED(mStatus) || !mIPCOpen) {
331     return mStatus;
332   }
333 
334   SendAsyncClear(aCache->OriginSuffix(), aCache->OriginNoSuffix());
335   OriginsHavingData().Remove(aCache->Origin());
336   return NS_OK;
337 }
338 
ShouldPreloadOrigin(const nsACString & aOrigin)339 bool StorageDBChild::ShouldPreloadOrigin(const nsACString& aOrigin) {
340   // Return true if we didn't receive the origins list yet.
341   // I tend to rather preserve a bit of early-after-start performance
342   // than a bit of memory here.
343   return !mOriginsHavingData || mOriginsHavingData->Contains(aOrigin);
344 }
345 
RecvObserve(const nsCString & aTopic,const nsString & aOriginAttributesPattern,const nsCString & aOriginScope)346 mozilla::ipc::IPCResult StorageDBChild::RecvObserve(
347     const nsCString& aTopic, const nsString& aOriginAttributesPattern,
348     const nsCString& aOriginScope) {
349   MOZ_ASSERT(!XRE_IsParentProcess());
350 
351   StorageObserver* obs = StorageObserver::Self();
352   if (obs) {
353     obs->Notify(aTopic.get(), aOriginAttributesPattern,
354                 aOriginScope);
355   }
356 
357   return IPC_OK();
358 }
359 
RecvOriginsHavingData(nsTArray<nsCString> && aOrigins)360 mozilla::ipc::IPCResult StorageDBChild::RecvOriginsHavingData(
361     nsTArray<nsCString>&& aOrigins) {
362   // Force population of mOriginsHavingData even if there are no origins so that
363   // ShouldPreloadOrigin does not generate false positives for all origins.
364   if (!aOrigins.Length()) {
365     Unused << OriginsHavingData();
366   }
367 
368   for (uint32_t i = 0; i < aOrigins.Length(); ++i) {
369     OriginsHavingData().Insert(aOrigins[i]);
370   }
371 
372   return IPC_OK();
373 }
374 
RecvLoadItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey,const nsString & aValue)375 mozilla::ipc::IPCResult StorageDBChild::RecvLoadItem(
376     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
377     const nsString& aKey, const nsString& aValue) {
378   LocalStorageCache* aCache =
379       mManager->GetCache(aOriginSuffix, aOriginNoSuffix);
380   if (aCache) {
381     aCache->LoadItem(aKey, aValue);
382   }
383 
384   return IPC_OK();
385 }
386 
RecvLoadDone(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsresult & aRv)387 mozilla::ipc::IPCResult StorageDBChild::RecvLoadDone(
388     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
389     const nsresult& aRv) {
390   LocalStorageCache* aCache =
391       mManager->GetCache(aOriginSuffix, aOriginNoSuffix);
392   if (aCache) {
393     aCache->LoadDone(aRv);
394 
395     // Just drop reference to this cache now since the load is done.
396     mLoadingCaches.Remove(static_cast<LocalStorageCacheBridge*>(aCache));
397   }
398 
399   return IPC_OK();
400 }
401 
RecvLoadUsage(const nsCString & aOriginNoSuffix,const int64_t & aUsage)402 mozilla::ipc::IPCResult StorageDBChild::RecvLoadUsage(
403     const nsCString& aOriginNoSuffix, const int64_t& aUsage) {
404   RefPtr<StorageUsageBridge> scopeUsage =
405       mManager->GetOriginUsage(aOriginNoSuffix, mPrivateBrowsingId);
406   scopeUsage->LoadUsage(aUsage);
407   return IPC_OK();
408 }
409 
RecvError(const nsresult & aRv)410 mozilla::ipc::IPCResult StorageDBChild::RecvError(const nsresult& aRv) {
411   mStatus = aRv;
412   return IPC_OK();
413 }
414 
NS_IMPL_ISUPPORTS(StorageDBChild::ShutdownObserver,nsIObserver)415 NS_IMPL_ISUPPORTS(StorageDBChild::ShutdownObserver, nsIObserver)
416 
417 NS_IMETHODIMP
418 StorageDBChild::ShutdownObserver::Observe(nsISupports* aSubject,
419                                           const char* aTopic,
420                                           const char16_t* aData) {
421   MOZ_ASSERT(NS_IsMainThread());
422   MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
423 
424   nsCOMPtr<nsIObserverService> observerService =
425       mozilla::services::GetObserverService();
426   if (NS_WARN_IF(!observerService)) {
427     return NS_ERROR_FAILURE;
428   }
429 
430   Unused << observerService->RemoveObserver(this, "xpcom-shutdown");
431 
432   StorageDBChild*& storageChild = sStorageChild[mPrivateBrowsingId];
433   if (storageChild) {
434     sStorageChildDown[mPrivateBrowsingId] = true;
435 
436     MOZ_ALWAYS_TRUE(storageChild->PBackgroundStorageChild::SendDeleteMe());
437 
438     NS_RELEASE(storageChild);
439     storageChild = nullptr;
440   }
441 
442   return NS_OK;
443 }
444 
SessionStorageObserverChild(SessionStorageObserver * aObserver)445 SessionStorageObserverChild::SessionStorageObserverChild(
446     SessionStorageObserver* aObserver)
447     : mObserver(aObserver) {
448   AssertIsOnOwningThread();
449   MOZ_ASSERT(NextGenLocalStorageEnabled());
450   MOZ_ASSERT(aObserver);
451   aObserver->AssertIsOnOwningThread();
452 
453   MOZ_COUNT_CTOR(SessionStorageObserverChild);
454 }
455 
~SessionStorageObserverChild()456 SessionStorageObserverChild::~SessionStorageObserverChild() {
457   AssertIsOnOwningThread();
458 
459   MOZ_COUNT_DTOR(SessionStorageObserverChild);
460 }
461 
SendDeleteMeInternal()462 void SessionStorageObserverChild::SendDeleteMeInternal() {
463   AssertIsOnOwningThread();
464 
465   if (mObserver) {
466     mObserver->ClearActor();
467     mObserver = nullptr;
468 
469     // Don't check result here since IPC may no longer be available due to
470     // SessionStorageManager (which holds a strong reference to
471     // SessionStorageObserver) being destroyed very late in the game.
472     PSessionStorageObserverChild::SendDeleteMe();
473   }
474 }
475 
ActorDestroy(ActorDestroyReason aWhy)476 void SessionStorageObserverChild::ActorDestroy(ActorDestroyReason aWhy) {
477   AssertIsOnOwningThread();
478 
479   if (mObserver) {
480     mObserver->ClearActor();
481     mObserver = nullptr;
482   }
483 }
484 
RecvObserve(const nsCString & aTopic,const nsString & aOriginAttributesPattern,const nsCString & aOriginScope)485 mozilla::ipc::IPCResult SessionStorageObserverChild::RecvObserve(
486     const nsCString& aTopic, const nsString& aOriginAttributesPattern,
487     const nsCString& aOriginScope) {
488   AssertIsOnOwningThread();
489 
490   StorageObserver* obs = StorageObserver::Self();
491   if (obs) {
492     obs->Notify(aTopic.get(), aOriginAttributesPattern,
493                 aOriginScope);
494   }
495 
496   return IPC_OK();
497 }
498 
SessionStorageCacheChild(SessionStorageCache * aCache)499 SessionStorageCacheChild::SessionStorageCacheChild(SessionStorageCache* aCache)
500     : mCache(aCache) {
501   AssertIsOnOwningThread();
502   MOZ_ASSERT(mCache);
503 
504   MOZ_COUNT_CTOR(SessionStorageCacheChild);
505 }
506 
~SessionStorageCacheChild()507 SessionStorageCacheChild::~SessionStorageCacheChild() {
508   AssertIsOnOwningThread();
509 
510   MOZ_COUNT_DTOR(SessionStorageCacheChild);
511 }
512 
SendDeleteMeInternal()513 void SessionStorageCacheChild::SendDeleteMeInternal() {
514   AssertIsOnOwningThread();
515 
516   if (mCache) {
517     mCache->ClearActor();
518     mCache = nullptr;
519 
520     MOZ_ALWAYS_TRUE(PBackgroundSessionStorageCacheChild::SendDeleteMe());
521   }
522 }
523 
ActorDestroy(ActorDestroyReason aWhy)524 void SessionStorageCacheChild::ActorDestroy(ActorDestroyReason aWhy) {
525   AssertIsOnOwningThread();
526 
527   if (mCache) {
528     mCache->ClearActor();
529     mCache = nullptr;
530   }
531 }
532 
SessionStorageManagerChild(SessionStorageManager * aSSManager)533 SessionStorageManagerChild::SessionStorageManagerChild(
534     SessionStorageManager* aSSManager)
535     : mSSManager(aSSManager) {
536   AssertIsOnOwningThread();
537   MOZ_ASSERT(mSSManager);
538 
539   MOZ_COUNT_CTOR(SessionStorageManagerChild);
540 }
541 
~SessionStorageManagerChild()542 SessionStorageManagerChild::~SessionStorageManagerChild() {
543   AssertIsOnOwningThread();
544 
545   MOZ_COUNT_DTOR(SessionStorageManagerChild);
546 }
547 
SendDeleteMeInternal()548 void SessionStorageManagerChild::SendDeleteMeInternal() {
549   AssertIsOnOwningThread();
550 
551   if (mSSManager) {
552     mSSManager->ClearActor();
553     mSSManager = nullptr;
554 
555     MOZ_ALWAYS_TRUE(PBackgroundSessionStorageManagerChild::SendDeleteMe());
556   }
557 }
558 
ActorDestroy(ActorDestroyReason aWhy)559 void SessionStorageManagerChild::ActorDestroy(ActorDestroyReason aWhy) {
560   AssertIsOnOwningThread();
561 
562   if (mSSManager) {
563     mSSManager->ClearActor();
564     mSSManager = nullptr;
565   }
566 }
567 
LocalStorageCacheParent(const mozilla::ipc::PrincipalInfo & aPrincipalInfo,const nsACString & aOriginKey,uint32_t aPrivateBrowsingId)568 LocalStorageCacheParent::LocalStorageCacheParent(
569     const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
570     const nsACString& aOriginKey, uint32_t aPrivateBrowsingId)
571     : mPrincipalInfo(aPrincipalInfo),
572       mOriginKey(aOriginKey),
573       mPrivateBrowsingId(aPrivateBrowsingId),
574       mActorDestroyed(false) {
575   ::mozilla::ipc::AssertIsOnBackgroundThread();
576 }
577 
~LocalStorageCacheParent()578 LocalStorageCacheParent::~LocalStorageCacheParent() {
579   MOZ_ASSERT(mActorDestroyed);
580 }
581 
ActorDestroy(ActorDestroyReason aWhy)582 void LocalStorageCacheParent::ActorDestroy(ActorDestroyReason aWhy) {
583   ::mozilla::ipc::AssertIsOnBackgroundThread();
584   MOZ_ASSERT(!mActorDestroyed);
585 
586   mActorDestroyed = true;
587 
588   MOZ_ASSERT(gLocalStorageCacheParents);
589 
590   nsTArray<LocalStorageCacheParent*>* array;
591   gLocalStorageCacheParents->Get(mOriginKey, &array);
592   MOZ_ASSERT(array);
593 
594   array->RemoveElement(this);
595 
596   if (array->IsEmpty()) {
597     gLocalStorageCacheParents->Remove(mOriginKey);
598   }
599 
600   if (!gLocalStorageCacheParents->Count()) {
601     gLocalStorageCacheParents = nullptr;
602   }
603 }
604 
RecvDeleteMe()605 mozilla::ipc::IPCResult LocalStorageCacheParent::RecvDeleteMe() {
606   ::mozilla::ipc::AssertIsOnBackgroundThread();
607   MOZ_ASSERT(!mActorDestroyed);
608 
609   IProtocol* mgr = Manager();
610   if (!PBackgroundLocalStorageCacheParent::Send__delete__(this)) {
611     return IPC_FAIL_NO_REASON(mgr);
612   }
613   return IPC_OK();
614 }
615 
RecvNotify(const nsString & aDocumentURI,const nsString & aKey,const nsString & aOldValue,const nsString & aNewValue)616 mozilla::ipc::IPCResult LocalStorageCacheParent::RecvNotify(
617     const nsString& aDocumentURI, const nsString& aKey,
618     const nsString& aOldValue, const nsString& aNewValue) {
619   ::mozilla::ipc::AssertIsOnBackgroundThread();
620   MOZ_ASSERT(gLocalStorageCacheParents);
621 
622   nsTArray<LocalStorageCacheParent*>* array;
623   gLocalStorageCacheParents->Get(mOriginKey, &array);
624   MOZ_ASSERT(array);
625 
626   for (LocalStorageCacheParent* localStorageCacheParent : *array) {
627     if (localStorageCacheParent != this) {
628       // When bug 1443925 is fixed, we can compare mPrincipalInfo against
629       // localStorageCacheParent->PrincipalInfo() here on the background thread
630       // instead of posting it to the main thread.  The advantage of doing so is
631       // that it would save an IPC message in the case where the principals do
632       // not match.
633       Unused << localStorageCacheParent->SendObserve(
634           mPrincipalInfo, localStorageCacheParent->PrincipalInfo(),
635           mPrivateBrowsingId, aDocumentURI, aKey, aOldValue, aNewValue);
636     }
637   }
638 
639   return IPC_OK();
640 }
641 
642 // ----------------------------------------------------------------------------
643 // Parent
644 // ----------------------------------------------------------------------------
645 
646 class StorageDBParent::ObserverSink : public StorageObserverSink {
647   nsCOMPtr<nsIEventTarget> mOwningEventTarget;
648 
649   // Only touched on the PBackground thread.
650   StorageDBParent* MOZ_NON_OWNING_REF mActor;
651 
652  public:
ObserverSink(StorageDBParent * aActor)653   explicit ObserverSink(StorageDBParent* aActor)
654       : mOwningEventTarget(GetCurrentEventTarget()), mActor(aActor) {
655     ::mozilla::ipc::AssertIsOnBackgroundThread();
656     MOZ_ASSERT(aActor);
657   }
658 
659   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageDBParent::ObserverSink);
660 
661   void Start();
662 
663   void Stop();
664 
665  private:
666   ~ObserverSink() = default;
667 
668   void AddSink();
669 
670   void RemoveSink();
671 
672   void Notify(const nsCString& aTopic, const nsString& aOriginAttributesPattern,
673               const nsCString& aOriginScope);
674 
675   // StorageObserverSink
676   nsresult Observe(const char* aTopic, const nsAString& aOriginAttrPattern,
677                    const nsACString& aOriginScope) override;
678 };
679 
680 NS_IMPL_ADDREF(StorageDBParent)
NS_IMPL_RELEASE(StorageDBParent)681 NS_IMPL_RELEASE(StorageDBParent)
682 
683 void StorageDBParent::AddIPDLReference() {
684   MOZ_ASSERT(!mIPCOpen, "Attempting to retain multiple IPDL references");
685   mIPCOpen = true;
686   AddRef();
687 }
688 
ReleaseIPDLReference()689 void StorageDBParent::ReleaseIPDLReference() {
690   MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference");
691   mIPCOpen = false;
692   Release();
693 }
694 
695 namespace {}  // namespace
696 
StorageDBParent(const nsString & aProfilePath,const uint32_t aPrivateBrowsingId)697 StorageDBParent::StorageDBParent(const nsString& aProfilePath,
698                                  const uint32_t aPrivateBrowsingId)
699     : mProfilePath(aProfilePath),
700       mPrivateBrowsingId(aPrivateBrowsingId),
701       mIPCOpen(false) {
702   ::mozilla::ipc::AssertIsOnBackgroundThread();
703 
704   // We are always open by IPC only
705   AddIPDLReference();
706 }
707 
~StorageDBParent()708 StorageDBParent::~StorageDBParent() {
709   ::mozilla::ipc::AssertIsOnBackgroundThread();
710 
711   if (mObserverSink) {
712     mObserverSink->Stop();
713     mObserverSink = nullptr;
714   }
715 }
716 
Init()717 void StorageDBParent::Init() {
718   ::mozilla::ipc::AssertIsOnBackgroundThread();
719 
720   PBackgroundParent* actor = Manager();
721   MOZ_ASSERT(actor);
722 
723   if (::mozilla::ipc::BackgroundParent::IsOtherProcessActor(actor)) {
724     mObserverSink = new ObserverSink(this);
725     mObserverSink->Start();
726   }
727 
728   StorageDBThread* storageThread = StorageDBThread::Get(mPrivateBrowsingId);
729   if (storageThread) {
730     nsTArray<nsCString> scopes;
731     storageThread->GetOriginsHavingData(&scopes);
732     mozilla::Unused << SendOriginsHavingData(scopes);
733   }
734 }
735 
NewCache(const nsACString & aOriginSuffix,const nsACString & aOriginNoSuffix)736 StorageDBParent::CacheParentBridge* StorageDBParent::NewCache(
737     const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix) {
738   return new CacheParentBridge(this, aOriginSuffix, aOriginNoSuffix);
739 }
740 
ActorDestroy(ActorDestroyReason aWhy)741 void StorageDBParent::ActorDestroy(ActorDestroyReason aWhy) {
742   // Implement me! Bug 1005169
743 }
744 
RecvDeleteMe()745 mozilla::ipc::IPCResult StorageDBParent::RecvDeleteMe() {
746   ::mozilla::ipc::AssertIsOnBackgroundThread();
747 
748   IProtocol* mgr = Manager();
749   if (!PBackgroundStorageParent::Send__delete__(this)) {
750     return IPC_FAIL_NO_REASON(mgr);
751   }
752   return IPC_OK();
753 }
754 
RecvAsyncPreload(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const bool & aPriority)755 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncPreload(
756     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
757     const bool& aPriority) {
758   StorageDBThread* storageThread =
759       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
760   if (!storageThread) {
761     return IPC_FAIL_NO_REASON(this);
762   }
763 
764   storageThread->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix),
765                               aPriority);
766 
767   return IPC_OK();
768 }
769 
RecvAsyncGetUsage(const nsCString & aOriginNoSuffix)770 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncGetUsage(
771     const nsCString& aOriginNoSuffix) {
772   StorageDBThread* storageThread =
773       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
774   if (!storageThread) {
775     return IPC_FAIL_NO_REASON(this);
776   }
777 
778   // The object releases it self in LoadUsage method
779   RefPtr<UsageParentBridge> usage =
780       new UsageParentBridge(this, aOriginNoSuffix);
781 
782   storageThread->AsyncGetUsage(usage);
783 
784   return IPC_OK();
785 }
786 
787 namespace {
788 
789 // We need another implementation of LocalStorageCacheBridge to do
790 // synchronous IPC preload.  This class just receives Load* notifications
791 // and fills the returning arguments of RecvPreload with the database
792 // values for us.
793 class SyncLoadCacheHelper : public LocalStorageCacheBridge {
794  public:
SyncLoadCacheHelper(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,uint32_t aAlreadyLoadedCount,nsTArray<nsString> * aKeys,nsTArray<nsString> * aValues,nsresult * rv)795   SyncLoadCacheHelper(const nsCString& aOriginSuffix,
796                       const nsCString& aOriginNoSuffix,
797                       uint32_t aAlreadyLoadedCount, nsTArray<nsString>* aKeys,
798                       nsTArray<nsString>* aValues, nsresult* rv)
799       : mMonitor("DOM Storage SyncLoad IPC"),
800         mSuffix(aOriginSuffix),
801         mOrigin(aOriginNoSuffix),
802         mKeys(aKeys),
803         mValues(aValues),
804         mRv(rv),
805         mLoaded(false),
806         mLoadedCount(aAlreadyLoadedCount) {
807     // Precaution
808     *mRv = NS_ERROR_UNEXPECTED;
809   }
810 
Origin() const811   virtual const nsCString Origin() const override {
812     return LocalStorageManager::CreateOrigin(mSuffix, mOrigin);
813   }
OriginNoSuffix() const814   virtual const nsCString& OriginNoSuffix() const override { return mOrigin; }
OriginSuffix() const815   virtual const nsCString& OriginSuffix() const override { return mSuffix; }
Loaded()816   virtual bool Loaded() override { return mLoaded; }
LoadedCount()817   virtual uint32_t LoadedCount() override { return mLoadedCount; }
LoadItem(const nsAString & aKey,const nsString & aValue)818   virtual bool LoadItem(const nsAString& aKey,
819                         const nsString& aValue) override {
820     // Called on the aCache background thread
821     MOZ_ASSERT(!mLoaded);
822     if (mLoaded) {
823       return false;
824     }
825 
826     ++mLoadedCount;
827     mKeys->AppendElement(aKey);
828     mValues->AppendElement(aValue);
829     return true;
830   }
831 
LoadDone(nsresult aRv)832   virtual void LoadDone(nsresult aRv) override {
833     // Called on the aCache background thread
834     MonitorAutoLock monitor(mMonitor);
835     MOZ_ASSERT(!mLoaded && mRv);
836     mLoaded = true;
837     if (mRv) {
838       *mRv = aRv;
839       mRv = nullptr;
840     }
841     monitor.Notify();
842   }
843 
LoadWait()844   virtual void LoadWait() override {
845     // Called on the main thread, exits after LoadDone() call
846     MonitorAutoLock monitor(mMonitor);
847     while (!mLoaded) {
848       monitor.Wait();
849     }
850   }
851 
852  private:
853   Monitor mMonitor;
854   nsCString mSuffix, mOrigin;
855   nsTArray<nsString>* mKeys;
856   nsTArray<nsString>* mValues;
857   nsresult* mRv;
858   bool mLoaded;
859   uint32_t mLoadedCount;
860 };
861 
862 }  // namespace
863 
RecvPreload(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const uint32_t & aAlreadyLoadedCount,nsTArray<nsString> * aKeys,nsTArray<nsString> * aValues,nsresult * aRv)864 mozilla::ipc::IPCResult StorageDBParent::RecvPreload(
865     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
866     const uint32_t& aAlreadyLoadedCount, nsTArray<nsString>* aKeys,
867     nsTArray<nsString>* aValues, nsresult* aRv) {
868   StorageDBThread* storageThread =
869       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
870   if (!storageThread) {
871     return IPC_FAIL_NO_REASON(this);
872   }
873 
874   RefPtr<SyncLoadCacheHelper> cache(
875       new SyncLoadCacheHelper(aOriginSuffix, aOriginNoSuffix,
876                               aAlreadyLoadedCount, aKeys, aValues, aRv));
877 
878   storageThread->SyncPreload(cache, true);
879 
880   return IPC_OK();
881 }
882 
RecvAsyncAddItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey,const nsString & aValue)883 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncAddItem(
884     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
885     const nsString& aKey, const nsString& aValue) {
886   StorageDBThread* storageThread =
887       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
888   if (!storageThread) {
889     return IPC_FAIL_NO_REASON(this);
890   }
891 
892   nsresult rv = storageThread->AsyncAddItem(
893       NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
894   if (NS_FAILED(rv) && mIPCOpen) {
895     mozilla::Unused << SendError(rv);
896   }
897 
898   return IPC_OK();
899 }
900 
RecvAsyncUpdateItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey,const nsString & aValue)901 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncUpdateItem(
902     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
903     const nsString& aKey, const nsString& aValue) {
904   StorageDBThread* storageThread =
905       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
906   if (!storageThread) {
907     return IPC_FAIL_NO_REASON(this);
908   }
909 
910   nsresult rv = storageThread->AsyncUpdateItem(
911       NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
912   if (NS_FAILED(rv) && mIPCOpen) {
913     mozilla::Unused << SendError(rv);
914   }
915 
916   return IPC_OK();
917 }
918 
RecvAsyncRemoveItem(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix,const nsString & aKey)919 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncRemoveItem(
920     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
921     const nsString& aKey) {
922   StorageDBThread* storageThread =
923       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
924   if (!storageThread) {
925     return IPC_FAIL_NO_REASON(this);
926   }
927 
928   nsresult rv = storageThread->AsyncRemoveItem(
929       NewCache(aOriginSuffix, aOriginNoSuffix), aKey);
930   if (NS_FAILED(rv) && mIPCOpen) {
931     mozilla::Unused << SendError(rv);
932   }
933 
934   return IPC_OK();
935 }
936 
RecvAsyncClear(const nsCString & aOriginSuffix,const nsCString & aOriginNoSuffix)937 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncClear(
938     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix) {
939   StorageDBThread* storageThread =
940       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
941   if (!storageThread) {
942     return IPC_FAIL_NO_REASON(this);
943   }
944 
945   nsresult rv =
946       storageThread->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
947   if (NS_FAILED(rv) && mIPCOpen) {
948     mozilla::Unused << SendError(rv);
949   }
950 
951   return IPC_OK();
952 }
953 
RecvAsyncFlush()954 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncFlush() {
955   StorageDBThread* storageThread = StorageDBThread::Get(mPrivateBrowsingId);
956   if (!storageThread) {
957     return IPC_FAIL_NO_REASON(this);
958   }
959 
960   storageThread->AsyncFlush();
961 
962   return IPC_OK();
963 }
964 
RecvStartup()965 mozilla::ipc::IPCResult StorageDBParent::RecvStartup() {
966   StorageDBThread* storageThread =
967       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
968   if (!storageThread) {
969     return IPC_FAIL_NO_REASON(this);
970   }
971 
972   return IPC_OK();
973 }
974 
RecvClearAll()975 mozilla::ipc::IPCResult StorageDBParent::RecvClearAll() {
976   StorageDBThread* storageThread =
977       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
978   if (!storageThread) {
979     return IPC_FAIL_NO_REASON(this);
980   }
981 
982   storageThread->AsyncClearAll();
983 
984   return IPC_OK();
985 }
986 
RecvClearMatchingOrigin(const nsCString & aOriginNoSuffix)987 mozilla::ipc::IPCResult StorageDBParent::RecvClearMatchingOrigin(
988     const nsCString& aOriginNoSuffix) {
989   StorageDBThread* storageThread =
990       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
991   if (!storageThread) {
992     return IPC_FAIL_NO_REASON(this);
993   }
994 
995   storageThread->AsyncClearMatchingOrigin(aOriginNoSuffix);
996 
997   return IPC_OK();
998 }
999 
RecvClearMatchingOriginAttributes(const OriginAttributesPattern & aPattern)1000 mozilla::ipc::IPCResult StorageDBParent::RecvClearMatchingOriginAttributes(
1001     const OriginAttributesPattern& aPattern) {
1002   StorageDBThread* storageThread =
1003       StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
1004   if (!storageThread) {
1005     return IPC_FAIL_NO_REASON(this);
1006   }
1007 
1008   storageThread->AsyncClearMatchingOriginAttributes(aPattern);
1009 
1010   return IPC_OK();
1011 }
1012 
Observe(const nsCString & aTopic,const nsString & aOriginAttributesPattern,const nsCString & aOriginScope)1013 void StorageDBParent::Observe(const nsCString& aTopic,
1014                               const nsString& aOriginAttributesPattern,
1015                               const nsCString& aOriginScope) {
1016   if (mIPCOpen) {
1017     mozilla::Unused << SendObserve(aTopic, aOriginAttributesPattern,
1018                                    aOriginScope);
1019   }
1020 }
1021 
1022 namespace {
1023 
1024 // Results must be sent back on the main thread
1025 class LoadRunnable : public Runnable {
1026  public:
1027   enum TaskType { loadItem, loadDone };
1028 
LoadRunnable(StorageDBParent * aParent,TaskType aType,const nsACString & aOriginSuffix,const nsACString & aOriginNoSuffix,const nsAString & aKey=u""_ns,const nsAString & aValue=u""_ns)1029   LoadRunnable(StorageDBParent* aParent, TaskType aType,
1030                const nsACString& aOriginSuffix,
1031                const nsACString& aOriginNoSuffix,
1032                const nsAString& aKey = u""_ns, const nsAString& aValue = u""_ns)
1033       : Runnable("dom::LoadRunnable"),
1034         mParent(aParent),
1035         mType(aType),
1036         mSuffix(aOriginSuffix),
1037         mOrigin(aOriginNoSuffix),
1038         mKey(aKey),
1039         mValue(aValue),
1040         mRv(NS_ERROR_NOT_INITIALIZED) {}
1041 
LoadRunnable(StorageDBParent * aParent,TaskType aType,const nsACString & aOriginSuffix,const nsACString & aOriginNoSuffix,nsresult aRv)1042   LoadRunnable(StorageDBParent* aParent, TaskType aType,
1043                const nsACString& aOriginSuffix,
1044                const nsACString& aOriginNoSuffix, nsresult aRv)
1045       : Runnable("dom::LoadRunnable"),
1046         mParent(aParent),
1047         mType(aType),
1048         mSuffix(aOriginSuffix),
1049         mOrigin(aOriginNoSuffix),
1050         mRv(aRv) {}
1051 
1052  private:
1053   RefPtr<StorageDBParent> mParent;
1054   TaskType mType;
1055   nsCString mSuffix, mOrigin;
1056   nsString mKey;
1057   nsString mValue;
1058   nsresult mRv;
1059 
Run()1060   NS_IMETHOD Run() override {
1061     if (!mParent->IPCOpen()) {
1062       return NS_OK;
1063     }
1064 
1065     switch (mType) {
1066       case loadItem:
1067         mozilla::Unused << mParent->SendLoadItem(mSuffix, mOrigin, mKey,
1068                                                  mValue);
1069         break;
1070       case loadDone:
1071         mozilla::Unused << mParent->SendLoadDone(mSuffix, mOrigin, mRv);
1072         break;
1073     }
1074 
1075     mParent = nullptr;
1076 
1077     return NS_OK;
1078   }
1079 };
1080 
1081 }  // namespace
1082 
1083 // StorageDBParent::CacheParentBridge
1084 
Origin() const1085 const nsCString StorageDBParent::CacheParentBridge::Origin() const {
1086   return LocalStorageManager::CreateOrigin(mOriginSuffix, mOriginNoSuffix);
1087 }
1088 
LoadItem(const nsAString & aKey,const nsString & aValue)1089 bool StorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey,
1090                                                   const nsString& aValue) {
1091   if (mLoaded) {
1092     return false;
1093   }
1094 
1095   ++mLoadedCount;
1096 
1097   RefPtr<LoadRunnable> r =
1098       new LoadRunnable(mParent, LoadRunnable::loadItem, mOriginSuffix,
1099                        mOriginNoSuffix, aKey, aValue);
1100 
1101   MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
1102 
1103   return true;
1104 }
1105 
LoadDone(nsresult aRv)1106 void StorageDBParent::CacheParentBridge::LoadDone(nsresult aRv) {
1107   // Prevent send of duplicate LoadDone.
1108   if (mLoaded) {
1109     return;
1110   }
1111 
1112   mLoaded = true;
1113 
1114   RefPtr<LoadRunnable> r = new LoadRunnable(
1115       mParent, LoadRunnable::loadDone, mOriginSuffix, mOriginNoSuffix, aRv);
1116 
1117   MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
1118 }
1119 
LoadWait()1120 void StorageDBParent::CacheParentBridge::LoadWait() {
1121   // Should never be called on this implementation
1122   MOZ_ASSERT(false);
1123 }
1124 
1125 // XXX Fix me!
1126 // This should be just:
1127 // NS_IMPL_RELEASE_WITH_DESTROY(StorageDBParent::CacheParentBridge, Destroy)
1128 // But due to different strings used for refcount logging and different return
1129 // types, this is done manually for now.
NS_IMETHODIMP_(void)1130 NS_IMETHODIMP_(void)
1131 StorageDBParent::CacheParentBridge::Release(void) {
1132   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
1133   nsrefcnt count = --mRefCnt;
1134   NS_LOG_RELEASE(this, count, "LocalStorageCacheBridge");
1135   if (0 == count) {
1136     mRefCnt = 1; /* stabilize */
1137     /* enable this to find non-threadsafe destructors: */
1138     /* NS_ASSERT_OWNINGTHREAD(_class); */
1139     Destroy();
1140   }
1141 }
1142 
Destroy()1143 void StorageDBParent::CacheParentBridge::Destroy() {
1144   if (mOwningEventTarget->IsOnCurrentThread()) {
1145     delete this;
1146     return;
1147   }
1148 
1149   RefPtr<Runnable> destroyRunnable = NewNonOwningRunnableMethod(
1150       "CacheParentBridge::Destroy", this, &CacheParentBridge::Destroy);
1151 
1152   MOZ_ALWAYS_SUCCEEDS(
1153       mOwningEventTarget->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL));
1154 }
1155 
1156 // StorageDBParent::UsageParentBridge
1157 
1158 namespace {
1159 
1160 class UsageRunnable : public Runnable {
1161  public:
UsageRunnable(StorageDBParent * aParent,const nsACString & aOriginScope,const int64_t & aUsage)1162   UsageRunnable(StorageDBParent* aParent, const nsACString& aOriginScope,
1163                 const int64_t& aUsage)
1164       : Runnable("dom::UsageRunnable"),
1165         mParent(aParent),
1166         mOriginScope(aOriginScope),
1167         mUsage(aUsage) {}
1168 
1169  private:
Run()1170   NS_IMETHOD Run() override {
1171     if (!mParent->IPCOpen()) {
1172       return NS_OK;
1173     }
1174 
1175     mozilla::Unused << mParent->SendLoadUsage(mOriginScope, mUsage);
1176 
1177     mParent = nullptr;
1178 
1179     return NS_OK;
1180   }
1181 
1182   RefPtr<StorageDBParent> mParent;
1183   nsCString mOriginScope;
1184   int64_t mUsage;
1185 };
1186 
1187 }  // namespace
1188 
LoadUsage(const int64_t aUsage)1189 void StorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage) {
1190   RefPtr<UsageRunnable> r = new UsageRunnable(mParent, mOriginScope, aUsage);
1191 
1192   MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(r, NS_DISPATCH_NORMAL));
1193 }
1194 
1195 // XXX Fix me!
1196 // This should be just:
1197 // NS_IMPL_RELEASE_WITH_DESTROY(StorageDBParent::UsageParentBridge, Destroy)
1198 // But due to different strings used for refcount logging, this is done manually
1199 // for now.
NS_IMETHODIMP_(MozExternalRefCountType)1200 NS_IMETHODIMP_(MozExternalRefCountType)
1201 StorageDBParent::UsageParentBridge::Release(void) {
1202   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
1203   nsrefcnt count = --mRefCnt;
1204   NS_LOG_RELEASE(this, count, "StorageUsageBridge");
1205   if (count == 0) {
1206     Destroy();
1207     return 0;
1208   }
1209   return count;
1210 }
1211 
Destroy()1212 void StorageDBParent::UsageParentBridge::Destroy() {
1213   if (mOwningEventTarget->IsOnCurrentThread()) {
1214     delete this;
1215     return;
1216   }
1217 
1218   RefPtr<Runnable> destroyRunnable = NewNonOwningRunnableMethod(
1219       "UsageParentBridge::Destroy", this, &UsageParentBridge::Destroy);
1220 
1221   MOZ_ALWAYS_SUCCEEDS(
1222       mOwningEventTarget->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL));
1223 }
1224 
Start()1225 void StorageDBParent::ObserverSink::Start() {
1226   ::mozilla::ipc::AssertIsOnBackgroundThread();
1227 
1228   RefPtr<Runnable> runnable =
1229       NewRunnableMethod("StorageDBParent::ObserverSink::AddSink", this,
1230                         &StorageDBParent::ObserverSink::AddSink);
1231 
1232   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
1233 }
1234 
Stop()1235 void StorageDBParent::ObserverSink::Stop() {
1236   ::mozilla::ipc::AssertIsOnBackgroundThread();
1237 
1238   mActor = nullptr;
1239 
1240   RefPtr<Runnable> runnable =
1241       NewRunnableMethod("StorageDBParent::ObserverSink::RemoveSink", this,
1242                         &StorageDBParent::ObserverSink::RemoveSink);
1243 
1244   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
1245 }
1246 
AddSink()1247 void StorageDBParent::ObserverSink::AddSink() {
1248   MOZ_ASSERT(NS_IsMainThread());
1249 
1250   StorageObserver* observer = StorageObserver::Self();
1251   if (observer) {
1252     observer->AddSink(this);
1253   }
1254 }
1255 
RemoveSink()1256 void StorageDBParent::ObserverSink::RemoveSink() {
1257   MOZ_ASSERT(NS_IsMainThread());
1258 
1259   StorageObserver* observer = StorageObserver::Self();
1260   if (observer) {
1261     observer->RemoveSink(this);
1262   }
1263 }
1264 
Notify(const nsCString & aTopic,const nsString & aOriginAttributesPattern,const nsCString & aOriginScope)1265 void StorageDBParent::ObserverSink::Notify(
1266     const nsCString& aTopic, const nsString& aOriginAttributesPattern,
1267     const nsCString& aOriginScope) {
1268   ::mozilla::ipc::AssertIsOnBackgroundThread();
1269 
1270   if (mActor) {
1271     mActor->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
1272   }
1273 }
1274 
Observe(const char * aTopic,const nsAString & aOriginAttributesPattern,const nsACString & aOriginScope)1275 nsresult StorageDBParent::ObserverSink::Observe(
1276     const char* aTopic, const nsAString& aOriginAttributesPattern,
1277     const nsACString& aOriginScope) {
1278   MOZ_ASSERT(NS_IsMainThread());
1279 
1280   RefPtr<Runnable> runnable = NewRunnableMethod<nsCString, nsString, nsCString>(
1281       "StorageDBParent::ObserverSink::Observe2", this,
1282       &StorageDBParent::ObserverSink::Notify, aTopic, aOriginAttributesPattern,
1283       aOriginScope);
1284 
1285   MOZ_ALWAYS_SUCCEEDS(
1286       mOwningEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
1287 
1288   return NS_OK;
1289 }
1290 
SessionStorageObserverParent()1291 SessionStorageObserverParent::SessionStorageObserverParent()
1292     : mActorDestroyed(false) {
1293   MOZ_ASSERT(NS_IsMainThread());
1294 
1295   StorageObserver* observer = StorageObserver::Self();
1296   if (observer) {
1297     observer->AddSink(this);
1298   }
1299 }
1300 
~SessionStorageObserverParent()1301 SessionStorageObserverParent::~SessionStorageObserverParent() {
1302   MOZ_ASSERT(mActorDestroyed);
1303 
1304   StorageObserver* observer = StorageObserver::Self();
1305   if (observer) {
1306     observer->RemoveSink(this);
1307   }
1308 }
1309 
ActorDestroy(ActorDestroyReason aWhy)1310 void SessionStorageObserverParent::ActorDestroy(ActorDestroyReason aWhy) {
1311   MOZ_ASSERT(NS_IsMainThread());
1312   MOZ_ASSERT(!mActorDestroyed);
1313 
1314   mActorDestroyed = true;
1315 }
1316 
RecvDeleteMe()1317 mozilla::ipc::IPCResult SessionStorageObserverParent::RecvDeleteMe() {
1318   MOZ_ASSERT(NS_IsMainThread());
1319   MOZ_ASSERT(!mActorDestroyed);
1320 
1321   IProtocol* mgr = Manager();
1322   if (!PSessionStorageObserverParent::Send__delete__(this)) {
1323     return IPC_FAIL_NO_REASON(mgr);
1324   }
1325   return IPC_OK();
1326 }
1327 
Observe(const char * aTopic,const nsAString & aOriginAttributesPattern,const nsACString & aOriginScope)1328 nsresult SessionStorageObserverParent::Observe(
1329     const char* aTopic, const nsAString& aOriginAttributesPattern,
1330     const nsACString& aOriginScope) {
1331   MOZ_ASSERT(NS_IsMainThread());
1332 
1333   if (!mActorDestroyed) {
1334     mozilla::Unused << SendObserve(nsCString(aTopic),
1335                                    nsString(aOriginAttributesPattern),
1336                                    nsCString(aOriginScope));
1337   }
1338   return NS_OK;
1339 }
1340 
SessionStorageCacheParent(const nsCString & aOriginAttrs,const nsCString & aOriginKey,SessionStorageManagerParent * aActor)1341 SessionStorageCacheParent::SessionStorageCacheParent(
1342     const nsCString& aOriginAttrs, const nsCString& aOriginKey,
1343     SessionStorageManagerParent* aActor)
1344     : mOriginAttrs(aOriginAttrs),
1345       mOriginKey(aOriginKey),
1346       mManagerActor(aActor) {
1347   ::mozilla::ipc::AssertIsOnBackgroundThread();
1348   MOZ_ASSERT(mManagerActor);
1349 }
1350 
1351 SessionStorageCacheParent::~SessionStorageCacheParent() = default;
1352 
ActorDestroy(ActorDestroyReason aWhy)1353 void SessionStorageCacheParent::ActorDestroy(ActorDestroyReason aWhy) {
1354   ::mozilla::ipc::AssertIsOnBackgroundThread();
1355 
1356   mManagerActor = nullptr;
1357 }
1358 
RecvLoad(nsTArray<SSSetItemInfo> * aData)1359 mozilla::ipc::IPCResult SessionStorageCacheParent::RecvLoad(
1360     nsTArray<SSSetItemInfo>* aData) {
1361   ::mozilla::ipc::AssertIsOnBackgroundThread();
1362   MOZ_ASSERT(mManagerActor);
1363 
1364   mLoadReceived.Flip();
1365 
1366   RefPtr<BackgroundSessionStorageManager> manager = mManagerActor->GetManager();
1367   MOZ_ASSERT(manager);
1368 
1369   manager->CopyDataToContentProcess(mOriginAttrs, mOriginKey, *aData);
1370 
1371   return IPC_OK();
1372 }
1373 
RecvCheckpoint(nsTArray<SSWriteInfo> && aWriteInfos)1374 mozilla::ipc::IPCResult SessionStorageCacheParent::RecvCheckpoint(
1375     nsTArray<SSWriteInfo>&& aWriteInfos) {
1376   ::mozilla::ipc::AssertIsOnBackgroundThread();
1377   MOZ_ASSERT(mManagerActor);
1378 
1379   RefPtr<BackgroundSessionStorageManager> manager = mManagerActor->GetManager();
1380   MOZ_ASSERT(manager);
1381 
1382   manager->UpdateData(mOriginAttrs, mOriginKey, aWriteInfos);
1383 
1384   return IPC_OK();
1385 }
1386 
RecvDeleteMe()1387 mozilla::ipc::IPCResult SessionStorageCacheParent::RecvDeleteMe() {
1388   ::mozilla::ipc::AssertIsOnBackgroundThread();
1389   MOZ_ASSERT(mManagerActor);
1390 
1391   mManagerActor = nullptr;
1392 
1393   IProtocol* mgr = Manager();
1394   if (!PBackgroundSessionStorageCacheParent::Send__delete__(this)) {
1395     return IPC_FAIL(
1396         mgr, "Failed to delete PBackgroundSessionStorageCacheParent actor");
1397   }
1398   return IPC_OK();
1399 }
1400 
SessionStorageManagerParent(uint64_t aTopContextId)1401 SessionStorageManagerParent::SessionStorageManagerParent(uint64_t aTopContextId)
1402     : mBackgroundManager(
1403           BackgroundSessionStorageManager::GetOrCreate(aTopContextId)) {
1404   ::mozilla::ipc::AssertIsOnBackgroundThread();
1405   MOZ_ASSERT(mBackgroundManager);
1406 }
1407 
1408 SessionStorageManagerParent::~SessionStorageManagerParent() = default;
1409 
ActorDestroy(ActorDestroyReason aWhy)1410 void SessionStorageManagerParent::ActorDestroy(ActorDestroyReason aWhy) {
1411   ::mozilla::ipc::AssertIsOnBackgroundThread();
1412 
1413   mBackgroundManager = nullptr;
1414 }
1415 
1416 already_AddRefed<PBackgroundSessionStorageCacheParent>
AllocPBackgroundSessionStorageCacheParent(const nsCString & aOriginAttrs,const nsCString & aOriginKey)1417 SessionStorageManagerParent::AllocPBackgroundSessionStorageCacheParent(
1418     const nsCString& aOriginAttrs, const nsCString& aOriginKey) {
1419   return MakeAndAddRef<SessionStorageCacheParent>(aOriginAttrs, aOriginKey,
1420                                                   this);
1421 }
1422 
GetManager() const1423 BackgroundSessionStorageManager* SessionStorageManagerParent::GetManager()
1424     const {
1425   return mBackgroundManager;
1426 }
1427 
RecvDeleteMe()1428 mozilla::ipc::IPCResult SessionStorageManagerParent::RecvDeleteMe() {
1429   ::mozilla::ipc::AssertIsOnBackgroundThread();
1430   MOZ_ASSERT(mBackgroundManager);
1431 
1432   mBackgroundManager = nullptr;
1433 
1434   IProtocol* mgr = Manager();
1435   if (!PBackgroundSessionStorageManagerParent::Send__delete__(this)) {
1436     return IPC_FAIL(
1437         mgr, "Failed to delete PBackgroundSessionStorageManagerParent actor");
1438   }
1439   return IPC_OK();
1440 }
1441 
1442 /*******************************************************************************
1443  * Exported functions
1444  ******************************************************************************/
1445 
AllocPBackgroundLocalStorageCacheParent(const mozilla::ipc::PrincipalInfo & aPrincipalInfo,const nsCString & aOriginKey,const uint32_t & aPrivateBrowsingId)1446 PBackgroundLocalStorageCacheParent* AllocPBackgroundLocalStorageCacheParent(
1447     const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
1448     const nsCString& aOriginKey, const uint32_t& aPrivateBrowsingId) {
1449   ::mozilla::ipc::AssertIsOnBackgroundThread();
1450 
1451   RefPtr<LocalStorageCacheParent> actor = new LocalStorageCacheParent(
1452       aPrincipalInfo, aOriginKey, aPrivateBrowsingId);
1453 
1454   // Transfer ownership to IPDL.
1455   return actor.forget().take();
1456 }
1457 
RecvPBackgroundLocalStorageCacheConstructor(mozilla::ipc::PBackgroundParent * aBackgroundActor,PBackgroundLocalStorageCacheParent * aActor,const mozilla::ipc::PrincipalInfo & aPrincipalInfo,const nsCString & aOriginKey,const uint32_t & aPrivateBrowsingId)1458 mozilla::ipc::IPCResult RecvPBackgroundLocalStorageCacheConstructor(
1459     mozilla::ipc::PBackgroundParent* aBackgroundActor,
1460     PBackgroundLocalStorageCacheParent* aActor,
1461     const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
1462     const nsCString& aOriginKey, const uint32_t& aPrivateBrowsingId) {
1463   ::mozilla::ipc::AssertIsOnBackgroundThread();
1464   MOZ_ASSERT(aActor);
1465 
1466   auto* actor = static_cast<LocalStorageCacheParent*>(aActor);
1467 
1468   if (!gLocalStorageCacheParents) {
1469     gLocalStorageCacheParents = new LocalStorageCacheParentHashtable();
1470   }
1471 
1472   gLocalStorageCacheParents->GetOrInsertNew(aOriginKey)->AppendElement(actor);
1473 
1474   // We are currently trusting the content process not to lie to us.  It is
1475   // future work to consult the ClientManager to determine whether this is a
1476   // legitimate origin for the content process.
1477 
1478   return IPC_OK();
1479 }
1480 
DeallocPBackgroundLocalStorageCacheParent(PBackgroundLocalStorageCacheParent * aActor)1481 bool DeallocPBackgroundLocalStorageCacheParent(
1482     PBackgroundLocalStorageCacheParent* aActor) {
1483   ::mozilla::ipc::AssertIsOnBackgroundThread();
1484   MOZ_ASSERT(aActor);
1485 
1486   // Transfer ownership back from IPDL.
1487   RefPtr<LocalStorageCacheParent> actor =
1488       dont_AddRef(static_cast<LocalStorageCacheParent*>(aActor));
1489 
1490   return true;
1491 }
1492 
AllocPBackgroundStorageParent(const nsString & aProfilePath,const uint32_t & aPrivateBrowsingId)1493 PBackgroundStorageParent* AllocPBackgroundStorageParent(
1494     const nsString& aProfilePath, const uint32_t& aPrivateBrowsingId) {
1495   ::mozilla::ipc::AssertIsOnBackgroundThread();
1496 
1497   if (NS_WARN_IF(NextGenLocalStorageEnabled()) ||
1498       NS_WARN_IF(aPrivateBrowsingId >= kPrivateBrowsingIdCount)) {
1499     return nullptr;
1500   }
1501 
1502   return new StorageDBParent(aProfilePath, aPrivateBrowsingId);
1503 }
1504 
RecvPBackgroundStorageConstructor(PBackgroundStorageParent * aActor,const nsString & aProfilePath,const uint32_t & aPrivateBrowsingId)1505 mozilla::ipc::IPCResult RecvPBackgroundStorageConstructor(
1506     PBackgroundStorageParent* aActor, const nsString& aProfilePath,
1507     const uint32_t& aPrivateBrowsingId) {
1508   ::mozilla::ipc::AssertIsOnBackgroundThread();
1509   MOZ_ASSERT(aActor);
1510   MOZ_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount);
1511   MOZ_ASSERT(!NextGenLocalStorageEnabled());
1512 
1513   auto* actor = static_cast<StorageDBParent*>(aActor);
1514   actor->Init();
1515   return IPC_OK();
1516 }
1517 
DeallocPBackgroundStorageParent(PBackgroundStorageParent * aActor)1518 bool DeallocPBackgroundStorageParent(PBackgroundStorageParent* aActor) {
1519   ::mozilla::ipc::AssertIsOnBackgroundThread();
1520   MOZ_ASSERT(aActor);
1521 
1522   StorageDBParent* actor = static_cast<StorageDBParent*>(aActor);
1523   actor->ReleaseIPDLReference();
1524   return true;
1525 }
1526 
AllocPSessionStorageObserverParent()1527 PSessionStorageObserverParent* AllocPSessionStorageObserverParent() {
1528   MOZ_ASSERT(NS_IsMainThread());
1529 
1530   RefPtr<SessionStorageObserverParent> actor =
1531       new SessionStorageObserverParent();
1532 
1533   // Transfer ownership to IPDL.
1534   return actor.forget().take();
1535 }
1536 
RecvPSessionStorageObserverConstructor(PSessionStorageObserverParent * aActor)1537 bool RecvPSessionStorageObserverConstructor(
1538     PSessionStorageObserverParent* aActor) {
1539   MOZ_ASSERT(NS_IsMainThread());
1540   MOZ_ASSERT(aActor);
1541 
1542   return true;
1543 }
1544 
DeallocPSessionStorageObserverParent(PSessionStorageObserverParent * aActor)1545 bool DeallocPSessionStorageObserverParent(
1546     PSessionStorageObserverParent* aActor) {
1547   MOZ_ASSERT(NS_IsMainThread());
1548   MOZ_ASSERT(aActor);
1549 
1550   // Transfer ownership back from IPDL.
1551   RefPtr<SessionStorageObserverParent> actor =
1552       dont_AddRef(static_cast<SessionStorageObserverParent*>(aActor));
1553 
1554   return true;
1555 }
1556 
1557 already_AddRefed<PBackgroundSessionStorageManagerParent>
AllocPBackgroundSessionStorageManagerParent(const uint64_t & aTopContextId)1558 AllocPBackgroundSessionStorageManagerParent(const uint64_t& aTopContextId) {
1559   return MakeAndAddRef<SessionStorageManagerParent>(aTopContextId);
1560 }
1561 
1562 }  // namespace dom
1563 }  // namespace mozilla
1564