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 "SharedWorkerManager.h"
8 #include "SharedWorkerParent.h"
9 #include "SharedWorkerService.h"
10 #include "mozilla/dom/PSharedWorker.h"
11 #include "mozilla/ipc/BackgroundParent.h"
12 #include "mozilla/ipc/URIUtils.h"
13 #include "mozilla/dom/RemoteWorkerController.h"
14 #include "nsIConsoleReportCollector.h"
15 #include "nsIPrincipal.h"
16 #include "nsProxyRelease.h"
17 
18 namespace mozilla {
19 namespace dom {
20 
21 // static
Create(SharedWorkerService * aService,nsIEventTarget * aPBackgroundEventTarget,const RemoteWorkerData & aData,nsIPrincipal * aLoadingPrincipal,const OriginAttributes & aStoragePrincipalAttrs)22 already_AddRefed<SharedWorkerManagerHolder> SharedWorkerManager::Create(
23     SharedWorkerService* aService, nsIEventTarget* aPBackgroundEventTarget,
24     const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal,
25     const OriginAttributes& aStoragePrincipalAttrs) {
26   MOZ_ASSERT(NS_IsMainThread());
27 
28   RefPtr<SharedWorkerManager> manager =
29       new SharedWorkerManager(aPBackgroundEventTarget, aData, aLoadingPrincipal,
30                               aStoragePrincipalAttrs);
31 
32   RefPtr<SharedWorkerManagerHolder> holder =
33       new SharedWorkerManagerHolder(manager, aService);
34   return holder.forget();
35 }
36 
SharedWorkerManager(nsIEventTarget * aPBackgroundEventTarget,const RemoteWorkerData & aData,nsIPrincipal * aLoadingPrincipal,const OriginAttributes & aStoragePrincipalAttrs)37 SharedWorkerManager::SharedWorkerManager(
38     nsIEventTarget* aPBackgroundEventTarget, const RemoteWorkerData& aData,
39     nsIPrincipal* aLoadingPrincipal,
40     const OriginAttributes& aStoragePrincipalAttrs)
41     : mPBackgroundEventTarget(aPBackgroundEventTarget),
42       mLoadingPrincipal(aLoadingPrincipal),
43       mDomain(aData.domain()),
44       mStoragePrincipalAttrs(aStoragePrincipalAttrs),
45       mResolvedScriptURL(DeserializeURI(aData.resolvedScriptURL())),
46       mName(aData.name()),
47       mIsSecureContext(aData.isSecureContext()),
48       mSuspended(false),
49       mFrozen(false) {
50   MOZ_ASSERT(NS_IsMainThread());
51   MOZ_ASSERT(aLoadingPrincipal);
52 }
53 
~SharedWorkerManager()54 SharedWorkerManager::~SharedWorkerManager() {
55   NS_ReleaseOnMainThread("SharedWorkerManager::mLoadingPrincipal",
56                          mLoadingPrincipal.forget());
57   NS_ProxyRelease("SharedWorkerManager::mRemoteWorkerController",
58                   mPBackgroundEventTarget, mRemoteWorkerController.forget());
59 }
60 
MaybeCreateRemoteWorker(const RemoteWorkerData & aData,uint64_t aWindowID,UniqueMessagePortId & aPortIdentifier,base::ProcessId aProcessId)61 bool SharedWorkerManager::MaybeCreateRemoteWorker(
62     const RemoteWorkerData& aData, uint64_t aWindowID,
63     UniqueMessagePortId& aPortIdentifier, base::ProcessId aProcessId) {
64   AssertIsOnBackgroundThread();
65 
66   if (!mRemoteWorkerController) {
67     mRemoteWorkerController =
68         RemoteWorkerController::Create(aData, this, aProcessId);
69     if (NS_WARN_IF(!mRemoteWorkerController)) {
70       return false;
71     }
72   }
73 
74   if (aWindowID) {
75     mRemoteWorkerController->AddWindowID(aWindowID);
76   }
77 
78   mRemoteWorkerController->AddPortIdentifier(aPortIdentifier.release());
79   return true;
80 }
81 
82 already_AddRefed<SharedWorkerManagerHolder>
MatchOnMainThread(SharedWorkerService * aService,const nsACString & aDomain,nsIURI * aScriptURL,const nsAString & aName,nsIPrincipal * aLoadingPrincipal,const OriginAttributes & aStoragePrincipalAttrs)83 SharedWorkerManager::MatchOnMainThread(
84     SharedWorkerService* aService, const nsACString& aDomain,
85     nsIURI* aScriptURL, const nsAString& aName, nsIPrincipal* aLoadingPrincipal,
86     const OriginAttributes& aStoragePrincipalAttrs) {
87   MOZ_ASSERT(NS_IsMainThread());
88 
89   bool urlEquals;
90   if (NS_FAILED(aScriptURL->Equals(mResolvedScriptURL, &urlEquals))) {
91     return nullptr;
92   }
93 
94   bool match = aDomain == mDomain && urlEquals && aName == mName &&
95                // We want to be sure that the window's principal subsumes the
96                // SharedWorker's loading principal and vice versa.
97                mLoadingPrincipal->Subsumes(aLoadingPrincipal) &&
98                aLoadingPrincipal->Subsumes(mLoadingPrincipal) &&
99                mStoragePrincipalAttrs == aStoragePrincipalAttrs;
100   if (!match) {
101     return nullptr;
102   }
103 
104   RefPtr<SharedWorkerManagerHolder> holder =
105       new SharedWorkerManagerHolder(this, aService);
106   return holder.forget();
107 }
108 
AddActor(SharedWorkerParent * aParent)109 void SharedWorkerManager::AddActor(SharedWorkerParent* aParent) {
110   AssertIsOnBackgroundThread();
111   MOZ_ASSERT(aParent);
112   MOZ_ASSERT(!mActors.Contains(aParent));
113 
114   mActors.AppendElement(aParent);
115 
116   // NB: We don't update our Suspended/Frozen state here, yet. The aParent is
117   // responsible for doing so from SharedWorkerParent::ManagerCreated.
118   // XXX But we could avoid iterating all of our actors because if aParent is
119   // not frozen and we are, we would just need to thaw ourselves.
120 }
121 
RemoveActor(SharedWorkerParent * aParent)122 void SharedWorkerManager::RemoveActor(SharedWorkerParent* aParent) {
123   AssertIsOnBackgroundThread();
124   MOZ_ASSERT(aParent);
125   MOZ_ASSERT(mActors.Contains(aParent));
126 
127   uint64_t windowID = aParent->WindowID();
128   if (windowID) {
129     mRemoteWorkerController->RemoveWindowID(windowID);
130   }
131 
132   mActors.RemoveElement(aParent);
133 
134   if (!mActors.IsEmpty()) {
135     // Our remaining actors could be all suspended or frozen.
136     UpdateSuspend();
137     UpdateFrozen();
138     return;
139   }
140 }
141 
Terminate()142 void SharedWorkerManager::Terminate() {
143   AssertIsOnBackgroundThread();
144   MOZ_ASSERT(mActors.IsEmpty());
145   MOZ_ASSERT(mHolders.IsEmpty());
146 
147   mRemoteWorkerController->Terminate();
148   mRemoteWorkerController = nullptr;
149 }
150 
UpdateSuspend()151 void SharedWorkerManager::UpdateSuspend() {
152   AssertIsOnBackgroundThread();
153   MOZ_ASSERT(mRemoteWorkerController);
154 
155   uint32_t suspended = 0;
156 
157   for (SharedWorkerParent* actor : mActors) {
158     if (actor->IsSuspended()) {
159       ++suspended;
160     }
161   }
162 
163   // Call Suspend only when all of our actors' windows are suspended and call
164   // Resume only when one of them resumes.
165   if ((mSuspended && suspended == mActors.Length()) ||
166       (!mSuspended && suspended != mActors.Length())) {
167     return;
168   }
169 
170   if (!mSuspended) {
171     mSuspended = true;
172     mRemoteWorkerController->Suspend();
173   } else {
174     mSuspended = false;
175     mRemoteWorkerController->Resume();
176   }
177 }
178 
UpdateFrozen()179 void SharedWorkerManager::UpdateFrozen() {
180   AssertIsOnBackgroundThread();
181   MOZ_ASSERT(mRemoteWorkerController);
182 
183   uint32_t frozen = 0;
184 
185   for (SharedWorkerParent* actor : mActors) {
186     if (actor->IsFrozen()) {
187       ++frozen;
188     }
189   }
190 
191   // Similar to UpdateSuspend, above, we only want to be frozen when all of our
192   // actors are frozen.
193   if ((mFrozen && frozen == mActors.Length()) ||
194       (!mFrozen && frozen != mActors.Length())) {
195     return;
196   }
197 
198   if (!mFrozen) {
199     mFrozen = true;
200     mRemoteWorkerController->Freeze();
201   } else {
202     mFrozen = false;
203     mRemoteWorkerController->Thaw();
204   }
205 }
206 
IsSecureContext() const207 bool SharedWorkerManager::IsSecureContext() const { return mIsSecureContext; }
208 
CreationFailed()209 void SharedWorkerManager::CreationFailed() {
210   AssertIsOnBackgroundThread();
211 
212   for (SharedWorkerParent* actor : mActors) {
213     Unused << actor->SendError(NS_ERROR_FAILURE);
214   }
215 }
216 
CreationSucceeded()217 void SharedWorkerManager::CreationSucceeded() {
218   AssertIsOnBackgroundThread();
219   // Nothing to do here.
220 }
221 
ErrorReceived(const ErrorValue & aValue)222 void SharedWorkerManager::ErrorReceived(const ErrorValue& aValue) {
223   AssertIsOnBackgroundThread();
224 
225   for (SharedWorkerParent* actor : mActors) {
226     Unused << actor->SendError(aValue);
227   }
228 }
229 
Terminated()230 void SharedWorkerManager::Terminated() {
231   AssertIsOnBackgroundThread();
232 
233   for (SharedWorkerParent* actor : mActors) {
234     Unused << actor->SendTerminate();
235   }
236 }
237 
RegisterHolder(SharedWorkerManagerHolder * aHolder)238 void SharedWorkerManager::RegisterHolder(SharedWorkerManagerHolder* aHolder) {
239   MOZ_ASSERT(NS_IsMainThread());
240   MOZ_ASSERT(aHolder);
241   MOZ_ASSERT(!mHolders.Contains(aHolder));
242 
243   mHolders.AppendElement(aHolder);
244 }
245 
UnregisterHolder(SharedWorkerManagerHolder * aHolder)246 void SharedWorkerManager::UnregisterHolder(SharedWorkerManagerHolder* aHolder) {
247   MOZ_ASSERT(NS_IsMainThread());
248   MOZ_ASSERT(aHolder);
249   MOZ_ASSERT(mHolders.Contains(aHolder));
250 
251   mHolders.RemoveElement(aHolder);
252 
253   if (!mHolders.IsEmpty()) {
254     return;
255   }
256 
257   // Time to go.
258 
259   aHolder->Service()->RemoveWorkerManagerOnMainThread(this);
260 
261   RefPtr<SharedWorkerManager> self = this;
262   mPBackgroundEventTarget->Dispatch(
263       NS_NewRunnableFunction(
264           "SharedWorkerService::RemoveWorkerManagerOnMainThread",
265           [self]() { self->Terminate(); }),
266       NS_DISPATCH_NORMAL);
267 }
268 
SharedWorkerManagerHolder(SharedWorkerManager * aManager,SharedWorkerService * aService)269 SharedWorkerManagerHolder::SharedWorkerManagerHolder(
270     SharedWorkerManager* aManager, SharedWorkerService* aService)
271     : mManager(aManager), mService(aService) {
272   MOZ_ASSERT(NS_IsMainThread());
273   MOZ_ASSERT(aManager);
274   MOZ_ASSERT(aService);
275 
276   aManager->RegisterHolder(this);
277 }
278 
~SharedWorkerManagerHolder()279 SharedWorkerManagerHolder::~SharedWorkerManagerHolder() {
280   MOZ_ASSERT(NS_IsMainThread());
281   mManager->UnregisterHolder(this);
282 }
283 
SharedWorkerManagerWrapper(already_AddRefed<SharedWorkerManagerHolder> aHolder)284 SharedWorkerManagerWrapper::SharedWorkerManagerWrapper(
285     already_AddRefed<SharedWorkerManagerHolder> aHolder)
286     : mHolder(aHolder) {
287   MOZ_ASSERT(NS_IsMainThread());
288 }
289 
~SharedWorkerManagerWrapper()290 SharedWorkerManagerWrapper::~SharedWorkerManagerWrapper() {
291   NS_ReleaseOnMainThread("SharedWorkerManagerWrapper::mHolder",
292                          mHolder.forget());
293 }
294 
295 }  // namespace dom
296 }  // namespace mozilla
297