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