Bug 1914004 - Part 1: Add RootedTuple and RootedField to allow rooting multiple thing...
[gecko.git] / dom / workers / Worker.cpp
blob6d85948d96fe808e6ecb17c18328394dbbaeacb7
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/. */
7 #include "Worker.h"
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"
21 #include "nsDebug.h"
22 #include "mozilla/dom/WorkerStatus.h"
23 #include "mozilla/RefPtr.h"
25 #ifdef XP_WIN
26 # undef PostMessage
27 #endif
29 namespace mozilla::dom {
31 /* static */
32 already_AddRefed<Worker> Worker::Constructor(const GlobalObject& aGlobal,
33 const nsAString& aScriptURL,
34 const WorkerOptions& aOptions,
35 ErrorResult& aRv) {
36 JSContext* cx = aGlobal.Context();
38 nsCOMPtr<nsIGlobalObject> globalObject =
39 do_QueryInterface(aGlobal.GetAsSupports());
41 if (globalObject->GetAsInnerWindow() &&
42 !globalObject->GetAsInnerWindow()->IsCurrentInnerWindow()) {
43 aRv.ThrowInvalidStateError(
44 "Cannot create worker for a going to be discarded document");
45 return nullptr;
48 RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
49 cx, aScriptURL, false /* aIsChromeWorker */, WorkerKindDedicated,
50 aOptions.mCredentials, aOptions.mType, aOptions.mName, VoidCString(),
51 nullptr /*aLoadInfo */, aRv);
52 if (NS_WARN_IF(aRv.Failed())) {
53 return nullptr;
56 RefPtr<Worker> worker = new Worker(globalObject, workerPrivate.forget());
57 return worker.forget();
60 Worker::Worker(nsIGlobalObject* aGlobalObject,
61 already_AddRefed<WorkerPrivate> aWorkerPrivate)
62 : DOMEventTargetHelper(aGlobalObject),
63 mWorkerPrivate(std::move(aWorkerPrivate)) {
64 MOZ_ASSERT(mWorkerPrivate);
65 mWorkerPrivate->SetParentEventTargetRef(this);
68 Worker::~Worker() { Terminate(); }
70 JSObject* Worker::WrapObject(JSContext* aCx,
71 JS::Handle<JSObject*> aGivenProto) {
72 JS::Rooted<JSObject*> wrapper(aCx,
73 Worker_Binding::Wrap(aCx, this, aGivenProto));
74 if (wrapper) {
75 // Most DOM objects don't assume they have a reflector. If they don't have
76 // one and need one, they create it. But in workers code, we assume that the
77 // reflector is always present. In order to guarantee that it's always
78 // present, we have to preserve it. Otherwise the GC will happily collect it
79 // as needed.
80 MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
83 return wrapper;
86 bool Worker::IsEligibleForMessaging() {
87 NS_ASSERT_OWNINGTHREAD(Worker);
89 return mWorkerPrivate && mWorkerPrivate->ParentStatusProtected() <= Running;
92 void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
93 const Sequence<JSObject*>& aTransferable,
94 ErrorResult& aRv) {
95 NS_ASSERT_OWNINGTHREAD(Worker);
97 if (!mWorkerPrivate || mWorkerPrivate->ParentStatusProtected() > Running) {
98 return;
100 RefPtr<WorkerPrivate> workerPrivate = mWorkerPrivate;
101 Unused << workerPrivate;
103 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
105 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
106 &transferable);
107 if (NS_WARN_IF(aRv.Failed())) {
108 return;
111 NS_ConvertUTF16toUTF8 nameOrScriptURL(
112 mWorkerPrivate->WorkerName().IsEmpty()
113 ? Substring(
114 mWorkerPrivate->ScriptURL(), 0,
115 std::min(size_t(1024), mWorkerPrivate->ScriptURL().Length()))
116 : Substring(
117 mWorkerPrivate->WorkerName(), 0,
118 std::min(size_t(1024), mWorkerPrivate->WorkerName().Length())));
119 AUTO_PROFILER_MARKER_TEXT("Worker.postMessage", DOM, {}, nameOrScriptURL);
120 uint32_t flags = uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS);
121 if (mWorkerPrivate->IsChromeWorker()) {
122 flags |= uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE);
124 mozilla::AutoProfilerLabel PROFILER_RAII(
125 "Worker.postMessage", nameOrScriptURL.get(),
126 JS::ProfilingCategoryPair::DOM, flags);
128 RefPtr<MessageEventRunnable> runnable =
129 new MessageEventRunnable(mWorkerPrivate);
131 JS::CloneDataPolicy clonePolicy;
132 // DedicatedWorkers are always part of the same agent cluster.
133 clonePolicy.allowIntraClusterClonableSharedObjects();
135 if (NS_IsMainThread()) {
136 nsGlobalWindowInner* win = nsContentUtils::IncumbentInnerWindow();
137 if (win && win->IsSharedMemoryAllowed()) {
138 clonePolicy.allowSharedMemoryObjects();
140 } else {
141 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
142 if (worker && worker->IsSharedMemoryAllowed()) {
143 clonePolicy.allowSharedMemoryObjects();
147 runnable->Write(aCx, aMessage, transferable, clonePolicy, aRv);
149 if (!mWorkerPrivate || mWorkerPrivate->ParentStatusProtected() > Running) {
150 return;
153 if (NS_WARN_IF(aRv.Failed())) {
154 return;
157 // The worker could have closed between the time we entered this function and
158 // checked ParentStatusProtected and now, which could cause the dispatch to
159 // fail.
160 Unused << NS_WARN_IF(!runnable->Dispatch(mWorkerPrivate));
163 void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
164 const StructuredSerializeOptions& aOptions,
165 ErrorResult& aRv) {
166 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
169 void Worker::PostEventWithOptions(JSContext* aCx,
170 JS::Handle<JS::Value> aOptions,
171 const Sequence<JSObject*>& aTransferable,
172 EventWithOptionsRunnable* aRunnable,
173 ErrorResult& aRv) {
174 NS_ASSERT_OWNINGTHREAD(Worker);
176 if (NS_WARN_IF(!mWorkerPrivate ||
177 mWorkerPrivate->ParentStatusProtected() > Running)) {
178 return;
180 RefPtr<WorkerPrivate> workerPrivate = mWorkerPrivate;
181 Unused << workerPrivate;
183 aRunnable->InitOptions(aCx, aOptions, aTransferable, aRv);
185 if (NS_WARN_IF(!mWorkerPrivate ||
186 mWorkerPrivate->ParentStatusProtected() > Running)) {
187 return;
190 if (NS_WARN_IF(aRv.Failed())) {
191 return;
194 Unused << NS_WARN_IF(!aRunnable->Dispatch(mWorkerPrivate));
197 void Worker::Terminate() {
198 NS_ASSERT_OWNINGTHREAD(Worker);
200 if (mWorkerPrivate) {
201 mWorkerPrivate->Cancel();
202 mWorkerPrivate = nullptr;
206 NS_IMPL_CYCLE_COLLECTION_CLASS(Worker)
208 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
209 if (tmp->mWorkerPrivate) {
210 tmp->mWorkerPrivate->Traverse(cb);
212 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
214 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
215 tmp->Terminate();
216 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
217 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
219 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
220 NS_IMPL_CYCLE_COLLECTION_TRACE_END
222 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worker)
223 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
225 NS_IMPL_ADDREF_INHERITED(Worker, DOMEventTargetHelper)
226 NS_IMPL_RELEASE_INHERITED(Worker, DOMEventTargetHelper)
228 } // namespace mozilla::dom