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 "SharedWorkerService.h"
8 #include "mozilla/dom/RemoteWorkerTypes.h"
9 #include "mozilla/ipc/BackgroundParent.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/SchedulerGroup.h"
12 #include "mozilla/StaticMutex.h"
13 #include "nsIPrincipal.h"
14 #include "nsProxyRelease.h"
15 
16 namespace mozilla {
17 
18 using namespace ipc;
19 
20 namespace dom {
21 
22 namespace {
23 
24 StaticMutex sSharedWorkerMutex;
25 
26 StaticRefPtr<SharedWorkerService> sSharedWorkerService;
27 
28 class GetOrCreateWorkerManagerRunnable final : public Runnable {
29  public:
GetOrCreateWorkerManagerRunnable(SharedWorkerService * aService,SharedWorkerParent * aActor,const RemoteWorkerData & aData,uint64_t aWindowID,const MessagePortIdentifier & aPortIdentifier)30   GetOrCreateWorkerManagerRunnable(SharedWorkerService* aService,
31                                    SharedWorkerParent* aActor,
32                                    const RemoteWorkerData& aData,
33                                    uint64_t aWindowID,
34                                    const MessagePortIdentifier& aPortIdentifier)
35       : Runnable("GetOrCreateWorkerManagerRunnable"),
36         mBackgroundEventTarget(GetCurrentThreadEventTarget()),
37         mService(aService),
38         mActor(aActor),
39         mData(aData),
40         mWindowID(aWindowID),
41         mPortIdentifier(aPortIdentifier) {}
42 
43   NS_IMETHOD
Run()44   Run() {
45     mService->GetOrCreateWorkerManagerOnMainThread(
46         mBackgroundEventTarget, mActor, mData, mWindowID, mPortIdentifier);
47 
48     return NS_OK;
49   }
50 
51  private:
52   nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
53   RefPtr<SharedWorkerService> mService;
54   RefPtr<SharedWorkerParent> mActor;
55   RemoteWorkerData mData;
56   uint64_t mWindowID;
57   UniqueMessagePortId mPortIdentifier;
58 };
59 
60 class WorkerManagerCreatedRunnable final : public Runnable {
61  public:
WorkerManagerCreatedRunnable(already_AddRefed<SharedWorkerManagerWrapper> aManagerWrapper,SharedWorkerParent * aActor,const RemoteWorkerData & aData,uint64_t aWindowID,UniqueMessagePortId & aPortIdentifier)62   WorkerManagerCreatedRunnable(
63       already_AddRefed<SharedWorkerManagerWrapper> aManagerWrapper,
64       SharedWorkerParent* aActor, const RemoteWorkerData& aData,
65       uint64_t aWindowID, UniqueMessagePortId& aPortIdentifier)
66       : Runnable("WorkerManagerCreatedRunnable"),
67         mManagerWrapper(aManagerWrapper),
68         mActor(aActor),
69         mData(aData),
70         mWindowID(aWindowID),
71         mPortIdentifier(std::move(aPortIdentifier)) {}
72 
73   NS_IMETHOD
Run()74   Run() {
75     AssertIsOnBackgroundThread();
76 
77     if (NS_WARN_IF(!mManagerWrapper->Manager()->MaybeCreateRemoteWorker(
78             mData, mWindowID, mPortIdentifier, mActor->OtherPid()))) {
79       mActor->ErrorPropagation(NS_ERROR_FAILURE);
80       return NS_OK;
81     }
82 
83     mManagerWrapper->Manager()->AddActor(mActor);
84     mActor->ManagerCreated(mManagerWrapper.forget());
85     return NS_OK;
86   }
87 
88  private:
89   RefPtr<SharedWorkerManagerWrapper> mManagerWrapper;
90   RefPtr<SharedWorkerParent> mActor;
91   RemoteWorkerData mData;
92   uint64_t mWindowID;
93   UniqueMessagePortId mPortIdentifier;
94 };
95 
96 class ErrorPropagationRunnable final : public Runnable {
97  public:
ErrorPropagationRunnable(SharedWorkerParent * aActor,nsresult aError)98   ErrorPropagationRunnable(SharedWorkerParent* aActor, nsresult aError)
99       : Runnable("ErrorPropagationRunnable"), mActor(aActor), mError(aError) {}
100 
101   NS_IMETHOD
Run()102   Run() {
103     AssertIsOnBackgroundThread();
104     mActor->ErrorPropagation(mError);
105     return NS_OK;
106   }
107 
108  private:
109   RefPtr<SharedWorkerParent> mActor;
110   nsresult mError;
111 };
112 
113 }  // namespace
114 
115 /* static */
GetOrCreate()116 already_AddRefed<SharedWorkerService> SharedWorkerService::GetOrCreate() {
117   AssertIsOnBackgroundThread();
118 
119   StaticMutexAutoLock lock(sSharedWorkerMutex);
120 
121   if (!sSharedWorkerService) {
122     sSharedWorkerService = new SharedWorkerService();
123     // ClearOnShutdown can only be called on main thread
124     nsresult rv = SchedulerGroup::Dispatch(
125         TaskCategory::Other,
126         NS_NewRunnableFunction("RegisterSharedWorkerServiceClearOnShutdown",
127                                []() {
128                                  StaticMutexAutoLock lock(sSharedWorkerMutex);
129                                  MOZ_ASSERT(sSharedWorkerService);
130                                  ClearOnShutdown(&sSharedWorkerService);
131                                }));
132     Unused << NS_WARN_IF(NS_FAILED(rv));
133   }
134 
135   RefPtr<SharedWorkerService> instance = sSharedWorkerService;
136   return instance.forget();
137 }
138 
139 /* static */
Get()140 SharedWorkerService* SharedWorkerService::Get() {
141   StaticMutexAutoLock lock(sSharedWorkerMutex);
142 
143   MOZ_ASSERT(sSharedWorkerService);
144   return sSharedWorkerService;
145 }
146 
GetOrCreateWorkerManager(SharedWorkerParent * aActor,const RemoteWorkerData & aData,uint64_t aWindowID,const MessagePortIdentifier & aPortIdentifier)147 void SharedWorkerService::GetOrCreateWorkerManager(
148     SharedWorkerParent* aActor, const RemoteWorkerData& aData,
149     uint64_t aWindowID, const MessagePortIdentifier& aPortIdentifier) {
150   AssertIsOnBackgroundThread();
151 
152   // The real check happens on main-thread.
153   RefPtr<GetOrCreateWorkerManagerRunnable> r =
154       new GetOrCreateWorkerManagerRunnable(this, aActor, aData, aWindowID,
155                                            aPortIdentifier);
156 
157   nsresult rv = SchedulerGroup::Dispatch(TaskCategory::Other, r.forget());
158   Unused << NS_WARN_IF(NS_FAILED(rv));
159 }
160 
GetOrCreateWorkerManagerOnMainThread(nsIEventTarget * aBackgroundEventTarget,SharedWorkerParent * aActor,const RemoteWorkerData & aData,uint64_t aWindowID,UniqueMessagePortId & aPortIdentifier)161 void SharedWorkerService::GetOrCreateWorkerManagerOnMainThread(
162     nsIEventTarget* aBackgroundEventTarget, SharedWorkerParent* aActor,
163     const RemoteWorkerData& aData, uint64_t aWindowID,
164     UniqueMessagePortId& aPortIdentifier) {
165   MOZ_ASSERT(NS_IsMainThread());
166   MOZ_ASSERT(aBackgroundEventTarget);
167   MOZ_ASSERT(aActor);
168 
169   auto storagePrincipalOrErr =
170       PrincipalInfoToPrincipal(aData.storagePrincipalInfo());
171   if (NS_WARN_IF(storagePrincipalOrErr.isErr())) {
172     ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor,
173                                  storagePrincipalOrErr.unwrapErr());
174     return;
175   }
176 
177   auto loadingPrincipalOrErr =
178       PrincipalInfoToPrincipal(aData.loadingPrincipalInfo());
179   if (NS_WARN_IF(loadingPrincipalOrErr.isErr())) {
180     ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor,
181                                  loadingPrincipalOrErr.unwrapErr());
182     return;
183   }
184 
185   RefPtr<SharedWorkerManagerHolder> managerHolder;
186 
187   nsCOMPtr<nsIPrincipal> loadingPrincipal = loadingPrincipalOrErr.unwrap();
188   nsCOMPtr<nsIPrincipal> storagePrincipal = storagePrincipalOrErr.unwrap();
189 
190   // Let's see if there is already a SharedWorker to share.
191   nsCOMPtr<nsIURI> resolvedScriptURL =
192       DeserializeURI(aData.resolvedScriptURL());
193   for (SharedWorkerManager* workerManager : mWorkerManagers) {
194     managerHolder = workerManager->MatchOnMainThread(
195         this, aData.domain(), resolvedScriptURL, aData.name(), loadingPrincipal,
196         BasePrincipal::Cast(storagePrincipal)->OriginAttributesRef());
197     if (managerHolder) {
198       break;
199     }
200   }
201 
202   // Let's create a new one.
203   if (!managerHolder) {
204     managerHolder = SharedWorkerManager::Create(
205         this, aBackgroundEventTarget, aData, loadingPrincipal,
206         BasePrincipal::Cast(storagePrincipal)->OriginAttributesRef());
207 
208     mWorkerManagers.AppendElement(managerHolder->Manager());
209   } else {
210     // We are attaching the actor to an existing one.
211     if (managerHolder->Manager()->IsSecureContext() !=
212         aData.isSecureContext()) {
213       ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor,
214                                    NS_ERROR_DOM_SECURITY_ERR);
215       return;
216     }
217   }
218 
219   RefPtr<SharedWorkerManagerWrapper> wrapper =
220       new SharedWorkerManagerWrapper(managerHolder.forget());
221 
222   RefPtr<WorkerManagerCreatedRunnable> r = new WorkerManagerCreatedRunnable(
223       wrapper.forget(), aActor, aData, aWindowID, aPortIdentifier);
224   aBackgroundEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
225 }
226 
ErrorPropagationOnMainThread(nsIEventTarget * aBackgroundEventTarget,SharedWorkerParent * aActor,nsresult aError)227 void SharedWorkerService::ErrorPropagationOnMainThread(
228     nsIEventTarget* aBackgroundEventTarget, SharedWorkerParent* aActor,
229     nsresult aError) {
230   MOZ_ASSERT(NS_IsMainThread());
231   MOZ_ASSERT(aBackgroundEventTarget);
232   MOZ_ASSERT(aActor);
233   MOZ_ASSERT(NS_FAILED(aError));
234 
235   RefPtr<ErrorPropagationRunnable> r =
236       new ErrorPropagationRunnable(aActor, aError);
237   aBackgroundEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
238 }
239 
RemoveWorkerManagerOnMainThread(SharedWorkerManager * aManager)240 void SharedWorkerService::RemoveWorkerManagerOnMainThread(
241     SharedWorkerManager* aManager) {
242   MOZ_ASSERT(NS_IsMainThread());
243   MOZ_ASSERT(aManager);
244   MOZ_ASSERT(mWorkerManagers.Contains(aManager));
245 
246   mWorkerManagers.RemoveElement(aManager);
247 }
248 
249 }  // namespace dom
250 }  // namespace mozilla
251