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 "StorageNotifierService.h"
8 #include "StorageUtils.h"
9 #include "mozilla/dom/StorageEvent.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/StaticPtr.h"
12 #include "nsThreadUtils.h"
13 
14 namespace mozilla {
15 namespace dom {
16 
17 namespace {
18 
19 // This boolean is used to avoid the creation of the service after been
20 // distroyed on shutdown.
21 bool gStorageShuttingDown = false;
22 
23 StaticRefPtr<StorageNotifierService> gStorageNotifierService;
24 
25 }  // namespace
26 
27 /* static */
GetOrCreate()28 StorageNotifierService* StorageNotifierService::GetOrCreate() {
29   MOZ_ASSERT(NS_IsMainThread());
30   if (!gStorageNotifierService && !gStorageShuttingDown) {
31     gStorageNotifierService = new StorageNotifierService();
32     ClearOnShutdown(&gStorageNotifierService);
33   }
34 
35   return gStorageNotifierService;
36 }
37 
StorageNotifierService()38 StorageNotifierService::StorageNotifierService() {
39   MOZ_ASSERT(NS_IsMainThread());
40   MOZ_ASSERT(!gStorageNotifierService);
41 }
42 
~StorageNotifierService()43 StorageNotifierService::~StorageNotifierService() {
44   MOZ_ASSERT(NS_IsMainThread());
45   MOZ_ASSERT(!gStorageNotifierService);
46   gStorageShuttingDown = true;
47 }
48 
49 /* static */
Broadcast(StorageEvent * aEvent,const char16_t * aStorageType,bool aPrivateBrowsing,bool aImmediateDispatch)50 void StorageNotifierService::Broadcast(StorageEvent* aEvent,
51                                        const char16_t* aStorageType,
52                                        bool aPrivateBrowsing,
53                                        bool aImmediateDispatch) {
54   MOZ_ASSERT(NS_IsMainThread());
55 
56   RefPtr<StorageNotifierService> service = gStorageNotifierService;
57   if (!service) {
58     return;
59   }
60 
61   RefPtr<StorageEvent> event = aEvent;
62 
63   for (const auto& observer : service->mObservers.ForwardRange()) {
64     // Enforce that the source storage area's private browsing state matches
65     // this window's state.  These flag checks and their maintenance independent
66     // from the principal's OriginAttributes matter because chrome docshells
67     // that are part of private browsing windows can be private browsing without
68     // having their OriginAttributes set (because they have the system
69     // principal).
70     if (aPrivateBrowsing != observer->IsPrivateBrowsing()) {
71       continue;
72     }
73 
74     // No reasons to continue if the principal of the event doesn't match with
75     // the window's one.
76     if (!StorageUtils::PrincipalsEqual(
77             aEvent->GetPrincipal(), observer->GetEffectiveStoragePrincipal())) {
78       continue;
79     }
80 
81     const auto pinnedObserver = observer;
82 
83     RefPtr<Runnable> r = NS_NewRunnableFunction(
84         "StorageNotifierService::Broadcast",
85         [pinnedObserver, event, aStorageType, aPrivateBrowsing,
86          aImmediateDispatch]() {
87           // Check principals again. EffectiveStoragePrincipal may be changed
88           // when relaxed.
89           if (!aImmediateDispatch &&
90               !StorageUtils::PrincipalsEqual(
91                   event->GetPrincipal(),
92                   pinnedObserver->GetEffectiveStoragePrincipal())) {
93             return;
94           }
95 
96           pinnedObserver->ObserveStorageNotification(event, aStorageType,
97                                                      aPrivateBrowsing);
98         });
99 
100     if (aImmediateDispatch) {
101       r->Run();
102     } else {
103       nsCOMPtr<nsIEventTarget> et = pinnedObserver->GetEventTarget();
104       if (et) {
105         et->Dispatch(r.forget());
106       }
107     }
108   }
109 }
110 
Register(StorageNotificationObserver * aObserver)111 void StorageNotifierService::Register(StorageNotificationObserver* aObserver) {
112   MOZ_ASSERT(NS_IsMainThread());
113   MOZ_ASSERT(aObserver);
114   MOZ_ASSERT(!mObservers.Contains(aObserver));
115 
116   mObservers.AppendElement(aObserver);
117 }
118 
Unregister(StorageNotificationObserver * aObserver)119 void StorageNotifierService::Unregister(
120     StorageNotificationObserver* aObserver) {
121   MOZ_ASSERT(NS_IsMainThread());
122   MOZ_ASSERT(aObserver);
123 
124   // No assertion about mObservers containing aObserver because window calls
125   // this method multiple times.
126 
127   mObservers.RemoveElement(aObserver);
128 }
129 
130 }  // namespace dom
131 }  // namespace mozilla
132