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