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