Backed out changeset b09d48d2b473 (bug 1655101) for causing mochitest webgl failures...
[gecko.git] / dom / workers / Worker.cpp
blob372ef74f6aa060a136e68c416bf4ce06841ef32f
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/TimelineConsumers.h"
14 #include "mozilla/Unused.h"
15 #include "mozilla/WorkerTimelineMarker.h"
16 #include "nsContentUtils.h"
17 #include "nsGlobalWindowInner.h"
18 #include "WorkerPrivate.h"
19 #include "EventWithOptionsRunnable.h"
20 #include "js/RootingAPI.h"
21 #include "mozilla/dom/BindingDeclarations.h"
22 #include "nsISupports.h"
23 #include "nsDebug.h"
24 #include "mozilla/dom/WorkerStatus.h"
25 #include "mozilla/RefPtr.h"
27 #ifdef XP_WIN
28 # undef PostMessage
29 #endif
31 namespace mozilla::dom {
33 /* static */
34 already_AddRefed<Worker> Worker::Constructor(const GlobalObject& aGlobal,
35 const nsAString& aScriptURL,
36 const WorkerOptions& aOptions,
37 ErrorResult& aRv) {
38 JSContext* cx = aGlobal.Context();
40 nsCOMPtr<nsIGlobalObject> globalObject =
41 do_QueryInterface(aGlobal.GetAsSupports());
43 if (globalObject->GetAsInnerWindow() &&
44 !globalObject->GetAsInnerWindow()->IsCurrentInnerWindow()) {
45 aRv.ThrowInvalidStateError(
46 "Cannot create worker for a going to be discarded document");
47 return nullptr;
50 RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
51 cx, aScriptURL, false /* aIsChromeWorker */, WorkerKindDedicated,
52 aOptions.mCredentials, aOptions.mType, aOptions.mName, VoidCString(),
53 nullptr /*aLoadInfo */, aRv);
54 if (NS_WARN_IF(aRv.Failed())) {
55 return nullptr;
58 RefPtr<Worker> worker = new Worker(globalObject, workerPrivate.forget());
59 return worker.forget();
62 Worker::Worker(nsIGlobalObject* aGlobalObject,
63 already_AddRefed<WorkerPrivate> aWorkerPrivate)
64 : DOMEventTargetHelper(aGlobalObject),
65 mWorkerPrivate(std::move(aWorkerPrivate)) {
66 MOZ_ASSERT(mWorkerPrivate);
67 mWorkerPrivate->SetParentEventTargetRef(this);
70 Worker::~Worker() { Terminate(); }
72 JSObject* Worker::WrapObject(JSContext* aCx,
73 JS::Handle<JSObject*> aGivenProto) {
74 JS::Rooted<JSObject*> wrapper(aCx,
75 Worker_Binding::Wrap(aCx, this, aGivenProto));
76 if (wrapper) {
77 // Most DOM objects don't assume they have a reflector. If they don't have
78 // one and need one, they create it. But in workers code, we assume that the
79 // reflector is always present. In order to guarantee that it's always
80 // present, we have to preserve it. Otherwise the GC will happily collect it
81 // as needed.
82 MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
85 return wrapper;
88 void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
89 const Sequence<JSObject*>& aTransferable,
90 ErrorResult& aRv) {
91 NS_ASSERT_OWNINGTHREAD(Worker);
93 if (!mWorkerPrivate || mWorkerPrivate->ParentStatusProtected() > Running) {
94 return;
96 RefPtr<WorkerPrivate> workerPrivate = mWorkerPrivate;
97 Unused << workerPrivate;
99 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
101 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
102 &transferable);
103 if (NS_WARN_IF(aRv.Failed())) {
104 return;
107 NS_ConvertUTF16toUTF8 nameOrScriptURL(
108 mWorkerPrivate->WorkerName().IsEmpty()
109 ? Substring(
110 mWorkerPrivate->ScriptURL(), 0,
111 std::min(size_t(1024), mWorkerPrivate->ScriptURL().Length()))
112 : Substring(
113 mWorkerPrivate->WorkerName(), 0,
114 std::min(size_t(1024), mWorkerPrivate->WorkerName().Length())));
115 AUTO_PROFILER_MARKER_TEXT("Worker.postMessage", DOM, {}, nameOrScriptURL);
116 uint32_t flags = uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS);
117 if (mWorkerPrivate->IsChromeWorker()) {
118 flags |= uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE);
120 mozilla::AutoProfilerLabel PROFILER_RAII(
121 "Worker.postMessage", nameOrScriptURL.get(),
122 JS::ProfilingCategoryPair::DOM, flags);
124 RefPtr<MessageEventRunnable> runnable = new MessageEventRunnable(
125 mWorkerPrivate, WorkerRunnable::WorkerThreadModifyBusyCount);
127 UniquePtr<AbstractTimelineMarker> start;
128 UniquePtr<AbstractTimelineMarker> end;
129 bool isTimelineRecording = !TimelineConsumers::IsEmpty();
131 if (isTimelineRecording) {
132 start = MakeUnique<WorkerTimelineMarker>(
133 NS_IsMainThread()
134 ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
135 : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
136 MarkerTracingType::START);
139 JS::CloneDataPolicy clonePolicy;
140 // DedicatedWorkers are always part of the same agent cluster.
141 clonePolicy.allowIntraClusterClonableSharedObjects();
143 if (NS_IsMainThread()) {
144 nsGlobalWindowInner* win = nsContentUtils::IncumbentInnerWindow();
145 if (win && win->IsSharedMemoryAllowed()) {
146 clonePolicy.allowSharedMemoryObjects();
148 } else {
149 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
150 if (worker && worker->IsSharedMemoryAllowed()) {
151 clonePolicy.allowSharedMemoryObjects();
155 runnable->Write(aCx, aMessage, transferable, clonePolicy, aRv);
157 if (!mWorkerPrivate || mWorkerPrivate->ParentStatusProtected() > Running) {
158 return;
161 if (isTimelineRecording) {
162 end = MakeUnique<WorkerTimelineMarker>(
163 NS_IsMainThread()
164 ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
165 : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
166 MarkerTracingType::END);
167 TimelineConsumers::AddMarkerForAllObservedDocShells(start);
168 TimelineConsumers::AddMarkerForAllObservedDocShells(end);
171 if (NS_WARN_IF(aRv.Failed())) {
172 return;
175 // The worker could have closed between the time we entered this function and
176 // checked ParentStatusProtected and now, which could cause the dispatch to
177 // fail.
178 Unused << NS_WARN_IF(!runnable->Dispatch());
181 void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
182 const StructuredSerializeOptions& aOptions,
183 ErrorResult& aRv) {
184 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
187 void Worker::PostEventWithOptions(JSContext* aCx,
188 JS::Handle<JS::Value> aOptions,
189 const Sequence<JSObject*>& aTransferable,
190 EventWithOptionsRunnable* aRunnable,
191 ErrorResult& aRv) {
192 NS_ASSERT_OWNINGTHREAD(Worker);
194 if (NS_WARN_IF(!mWorkerPrivate ||
195 mWorkerPrivate->ParentStatusProtected() > Running)) {
196 return;
198 RefPtr<WorkerPrivate> workerPrivate = mWorkerPrivate;
199 Unused << workerPrivate;
201 aRunnable->InitOptions(aCx, aOptions, aTransferable, aRv);
203 if (NS_WARN_IF(!mWorkerPrivate ||
204 mWorkerPrivate->ParentStatusProtected() > Running)) {
205 return;
208 if (NS_WARN_IF(aRv.Failed())) {
209 return;
212 Unused << NS_WARN_IF(!aRunnable->Dispatch());
215 void Worker::Terminate() {
216 NS_ASSERT_OWNINGTHREAD(Worker);
218 if (mWorkerPrivate) {
219 mWorkerPrivate->Cancel();
220 mWorkerPrivate = nullptr;
224 NS_IMPL_CYCLE_COLLECTION_CLASS(Worker)
226 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
227 if (tmp->mWorkerPrivate) {
228 tmp->mWorkerPrivate->Traverse(cb);
230 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
232 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
233 tmp->Terminate();
234 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
235 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
237 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
238 NS_IMPL_CYCLE_COLLECTION_TRACE_END
240 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worker)
241 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
243 NS_IMPL_ADDREF_INHERITED(Worker, DOMEventTargetHelper)
244 NS_IMPL_RELEASE_INHERITED(Worker, DOMEventTargetHelper)
246 } // namespace mozilla::dom