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/. */
9 #include "MessageEventRunnable.h"
10 #include "mozilla/dom/WorkerBinding.h"
11 #include "mozilla/ProfilerLabels.h"
12 #include "mozilla/ProfilerMarkers.h"
13 #include "mozilla/Unused.h"
14 #include "nsContentUtils.h"
15 #include "nsGlobalWindowInner.h"
16 #include "WorkerPrivate.h"
17 #include "EventWithOptionsRunnable.h"
18 #include "js/RootingAPI.h"
19 #include "mozilla/dom/BindingDeclarations.h"
20 #include "nsISupports.h"
22 #include "mozilla/dom/WorkerStatus.h"
23 #include "mozilla/RefPtr.h"
24 #include "mozilla/dom/TrustedScriptURL.h"
25 #include "mozilla/dom/TrustedTypeUtils.h"
26 #include "mozilla/dom/TrustedTypesConstants.h"
32 namespace mozilla::dom
{
35 already_AddRefed
<Worker
> Worker::Constructor(
36 const GlobalObject
& aGlobal
, const TrustedScriptURLOrUSVString
& aScriptURL
,
37 const WorkerOptions
& aOptions
, ErrorResult
& aRv
) {
38 JSContext
* cx
= aGlobal
.Context();
40 nsCOMPtr
<nsIGlobalObject
> globalObject
=
41 do_QueryInterface(aGlobal
.GetAsSupports());
43 nsPIDOMWindowInner
* innerWindow
= globalObject
->GetAsInnerWindow();
44 if (innerWindow
&& !innerWindow
->IsCurrentInnerWindow()) {
45 aRv
.ThrowInvalidStateError(
46 "Cannot create worker for a going to be discarded document");
50 // The spec only mentions Window and WorkerGlobalScope global objects, but
51 // Gecko can actually call the constructor with other ones, so we just skip
52 // trusted types handling in that case.
53 // https://html.spec.whatwg.org/multipage/workers.html#dedicated-workers-and-the-worker-interface
54 const nsAString
* compliantString
= nullptr;
55 bool performTrustedTypeConversion
= innerWindow
;
56 if (!performTrustedTypeConversion
) {
57 if (JSObject
* globalJSObject
= globalObject
->GetGlobalJSObject()) {
58 performTrustedTypeConversion
= IsWorkerGlobal(globalJSObject
);
61 Maybe
<nsAutoString
> compliantStringHolder
;
62 if (performTrustedTypeConversion
) {
63 constexpr nsLiteralString sink
= u
"Worker constructor"_ns
;
64 compliantString
= TrustedTypeUtils::GetTrustedTypesCompliantString(
65 aScriptURL
, sink
, kTrustedTypesOnlySinkGroup
, *globalObject
,
66 compliantStringHolder
, aRv
);
71 compliantString
= aScriptURL
.IsUSVString()
72 ? &aScriptURL
.GetAsUSVString()
73 : &aScriptURL
.GetAsTrustedScriptURL().mData
;
75 MOZ_ASSERT(compliantString
);
77 RefPtr
<WorkerPrivate
> workerPrivate
= WorkerPrivate::Constructor(
78 cx
, *compliantString
, false /* aIsChromeWorker */, WorkerKindDedicated
,
79 aOptions
.mCredentials
, aOptions
.mType
, aOptions
.mName
, VoidCString(),
80 nullptr /*aLoadInfo */, aRv
);
81 if (NS_WARN_IF(aRv
.Failed())) {
85 RefPtr
<Worker
> worker
= new Worker(globalObject
, workerPrivate
.forget());
86 return worker
.forget();
89 Worker::Worker(nsIGlobalObject
* aGlobalObject
,
90 already_AddRefed
<WorkerPrivate
> aWorkerPrivate
)
91 : DOMEventTargetHelper(aGlobalObject
),
92 mWorkerPrivate(std::move(aWorkerPrivate
)) {
93 MOZ_ASSERT(mWorkerPrivate
);
94 mWorkerPrivate
->SetParentEventTargetRef(this);
97 Worker::~Worker() { Terminate(); }
99 JSObject
* Worker::WrapObject(JSContext
* aCx
,
100 JS::Handle
<JSObject
*> aGivenProto
) {
101 JS::Rooted
<JSObject
*> wrapper(aCx
,
102 Worker_Binding::Wrap(aCx
, this, aGivenProto
));
104 // Most DOM objects don't assume they have a reflector. If they don't have
105 // one and need one, they create it. But in workers code, we assume that the
106 // reflector is always present. In order to guarantee that it's always
107 // present, we have to preserve it. Otherwise the GC will happily collect it
109 MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper
));
115 bool Worker::IsEligibleForMessaging() {
116 NS_ASSERT_OWNINGTHREAD(Worker
);
118 return mWorkerPrivate
&& mWorkerPrivate
->ParentStatusProtected() <= Running
;
121 void Worker::PostMessage(JSContext
* aCx
, JS::Handle
<JS::Value
> aMessage
,
122 const Sequence
<JSObject
*>& aTransferable
,
124 NS_ASSERT_OWNINGTHREAD(Worker
);
126 if (!mWorkerPrivate
|| mWorkerPrivate
->ParentStatusProtected() > Running
) {
129 RefPtr
<WorkerPrivate
> workerPrivate
= mWorkerPrivate
;
130 Unused
<< workerPrivate
;
132 JS::Rooted
<JS::Value
> transferable(aCx
, JS::UndefinedValue());
134 aRv
= nsContentUtils::CreateJSValueFromSequenceOfObject(aCx
, aTransferable
,
136 if (NS_WARN_IF(aRv
.Failed())) {
140 NS_ConvertUTF16toUTF8
nameOrScriptURL(
141 mWorkerPrivate
->WorkerName().IsEmpty()
143 mWorkerPrivate
->ScriptURL(), 0,
144 std::min(size_t(1024), mWorkerPrivate
->ScriptURL().Length()))
146 mWorkerPrivate
->WorkerName(), 0,
147 std::min(size_t(1024), mWorkerPrivate
->WorkerName().Length())));
148 AUTO_PROFILER_MARKER_TEXT("Worker.postMessage", DOM
, {}, nameOrScriptURL
);
149 uint32_t flags
= uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS
);
150 if (mWorkerPrivate
->IsChromeWorker()) {
151 flags
|= uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE
);
153 mozilla::AutoProfilerLabel
PROFILER_RAII(
154 "Worker.postMessage", nameOrScriptURL
.get(),
155 JS::ProfilingCategoryPair::DOM
, flags
);
157 RefPtr
<MessageEventRunnable
> runnable
=
158 new MessageEventRunnable(mWorkerPrivate
);
160 JS::CloneDataPolicy clonePolicy
;
161 // DedicatedWorkers are always part of the same agent cluster.
162 clonePolicy
.allowIntraClusterClonableSharedObjects();
164 if (NS_IsMainThread()) {
165 nsGlobalWindowInner
* win
= nsContentUtils::IncumbentInnerWindow();
166 if (win
&& win
->IsSharedMemoryAllowed()) {
167 clonePolicy
.allowSharedMemoryObjects();
170 WorkerPrivate
* worker
= GetCurrentThreadWorkerPrivate();
171 if (worker
&& worker
->IsSharedMemoryAllowed()) {
172 clonePolicy
.allowSharedMemoryObjects();
176 runnable
->Write(aCx
, aMessage
, transferable
, clonePolicy
, aRv
);
178 if (!mWorkerPrivate
|| mWorkerPrivate
->ParentStatusProtected() > Running
) {
182 if (NS_WARN_IF(aRv
.Failed())) {
186 // The worker could have closed between the time we entered this function and
187 // checked ParentStatusProtected and now, which could cause the dispatch to
189 Unused
<< NS_WARN_IF(!runnable
->Dispatch(mWorkerPrivate
));
192 void Worker::PostMessage(JSContext
* aCx
, JS::Handle
<JS::Value
> aMessage
,
193 const StructuredSerializeOptions
& aOptions
,
195 PostMessage(aCx
, aMessage
, aOptions
.mTransfer
, aRv
);
198 void Worker::PostEventWithOptions(JSContext
* aCx
,
199 JS::Handle
<JS::Value
> aOptions
,
200 const Sequence
<JSObject
*>& aTransferable
,
201 EventWithOptionsRunnable
* aRunnable
,
203 NS_ASSERT_OWNINGTHREAD(Worker
);
205 if (NS_WARN_IF(!mWorkerPrivate
||
206 mWorkerPrivate
->ParentStatusProtected() > Running
)) {
209 RefPtr
<WorkerPrivate
> workerPrivate
= mWorkerPrivate
;
210 Unused
<< workerPrivate
;
212 aRunnable
->InitOptions(aCx
, aOptions
, aTransferable
, aRv
);
214 if (NS_WARN_IF(!mWorkerPrivate
||
215 mWorkerPrivate
->ParentStatusProtected() > Running
)) {
219 if (NS_WARN_IF(aRv
.Failed())) {
223 Unused
<< NS_WARN_IF(!aRunnable
->Dispatch(mWorkerPrivate
));
226 void Worker::Terminate() {
227 NS_ASSERT_OWNINGTHREAD(Worker
);
229 if (mWorkerPrivate
) {
230 mWorkerPrivate
->Cancel();
231 mWorkerPrivate
= nullptr;
235 NS_IMPL_CYCLE_COLLECTION_CLASS(Worker
)
237 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Worker
, DOMEventTargetHelper
)
238 if (tmp
->mWorkerPrivate
) {
239 tmp
->mWorkerPrivate
->Traverse(cb
);
241 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
243 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Worker
, DOMEventTargetHelper
)
245 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
246 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
248 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Worker
, DOMEventTargetHelper
)
249 NS_IMPL_CYCLE_COLLECTION_TRACE_END
251 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worker
)
252 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
254 NS_IMPL_ADDREF_INHERITED(Worker
, DOMEventTargetHelper
)
255 NS_IMPL_RELEASE_INHERITED(Worker
, DOMEventTargetHelper
)
257 } // namespace mozilla::dom