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