|
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
|
2 /* vim: set ts=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 "WorkerPrivate.h" |
|
8 |
|
9 #include "amIAddonManager.h" |
|
10 #include "nsIClassInfo.h" |
|
11 #include "nsIContentSecurityPolicy.h" |
|
12 #include "nsIConsoleService.h" |
|
13 #include "nsIDOMDOMException.h" |
|
14 #include "nsIDOMEvent.h" |
|
15 #include "nsIDOMFile.h" |
|
16 #include "nsIDOMMessageEvent.h" |
|
17 #include "nsIDocument.h" |
|
18 #include "nsIDocShell.h" |
|
19 #include "nsIMemoryReporter.h" |
|
20 #include "nsIPermissionManager.h" |
|
21 #include "nsIScriptError.h" |
|
22 #include "nsIScriptGlobalObject.h" |
|
23 #include "nsIScriptSecurityManager.h" |
|
24 #include "nsPIDOMWindow.h" |
|
25 #include "nsITextToSubURI.h" |
|
26 #include "nsIThreadInternal.h" |
|
27 #include "nsITimer.h" |
|
28 #include "nsIURI.h" |
|
29 #include "nsIURL.h" |
|
30 #include "nsIXPConnect.h" |
|
31 |
|
32 #include <algorithm> |
|
33 #include "jsfriendapi.h" |
|
34 #include "js/OldDebugAPI.h" |
|
35 #include "js/MemoryMetrics.h" |
|
36 #include "mozilla/Assertions.h" |
|
37 #include "mozilla/ContentEvents.h" |
|
38 #include "mozilla/EventDispatcher.h" |
|
39 #include "mozilla/Likely.h" |
|
40 #include "mozilla/dom/BindingUtils.h" |
|
41 #include "mozilla/dom/ErrorEvent.h" |
|
42 #include "mozilla/dom/ErrorEventBinding.h" |
|
43 #include "mozilla/dom/Exceptions.h" |
|
44 #include "mozilla/dom/FunctionBinding.h" |
|
45 #include "mozilla/dom/ImageData.h" |
|
46 #include "mozilla/dom/ImageDataBinding.h" |
|
47 #include "mozilla/dom/MessageEvent.h" |
|
48 #include "mozilla/dom/MessageEventBinding.h" |
|
49 #include "mozilla/dom/MessagePortList.h" |
|
50 #include "mozilla/dom/WorkerBinding.h" |
|
51 #include "mozilla/Preferences.h" |
|
52 #include "nsAlgorithm.h" |
|
53 #include "nsContentUtils.h" |
|
54 #include "nsCxPusher.h" |
|
55 #include "nsError.h" |
|
56 #include "nsDOMJSUtils.h" |
|
57 #include "nsHostObjectProtocolHandler.h" |
|
58 #include "nsJSEnvironment.h" |
|
59 #include "nsJSUtils.h" |
|
60 #include "nsNetUtil.h" |
|
61 #include "nsPrintfCString.h" |
|
62 #include "nsProxyRelease.h" |
|
63 #include "nsSandboxFlags.h" |
|
64 #include "xpcpublic.h" |
|
65 |
|
66 #ifdef ANDROID |
|
67 #include <android/log.h> |
|
68 #endif |
|
69 |
|
70 #ifdef DEBUG |
|
71 #include "nsThreadManager.h" |
|
72 #endif |
|
73 |
|
74 #include "File.h" |
|
75 #include "MessagePort.h" |
|
76 #include "Navigator.h" |
|
77 #include "Principal.h" |
|
78 #include "RuntimeService.h" |
|
79 #include "ScriptLoader.h" |
|
80 #include "SharedWorker.h" |
|
81 #include "WorkerFeature.h" |
|
82 #include "WorkerRunnable.h" |
|
83 #include "WorkerScope.h" |
|
84 |
|
85 // JS_MaybeGC will run once every second during normal execution. |
|
86 #define PERIODIC_GC_TIMER_DELAY_SEC 1 |
|
87 |
|
88 // A shrinking GC will run five seconds after the last event is processed. |
|
89 #define IDLE_GC_TIMER_DELAY_SEC 5 |
|
90 |
|
91 #define PREF_WORKERS_ENABLED "dom.workers.enabled" |
|
92 |
|
93 #ifdef WORKER_LOGGING |
|
94 #define LOG(_args) do { printf _args ; fflush(stdout); } while (0) |
|
95 #else |
|
96 #define LOG(_args) do { } while (0) |
|
97 #endif |
|
98 |
|
99 using namespace mozilla; |
|
100 using namespace mozilla::dom; |
|
101 USING_WORKERS_NAMESPACE |
|
102 |
|
103 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf) |
|
104 |
|
105 #ifdef DEBUG |
|
106 |
|
107 BEGIN_WORKERS_NAMESPACE |
|
108 |
|
109 void |
|
110 AssertIsOnMainThread() |
|
111 { |
|
112 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
|
113 } |
|
114 |
|
115 END_WORKERS_NAMESPACE |
|
116 |
|
117 #endif |
|
118 |
|
119 namespace { |
|
120 |
|
121 #ifdef DEBUG |
|
122 |
|
123 const nsIID kDEBUGWorkerEventTargetIID = { |
|
124 0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb } |
|
125 }; |
|
126 |
|
127 #endif |
|
128 |
|
129 template <class T> |
|
130 class AutoPtrComparator |
|
131 { |
|
132 typedef nsAutoPtr<T> A; |
|
133 typedef T* B; |
|
134 |
|
135 public: |
|
136 bool Equals(const A& a, const B& b) const { |
|
137 return a && b ? *a == *b : !a && !b ? true : false; |
|
138 } |
|
139 bool LessThan(const A& a, const B& b) const { |
|
140 return a && b ? *a < *b : b ? true : false; |
|
141 } |
|
142 }; |
|
143 |
|
144 template <class T> |
|
145 inline AutoPtrComparator<T> |
|
146 GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&) |
|
147 { |
|
148 return AutoPtrComparator<T>(); |
|
149 } |
|
150 |
|
151 // Specialize this if there's some class that has multiple nsISupports bases. |
|
152 template <class T> |
|
153 struct ISupportsBaseInfo |
|
154 { |
|
155 typedef T ISupportsBase; |
|
156 }; |
|
157 |
|
158 template <template <class> class SmartPtr, class T> |
|
159 inline void |
|
160 SwapToISupportsArray(SmartPtr<T>& aSrc, |
|
161 nsTArray<nsCOMPtr<nsISupports> >& aDest) |
|
162 { |
|
163 nsCOMPtr<nsISupports>* dest = aDest.AppendElement(); |
|
164 |
|
165 T* raw = nullptr; |
|
166 aSrc.swap(raw); |
|
167 |
|
168 nsISupports* rawSupports = |
|
169 static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw); |
|
170 dest->swap(rawSupports); |
|
171 } |
|
172 |
|
173 // This class is used to wrap any runnables that the worker receives via the |
|
174 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or |
|
175 // from the worker's EventTarget). |
|
176 class ExternalRunnableWrapper MOZ_FINAL : public WorkerRunnable |
|
177 { |
|
178 nsCOMPtr<nsICancelableRunnable> mWrappedRunnable; |
|
179 |
|
180 public: |
|
181 ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate, |
|
182 nsICancelableRunnable* aWrappedRunnable) |
|
183 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
184 mWrappedRunnable(aWrappedRunnable) |
|
185 { |
|
186 MOZ_ASSERT(aWorkerPrivate); |
|
187 MOZ_ASSERT(aWrappedRunnable); |
|
188 } |
|
189 |
|
190 NS_DECL_ISUPPORTS_INHERITED |
|
191 |
|
192 private: |
|
193 ~ExternalRunnableWrapper() |
|
194 { } |
|
195 |
|
196 virtual bool |
|
197 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
198 { |
|
199 nsresult rv = mWrappedRunnable->Run(); |
|
200 if (NS_FAILED(rv)) { |
|
201 if (!JS_IsExceptionPending(aCx)) { |
|
202 Throw(aCx, rv); |
|
203 } |
|
204 return false; |
|
205 } |
|
206 return true; |
|
207 } |
|
208 |
|
209 NS_IMETHOD |
|
210 Cancel() MOZ_OVERRIDE |
|
211 { |
|
212 nsresult rv = mWrappedRunnable->Cancel(); |
|
213 nsresult rv2 = WorkerRunnable::Cancel(); |
|
214 return NS_FAILED(rv) ? rv : rv2; |
|
215 } |
|
216 }; |
|
217 |
|
218 struct WindowAction |
|
219 { |
|
220 nsPIDOMWindow* mWindow; |
|
221 JSContext* mJSContext; |
|
222 bool mDefaultAction; |
|
223 |
|
224 WindowAction(nsPIDOMWindow* aWindow, JSContext* aJSContext) |
|
225 : mWindow(aWindow), mJSContext(aJSContext), mDefaultAction(true) |
|
226 { |
|
227 MOZ_ASSERT(aJSContext); |
|
228 } |
|
229 |
|
230 WindowAction(nsPIDOMWindow* aWindow) |
|
231 : mWindow(aWindow), mJSContext(nullptr), mDefaultAction(true) |
|
232 { } |
|
233 |
|
234 bool |
|
235 operator==(const WindowAction& aOther) const |
|
236 { |
|
237 return mWindow == aOther.mWindow; |
|
238 } |
|
239 }; |
|
240 |
|
241 void |
|
242 LogErrorToConsole(const nsAString& aMessage, |
|
243 const nsAString& aFilename, |
|
244 const nsAString& aLine, |
|
245 uint32_t aLineNumber, |
|
246 uint32_t aColumnNumber, |
|
247 uint32_t aFlags, |
|
248 uint64_t aInnerWindowId) |
|
249 { |
|
250 AssertIsOnMainThread(); |
|
251 |
|
252 nsCOMPtr<nsIScriptError> scriptError = |
|
253 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); |
|
254 NS_WARN_IF_FALSE(scriptError, "Failed to create script error!"); |
|
255 |
|
256 if (scriptError) { |
|
257 if (NS_FAILED(scriptError->InitWithWindowID(aMessage, aFilename, aLine, |
|
258 aLineNumber, aColumnNumber, |
|
259 aFlags, "Web Worker", |
|
260 aInnerWindowId))) { |
|
261 NS_WARNING("Failed to init script error!"); |
|
262 scriptError = nullptr; |
|
263 } |
|
264 } |
|
265 |
|
266 nsCOMPtr<nsIConsoleService> consoleService = |
|
267 do_GetService(NS_CONSOLESERVICE_CONTRACTID); |
|
268 NS_WARN_IF_FALSE(consoleService, "Failed to get console service!"); |
|
269 |
|
270 if (consoleService) { |
|
271 if (scriptError) { |
|
272 if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) { |
|
273 return; |
|
274 } |
|
275 NS_WARNING("LogMessage failed!"); |
|
276 } else if (NS_SUCCEEDED(consoleService->LogStringMessage( |
|
277 aMessage.BeginReading()))) { |
|
278 return; |
|
279 } |
|
280 NS_WARNING("LogStringMessage failed!"); |
|
281 } |
|
282 |
|
283 NS_ConvertUTF16toUTF8 msg(aMessage); |
|
284 NS_ConvertUTF16toUTF8 filename(aFilename); |
|
285 |
|
286 static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]"; |
|
287 |
|
288 #ifdef ANDROID |
|
289 __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(), |
|
290 filename.get(), aLineNumber); |
|
291 #endif |
|
292 |
|
293 fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber); |
|
294 fflush(stderr); |
|
295 } |
|
296 |
|
297 struct WorkerStructuredCloneCallbacks |
|
298 { |
|
299 static JSObject* |
|
300 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, |
|
301 uint32_t aData, void* aClosure) |
|
302 { |
|
303 // See if object is a nsIDOMFile pointer. |
|
304 if (aTag == DOMWORKER_SCTAG_FILE) { |
|
305 MOZ_ASSERT(!aData); |
|
306 |
|
307 nsIDOMFile* file; |
|
308 if (JS_ReadBytes(aReader, &file, sizeof(file))) { |
|
309 MOZ_ASSERT(file); |
|
310 |
|
311 #ifdef DEBUG |
|
312 { |
|
313 // File should not be mutable. |
|
314 nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); |
|
315 bool isMutable; |
|
316 NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) && |
|
317 !isMutable, |
|
318 "Only immutable file should be passed to worker"); |
|
319 } |
|
320 #endif |
|
321 |
|
322 // nsIDOMFiles should be threadsafe, thus we will use the same instance |
|
323 // in the worker. |
|
324 JSObject* jsFile = file::CreateFile(aCx, file); |
|
325 return jsFile; |
|
326 } |
|
327 } |
|
328 // See if object is a nsIDOMBlob pointer. |
|
329 else if (aTag == DOMWORKER_SCTAG_BLOB) { |
|
330 MOZ_ASSERT(!aData); |
|
331 |
|
332 nsIDOMBlob* blob; |
|
333 if (JS_ReadBytes(aReader, &blob, sizeof(blob))) { |
|
334 MOZ_ASSERT(blob); |
|
335 |
|
336 #ifdef DEBUG |
|
337 { |
|
338 // Blob should not be mutable. |
|
339 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); |
|
340 bool isMutable; |
|
341 NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) && |
|
342 !isMutable, |
|
343 "Only immutable blob should be passed to worker"); |
|
344 } |
|
345 #endif |
|
346 |
|
347 // nsIDOMBlob should be threadsafe, thus we will use the same instance |
|
348 // in the worker. |
|
349 JSObject* jsBlob = file::CreateBlob(aCx, blob); |
|
350 return jsBlob; |
|
351 } |
|
352 } |
|
353 // See if the object is an ImageData. |
|
354 else if (aTag == SCTAG_DOM_IMAGEDATA) { |
|
355 MOZ_ASSERT(!aData); |
|
356 |
|
357 // Read the information out of the stream. |
|
358 uint32_t width, height; |
|
359 JS::Rooted<JS::Value> dataArray(aCx); |
|
360 if (!JS_ReadUint32Pair(aReader, &width, &height) || |
|
361 !JS_ReadTypedArray(aReader, &dataArray)) |
|
362 { |
|
363 return nullptr; |
|
364 } |
|
365 MOZ_ASSERT(dataArray.isObject()); |
|
366 |
|
367 JS::Rooted<JSObject*> result(aCx); |
|
368 { |
|
369 // Construct the ImageData. |
|
370 nsRefPtr<ImageData> imageData = new ImageData(width, height, |
|
371 dataArray.toObject()); |
|
372 // Wrap it in a JS::Value, protected from a moving GC during ~nsRefPtr. |
|
373 result = imageData->WrapObject(aCx); |
|
374 } |
|
375 return result; |
|
376 } |
|
377 |
|
378 Error(aCx, 0); |
|
379 return nullptr; |
|
380 } |
|
381 |
|
382 static bool |
|
383 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, |
|
384 JS::Handle<JSObject*> aObj, void* aClosure) |
|
385 { |
|
386 NS_ASSERTION(aClosure, "Null pointer!"); |
|
387 |
|
388 // We'll stash any nsISupports pointers that need to be AddRef'd here. |
|
389 nsTArray<nsCOMPtr<nsISupports> >* clonedObjects = |
|
390 static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure); |
|
391 |
|
392 // See if this is a File object. |
|
393 { |
|
394 nsIDOMFile* file = file::GetDOMFileFromJSObject(aObj); |
|
395 if (file) { |
|
396 if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) && |
|
397 JS_WriteBytes(aWriter, &file, sizeof(file))) { |
|
398 clonedObjects->AppendElement(file); |
|
399 return true; |
|
400 } |
|
401 } |
|
402 } |
|
403 |
|
404 // See if this is a Blob object. |
|
405 { |
|
406 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj); |
|
407 if (blob) { |
|
408 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); |
|
409 if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false)) && |
|
410 JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) && |
|
411 JS_WriteBytes(aWriter, &blob, sizeof(blob))) { |
|
412 clonedObjects->AppendElement(blob); |
|
413 return true; |
|
414 } |
|
415 } |
|
416 } |
|
417 |
|
418 // See if this is an ImageData object. |
|
419 { |
|
420 ImageData* imageData = nullptr; |
|
421 if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) { |
|
422 // Prepare the ImageData internals. |
|
423 uint32_t width = imageData->Width(); |
|
424 uint32_t height = imageData->Height(); |
|
425 JS::Rooted<JSObject*> dataArray(aCx, imageData->GetDataObject()); |
|
426 |
|
427 // Write the internals to the stream. |
|
428 JSAutoCompartment ac(aCx, dataArray); |
|
429 JS::Rooted<JS::Value> arrayValue(aCx, JS::ObjectValue(*dataArray)); |
|
430 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEDATA, 0) && |
|
431 JS_WriteUint32Pair(aWriter, width, height) && |
|
432 JS_WriteTypedArray(aWriter, arrayValue); |
|
433 } |
|
434 } |
|
435 |
|
436 Error(aCx, 0); |
|
437 return false; |
|
438 } |
|
439 |
|
440 static void |
|
441 Error(JSContext* aCx, uint32_t /* aErrorId */) |
|
442 { |
|
443 Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); |
|
444 } |
|
445 }; |
|
446 |
|
447 JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = { |
|
448 WorkerStructuredCloneCallbacks::Read, |
|
449 WorkerStructuredCloneCallbacks::Write, |
|
450 WorkerStructuredCloneCallbacks::Error, |
|
451 nullptr, |
|
452 nullptr, |
|
453 nullptr |
|
454 }; |
|
455 |
|
456 struct MainThreadWorkerStructuredCloneCallbacks |
|
457 { |
|
458 static JSObject* |
|
459 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, |
|
460 uint32_t aData, void* aClosure) |
|
461 { |
|
462 AssertIsOnMainThread(); |
|
463 |
|
464 // See if object is a nsIDOMFile pointer. |
|
465 if (aTag == DOMWORKER_SCTAG_FILE) { |
|
466 MOZ_ASSERT(!aData); |
|
467 |
|
468 nsIDOMFile* file; |
|
469 if (JS_ReadBytes(aReader, &file, sizeof(file))) { |
|
470 MOZ_ASSERT(file); |
|
471 |
|
472 #ifdef DEBUG |
|
473 { |
|
474 // File should not be mutable. |
|
475 nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); |
|
476 bool isMutable; |
|
477 NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) && |
|
478 !isMutable, |
|
479 "Only immutable file should be passed to worker"); |
|
480 } |
|
481 #endif |
|
482 |
|
483 // nsIDOMFiles should be threadsafe, thus we will use the same instance |
|
484 // on the main thread. |
|
485 JS::Rooted<JS::Value> wrappedFile(aCx); |
|
486 nsresult rv = nsContentUtils::WrapNative(aCx, file, |
|
487 &NS_GET_IID(nsIDOMFile), |
|
488 &wrappedFile); |
|
489 if (NS_FAILED(rv)) { |
|
490 Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR); |
|
491 return nullptr; |
|
492 } |
|
493 |
|
494 return &wrappedFile.toObject(); |
|
495 } |
|
496 } |
|
497 // See if object is a nsIDOMBlob pointer. |
|
498 else if (aTag == DOMWORKER_SCTAG_BLOB) { |
|
499 MOZ_ASSERT(!aData); |
|
500 |
|
501 nsIDOMBlob* blob; |
|
502 if (JS_ReadBytes(aReader, &blob, sizeof(blob))) { |
|
503 MOZ_ASSERT(blob); |
|
504 |
|
505 #ifdef DEBUG |
|
506 { |
|
507 // Blob should not be mutable. |
|
508 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); |
|
509 bool isMutable; |
|
510 NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) && |
|
511 !isMutable, |
|
512 "Only immutable blob should be passed to worker"); |
|
513 } |
|
514 #endif |
|
515 |
|
516 // nsIDOMBlobs should be threadsafe, thus we will use the same instance |
|
517 // on the main thread. |
|
518 JS::Rooted<JS::Value> wrappedBlob(aCx); |
|
519 nsresult rv = nsContentUtils::WrapNative(aCx, blob, |
|
520 &NS_GET_IID(nsIDOMBlob), |
|
521 &wrappedBlob); |
|
522 if (NS_FAILED(rv)) { |
|
523 Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR); |
|
524 return nullptr; |
|
525 } |
|
526 |
|
527 return &wrappedBlob.toObject(); |
|
528 } |
|
529 } |
|
530 |
|
531 JS_ClearPendingException(aCx); |
|
532 return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr); |
|
533 } |
|
534 |
|
535 static bool |
|
536 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, |
|
537 JS::Handle<JSObject*> aObj, void* aClosure) |
|
538 { |
|
539 AssertIsOnMainThread(); |
|
540 |
|
541 NS_ASSERTION(aClosure, "Null pointer!"); |
|
542 |
|
543 // We'll stash any nsISupports pointers that need to be AddRef'd here. |
|
544 nsTArray<nsCOMPtr<nsISupports> >* clonedObjects = |
|
545 static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure); |
|
546 |
|
547 // See if this is a wrapped native. |
|
548 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative; |
|
549 nsContentUtils::XPConnect()-> |
|
550 GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); |
|
551 |
|
552 if (wrappedNative) { |
|
553 // Get the raw nsISupports out of it. |
|
554 nsISupports* wrappedObject = wrappedNative->Native(); |
|
555 NS_ASSERTION(wrappedObject, "Null pointer?!"); |
|
556 |
|
557 nsISupports* ccISupports = nullptr; |
|
558 wrappedObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), |
|
559 reinterpret_cast<void**>(&ccISupports)); |
|
560 if (ccISupports) { |
|
561 NS_WARNING("Cycle collected objects are not supported!"); |
|
562 } |
|
563 else { |
|
564 // See if the wrapped native is a nsIDOMFile. |
|
565 nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject); |
|
566 if (file) { |
|
567 nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); |
|
568 if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(false))) { |
|
569 nsIDOMFile* filePtr = file; |
|
570 if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) && |
|
571 JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) { |
|
572 clonedObjects->AppendElement(file); |
|
573 return true; |
|
574 } |
|
575 } |
|
576 } |
|
577 |
|
578 // See if the wrapped native is a nsIDOMBlob. |
|
579 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject); |
|
580 if (blob) { |
|
581 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); |
|
582 if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false))) { |
|
583 nsIDOMBlob* blobPtr = blob; |
|
584 if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) && |
|
585 JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) { |
|
586 clonedObjects->AppendElement(blob); |
|
587 return true; |
|
588 } |
|
589 } |
|
590 } |
|
591 } |
|
592 } |
|
593 |
|
594 JS_ClearPendingException(aCx); |
|
595 return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr); |
|
596 } |
|
597 |
|
598 static void |
|
599 Error(JSContext* aCx, uint32_t aErrorId) |
|
600 { |
|
601 AssertIsOnMainThread(); |
|
602 |
|
603 NS_DOMStructuredCloneError(aCx, aErrorId); |
|
604 } |
|
605 }; |
|
606 |
|
607 JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = { |
|
608 MainThreadWorkerStructuredCloneCallbacks::Read, |
|
609 MainThreadWorkerStructuredCloneCallbacks::Write, |
|
610 MainThreadWorkerStructuredCloneCallbacks::Error, |
|
611 nullptr, |
|
612 nullptr, |
|
613 nullptr |
|
614 }; |
|
615 |
|
616 struct ChromeWorkerStructuredCloneCallbacks |
|
617 { |
|
618 static JSObject* |
|
619 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, |
|
620 uint32_t aData, void* aClosure) |
|
621 { |
|
622 return WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, |
|
623 aClosure); |
|
624 } |
|
625 |
|
626 static bool |
|
627 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, |
|
628 JS::Handle<JSObject*> aObj, void* aClosure) |
|
629 { |
|
630 return WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure); |
|
631 } |
|
632 |
|
633 static void |
|
634 Error(JSContext* aCx, uint32_t aErrorId) |
|
635 { |
|
636 return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId); |
|
637 } |
|
638 }; |
|
639 |
|
640 JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = { |
|
641 ChromeWorkerStructuredCloneCallbacks::Read, |
|
642 ChromeWorkerStructuredCloneCallbacks::Write, |
|
643 ChromeWorkerStructuredCloneCallbacks::Error, |
|
644 nullptr, |
|
645 nullptr, |
|
646 nullptr |
|
647 }; |
|
648 |
|
649 struct MainThreadChromeWorkerStructuredCloneCallbacks |
|
650 { |
|
651 static JSObject* |
|
652 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, |
|
653 uint32_t aData, void* aClosure) |
|
654 { |
|
655 AssertIsOnMainThread(); |
|
656 |
|
657 JSObject* clone = |
|
658 MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, |
|
659 aClosure); |
|
660 if (clone) { |
|
661 return clone; |
|
662 } |
|
663 |
|
664 clone = |
|
665 ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, |
|
666 aClosure); |
|
667 if (clone) { |
|
668 return clone; |
|
669 } |
|
670 |
|
671 JS_ClearPendingException(aCx); |
|
672 return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr); |
|
673 } |
|
674 |
|
675 static bool |
|
676 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, |
|
677 JS::Handle<JSObject*> aObj, void* aClosure) |
|
678 { |
|
679 AssertIsOnMainThread(); |
|
680 |
|
681 if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, |
|
682 aClosure) || |
|
683 ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, |
|
684 aClosure) || |
|
685 NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr)) { |
|
686 return true; |
|
687 } |
|
688 |
|
689 return false; |
|
690 } |
|
691 |
|
692 static void |
|
693 Error(JSContext* aCx, uint32_t aErrorId) |
|
694 { |
|
695 AssertIsOnMainThread(); |
|
696 |
|
697 NS_DOMStructuredCloneError(aCx, aErrorId); |
|
698 } |
|
699 }; |
|
700 |
|
701 JSStructuredCloneCallbacks gMainThreadChromeWorkerStructuredCloneCallbacks = { |
|
702 MainThreadChromeWorkerStructuredCloneCallbacks::Read, |
|
703 MainThreadChromeWorkerStructuredCloneCallbacks::Write, |
|
704 MainThreadChromeWorkerStructuredCloneCallbacks::Error, |
|
705 nullptr, |
|
706 nullptr, |
|
707 nullptr |
|
708 }; |
|
709 |
|
710 class MainThreadReleaseRunnable MOZ_FINAL : public nsRunnable |
|
711 { |
|
712 nsTArray<nsCOMPtr<nsISupports>> mDoomed; |
|
713 nsTArray<nsCString> mHostObjectURIs; |
|
714 |
|
715 public: |
|
716 MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>& aDoomed, |
|
717 nsTArray<nsCString>& aHostObjectURIs) |
|
718 { |
|
719 mDoomed.SwapElements(aDoomed); |
|
720 mHostObjectURIs.SwapElements(aHostObjectURIs); |
|
721 } |
|
722 |
|
723 NS_DECL_ISUPPORTS_INHERITED |
|
724 |
|
725 NS_IMETHOD |
|
726 Run() MOZ_OVERRIDE |
|
727 { |
|
728 mDoomed.Clear(); |
|
729 |
|
730 for (uint32_t index = 0; index < mHostObjectURIs.Length(); index++) { |
|
731 nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[index]); |
|
732 } |
|
733 |
|
734 return NS_OK; |
|
735 } |
|
736 |
|
737 private: |
|
738 ~MainThreadReleaseRunnable() |
|
739 { } |
|
740 }; |
|
741 |
|
742 class WorkerFinishedRunnable MOZ_FINAL : public WorkerControlRunnable |
|
743 { |
|
744 WorkerPrivate* mFinishedWorker; |
|
745 |
|
746 public: |
|
747 WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate, |
|
748 WorkerPrivate* aFinishedWorker) |
|
749 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
750 mFinishedWorker(aFinishedWorker) |
|
751 { } |
|
752 |
|
753 private: |
|
754 virtual bool |
|
755 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
756 { |
|
757 // Silence bad assertions. |
|
758 return true; |
|
759 } |
|
760 |
|
761 virtual void |
|
762 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
763 bool aDispatchResult) MOZ_OVERRIDE |
|
764 { |
|
765 // Silence bad assertions. |
|
766 } |
|
767 |
|
768 virtual bool |
|
769 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
770 { |
|
771 nsTArray<nsCOMPtr<nsISupports>> doomed; |
|
772 mFinishedWorker->ForgetMainThreadObjects(doomed); |
|
773 |
|
774 nsTArray<nsCString> hostObjectURIs; |
|
775 mFinishedWorker->StealHostObjectURIs(hostObjectURIs); |
|
776 |
|
777 nsRefPtr<MainThreadReleaseRunnable> runnable = |
|
778 new MainThreadReleaseRunnable(doomed, hostObjectURIs); |
|
779 if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { |
|
780 NS_WARNING("Failed to dispatch, going to leak!"); |
|
781 } |
|
782 |
|
783 RuntimeService* runtime = RuntimeService::GetService(); |
|
784 NS_ASSERTION(runtime, "This should never be null!"); |
|
785 |
|
786 runtime->UnregisterWorker(aCx, mFinishedWorker); |
|
787 |
|
788 mFinishedWorker->ClearSelfRef(); |
|
789 return true; |
|
790 } |
|
791 }; |
|
792 |
|
793 class TopLevelWorkerFinishedRunnable MOZ_FINAL : public nsRunnable |
|
794 { |
|
795 WorkerPrivate* mFinishedWorker; |
|
796 |
|
797 public: |
|
798 TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker) |
|
799 : mFinishedWorker(aFinishedWorker) |
|
800 { |
|
801 aFinishedWorker->AssertIsOnWorkerThread(); |
|
802 } |
|
803 |
|
804 NS_DECL_ISUPPORTS_INHERITED |
|
805 |
|
806 private: |
|
807 NS_IMETHOD |
|
808 Run() MOZ_OVERRIDE |
|
809 { |
|
810 AssertIsOnMainThread(); |
|
811 |
|
812 RuntimeService* runtime = RuntimeService::GetService(); |
|
813 MOZ_ASSERT(runtime); |
|
814 |
|
815 AutoSafeJSContext cx; |
|
816 JSAutoRequest ar(cx); |
|
817 |
|
818 runtime->UnregisterWorker(cx, mFinishedWorker); |
|
819 |
|
820 nsTArray<nsCOMPtr<nsISupports> > doomed; |
|
821 mFinishedWorker->ForgetMainThreadObjects(doomed); |
|
822 |
|
823 nsTArray<nsCString> hostObjectURIs; |
|
824 mFinishedWorker->StealHostObjectURIs(hostObjectURIs); |
|
825 |
|
826 nsRefPtr<MainThreadReleaseRunnable> runnable = |
|
827 new MainThreadReleaseRunnable(doomed, hostObjectURIs); |
|
828 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { |
|
829 NS_WARNING("Failed to dispatch, going to leak!"); |
|
830 } |
|
831 |
|
832 mFinishedWorker->ClearSelfRef(); |
|
833 return NS_OK; |
|
834 } |
|
835 }; |
|
836 |
|
837 class ModifyBusyCountRunnable MOZ_FINAL : public WorkerControlRunnable |
|
838 { |
|
839 bool mIncrease; |
|
840 |
|
841 public: |
|
842 ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease) |
|
843 : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount), |
|
844 mIncrease(aIncrease) |
|
845 { } |
|
846 |
|
847 private: |
|
848 virtual bool |
|
849 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
850 { |
|
851 return aWorkerPrivate->ModifyBusyCount(aCx, mIncrease); |
|
852 } |
|
853 |
|
854 virtual void |
|
855 PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) |
|
856 MOZ_OVERRIDE |
|
857 { |
|
858 if (mIncrease) { |
|
859 WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult); |
|
860 return; |
|
861 } |
|
862 // Don't do anything here as it's possible that aWorkerPrivate has been |
|
863 // deleted. |
|
864 } |
|
865 }; |
|
866 |
|
867 class CompileScriptRunnable MOZ_FINAL : public WorkerRunnable |
|
868 { |
|
869 public: |
|
870 CompileScriptRunnable(WorkerPrivate* aWorkerPrivate) |
|
871 : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) |
|
872 { } |
|
873 |
|
874 private: |
|
875 virtual bool |
|
876 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
877 { |
|
878 JS::Rooted<JSObject*> global(aCx, |
|
879 aWorkerPrivate->CreateGlobalScope(aCx)); |
|
880 if (!global) { |
|
881 NS_WARNING("Failed to make global!"); |
|
882 return false; |
|
883 } |
|
884 |
|
885 JSAutoCompartment ac(aCx, global); |
|
886 return scriptloader::LoadWorkerScript(aCx); |
|
887 } |
|
888 }; |
|
889 |
|
890 class CloseEventRunnable MOZ_FINAL : public WorkerRunnable |
|
891 { |
|
892 public: |
|
893 CloseEventRunnable(WorkerPrivate* aWorkerPrivate) |
|
894 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) |
|
895 { } |
|
896 |
|
897 private: |
|
898 virtual bool |
|
899 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
900 { |
|
901 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!"); |
|
902 } |
|
903 |
|
904 virtual void |
|
905 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
906 bool aDispatchResult) MOZ_OVERRIDE |
|
907 { |
|
908 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!"); |
|
909 } |
|
910 |
|
911 virtual bool |
|
912 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
913 { |
|
914 JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx)); |
|
915 NS_ASSERTION(target, "This must never be null!"); |
|
916 |
|
917 aWorkerPrivate->CloseHandlerStarted(); |
|
918 |
|
919 WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope(); |
|
920 |
|
921 nsCOMPtr<nsIDOMEvent> event; |
|
922 nsresult rv = |
|
923 NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr); |
|
924 if (NS_FAILED(rv)) { |
|
925 Throw(aCx, rv); |
|
926 return false; |
|
927 } |
|
928 |
|
929 rv = event->InitEvent(NS_LITERAL_STRING("close"), false, false); |
|
930 if (NS_FAILED(rv)) { |
|
931 Throw(aCx, rv); |
|
932 return false; |
|
933 } |
|
934 |
|
935 event->SetTrusted(true); |
|
936 |
|
937 globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr); |
|
938 |
|
939 return true; |
|
940 } |
|
941 |
|
942 virtual void |
|
943 PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) |
|
944 MOZ_OVERRIDE |
|
945 { |
|
946 // Report errors. |
|
947 WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult); |
|
948 |
|
949 // Match the busy count increase from NotifyRunnable. |
|
950 if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) { |
|
951 JS_ReportPendingException(aCx); |
|
952 } |
|
953 |
|
954 aWorkerPrivate->CloseHandlerFinished(); |
|
955 } |
|
956 }; |
|
957 |
|
958 class MessageEventRunnable MOZ_FINAL : public WorkerRunnable |
|
959 { |
|
960 JSAutoStructuredCloneBuffer mBuffer; |
|
961 nsTArray<nsCOMPtr<nsISupports> > mClonedObjects; |
|
962 uint64_t mMessagePortSerial; |
|
963 bool mToMessagePort; |
|
964 |
|
965 public: |
|
966 MessageEventRunnable(WorkerPrivate* aWorkerPrivate, |
|
967 TargetAndBusyBehavior aBehavior, |
|
968 JSAutoStructuredCloneBuffer&& aData, |
|
969 nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, |
|
970 bool aToMessagePort, uint64_t aMessagePortSerial) |
|
971 : WorkerRunnable(aWorkerPrivate, aBehavior) |
|
972 , mBuffer(Move(aData)) |
|
973 , mMessagePortSerial(aMessagePortSerial) |
|
974 , mToMessagePort(aToMessagePort) |
|
975 { |
|
976 mClonedObjects.SwapElements(aClonedObjects); |
|
977 } |
|
978 |
|
979 bool |
|
980 DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
981 DOMEventTargetHelper* aTarget, bool aIsMainThread) |
|
982 { |
|
983 // Release reference to objects that were AddRef'd for |
|
984 // cloning into worker when array goes out of scope. |
|
985 nsTArray<nsCOMPtr<nsISupports>> clonedObjects; |
|
986 clonedObjects.SwapElements(mClonedObjects); |
|
987 |
|
988 JS::Rooted<JS::Value> messageData(aCx); |
|
989 if (!mBuffer.read(aCx, &messageData, |
|
990 workers::WorkerStructuredCloneCallbacks(aIsMainThread))) { |
|
991 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); |
|
992 return false; |
|
993 } |
|
994 |
|
995 nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr); |
|
996 nsresult rv = |
|
997 event->InitMessageEvent(NS_LITERAL_STRING("message"), |
|
998 false /* non-bubbling */, |
|
999 true /* cancelable */, |
|
1000 messageData, |
|
1001 EmptyString(), |
|
1002 EmptyString(), |
|
1003 nullptr); |
|
1004 if (NS_FAILED(rv)) { |
|
1005 xpc::Throw(aCx, rv); |
|
1006 return false; |
|
1007 } |
|
1008 |
|
1009 event->SetTrusted(true); |
|
1010 |
|
1011 nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event); |
|
1012 |
|
1013 nsEventStatus dummy = nsEventStatus_eIgnore; |
|
1014 aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy); |
|
1015 return true; |
|
1016 } |
|
1017 |
|
1018 private: |
|
1019 virtual bool |
|
1020 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1021 { |
|
1022 MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker()); |
|
1023 |
|
1024 if (mBehavior == ParentThreadUnchangedBusyCount) { |
|
1025 // Don't fire this event if the JS object has been disconnected from the |
|
1026 // private object. |
|
1027 if (!aWorkerPrivate->IsAcceptingEvents()) { |
|
1028 return true; |
|
1029 } |
|
1030 |
|
1031 if (mToMessagePort) { |
|
1032 return |
|
1033 aWorkerPrivate->DispatchMessageEventToMessagePort(aCx, |
|
1034 mMessagePortSerial, |
|
1035 Move(mBuffer), |
|
1036 mClonedObjects); |
|
1037 } |
|
1038 |
|
1039 if (aWorkerPrivate->IsSuspended()) { |
|
1040 aWorkerPrivate->QueueRunnable(this); |
|
1041 return true; |
|
1042 } |
|
1043 |
|
1044 aWorkerPrivate->AssertInnerWindowIsCorrect(); |
|
1045 |
|
1046 return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate, |
|
1047 !aWorkerPrivate->GetParent()); |
|
1048 } |
|
1049 |
|
1050 MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx)); |
|
1051 |
|
1052 if (mToMessagePort) { |
|
1053 nsRefPtr<workers::MessagePort> port = |
|
1054 aWorkerPrivate->GetMessagePort(mMessagePortSerial); |
|
1055 if (!port) { |
|
1056 // Must have been closed already. |
|
1057 return true; |
|
1058 } |
|
1059 return DispatchDOMEvent(aCx, aWorkerPrivate, port, false); |
|
1060 } |
|
1061 |
|
1062 return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(), |
|
1063 false); |
|
1064 } |
|
1065 }; |
|
1066 |
|
1067 class NotifyRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1068 { |
|
1069 Status mStatus; |
|
1070 |
|
1071 public: |
|
1072 NotifyRunnable(WorkerPrivate* aWorkerPrivate, Status aStatus) |
|
1073 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
1074 mStatus(aStatus) |
|
1075 { |
|
1076 MOZ_ASSERT(aStatus == Closing || aStatus == Terminating || |
|
1077 aStatus == Canceling || aStatus == Killing); |
|
1078 } |
|
1079 |
|
1080 private: |
|
1081 virtual bool |
|
1082 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1083 { |
|
1084 // Modify here, but not in PostRun! This busy count addition will be matched |
|
1085 // by the CloseEventRunnable. |
|
1086 return aWorkerPrivate->ModifyBusyCount(aCx, true); |
|
1087 } |
|
1088 |
|
1089 virtual void |
|
1090 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
1091 bool aDispatchResult) MOZ_OVERRIDE |
|
1092 { |
|
1093 if (!aDispatchResult) { |
|
1094 // We couldn't dispatch to the worker, which means it's already dead. |
|
1095 // Undo the busy count modification. |
|
1096 aWorkerPrivate->ModifyBusyCount(aCx, false); |
|
1097 } |
|
1098 } |
|
1099 |
|
1100 virtual bool |
|
1101 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1102 { |
|
1103 return aWorkerPrivate->NotifyInternal(aCx, mStatus); |
|
1104 } |
|
1105 }; |
|
1106 |
|
1107 class CloseRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1108 { |
|
1109 public: |
|
1110 CloseRunnable(WorkerPrivate* aWorkerPrivate) |
|
1111 : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) |
|
1112 { } |
|
1113 |
|
1114 private: |
|
1115 virtual bool |
|
1116 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1117 { |
|
1118 // This busy count will be matched by the CloseEventRunnable. |
|
1119 return aWorkerPrivate->ModifyBusyCount(aCx, true) && |
|
1120 aWorkerPrivate->Close(aCx); |
|
1121 } |
|
1122 }; |
|
1123 |
|
1124 class SuspendRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1125 { |
|
1126 public: |
|
1127 SuspendRunnable(WorkerPrivate* aWorkerPrivate) |
|
1128 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) |
|
1129 { } |
|
1130 |
|
1131 private: |
|
1132 virtual bool |
|
1133 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1134 { |
|
1135 return aWorkerPrivate->SuspendInternal(aCx); |
|
1136 } |
|
1137 }; |
|
1138 |
|
1139 class ResumeRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1140 { |
|
1141 public: |
|
1142 ResumeRunnable(WorkerPrivate* aWorkerPrivate) |
|
1143 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) |
|
1144 { } |
|
1145 |
|
1146 private: |
|
1147 virtual bool |
|
1148 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1149 { |
|
1150 return aWorkerPrivate->ResumeInternal(aCx); |
|
1151 } |
|
1152 }; |
|
1153 |
|
1154 class ReportErrorRunnable MOZ_FINAL : public WorkerRunnable |
|
1155 { |
|
1156 nsString mMessage; |
|
1157 nsString mFilename; |
|
1158 nsString mLine; |
|
1159 uint32_t mLineNumber; |
|
1160 uint32_t mColumnNumber; |
|
1161 uint32_t mFlags; |
|
1162 uint32_t mErrorNumber; |
|
1163 |
|
1164 public: |
|
1165 // aWorkerPrivate is the worker thread we're on (or the main thread, if null) |
|
1166 // aTarget is the worker object that we are going to fire an error at |
|
1167 // (if any). |
|
1168 static bool |
|
1169 ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
1170 bool aFireAtScope, WorkerPrivate* aTarget, |
|
1171 const nsString& aMessage, const nsString& aFilename, |
|
1172 const nsString& aLine, uint32_t aLineNumber, |
|
1173 uint32_t aColumnNumber, uint32_t aFlags, |
|
1174 uint32_t aErrorNumber, uint64_t aInnerWindowId) |
|
1175 { |
|
1176 if (aWorkerPrivate) { |
|
1177 aWorkerPrivate->AssertIsOnWorkerThread(); |
|
1178 } |
|
1179 else { |
|
1180 AssertIsOnMainThread(); |
|
1181 } |
|
1182 |
|
1183 JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, aMessage.get(), |
|
1184 aMessage.Length())); |
|
1185 if (!message) { |
|
1186 return false; |
|
1187 } |
|
1188 |
|
1189 JS::Rooted<JSString*> filename(aCx, JS_NewUCStringCopyN(aCx, aFilename.get(), |
|
1190 aFilename.Length())); |
|
1191 if (!filename) { |
|
1192 return false; |
|
1193 } |
|
1194 |
|
1195 // We should not fire error events for warnings but instead make sure that |
|
1196 // they show up in the error console. |
|
1197 if (!JSREPORT_IS_WARNING(aFlags)) { |
|
1198 // First fire an ErrorEvent at the worker. |
|
1199 RootedDictionary<ErrorEventInit> init(aCx); |
|
1200 init.mMessage = aMessage; |
|
1201 init.mFilename = aFilename; |
|
1202 init.mLineno = aLineNumber; |
|
1203 init.mCancelable = true; |
|
1204 init.mBubbles = true; |
|
1205 |
|
1206 if (aTarget) { |
|
1207 nsRefPtr<ErrorEvent> event = |
|
1208 ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init); |
|
1209 event->SetTrusted(true); |
|
1210 |
|
1211 nsEventStatus status = nsEventStatus_eIgnore; |
|
1212 aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status); |
|
1213 |
|
1214 if (status == nsEventStatus_eConsumeNoDefault) { |
|
1215 return true; |
|
1216 } |
|
1217 } |
|
1218 |
|
1219 // Now fire an event at the global object, but don't do that if the error |
|
1220 // code is too much recursion and this is the same script threw the error. |
|
1221 if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) { |
|
1222 JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx)); |
|
1223 NS_ASSERTION(target, "This should never be null!"); |
|
1224 |
|
1225 nsEventStatus status = nsEventStatus_eIgnore; |
|
1226 nsIScriptGlobalObject* sgo; |
|
1227 |
|
1228 if (aWorkerPrivate) { |
|
1229 WorkerGlobalScope* globalTarget = aWorkerPrivate->GlobalScope(); |
|
1230 MOZ_ASSERT(target == globalTarget->GetWrapperPreserveColor()); |
|
1231 |
|
1232 nsRefPtr<ErrorEvent> event = |
|
1233 ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init); |
|
1234 event->SetTrusted(true); |
|
1235 |
|
1236 nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalTarget); |
|
1237 if (NS_FAILED(EventDispatcher::DispatchDOMEvent(target, nullptr, |
|
1238 event, nullptr, |
|
1239 &status))) { |
|
1240 NS_WARNING("Failed to dispatch worker thread error event!"); |
|
1241 status = nsEventStatus_eIgnore; |
|
1242 } |
|
1243 } |
|
1244 else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) { |
|
1245 MOZ_ASSERT(NS_IsMainThread()); |
|
1246 |
|
1247 if (NS_FAILED(sgo->HandleScriptError(init, &status))) { |
|
1248 NS_WARNING("Failed to dispatch main thread error event!"); |
|
1249 status = nsEventStatus_eIgnore; |
|
1250 } |
|
1251 } |
|
1252 |
|
1253 // Was preventDefault() called? |
|
1254 if (status == nsEventStatus_eConsumeNoDefault) { |
|
1255 return true; |
|
1256 } |
|
1257 } |
|
1258 } |
|
1259 |
|
1260 // Now fire a runnable to do the same on the parent's thread if we can. |
|
1261 if (aWorkerPrivate) { |
|
1262 nsRefPtr<ReportErrorRunnable> runnable = |
|
1263 new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine, |
|
1264 aLineNumber, aColumnNumber, aFlags, |
|
1265 aErrorNumber); |
|
1266 return runnable->Dispatch(aCx); |
|
1267 } |
|
1268 |
|
1269 // Otherwise log an error to the error console. |
|
1270 LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber, |
|
1271 aFlags, aInnerWindowId); |
|
1272 return true; |
|
1273 } |
|
1274 |
|
1275 private: |
|
1276 ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage, |
|
1277 const nsString& aFilename, const nsString& aLine, |
|
1278 uint32_t aLineNumber, uint32_t aColumnNumber, |
|
1279 uint32_t aFlags, uint32_t aErrorNumber) |
|
1280 : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount), |
|
1281 mMessage(aMessage), mFilename(aFilename), mLine(aLine), |
|
1282 mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags), |
|
1283 mErrorNumber(aErrorNumber) |
|
1284 { } |
|
1285 |
|
1286 virtual void |
|
1287 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
1288 bool aDispatchResult) MOZ_OVERRIDE |
|
1289 { |
|
1290 aWorkerPrivate->AssertIsOnWorkerThread(); |
|
1291 |
|
1292 // Dispatch may fail if the worker was canceled, no need to report that as |
|
1293 // an error, so don't call base class PostDispatch. |
|
1294 } |
|
1295 |
|
1296 virtual bool |
|
1297 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1298 { |
|
1299 // Don't fire this event if the JS object has been disconnected from the |
|
1300 // private object. |
|
1301 if (!aWorkerPrivate->IsAcceptingEvents()) { |
|
1302 return true; |
|
1303 } |
|
1304 |
|
1305 JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper()); |
|
1306 |
|
1307 uint64_t innerWindowId; |
|
1308 bool fireAtScope = true; |
|
1309 |
|
1310 WorkerPrivate* parent = aWorkerPrivate->GetParent(); |
|
1311 if (parent) { |
|
1312 innerWindowId = 0; |
|
1313 } |
|
1314 else { |
|
1315 AssertIsOnMainThread(); |
|
1316 |
|
1317 if (aWorkerPrivate->IsSuspended()) { |
|
1318 aWorkerPrivate->QueueRunnable(this); |
|
1319 return true; |
|
1320 } |
|
1321 |
|
1322 if (aWorkerPrivate->IsSharedWorker()) { |
|
1323 aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename, |
|
1324 mLine, mLineNumber, |
|
1325 mColumnNumber, mFlags); |
|
1326 return true; |
|
1327 } |
|
1328 |
|
1329 aWorkerPrivate->AssertInnerWindowIsCorrect(); |
|
1330 |
|
1331 innerWindowId = aWorkerPrivate->GetInnerWindowId(); |
|
1332 } |
|
1333 |
|
1334 return ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage, |
|
1335 mFilename, mLine, mLineNumber, mColumnNumber, mFlags, |
|
1336 mErrorNumber, innerWindowId); |
|
1337 } |
|
1338 }; |
|
1339 |
|
1340 class TimerRunnable MOZ_FINAL : public WorkerRunnable |
|
1341 { |
|
1342 public: |
|
1343 TimerRunnable(WorkerPrivate* aWorkerPrivate) |
|
1344 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) |
|
1345 { } |
|
1346 |
|
1347 private: |
|
1348 virtual bool |
|
1349 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1350 { |
|
1351 // Silence bad assertions. |
|
1352 return true; |
|
1353 } |
|
1354 |
|
1355 virtual void |
|
1356 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
1357 bool aDispatchResult) MOZ_OVERRIDE |
|
1358 { |
|
1359 // Silence bad assertions. |
|
1360 } |
|
1361 |
|
1362 virtual bool |
|
1363 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1364 { |
|
1365 return aWorkerPrivate->RunExpiredTimeouts(aCx); |
|
1366 } |
|
1367 }; |
|
1368 |
|
1369 void |
|
1370 DummyCallback(nsITimer* aTimer, void* aClosure) |
|
1371 { |
|
1372 // Nothing! |
|
1373 } |
|
1374 |
|
1375 class TimerThreadEventTarget MOZ_FINAL : public nsIEventTarget |
|
1376 { |
|
1377 WorkerPrivate* mWorkerPrivate; |
|
1378 nsRefPtr<WorkerRunnable> mWorkerRunnable; |
|
1379 |
|
1380 public: |
|
1381 TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate, |
|
1382 WorkerRunnable* aWorkerRunnable) |
|
1383 : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable) |
|
1384 { |
|
1385 MOZ_ASSERT(aWorkerPrivate); |
|
1386 MOZ_ASSERT(aWorkerRunnable); |
|
1387 } |
|
1388 |
|
1389 NS_DECL_THREADSAFE_ISUPPORTS |
|
1390 |
|
1391 protected: |
|
1392 NS_IMETHOD |
|
1393 Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE |
|
1394 { |
|
1395 // This should only happen on the timer thread. |
|
1396 MOZ_ASSERT(!NS_IsMainThread()); |
|
1397 MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL); |
|
1398 |
|
1399 nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this; |
|
1400 |
|
1401 // Run the runnable we're given now (should just call DummyCallback()), |
|
1402 // otherwise the timer thread will leak it... If we run this after |
|
1403 // dispatch running the event can race against resetting the timer. |
|
1404 aRunnable->Run(); |
|
1405 |
|
1406 // This can fail if we're racing to terminate or cancel, should be handled |
|
1407 // by the terminate or cancel code. |
|
1408 mWorkerRunnable->Dispatch(nullptr); |
|
1409 |
|
1410 return NS_OK; |
|
1411 } |
|
1412 |
|
1413 NS_IMETHOD |
|
1414 IsOnCurrentThread(bool* aIsOnCurrentThread) MOZ_OVERRIDE |
|
1415 { |
|
1416 MOZ_ASSERT(aIsOnCurrentThread); |
|
1417 |
|
1418 nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread); |
|
1419 if (NS_WARN_IF(NS_FAILED(rv))) { |
|
1420 return rv; |
|
1421 } |
|
1422 |
|
1423 return NS_OK; |
|
1424 } |
|
1425 }; |
|
1426 |
|
1427 class KillCloseEventRunnable MOZ_FINAL : public WorkerRunnable |
|
1428 { |
|
1429 nsCOMPtr<nsITimer> mTimer; |
|
1430 |
|
1431 class KillScriptRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1432 { |
|
1433 public: |
|
1434 KillScriptRunnable(WorkerPrivate* aWorkerPrivate) |
|
1435 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) |
|
1436 { } |
|
1437 |
|
1438 private: |
|
1439 virtual bool |
|
1440 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1441 { |
|
1442 // Silence bad assertions, this is dispatched from the timer thread. |
|
1443 return true; |
|
1444 } |
|
1445 |
|
1446 virtual void |
|
1447 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
1448 bool aDispatchResult) MOZ_OVERRIDE |
|
1449 { |
|
1450 // Silence bad assertions, this is dispatched from the timer thread. |
|
1451 } |
|
1452 |
|
1453 virtual bool |
|
1454 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1455 { |
|
1456 // Kill running script. |
|
1457 return false; |
|
1458 } |
|
1459 }; |
|
1460 |
|
1461 public: |
|
1462 KillCloseEventRunnable(WorkerPrivate* aWorkerPrivate) |
|
1463 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) |
|
1464 { } |
|
1465 |
|
1466 bool |
|
1467 SetTimeout(JSContext* aCx, uint32_t aDelayMS) |
|
1468 { |
|
1469 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID); |
|
1470 if (!timer) { |
|
1471 JS_ReportError(aCx, "Failed to create timer!"); |
|
1472 return false; |
|
1473 } |
|
1474 |
|
1475 nsRefPtr<KillScriptRunnable> runnable = |
|
1476 new KillScriptRunnable(mWorkerPrivate); |
|
1477 |
|
1478 nsRefPtr<TimerThreadEventTarget> target = |
|
1479 new TimerThreadEventTarget(mWorkerPrivate, runnable); |
|
1480 |
|
1481 if (NS_FAILED(timer->SetTarget(target))) { |
|
1482 JS_ReportError(aCx, "Failed to set timer's target!"); |
|
1483 return false; |
|
1484 } |
|
1485 |
|
1486 if (NS_FAILED(timer->InitWithFuncCallback(DummyCallback, nullptr, aDelayMS, |
|
1487 nsITimer::TYPE_ONE_SHOT))) { |
|
1488 JS_ReportError(aCx, "Failed to start timer!"); |
|
1489 return false; |
|
1490 } |
|
1491 |
|
1492 mTimer.swap(timer); |
|
1493 return true; |
|
1494 } |
|
1495 |
|
1496 private: |
|
1497 ~KillCloseEventRunnable() |
|
1498 { |
|
1499 if (mTimer) { |
|
1500 mTimer->Cancel(); |
|
1501 } |
|
1502 } |
|
1503 |
|
1504 virtual bool |
|
1505 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1506 { |
|
1507 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!"); |
|
1508 } |
|
1509 |
|
1510 virtual void |
|
1511 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
1512 bool aDispatchResult) MOZ_OVERRIDE |
|
1513 { |
|
1514 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!"); |
|
1515 } |
|
1516 |
|
1517 virtual bool |
|
1518 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1519 { |
|
1520 if (mTimer) { |
|
1521 mTimer->Cancel(); |
|
1522 mTimer = nullptr; |
|
1523 } |
|
1524 |
|
1525 return true; |
|
1526 } |
|
1527 }; |
|
1528 |
|
1529 class UpdateRuntimeAndContextOptionsRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1530 { |
|
1531 JS::RuntimeOptions mRuntimeOptions; |
|
1532 JS::ContextOptions mContentCxOptions; |
|
1533 JS::ContextOptions mChromeCxOptions; |
|
1534 |
|
1535 public: |
|
1536 UpdateRuntimeAndContextOptionsRunnable( |
|
1537 WorkerPrivate* aWorkerPrivate, |
|
1538 const JS::RuntimeOptions& aRuntimeOptions, |
|
1539 const JS::ContextOptions& aContentCxOptions, |
|
1540 const JS::ContextOptions& aChromeCxOptions) |
|
1541 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
1542 mRuntimeOptions(aRuntimeOptions), |
|
1543 mContentCxOptions(aContentCxOptions), |
|
1544 mChromeCxOptions(aChromeCxOptions) |
|
1545 { } |
|
1546 |
|
1547 private: |
|
1548 virtual bool |
|
1549 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1550 { |
|
1551 aWorkerPrivate->UpdateRuntimeAndContextOptionsInternal(aCx, |
|
1552 mRuntimeOptions, |
|
1553 mContentCxOptions, |
|
1554 mChromeCxOptions); |
|
1555 return true; |
|
1556 } |
|
1557 }; |
|
1558 |
|
1559 class UpdatePreferenceRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1560 { |
|
1561 WorkerPreference mPref; |
|
1562 bool mValue; |
|
1563 |
|
1564 public: |
|
1565 UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate, |
|
1566 WorkerPreference aPref, |
|
1567 bool aValue) |
|
1568 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
1569 mPref(aPref), |
|
1570 mValue(aValue) |
|
1571 { } |
|
1572 |
|
1573 virtual bool |
|
1574 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1575 { |
|
1576 aWorkerPrivate->UpdatePreferenceInternal(aCx, mPref, mValue); |
|
1577 return true; |
|
1578 } |
|
1579 }; |
|
1580 |
|
1581 class UpdateJSWorkerMemoryParameterRunnable MOZ_FINAL : |
|
1582 public WorkerControlRunnable |
|
1583 { |
|
1584 uint32_t mValue; |
|
1585 JSGCParamKey mKey; |
|
1586 |
|
1587 public: |
|
1588 UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate, |
|
1589 JSGCParamKey aKey, |
|
1590 uint32_t aValue) |
|
1591 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
1592 mValue(aValue), mKey(aKey) |
|
1593 { } |
|
1594 |
|
1595 private: |
|
1596 virtual bool |
|
1597 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1598 { |
|
1599 aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue); |
|
1600 return true; |
|
1601 } |
|
1602 }; |
|
1603 |
|
1604 #ifdef JS_GC_ZEAL |
|
1605 class UpdateGCZealRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1606 { |
|
1607 uint8_t mGCZeal; |
|
1608 uint32_t mFrequency; |
|
1609 |
|
1610 public: |
|
1611 UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, |
|
1612 uint8_t aGCZeal, |
|
1613 uint32_t aFrequency) |
|
1614 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
1615 mGCZeal(aGCZeal), mFrequency(aFrequency) |
|
1616 { } |
|
1617 |
|
1618 private: |
|
1619 virtual bool |
|
1620 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1621 { |
|
1622 aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency); |
|
1623 return true; |
|
1624 } |
|
1625 }; |
|
1626 #endif |
|
1627 |
|
1628 class GarbageCollectRunnable MOZ_FINAL : public WorkerControlRunnable |
|
1629 { |
|
1630 bool mShrinking; |
|
1631 bool mCollectChildren; |
|
1632 |
|
1633 public: |
|
1634 GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking, |
|
1635 bool aCollectChildren) |
|
1636 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
1637 mShrinking(aShrinking), mCollectChildren(aCollectChildren) |
|
1638 { } |
|
1639 |
|
1640 private: |
|
1641 virtual bool |
|
1642 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1643 { |
|
1644 // Silence bad assertions, this can be dispatched from either the main |
|
1645 // thread or the timer thread.. |
|
1646 return true; |
|
1647 } |
|
1648 |
|
1649 virtual void |
|
1650 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
|
1651 bool aDispatchResult) MOZ_OVERRIDE |
|
1652 { |
|
1653 // Silence bad assertions, this can be dispatched from either the main |
|
1654 // thread or the timer thread.. |
|
1655 } |
|
1656 |
|
1657 virtual bool |
|
1658 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1659 { |
|
1660 aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren); |
|
1661 return true; |
|
1662 } |
|
1663 }; |
|
1664 |
|
1665 class CycleCollectRunnable : public WorkerControlRunnable |
|
1666 { |
|
1667 bool mCollectChildren; |
|
1668 |
|
1669 public: |
|
1670 CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren) |
|
1671 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), |
|
1672 mCollectChildren(aCollectChildren) |
|
1673 { } |
|
1674 |
|
1675 bool |
|
1676 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) |
|
1677 { |
|
1678 aWorkerPrivate->CycleCollectInternal(aCx, mCollectChildren); |
|
1679 return true; |
|
1680 } |
|
1681 }; |
|
1682 |
|
1683 class OfflineStatusChangeRunnable : public WorkerRunnable |
|
1684 { |
|
1685 public: |
|
1686 OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline) |
|
1687 : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount), |
|
1688 mIsOffline(aIsOffline) |
|
1689 { |
|
1690 } |
|
1691 |
|
1692 bool |
|
1693 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) |
|
1694 { |
|
1695 aWorkerPrivate->OfflineStatusChangeEventInternal(aCx, mIsOffline); |
|
1696 return true; |
|
1697 } |
|
1698 |
|
1699 private: |
|
1700 bool mIsOffline; |
|
1701 }; |
|
1702 |
|
1703 class WorkerJSRuntimeStats : public JS::RuntimeStats |
|
1704 { |
|
1705 const nsACString& mRtPath; |
|
1706 |
|
1707 public: |
|
1708 WorkerJSRuntimeStats(const nsACString& aRtPath) |
|
1709 : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) |
|
1710 { } |
|
1711 |
|
1712 ~WorkerJSRuntimeStats() |
|
1713 { |
|
1714 for (size_t i = 0; i != zoneStatsVector.length(); i++) { |
|
1715 delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra); |
|
1716 } |
|
1717 |
|
1718 for (size_t i = 0; i != compartmentStatsVector.length(); i++) { |
|
1719 delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra); |
|
1720 } |
|
1721 } |
|
1722 |
|
1723 virtual void |
|
1724 initExtraZoneStats(JS::Zone* aZone, |
|
1725 JS::ZoneStats* aZoneStats) |
|
1726 MOZ_OVERRIDE |
|
1727 { |
|
1728 MOZ_ASSERT(!aZoneStats->extra); |
|
1729 |
|
1730 // ReportJSRuntimeExplicitTreeStats expects that |
|
1731 // aZoneStats->extra is a xpc::ZoneStatsExtras pointer. |
|
1732 xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras; |
|
1733 extras->pathPrefix = mRtPath; |
|
1734 extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone); |
|
1735 aZoneStats->extra = extras; |
|
1736 } |
|
1737 |
|
1738 virtual void |
|
1739 initExtraCompartmentStats(JSCompartment* aCompartment, |
|
1740 JS::CompartmentStats* aCompartmentStats) |
|
1741 MOZ_OVERRIDE |
|
1742 { |
|
1743 MOZ_ASSERT(!aCompartmentStats->extra); |
|
1744 |
|
1745 // ReportJSRuntimeExplicitTreeStats expects that |
|
1746 // aCompartmentStats->extra is a xpc::CompartmentStatsExtras pointer. |
|
1747 xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras; |
|
1748 |
|
1749 // This is the |jsPathPrefix|. Each worker has exactly two compartments: |
|
1750 // one for atoms, and one for everything else. |
|
1751 extras->jsPathPrefix.Assign(mRtPath); |
|
1752 extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", |
|
1753 (void *)js::GetCompartmentZone(aCompartment)); |
|
1754 extras->jsPathPrefix += js::IsAtomsCompartment(aCompartment) |
|
1755 ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/") |
|
1756 : NS_LITERAL_CSTRING("compartment(web-worker)/"); |
|
1757 |
|
1758 // This should never be used when reporting with workers (hence the "?!"). |
|
1759 extras->domPathPrefix.AssignLiteral("explicit/workers/?!/"); |
|
1760 |
|
1761 aCompartmentStats->extra = extras; |
|
1762 } |
|
1763 }; |
|
1764 |
|
1765 class MessagePortRunnable MOZ_FINAL : public WorkerRunnable |
|
1766 { |
|
1767 uint64_t mMessagePortSerial; |
|
1768 bool mConnect; |
|
1769 |
|
1770 public: |
|
1771 MessagePortRunnable(WorkerPrivate* aWorkerPrivate, |
|
1772 uint64_t aMessagePortSerial, |
|
1773 bool aConnect) |
|
1774 : WorkerRunnable(aWorkerPrivate, aConnect ? |
|
1775 WorkerThreadModifyBusyCount : |
|
1776 WorkerThreadUnchangedBusyCount), |
|
1777 mMessagePortSerial(aMessagePortSerial), mConnect(aConnect) |
|
1778 { } |
|
1779 |
|
1780 private: |
|
1781 ~MessagePortRunnable() |
|
1782 { } |
|
1783 |
|
1784 virtual bool |
|
1785 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
|
1786 { |
|
1787 if (mConnect) { |
|
1788 return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial); |
|
1789 } |
|
1790 |
|
1791 aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial); |
|
1792 return true; |
|
1793 } |
|
1794 }; |
|
1795 |
|
1796 #ifdef DEBUG |
|
1797 |
|
1798 PRThread* |
|
1799 PRThreadFromThread(nsIThread* aThread) |
|
1800 { |
|
1801 MOZ_ASSERT(aThread); |
|
1802 |
|
1803 PRThread* result; |
|
1804 MOZ_ASSERT(NS_SUCCEEDED(aThread->GetPRThread(&result))); |
|
1805 MOZ_ASSERT(result); |
|
1806 |
|
1807 return result; |
|
1808 } |
|
1809 |
|
1810 #endif // DEBUG |
|
1811 |
|
1812 } /* anonymous namespace */ |
|
1813 |
|
1814 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable) |
|
1815 |
|
1816 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable) |
|
1817 |
|
1818 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget) |
|
1819 |
|
1820 template <class Derived> |
|
1821 class WorkerPrivateParent<Derived>::SynchronizeAndResumeRunnable MOZ_FINAL |
|
1822 : public nsRunnable |
|
1823 { |
|
1824 friend class nsRevocableEventPtr<SynchronizeAndResumeRunnable>; |
|
1825 |
|
1826 WorkerPrivate* mWorkerPrivate; |
|
1827 nsCOMPtr<nsPIDOMWindow> mWindow; |
|
1828 nsCOMPtr<nsIScriptContext> mScriptContext; |
|
1829 |
|
1830 public: |
|
1831 SynchronizeAndResumeRunnable(WorkerPrivate* aWorkerPrivate, |
|
1832 nsPIDOMWindow* aWindow, |
|
1833 nsIScriptContext* aScriptContext) |
|
1834 : mWorkerPrivate(aWorkerPrivate), mWindow(aWindow), |
|
1835 mScriptContext(aScriptContext) |
|
1836 { |
|
1837 AssertIsOnMainThread(); |
|
1838 MOZ_ASSERT(aWorkerPrivate); |
|
1839 MOZ_ASSERT(aWindow); |
|
1840 MOZ_ASSERT(!aWorkerPrivate->GetParent()); |
|
1841 } |
|
1842 |
|
1843 private: |
|
1844 ~SynchronizeAndResumeRunnable() |
|
1845 { } |
|
1846 |
|
1847 NS_IMETHOD |
|
1848 Run() MOZ_OVERRIDE |
|
1849 { |
|
1850 AssertIsOnMainThread(); |
|
1851 |
|
1852 if (mWorkerPrivate) { |
|
1853 AutoPushJSContext cx(mScriptContext ? |
|
1854 mScriptContext->GetNativeContext() : |
|
1855 nsContentUtils::GetSafeJSContext()); |
|
1856 |
|
1857 if (!mWorkerPrivate->Resume(cx, mWindow)) { |
|
1858 JS_ReportPendingException(cx); |
|
1859 } |
|
1860 } |
|
1861 |
|
1862 return NS_OK; |
|
1863 } |
|
1864 |
|
1865 void |
|
1866 Revoke() |
|
1867 { |
|
1868 AssertIsOnMainThread(); |
|
1869 MOZ_ASSERT(mWorkerPrivate); |
|
1870 MOZ_ASSERT(mWindow); |
|
1871 |
|
1872 mWorkerPrivate = nullptr; |
|
1873 mWindow = nullptr; |
|
1874 mScriptContext = nullptr; |
|
1875 } |
|
1876 }; |
|
1877 |
|
1878 template <class Derived> |
|
1879 class WorkerPrivateParent<Derived>::EventTarget MOZ_FINAL |
|
1880 : public nsIEventTarget |
|
1881 { |
|
1882 // This mutex protects mWorkerPrivate and must be acquired *before* the |
|
1883 // WorkerPrivate's mutex whenever they must both be held. |
|
1884 mozilla::Mutex mMutex; |
|
1885 WorkerPrivate* mWorkerPrivate; |
|
1886 nsIEventTarget* mWeakNestedEventTarget; |
|
1887 nsCOMPtr<nsIEventTarget> mNestedEventTarget; |
|
1888 |
|
1889 public: |
|
1890 EventTarget(WorkerPrivate* aWorkerPrivate) |
|
1891 : mMutex("WorkerPrivateParent::EventTarget::mMutex"), |
|
1892 mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr) |
|
1893 { |
|
1894 MOZ_ASSERT(aWorkerPrivate); |
|
1895 } |
|
1896 |
|
1897 EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget) |
|
1898 : mMutex("WorkerPrivateParent::EventTarget::mMutex"), |
|
1899 mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget), |
|
1900 mNestedEventTarget(aNestedEventTarget) |
|
1901 { |
|
1902 MOZ_ASSERT(aWorkerPrivate); |
|
1903 MOZ_ASSERT(aNestedEventTarget); |
|
1904 } |
|
1905 |
|
1906 void |
|
1907 Disable() |
|
1908 { |
|
1909 nsCOMPtr<nsIEventTarget> nestedEventTarget; |
|
1910 { |
|
1911 MutexAutoLock lock(mMutex); |
|
1912 |
|
1913 MOZ_ASSERT(mWorkerPrivate); |
|
1914 mWorkerPrivate = nullptr; |
|
1915 mNestedEventTarget.swap(nestedEventTarget); |
|
1916 } |
|
1917 } |
|
1918 |
|
1919 nsIEventTarget* |
|
1920 GetWeakNestedEventTarget() const |
|
1921 { |
|
1922 MOZ_ASSERT(mWeakNestedEventTarget); |
|
1923 return mWeakNestedEventTarget; |
|
1924 } |
|
1925 |
|
1926 NS_DECL_THREADSAFE_ISUPPORTS |
|
1927 NS_DECL_NSIEVENTTARGET |
|
1928 |
|
1929 private: |
|
1930 ~EventTarget() |
|
1931 { } |
|
1932 }; |
|
1933 |
|
1934 struct WorkerPrivate::TimeoutInfo |
|
1935 { |
|
1936 TimeoutInfo() |
|
1937 : mTimeoutCallable(JS::UndefinedValue()), mLineNumber(0), mId(0), |
|
1938 mIsInterval(false), mCanceled(false) |
|
1939 { |
|
1940 MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo); |
|
1941 } |
|
1942 |
|
1943 ~TimeoutInfo() |
|
1944 { |
|
1945 MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo); |
|
1946 } |
|
1947 |
|
1948 bool operator==(const TimeoutInfo& aOther) |
|
1949 { |
|
1950 return mTargetTime == aOther.mTargetTime; |
|
1951 } |
|
1952 |
|
1953 bool operator<(const TimeoutInfo& aOther) |
|
1954 { |
|
1955 return mTargetTime < aOther.mTargetTime; |
|
1956 } |
|
1957 |
|
1958 JS::Heap<JS::Value> mTimeoutCallable; |
|
1959 nsString mTimeoutString; |
|
1960 nsTArray<JS::Heap<JS::Value> > mExtraArgVals; |
|
1961 mozilla::TimeStamp mTargetTime; |
|
1962 mozilla::TimeDuration mInterval; |
|
1963 nsCString mFilename; |
|
1964 uint32_t mLineNumber; |
|
1965 int32_t mId; |
|
1966 bool mIsInterval; |
|
1967 bool mCanceled; |
|
1968 }; |
|
1969 |
|
1970 class WorkerPrivate::MemoryReporter MOZ_FINAL : public nsIMemoryReporter |
|
1971 { |
|
1972 NS_DECL_THREADSAFE_ISUPPORTS |
|
1973 |
|
1974 friend class WorkerPrivate; |
|
1975 |
|
1976 SharedMutex mMutex; |
|
1977 WorkerPrivate* mWorkerPrivate; |
|
1978 nsCString mRtPath; |
|
1979 bool mAlreadyMappedToAddon; |
|
1980 |
|
1981 public: |
|
1982 MemoryReporter(WorkerPrivate* aWorkerPrivate) |
|
1983 : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate), |
|
1984 mAlreadyMappedToAddon(false) |
|
1985 { |
|
1986 aWorkerPrivate->AssertIsOnWorkerThread(); |
|
1987 |
|
1988 nsCString escapedDomain(aWorkerPrivate->Domain()); |
|
1989 escapedDomain.ReplaceChar('/', '\\'); |
|
1990 |
|
1991 NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL()); |
|
1992 escapedURL.ReplaceChar('/', '\\'); |
|
1993 |
|
1994 nsAutoCString addressString; |
|
1995 addressString.AppendPrintf("0x%p", static_cast<void*>(aWorkerPrivate)); |
|
1996 |
|
1997 mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") + |
|
1998 escapedDomain + NS_LITERAL_CSTRING(")/worker(") + |
|
1999 escapedURL + NS_LITERAL_CSTRING(", ") + addressString + |
|
2000 NS_LITERAL_CSTRING(")/"); |
|
2001 } |
|
2002 |
|
2003 NS_IMETHOD |
|
2004 CollectReports(nsIMemoryReporterCallback* aCallback, |
|
2005 nsISupports* aClosure) |
|
2006 { |
|
2007 AssertIsOnMainThread(); |
|
2008 |
|
2009 // Assumes that WorkerJSRuntimeStats will hold a reference to mRtPath, |
|
2010 // and not a copy, as TryToMapAddon() may later modify the string again. |
|
2011 WorkerJSRuntimeStats rtStats(mRtPath); |
|
2012 |
|
2013 { |
|
2014 MutexAutoLock lock(mMutex); |
|
2015 |
|
2016 TryToMapAddon(); |
|
2017 |
|
2018 if (!mWorkerPrivate || |
|
2019 !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats)) { |
|
2020 // Returning NS_OK here will effectively report 0 memory. |
|
2021 return NS_OK; |
|
2022 } |
|
2023 } |
|
2024 |
|
2025 return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath, |
|
2026 aCallback, aClosure); |
|
2027 } |
|
2028 |
|
2029 private: |
|
2030 ~MemoryReporter() |
|
2031 { } |
|
2032 |
|
2033 void |
|
2034 Disable() |
|
2035 { |
|
2036 // Called from WorkerPrivate::DisableMemoryReporter. |
|
2037 mMutex.AssertCurrentThreadOwns(); |
|
2038 |
|
2039 NS_ASSERTION(mWorkerPrivate, "Disabled more than once!"); |
|
2040 mWorkerPrivate = nullptr; |
|
2041 } |
|
2042 |
|
2043 // Only call this from the main thread and under mMutex lock. |
|
2044 void |
|
2045 TryToMapAddon() |
|
2046 { |
|
2047 AssertIsOnMainThread(); |
|
2048 mMutex.AssertCurrentThreadOwns(); |
|
2049 |
|
2050 if (mAlreadyMappedToAddon || !mWorkerPrivate) { |
|
2051 return; |
|
2052 } |
|
2053 |
|
2054 nsCOMPtr<nsIURI> scriptURI; |
|
2055 if (NS_FAILED(NS_NewURI(getter_AddRefs(scriptURI), |
|
2056 mWorkerPrivate->ScriptURL()))) { |
|
2057 return; |
|
2058 } |
|
2059 |
|
2060 mAlreadyMappedToAddon = true; |
|
2061 |
|
2062 if (XRE_GetProcessType() != GeckoProcessType_Default) { |
|
2063 // Only try to access the service from the main process. |
|
2064 return; |
|
2065 } |
|
2066 |
|
2067 nsAutoCString addonId; |
|
2068 bool ok; |
|
2069 nsCOMPtr<amIAddonManager> addonManager = |
|
2070 do_GetService("@mozilla.org/addons/integration;1"); |
|
2071 |
|
2072 if (!addonManager || |
|
2073 NS_FAILED(addonManager->MapURIToAddonID(scriptURI, addonId, &ok)) || |
|
2074 !ok) { |
|
2075 return; |
|
2076 } |
|
2077 |
|
2078 static const size_t explicitLength = strlen("explicit/"); |
|
2079 addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0); |
|
2080 addonId += "/"; |
|
2081 mRtPath.Insert(addonId, explicitLength); |
|
2082 } |
|
2083 }; |
|
2084 |
|
2085 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter) |
|
2086 |
|
2087 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget) |
|
2088 : mEventTarget(aEventTarget), mCompleted(false), mResult(false) |
|
2089 #ifdef DEBUG |
|
2090 , mHasRun(false) |
|
2091 #endif |
|
2092 { |
|
2093 } |
|
2094 |
|
2095 // Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the |
|
2096 // templates. |
|
2097 template <class Derived> |
|
2098 typename WorkerPrivateParent<Derived>::cycleCollection |
|
2099 WorkerPrivateParent<Derived>::_cycleCollectorGlobal = |
|
2100 WorkerPrivateParent<Derived>::cycleCollection(); |
|
2101 |
|
2102 template <class Derived> |
|
2103 WorkerPrivateParent<Derived>::WorkerPrivateParent( |
|
2104 JSContext* aCx, |
|
2105 WorkerPrivate* aParent, |
|
2106 const nsAString& aScriptURL, |
|
2107 bool aIsChromeWorker, |
|
2108 WorkerType aWorkerType, |
|
2109 const nsACString& aSharedWorkerName, |
|
2110 LoadInfo& aLoadInfo) |
|
2111 : mMutex("WorkerPrivateParent Mutex"), |
|
2112 mCondVar(mMutex, "WorkerPrivateParent CondVar"), |
|
2113 mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"), |
|
2114 mParent(aParent), mScriptURL(aScriptURL), |
|
2115 mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0), |
|
2116 mParentStatus(Pending), mParentSuspended(false), |
|
2117 mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false), |
|
2118 mWorkerType(aWorkerType), |
|
2119 mCreationTimeStamp(TimeStamp::Now()) |
|
2120 { |
|
2121 SetIsDOMBinding(); |
|
2122 |
|
2123 MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid() && |
|
2124 NS_IsMainThread()); |
|
2125 MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty()); |
|
2126 |
|
2127 if (aLoadInfo.mWindow) { |
|
2128 AssertIsOnMainThread(); |
|
2129 MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(), |
|
2130 "Should have inner window here!"); |
|
2131 BindToOwner(aLoadInfo.mWindow); |
|
2132 } |
|
2133 |
|
2134 mLoadInfo.StealFrom(aLoadInfo); |
|
2135 |
|
2136 if (aParent) { |
|
2137 aParent->AssertIsOnWorkerThread(); |
|
2138 |
|
2139 aParent->CopyJSSettings(mJSSettings); |
|
2140 } |
|
2141 else { |
|
2142 AssertIsOnMainThread(); |
|
2143 |
|
2144 RuntimeService::GetDefaultJSSettings(mJSSettings); |
|
2145 } |
|
2146 } |
|
2147 |
|
2148 template <class Derived> |
|
2149 WorkerPrivateParent<Derived>::~WorkerPrivateParent() |
|
2150 { |
|
2151 DropJSObjects(this); |
|
2152 } |
|
2153 |
|
2154 template <class Derived> |
|
2155 JSObject* |
|
2156 WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx) |
|
2157 { |
|
2158 MOZ_ASSERT(!IsSharedWorker(), |
|
2159 "We should never wrap a WorkerPrivate for a SharedWorker"); |
|
2160 |
|
2161 AssertIsOnParentThread(); |
|
2162 |
|
2163 // XXXkhuey this should not need to be rooted, the analysis is dumb. |
|
2164 // See bug 980181. |
|
2165 JS::Rooted<JSObject*> wrapper(aCx, |
|
2166 WorkerBinding::Wrap(aCx, ParentAsWorkerPrivate())); |
|
2167 if (wrapper) { |
|
2168 MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper)); |
|
2169 } |
|
2170 |
|
2171 return wrapper; |
|
2172 } |
|
2173 |
|
2174 template <class Derived> |
|
2175 nsresult |
|
2176 WorkerPrivateParent<Derived>::DispatchPrivate(WorkerRunnable* aRunnable, |
|
2177 nsIEventTarget* aSyncLoopTarget) |
|
2178 { |
|
2179 // May be called on any thread! |
|
2180 |
|
2181 WorkerPrivate* self = ParentAsWorkerPrivate(); |
|
2182 |
|
2183 { |
|
2184 MutexAutoLock lock(mMutex); |
|
2185 |
|
2186 MOZ_ASSERT_IF(aSyncLoopTarget, self->mThread); |
|
2187 |
|
2188 if (!self->mThread) { |
|
2189 if (ParentStatus() == Pending || self->mStatus == Pending) { |
|
2190 mPreStartRunnables.AppendElement(aRunnable); |
|
2191 return NS_OK; |
|
2192 } |
|
2193 |
|
2194 NS_WARNING("Using a worker event target after the thread has already" |
|
2195 "been released!"); |
|
2196 return NS_ERROR_UNEXPECTED; |
|
2197 } |
|
2198 |
|
2199 if (self->mStatus == Dead || |
|
2200 (!aSyncLoopTarget && ParentStatus() > Running)) { |
|
2201 NS_WARNING("A runnable was posted to a worker that is already shutting " |
|
2202 "down!"); |
|
2203 return NS_ERROR_UNEXPECTED; |
|
2204 } |
|
2205 |
|
2206 nsCOMPtr<nsIEventTarget> target; |
|
2207 if (aSyncLoopTarget) { |
|
2208 target = aSyncLoopTarget; |
|
2209 } |
|
2210 else { |
|
2211 target = self->mThread; |
|
2212 } |
|
2213 |
|
2214 nsresult rv = target->Dispatch(aRunnable, NS_DISPATCH_NORMAL); |
|
2215 if (NS_WARN_IF(NS_FAILED(rv))) { |
|
2216 return rv; |
|
2217 } |
|
2218 |
|
2219 mCondVar.Notify(); |
|
2220 } |
|
2221 |
|
2222 return NS_OK; |
|
2223 } |
|
2224 |
|
2225 template <class Derived> |
|
2226 nsresult |
|
2227 WorkerPrivateParent<Derived>::DispatchControlRunnable( |
|
2228 WorkerControlRunnable* aWorkerControlRunnable) |
|
2229 { |
|
2230 // May be called on any thread! |
|
2231 |
|
2232 MOZ_ASSERT(aWorkerControlRunnable); |
|
2233 |
|
2234 nsRefPtr<WorkerControlRunnable> runnable = aWorkerControlRunnable; |
|
2235 |
|
2236 WorkerPrivate* self = ParentAsWorkerPrivate(); |
|
2237 |
|
2238 { |
|
2239 MutexAutoLock lock(mMutex); |
|
2240 |
|
2241 if (self->mStatus == Dead) { |
|
2242 NS_WARNING("A control runnable was posted to a worker that is already " |
|
2243 "shutting down!"); |
|
2244 return NS_ERROR_UNEXPECTED; |
|
2245 } |
|
2246 |
|
2247 // Transfer ownership to the control queue. |
|
2248 self->mControlQueue.Push(runnable.forget().take()); |
|
2249 |
|
2250 if (JSContext* cx = self->mJSContext) { |
|
2251 MOZ_ASSERT(self->mThread); |
|
2252 |
|
2253 JSRuntime* rt = JS_GetRuntime(cx); |
|
2254 MOZ_ASSERT(rt); |
|
2255 |
|
2256 JS_RequestInterruptCallback(rt); |
|
2257 } |
|
2258 |
|
2259 mCondVar.Notify(); |
|
2260 } |
|
2261 |
|
2262 return NS_OK; |
|
2263 } |
|
2264 |
|
2265 template <class Derived> |
|
2266 already_AddRefed<WorkerRunnable> |
|
2267 WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable) |
|
2268 { |
|
2269 // May be called on any thread! |
|
2270 |
|
2271 MOZ_ASSERT(aRunnable); |
|
2272 |
|
2273 nsRefPtr<WorkerRunnable> workerRunnable = |
|
2274 WorkerRunnable::FromRunnable(aRunnable); |
|
2275 if (workerRunnable) { |
|
2276 return workerRunnable.forget(); |
|
2277 } |
|
2278 |
|
2279 nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable); |
|
2280 if (!cancelable) { |
|
2281 MOZ_CRASH("All runnables destined for a worker thread must be cancelable!"); |
|
2282 } |
|
2283 |
|
2284 workerRunnable = |
|
2285 new ExternalRunnableWrapper(ParentAsWorkerPrivate(), cancelable); |
|
2286 return workerRunnable.forget(); |
|
2287 } |
|
2288 |
|
2289 template <class Derived> |
|
2290 already_AddRefed<nsIEventTarget> |
|
2291 WorkerPrivateParent<Derived>::GetEventTarget() |
|
2292 { |
|
2293 WorkerPrivate* self = ParentAsWorkerPrivate(); |
|
2294 |
|
2295 nsCOMPtr<nsIEventTarget> target; |
|
2296 |
|
2297 { |
|
2298 MutexAutoLock lock(mMutex); |
|
2299 |
|
2300 if (!mEventTarget && |
|
2301 ParentStatus() <= Running && |
|
2302 self->mStatus <= Running) { |
|
2303 mEventTarget = new EventTarget(self); |
|
2304 } |
|
2305 |
|
2306 target = mEventTarget; |
|
2307 } |
|
2308 |
|
2309 NS_WARN_IF_FALSE(target, |
|
2310 "Requested event target for a worker that is already " |
|
2311 "shutting down!"); |
|
2312 |
|
2313 return target.forget(); |
|
2314 } |
|
2315 |
|
2316 template <class Derived> |
|
2317 bool |
|
2318 WorkerPrivateParent<Derived>::Start() |
|
2319 { |
|
2320 // May be called on any thread! |
|
2321 { |
|
2322 MutexAutoLock lock(mMutex); |
|
2323 |
|
2324 NS_ASSERTION(mParentStatus != Running, "How can this be?!"); |
|
2325 |
|
2326 if (mParentStatus == Pending) { |
|
2327 mParentStatus = Running; |
|
2328 return true; |
|
2329 } |
|
2330 } |
|
2331 |
|
2332 return false; |
|
2333 } |
|
2334 |
|
2335 // aCx is null when called from the finalizer |
|
2336 template <class Derived> |
|
2337 bool |
|
2338 WorkerPrivateParent<Derived>::NotifyPrivate(JSContext* aCx, Status aStatus) |
|
2339 { |
|
2340 AssertIsOnParentThread(); |
|
2341 |
|
2342 bool pending; |
|
2343 { |
|
2344 MutexAutoLock lock(mMutex); |
|
2345 |
|
2346 if (mParentStatus >= aStatus) { |
|
2347 return true; |
|
2348 } |
|
2349 |
|
2350 pending = mParentStatus == Pending; |
|
2351 mParentStatus = aStatus; |
|
2352 } |
|
2353 |
|
2354 if (IsSharedWorker()) { |
|
2355 RuntimeService* runtime = RuntimeService::GetService(); |
|
2356 MOZ_ASSERT(runtime); |
|
2357 |
|
2358 runtime->ForgetSharedWorker(ParentAsWorkerPrivate()); |
|
2359 } |
|
2360 |
|
2361 if (pending) { |
|
2362 WorkerPrivate* self = ParentAsWorkerPrivate(); |
|
2363 |
|
2364 #ifdef DEBUG |
|
2365 { |
|
2366 // Fake a thread here just so that our assertions don't go off for no |
|
2367 // reason. |
|
2368 nsIThread* currentThread = NS_GetCurrentThread(); |
|
2369 MOZ_ASSERT(currentThread); |
|
2370 |
|
2371 MOZ_ASSERT(!self->mPRThread); |
|
2372 self->mPRThread = PRThreadFromThread(currentThread); |
|
2373 MOZ_ASSERT(self->mPRThread); |
|
2374 } |
|
2375 #endif |
|
2376 |
|
2377 // Worker never got a chance to run, go ahead and delete it. |
|
2378 self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan); |
|
2379 return true; |
|
2380 } |
|
2381 |
|
2382 // Only top-level workers should have a synchronize runnable. |
|
2383 MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent()); |
|
2384 mSynchronizeRunnable.Revoke(); |
|
2385 |
|
2386 NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(), |
|
2387 "Shouldn't have anything queued!"); |
|
2388 |
|
2389 // Anything queued will be discarded. |
|
2390 mQueuedRunnables.Clear(); |
|
2391 |
|
2392 nsRefPtr<NotifyRunnable> runnable = |
|
2393 new NotifyRunnable(ParentAsWorkerPrivate(), aStatus); |
|
2394 return runnable->Dispatch(aCx); |
|
2395 } |
|
2396 |
|
2397 template <class Derived> |
|
2398 bool |
|
2399 WorkerPrivateParent<Derived>::Suspend(JSContext* aCx, nsPIDOMWindow* aWindow) |
|
2400 { |
|
2401 AssertIsOnParentThread(); |
|
2402 MOZ_ASSERT(aCx); |
|
2403 |
|
2404 // Shared workers are only suspended if all of their owning documents are |
|
2405 // suspended. |
|
2406 if (IsSharedWorker()) { |
|
2407 AssertIsOnMainThread(); |
|
2408 MOZ_ASSERT(mSharedWorkers.Count()); |
|
2409 |
|
2410 struct Closure |
|
2411 { |
|
2412 nsPIDOMWindow* mWindow; |
|
2413 bool mAllSuspended; |
|
2414 |
|
2415 Closure(nsPIDOMWindow* aWindow) |
|
2416 : mWindow(aWindow), mAllSuspended(true) |
|
2417 { |
|
2418 AssertIsOnMainThread(); |
|
2419 // aWindow may be null here. |
|
2420 } |
|
2421 |
|
2422 static PLDHashOperator |
|
2423 Suspend(const uint64_t& aKey, |
|
2424 SharedWorker* aSharedWorker, |
|
2425 void* aClosure) |
|
2426 { |
|
2427 AssertIsOnMainThread(); |
|
2428 MOZ_ASSERT(aSharedWorker); |
|
2429 MOZ_ASSERT(aClosure); |
|
2430 |
|
2431 auto closure = static_cast<Closure*>(aClosure); |
|
2432 |
|
2433 if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) { |
|
2434 // Calling Suspend() may change the refcount, ensure that the worker |
|
2435 // outlives this call. |
|
2436 nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker; |
|
2437 |
|
2438 aSharedWorker->Suspend(); |
|
2439 } else { |
|
2440 MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow, |
|
2441 !SameCOMIdentity(aSharedWorker->GetOwner(), |
|
2442 closure->mWindow)); |
|
2443 if (!aSharedWorker->IsSuspended()) { |
|
2444 closure->mAllSuspended = false; |
|
2445 } |
|
2446 } |
|
2447 return PL_DHASH_NEXT; |
|
2448 } |
|
2449 }; |
|
2450 |
|
2451 Closure closure(aWindow); |
|
2452 |
|
2453 mSharedWorkers.EnumerateRead(Closure::Suspend, &closure); |
|
2454 |
|
2455 if (!closure.mAllSuspended || mParentSuspended) { |
|
2456 return true; |
|
2457 } |
|
2458 } |
|
2459 |
|
2460 // MOZ_ASSERT(!mParentSuspended, "Suspended more than once!"); |
|
2461 |
|
2462 mParentSuspended = true; |
|
2463 |
|
2464 { |
|
2465 MutexAutoLock lock(mMutex); |
|
2466 |
|
2467 if (mParentStatus >= Terminating) { |
|
2468 return true; |
|
2469 } |
|
2470 } |
|
2471 |
|
2472 nsRefPtr<SuspendRunnable> runnable = |
|
2473 new SuspendRunnable(ParentAsWorkerPrivate()); |
|
2474 if (!runnable->Dispatch(aCx)) { |
|
2475 return false; |
|
2476 } |
|
2477 |
|
2478 return true; |
|
2479 } |
|
2480 |
|
2481 template <class Derived> |
|
2482 bool |
|
2483 WorkerPrivateParent<Derived>::Resume(JSContext* aCx, nsPIDOMWindow* aWindow) |
|
2484 { |
|
2485 AssertIsOnParentThread(); |
|
2486 MOZ_ASSERT(aCx); |
|
2487 MOZ_ASSERT_IF(!IsSharedWorker(), mParentSuspended); |
|
2488 |
|
2489 // Shared workers are resumed if any of their owning documents are resumed. |
|
2490 if (IsSharedWorker()) { |
|
2491 AssertIsOnMainThread(); |
|
2492 MOZ_ASSERT(mSharedWorkers.Count()); |
|
2493 |
|
2494 struct Closure |
|
2495 { |
|
2496 nsPIDOMWindow* mWindow; |
|
2497 bool mAnyRunning; |
|
2498 |
|
2499 Closure(nsPIDOMWindow* aWindow) |
|
2500 : mWindow(aWindow), mAnyRunning(false) |
|
2501 { |
|
2502 AssertIsOnMainThread(); |
|
2503 // aWindow may be null here. |
|
2504 } |
|
2505 |
|
2506 static PLDHashOperator |
|
2507 Resume(const uint64_t& aKey, |
|
2508 SharedWorker* aSharedWorker, |
|
2509 void* aClosure) |
|
2510 { |
|
2511 AssertIsOnMainThread(); |
|
2512 MOZ_ASSERT(aSharedWorker); |
|
2513 MOZ_ASSERT(aClosure); |
|
2514 |
|
2515 auto closure = static_cast<Closure*>(aClosure); |
|
2516 |
|
2517 if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) { |
|
2518 // Calling Resume() may change the refcount, ensure that the worker |
|
2519 // outlives this call. |
|
2520 nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker; |
|
2521 |
|
2522 aSharedWorker->Resume(); |
|
2523 closure->mAnyRunning = true; |
|
2524 } else { |
|
2525 MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow, |
|
2526 !SameCOMIdentity(aSharedWorker->GetOwner(), |
|
2527 closure->mWindow)); |
|
2528 if (!aSharedWorker->IsSuspended()) { |
|
2529 closure->mAnyRunning = true; |
|
2530 } |
|
2531 } |
|
2532 return PL_DHASH_NEXT; |
|
2533 } |
|
2534 }; |
|
2535 |
|
2536 Closure closure(aWindow); |
|
2537 |
|
2538 mSharedWorkers.EnumerateRead(Closure::Resume, &closure); |
|
2539 |
|
2540 if (!closure.mAnyRunning || !mParentSuspended) { |
|
2541 return true; |
|
2542 } |
|
2543 } |
|
2544 |
|
2545 MOZ_ASSERT(mParentSuspended); |
|
2546 |
|
2547 mParentSuspended = false; |
|
2548 |
|
2549 { |
|
2550 MutexAutoLock lock(mMutex); |
|
2551 |
|
2552 if (mParentStatus >= Terminating) { |
|
2553 return true; |
|
2554 } |
|
2555 } |
|
2556 |
|
2557 // Only top-level workers should have a synchronize runnable. |
|
2558 MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent()); |
|
2559 mSynchronizeRunnable.Revoke(); |
|
2560 |
|
2561 // Execute queued runnables before waking up the worker, otherwise the worker |
|
2562 // could post new messages before we run those that have been queued. |
|
2563 if (!mQueuedRunnables.IsEmpty()) { |
|
2564 AssertIsOnMainThread(); |
|
2565 MOZ_ASSERT(IsDedicatedWorker()); |
|
2566 |
|
2567 nsTArray<nsCOMPtr<nsIRunnable>> runnables; |
|
2568 mQueuedRunnables.SwapElements(runnables); |
|
2569 |
|
2570 for (uint32_t index = 0; index < runnables.Length(); index++) { |
|
2571 runnables[index]->Run(); |
|
2572 } |
|
2573 } |
|
2574 |
|
2575 nsRefPtr<ResumeRunnable> runnable = |
|
2576 new ResumeRunnable(ParentAsWorkerPrivate()); |
|
2577 if (!runnable->Dispatch(aCx)) { |
|
2578 return false; |
|
2579 } |
|
2580 |
|
2581 return true; |
|
2582 } |
|
2583 |
|
2584 template <class Derived> |
|
2585 bool |
|
2586 WorkerPrivateParent<Derived>::SynchronizeAndResume( |
|
2587 JSContext* aCx, |
|
2588 nsPIDOMWindow* aWindow, |
|
2589 nsIScriptContext* aScriptContext) |
|
2590 { |
|
2591 AssertIsOnMainThread(); |
|
2592 MOZ_ASSERT(!GetParent()); |
|
2593 MOZ_ASSERT_IF(IsDedicatedWorker(), mParentSuspended); |
|
2594 |
|
2595 // NB: There may be pending unqueued messages. If we resume here we will |
|
2596 // execute those messages out of order. Instead we post an event to the |
|
2597 // end of the event queue, allowing all of the outstanding messages to be |
|
2598 // queued up in order on the worker. Then and only then we execute all of |
|
2599 // the messages. |
|
2600 |
|
2601 nsRefPtr<SynchronizeAndResumeRunnable> runnable = |
|
2602 new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aWindow, |
|
2603 aScriptContext); |
|
2604 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { |
|
2605 JS_ReportError(aCx, "Failed to dispatch to current thread!"); |
|
2606 return false; |
|
2607 } |
|
2608 |
|
2609 mSynchronizeRunnable = runnable; |
|
2610 return true; |
|
2611 } |
|
2612 |
|
2613 template <class Derived> |
|
2614 bool |
|
2615 WorkerPrivateParent<Derived>::Close(JSContext* aCx) |
|
2616 { |
|
2617 AssertIsOnParentThread(); |
|
2618 |
|
2619 { |
|
2620 MutexAutoLock lock(mMutex); |
|
2621 |
|
2622 if (mParentStatus < Closing) { |
|
2623 mParentStatus = Closing; |
|
2624 } |
|
2625 } |
|
2626 |
|
2627 return true; |
|
2628 } |
|
2629 |
|
2630 template <class Derived> |
|
2631 bool |
|
2632 WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease) |
|
2633 { |
|
2634 AssertIsOnParentThread(); |
|
2635 |
|
2636 NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!"); |
|
2637 |
|
2638 if (aIncrease) { |
|
2639 mBusyCount++; |
|
2640 return true; |
|
2641 } |
|
2642 |
|
2643 if (--mBusyCount == 0) { |
|
2644 |
|
2645 bool shouldCancel; |
|
2646 { |
|
2647 MutexAutoLock lock(mMutex); |
|
2648 shouldCancel = mParentStatus == Terminating; |
|
2649 } |
|
2650 |
|
2651 if (shouldCancel && !Cancel(aCx)) { |
|
2652 return false; |
|
2653 } |
|
2654 } |
|
2655 |
|
2656 return true; |
|
2657 } |
|
2658 |
|
2659 template <class Derived> |
|
2660 void |
|
2661 WorkerPrivateParent<Derived>::ForgetMainThreadObjects( |
|
2662 nsTArray<nsCOMPtr<nsISupports> >& aDoomed) |
|
2663 { |
|
2664 AssertIsOnParentThread(); |
|
2665 MOZ_ASSERT(!mMainThreadObjectsForgotten); |
|
2666 |
|
2667 static const uint32_t kDoomedCount = 7; |
|
2668 |
|
2669 aDoomed.SetCapacity(kDoomedCount); |
|
2670 |
|
2671 SwapToISupportsArray(mLoadInfo.mWindow, aDoomed); |
|
2672 SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed); |
|
2673 SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed); |
|
2674 SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed); |
|
2675 SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed); |
|
2676 SwapToISupportsArray(mLoadInfo.mChannel, aDoomed); |
|
2677 SwapToISupportsArray(mLoadInfo.mCSP, aDoomed); |
|
2678 // Before adding anything here update kDoomedCount above! |
|
2679 |
|
2680 MOZ_ASSERT(aDoomed.Length() == kDoomedCount); |
|
2681 |
|
2682 mMainThreadObjectsForgotten = true; |
|
2683 } |
|
2684 |
|
2685 template <class Derived> |
|
2686 void |
|
2687 WorkerPrivateParent<Derived>::PostMessageInternal( |
|
2688 JSContext* aCx, |
|
2689 JS::Handle<JS::Value> aMessage, |
|
2690 const Optional<Sequence<JS::Value> >& aTransferable, |
|
2691 bool aToMessagePort, |
|
2692 uint64_t aMessagePortSerial, |
|
2693 ErrorResult& aRv) |
|
2694 { |
|
2695 AssertIsOnParentThread(); |
|
2696 |
|
2697 { |
|
2698 MutexAutoLock lock(mMutex); |
|
2699 if (mParentStatus > Running) { |
|
2700 return; |
|
2701 } |
|
2702 } |
|
2703 |
|
2704 JSStructuredCloneCallbacks* callbacks; |
|
2705 if (GetParent()) { |
|
2706 if (IsChromeWorker()) { |
|
2707 callbacks = &gChromeWorkerStructuredCloneCallbacks; |
|
2708 } |
|
2709 else { |
|
2710 callbacks = &gWorkerStructuredCloneCallbacks; |
|
2711 } |
|
2712 } |
|
2713 else { |
|
2714 AssertIsOnMainThread(); |
|
2715 |
|
2716 if (IsChromeWorker()) { |
|
2717 callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks; |
|
2718 } |
|
2719 else { |
|
2720 callbacks = &gMainThreadWorkerStructuredCloneCallbacks; |
|
2721 } |
|
2722 } |
|
2723 |
|
2724 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); |
|
2725 if (aTransferable.WasPassed()) { |
|
2726 const Sequence<JS::Value>& realTransferable = aTransferable.Value(); |
|
2727 |
|
2728 // The input sequence only comes from the generated bindings code, which |
|
2729 // ensures it is rooted. |
|
2730 JS::HandleValueArray elements = |
|
2731 JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(), |
|
2732 realTransferable.Elements()); |
|
2733 |
|
2734 JSObject* array = |
|
2735 JS_NewArrayObject(aCx, elements); |
|
2736 if (!array) { |
|
2737 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
2738 return; |
|
2739 } |
|
2740 transferable.setObject(*array); |
|
2741 } |
|
2742 |
|
2743 nsTArray<nsCOMPtr<nsISupports> > clonedObjects; |
|
2744 |
|
2745 JSAutoStructuredCloneBuffer buffer; |
|
2746 if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) { |
|
2747 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); |
|
2748 return; |
|
2749 } |
|
2750 |
|
2751 nsRefPtr<MessageEventRunnable> runnable = |
|
2752 new MessageEventRunnable(ParentAsWorkerPrivate(), |
|
2753 WorkerRunnable::WorkerThreadModifyBusyCount, |
|
2754 Move(buffer), clonedObjects, aToMessagePort, |
|
2755 aMessagePortSerial); |
|
2756 if (!runnable->Dispatch(aCx)) { |
|
2757 aRv.Throw(NS_ERROR_FAILURE); |
|
2758 } |
|
2759 } |
|
2760 |
|
2761 template <class Derived> |
|
2762 void |
|
2763 WorkerPrivateParent<Derived>::PostMessageToMessagePort( |
|
2764 JSContext* aCx, |
|
2765 uint64_t aMessagePortSerial, |
|
2766 JS::Handle<JS::Value> aMessage, |
|
2767 const Optional<Sequence<JS::Value>>& aTransferable, |
|
2768 ErrorResult& aRv) |
|
2769 { |
|
2770 AssertIsOnMainThread(); |
|
2771 |
|
2772 PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial, |
|
2773 aRv); |
|
2774 } |
|
2775 |
|
2776 template <class Derived> |
|
2777 bool |
|
2778 WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort( |
|
2779 JSContext* aCx, uint64_t aMessagePortSerial, |
|
2780 JSAutoStructuredCloneBuffer&& aBuffer, |
|
2781 nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects) |
|
2782 { |
|
2783 AssertIsOnMainThread(); |
|
2784 |
|
2785 JSAutoStructuredCloneBuffer buffer(Move(aBuffer)); |
|
2786 |
|
2787 nsTArray<nsCOMPtr<nsISupports>> clonedObjects; |
|
2788 clonedObjects.SwapElements(aClonedObjects); |
|
2789 |
|
2790 SharedWorker* sharedWorker; |
|
2791 if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) { |
|
2792 // SharedWorker has already been unregistered? |
|
2793 return true; |
|
2794 } |
|
2795 |
|
2796 nsRefPtr<MessagePort> port = sharedWorker->Port(); |
|
2797 NS_ASSERTION(port, "SharedWorkers always have a port!"); |
|
2798 |
|
2799 if (port->IsClosed()) { |
|
2800 return true; |
|
2801 } |
|
2802 |
|
2803 nsCOMPtr<nsIScriptGlobalObject> sgo; |
|
2804 port->GetParentObject(getter_AddRefs(sgo)); |
|
2805 MOZ_ASSERT(sgo, "Should never happen if IsClosed() returned false!"); |
|
2806 MOZ_ASSERT(sgo->GetGlobalJSObject()); |
|
2807 |
|
2808 nsCOMPtr<nsIScriptContext> scx = sgo->GetContext(); |
|
2809 MOZ_ASSERT_IF(scx, scx->GetNativeContext()); |
|
2810 |
|
2811 AutoPushJSContext cx(scx ? scx->GetNativeContext() : aCx); |
|
2812 JSAutoCompartment(cx, sgo->GetGlobalJSObject()); |
|
2813 |
|
2814 JS::Rooted<JS::Value> data(cx); |
|
2815 if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) { |
|
2816 return false; |
|
2817 } |
|
2818 |
|
2819 buffer.clear(); |
|
2820 |
|
2821 nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr); |
|
2822 nsresult rv = |
|
2823 event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data, |
|
2824 EmptyString(), EmptyString(), nullptr); |
|
2825 if (NS_FAILED(rv)) { |
|
2826 xpc::Throw(cx, rv); |
|
2827 return false; |
|
2828 } |
|
2829 |
|
2830 event->SetTrusted(true); |
|
2831 |
|
2832 nsTArray<nsRefPtr<MessagePortBase>> ports; |
|
2833 ports.AppendElement(port); |
|
2834 |
|
2835 nsRefPtr<MessagePortList> portList = new MessagePortList(port, ports); |
|
2836 event->SetPorts(portList); |
|
2837 |
|
2838 nsCOMPtr<nsIDOMEvent> domEvent; |
|
2839 CallQueryInterface(event.get(), getter_AddRefs(domEvent)); |
|
2840 NS_ASSERTION(domEvent, "This should never fail!"); |
|
2841 |
|
2842 bool ignored; |
|
2843 rv = port->DispatchEvent(domEvent, &ignored); |
|
2844 if (NS_FAILED(rv)) { |
|
2845 xpc::Throw(cx, rv); |
|
2846 return false; |
|
2847 } |
|
2848 |
|
2849 return true; |
|
2850 } |
|
2851 |
|
2852 template <class Derived> |
|
2853 uint64_t |
|
2854 WorkerPrivateParent<Derived>::GetInnerWindowId() |
|
2855 { |
|
2856 AssertIsOnMainThread(); |
|
2857 NS_ASSERTION(!mLoadInfo.mWindow || mLoadInfo.mWindow->IsInnerWindow(), |
|
2858 "Outer window?"); |
|
2859 return mLoadInfo.mWindow ? mLoadInfo.mWindow->WindowID() : 0; |
|
2860 } |
|
2861 |
|
2862 template <class Derived> |
|
2863 void |
|
2864 WorkerPrivateParent<Derived>::UpdateRuntimeAndContextOptions( |
|
2865 JSContext* aCx, |
|
2866 const JS::RuntimeOptions& aRuntimeOptions, |
|
2867 const JS::ContextOptions& aContentCxOptions, |
|
2868 const JS::ContextOptions& aChromeCxOptions) |
|
2869 { |
|
2870 AssertIsOnParentThread(); |
|
2871 |
|
2872 { |
|
2873 MutexAutoLock lock(mMutex); |
|
2874 mJSSettings.runtimeOptions = aRuntimeOptions; |
|
2875 mJSSettings.content.contextOptions = aContentCxOptions; |
|
2876 mJSSettings.chrome.contextOptions = aChromeCxOptions; |
|
2877 } |
|
2878 |
|
2879 nsRefPtr<UpdateRuntimeAndContextOptionsRunnable> runnable = |
|
2880 new UpdateRuntimeAndContextOptionsRunnable(ParentAsWorkerPrivate(), |
|
2881 aRuntimeOptions, |
|
2882 aContentCxOptions, |
|
2883 aChromeCxOptions); |
|
2884 if (!runnable->Dispatch(aCx)) { |
|
2885 NS_WARNING("Failed to update worker context options!"); |
|
2886 JS_ClearPendingException(aCx); |
|
2887 } |
|
2888 } |
|
2889 |
|
2890 template <class Derived> |
|
2891 void |
|
2892 WorkerPrivateParent<Derived>::UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue) |
|
2893 { |
|
2894 AssertIsOnParentThread(); |
|
2895 MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT); |
|
2896 |
|
2897 nsRefPtr<UpdatePreferenceRunnable> runnable = |
|
2898 new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue); |
|
2899 if (!runnable->Dispatch(aCx)) { |
|
2900 NS_WARNING("Failed to update worker preferences!"); |
|
2901 JS_ClearPendingException(aCx); |
|
2902 } |
|
2903 } |
|
2904 |
|
2905 template <class Derived> |
|
2906 void |
|
2907 WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSContext* aCx, |
|
2908 JSGCParamKey aKey, |
|
2909 uint32_t aValue) |
|
2910 { |
|
2911 AssertIsOnParentThread(); |
|
2912 |
|
2913 bool found = false; |
|
2914 |
|
2915 { |
|
2916 MutexAutoLock lock(mMutex); |
|
2917 found = mJSSettings.ApplyGCSetting(aKey, aValue); |
|
2918 } |
|
2919 |
|
2920 if (found) { |
|
2921 nsRefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable = |
|
2922 new UpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(), aKey, |
|
2923 aValue); |
|
2924 if (!runnable->Dispatch(aCx)) { |
|
2925 NS_WARNING("Failed to update memory parameter!"); |
|
2926 JS_ClearPendingException(aCx); |
|
2927 } |
|
2928 } |
|
2929 } |
|
2930 |
|
2931 #ifdef JS_GC_ZEAL |
|
2932 template <class Derived> |
|
2933 void |
|
2934 WorkerPrivateParent<Derived>::UpdateGCZeal(JSContext* aCx, uint8_t aGCZeal, |
|
2935 uint32_t aFrequency) |
|
2936 { |
|
2937 AssertIsOnParentThread(); |
|
2938 |
|
2939 { |
|
2940 MutexAutoLock lock(mMutex); |
|
2941 mJSSettings.gcZeal = aGCZeal; |
|
2942 mJSSettings.gcZealFrequency = aFrequency; |
|
2943 } |
|
2944 |
|
2945 nsRefPtr<UpdateGCZealRunnable> runnable = |
|
2946 new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency); |
|
2947 if (!runnable->Dispatch(aCx)) { |
|
2948 NS_WARNING("Failed to update worker gczeal!"); |
|
2949 JS_ClearPendingException(aCx); |
|
2950 } |
|
2951 } |
|
2952 #endif |
|
2953 |
|
2954 template <class Derived> |
|
2955 void |
|
2956 WorkerPrivateParent<Derived>::GarbageCollect(JSContext* aCx, bool aShrinking) |
|
2957 { |
|
2958 AssertIsOnParentThread(); |
|
2959 |
|
2960 nsRefPtr<GarbageCollectRunnable> runnable = |
|
2961 new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking, |
|
2962 /* collectChildren = */ true); |
|
2963 if (!runnable->Dispatch(aCx)) { |
|
2964 NS_WARNING("Failed to GC worker!"); |
|
2965 JS_ClearPendingException(aCx); |
|
2966 } |
|
2967 } |
|
2968 |
|
2969 template <class Derived> |
|
2970 void |
|
2971 WorkerPrivateParent<Derived>::CycleCollect(JSContext* aCx, bool aDummy) |
|
2972 { |
|
2973 AssertIsOnParentThread(); |
|
2974 |
|
2975 nsRefPtr<CycleCollectRunnable> runnable = |
|
2976 new CycleCollectRunnable(ParentAsWorkerPrivate(), |
|
2977 /* collectChildren = */ true); |
|
2978 if (!runnable->Dispatch(aCx)) { |
|
2979 NS_WARNING("Failed to CC worker!"); |
|
2980 JS_ClearPendingException(aCx); |
|
2981 } |
|
2982 } |
|
2983 |
|
2984 template <class Derived> |
|
2985 void |
|
2986 WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline) |
|
2987 { |
|
2988 AssertIsOnParentThread(); |
|
2989 |
|
2990 nsRefPtr<OfflineStatusChangeRunnable> runnable = |
|
2991 new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline); |
|
2992 if (!runnable->Dispatch(aCx)) { |
|
2993 NS_WARNING("Failed to dispatch offline status change event!"); |
|
2994 JS_ClearPendingException(aCx); |
|
2995 } |
|
2996 } |
|
2997 |
|
2998 void |
|
2999 WorkerPrivate::OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline) |
|
3000 { |
|
3001 AssertIsOnWorkerThread(); |
|
3002 |
|
3003 for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) { |
|
3004 mChildWorkers[index]->OfflineStatusChangeEvent(aCx, aIsOffline); |
|
3005 } |
|
3006 |
|
3007 mOnLine = !aIsOffline; |
|
3008 WorkerGlobalScope* globalScope = GlobalScope(); |
|
3009 nsRefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator(); |
|
3010 if (nav) { |
|
3011 nav->SetOnLine(mOnLine); |
|
3012 } |
|
3013 |
|
3014 nsString eventType; |
|
3015 if (aIsOffline) { |
|
3016 eventType.AssignLiteral("offline"); |
|
3017 } else { |
|
3018 eventType.AssignLiteral("online"); |
|
3019 } |
|
3020 |
|
3021 nsCOMPtr<nsIDOMEvent> event; |
|
3022 nsresult rv = |
|
3023 NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr); |
|
3024 NS_ENSURE_SUCCESS_VOID(rv); |
|
3025 |
|
3026 rv = event->InitEvent(eventType, false, false); |
|
3027 NS_ENSURE_SUCCESS_VOID(rv); |
|
3028 |
|
3029 event->SetTrusted(true); |
|
3030 |
|
3031 globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr); |
|
3032 } |
|
3033 |
|
3034 template <class Derived> |
|
3035 bool |
|
3036 WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx, |
|
3037 SharedWorker* aSharedWorker) |
|
3038 { |
|
3039 AssertIsOnMainThread(); |
|
3040 MOZ_ASSERT(aSharedWorker); |
|
3041 MOZ_ASSERT(IsSharedWorker()); |
|
3042 MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial())); |
|
3043 |
|
3044 nsRefPtr<MessagePortRunnable> runnable = |
|
3045 new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(), |
|
3046 true); |
|
3047 if (!runnable->Dispatch(aCx)) { |
|
3048 return false; |
|
3049 } |
|
3050 |
|
3051 mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker); |
|
3052 |
|
3053 // If there were other SharedWorker objects attached to this worker then they |
|
3054 // may all have been suspended and this worker would need to be resumed. |
|
3055 if (mSharedWorkers.Count() > 1 && !Resume(aCx, nullptr)) { |
|
3056 return false; |
|
3057 } |
|
3058 |
|
3059 return true; |
|
3060 } |
|
3061 |
|
3062 template <class Derived> |
|
3063 void |
|
3064 WorkerPrivateParent<Derived>::UnregisterSharedWorker( |
|
3065 JSContext* aCx, |
|
3066 SharedWorker* aSharedWorker) |
|
3067 { |
|
3068 AssertIsOnMainThread(); |
|
3069 MOZ_ASSERT(aSharedWorker); |
|
3070 MOZ_ASSERT(IsSharedWorker()); |
|
3071 MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial())); |
|
3072 |
|
3073 nsRefPtr<MessagePortRunnable> runnable = |
|
3074 new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(), |
|
3075 false); |
|
3076 if (!runnable->Dispatch(aCx)) { |
|
3077 JS_ReportPendingException(aCx); |
|
3078 } |
|
3079 |
|
3080 mSharedWorkers.Remove(aSharedWorker->Serial()); |
|
3081 |
|
3082 // If there are still SharedWorker objects attached to this worker then they |
|
3083 // may all be suspended and this worker would need to be suspended. Otherwise, |
|
3084 // if that was the last SharedWorker then it's time to cancel this worker. |
|
3085 if (mSharedWorkers.Count()) { |
|
3086 if (!Suspend(aCx, nullptr)) { |
|
3087 JS_ReportPendingException(aCx); |
|
3088 } |
|
3089 } else if (!Cancel(aCx)) { |
|
3090 JS_ReportPendingException(aCx); |
|
3091 } |
|
3092 } |
|
3093 |
|
3094 template <class Derived> |
|
3095 void |
|
3096 WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers( |
|
3097 JSContext* aCx, |
|
3098 const nsAString& aMessage, |
|
3099 const nsAString& aFilename, |
|
3100 const nsAString& aLine, |
|
3101 uint32_t aLineNumber, |
|
3102 uint32_t aColumnNumber, |
|
3103 uint32_t aFlags) |
|
3104 { |
|
3105 AssertIsOnMainThread(); |
|
3106 |
|
3107 nsAutoTArray<nsRefPtr<SharedWorker>, 10> sharedWorkers; |
|
3108 GetAllSharedWorkers(sharedWorkers); |
|
3109 |
|
3110 if (sharedWorkers.IsEmpty()) { |
|
3111 return; |
|
3112 } |
|
3113 |
|
3114 nsAutoTArray<WindowAction, 10> windowActions; |
|
3115 nsresult rv; |
|
3116 |
|
3117 // First fire the error event at all SharedWorker objects. This may include |
|
3118 // multiple objects in a single window as well as objects in different |
|
3119 // windows. |
|
3120 for (uint32_t index = 0; index < sharedWorkers.Length(); index++) { |
|
3121 nsRefPtr<SharedWorker>& sharedWorker = sharedWorkers[index]; |
|
3122 |
|
3123 // May be null. |
|
3124 nsPIDOMWindow* window = sharedWorker->GetOwner(); |
|
3125 |
|
3126 uint32_t actionsIndex = windowActions.LastIndexOf(WindowAction(window)); |
|
3127 |
|
3128 nsIGlobalObject* global = sharedWorker->GetParentObject(); |
|
3129 AutoJSAPIWithErrorsReportedToWindow jsapi(global); |
|
3130 JSContext* cx = jsapi.cx(); |
|
3131 JSAutoCompartment ac(cx, global->GetGlobalJSObject()); |
|
3132 |
|
3133 RootedDictionary<ErrorEventInit> errorInit(aCx); |
|
3134 errorInit.mBubbles = false; |
|
3135 errorInit.mCancelable = true; |
|
3136 errorInit.mMessage = aMessage; |
|
3137 errorInit.mFilename = aFilename; |
|
3138 errorInit.mLineno = aLineNumber; |
|
3139 errorInit.mColno = aColumnNumber; |
|
3140 |
|
3141 nsRefPtr<ErrorEvent> errorEvent = |
|
3142 ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"), |
|
3143 errorInit); |
|
3144 if (!errorEvent) { |
|
3145 Throw(cx, NS_ERROR_UNEXPECTED); |
|
3146 JS_ReportPendingException(cx); |
|
3147 continue; |
|
3148 } |
|
3149 |
|
3150 errorEvent->SetTrusted(true); |
|
3151 |
|
3152 bool defaultActionEnabled; |
|
3153 nsresult rv = sharedWorker->DispatchEvent(errorEvent, &defaultActionEnabled); |
|
3154 if (NS_FAILED(rv)) { |
|
3155 Throw(cx, rv); |
|
3156 JS_ReportPendingException(cx); |
|
3157 continue; |
|
3158 } |
|
3159 |
|
3160 if (defaultActionEnabled) { |
|
3161 // Add the owning window to our list so that we will fire an error event |
|
3162 // at it later. |
|
3163 if (!windowActions.Contains(window)) { |
|
3164 windowActions.AppendElement(WindowAction(window, cx)); |
|
3165 } |
|
3166 } else if (actionsIndex != windowActions.NoIndex) { |
|
3167 // Any listener that calls preventDefault() will prevent the window from |
|
3168 // receiving the error event. |
|
3169 windowActions[actionsIndex].mDefaultAction = false; |
|
3170 } |
|
3171 } |
|
3172 |
|
3173 // If there are no windows to consider further then we're done. |
|
3174 if (windowActions.IsEmpty()) { |
|
3175 return; |
|
3176 } |
|
3177 |
|
3178 bool shouldLogErrorToConsole = true; |
|
3179 |
|
3180 // Now fire error events at all the windows remaining. |
|
3181 for (uint32_t index = 0; index < windowActions.Length(); index++) { |
|
3182 WindowAction& windowAction = windowActions[index]; |
|
3183 |
|
3184 // If there is no window or the script already called preventDefault then |
|
3185 // skip this window. |
|
3186 if (!windowAction.mWindow || !windowAction.mDefaultAction) { |
|
3187 continue; |
|
3188 } |
|
3189 |
|
3190 JSContext* cx = windowAction.mJSContext; |
|
3191 |
|
3192 AutoCxPusher autoPush(cx); |
|
3193 |
|
3194 nsCOMPtr<nsIScriptGlobalObject> sgo = |
|
3195 do_QueryInterface(windowAction.mWindow); |
|
3196 MOZ_ASSERT(sgo); |
|
3197 |
|
3198 MOZ_ASSERT(NS_IsMainThread()); |
|
3199 RootedDictionary<ErrorEventInit> init(aCx); |
|
3200 init.mLineno = aLineNumber; |
|
3201 init.mFilename = aFilename; |
|
3202 init.mMessage = aMessage; |
|
3203 init.mCancelable = true; |
|
3204 init.mBubbles = true; |
|
3205 |
|
3206 nsEventStatus status = nsEventStatus_eIgnore; |
|
3207 rv = sgo->HandleScriptError(init, &status); |
|
3208 if (NS_FAILED(rv)) { |
|
3209 Throw(cx, rv); |
|
3210 JS_ReportPendingException(cx); |
|
3211 continue; |
|
3212 } |
|
3213 |
|
3214 if (status == nsEventStatus_eConsumeNoDefault) { |
|
3215 shouldLogErrorToConsole = false; |
|
3216 } |
|
3217 } |
|
3218 |
|
3219 // Finally log a warning in the console if no window tried to prevent it. |
|
3220 if (shouldLogErrorToConsole) { |
|
3221 LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber, |
|
3222 aFlags, 0); |
|
3223 } |
|
3224 } |
|
3225 |
|
3226 template <class Derived> |
|
3227 void |
|
3228 WorkerPrivateParent<Derived>::GetAllSharedWorkers( |
|
3229 nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers) |
|
3230 { |
|
3231 AssertIsOnMainThread(); |
|
3232 MOZ_ASSERT(IsSharedWorker()); |
|
3233 |
|
3234 struct Helper |
|
3235 { |
|
3236 static PLDHashOperator |
|
3237 Collect(const uint64_t& aKey, |
|
3238 SharedWorker* aSharedWorker, |
|
3239 void* aClosure) |
|
3240 { |
|
3241 AssertIsOnMainThread(); |
|
3242 MOZ_ASSERT(aSharedWorker); |
|
3243 MOZ_ASSERT(aClosure); |
|
3244 |
|
3245 auto array = static_cast<nsTArray<nsRefPtr<SharedWorker>>*>(aClosure); |
|
3246 array->AppendElement(aSharedWorker); |
|
3247 |
|
3248 return PL_DHASH_NEXT; |
|
3249 } |
|
3250 }; |
|
3251 |
|
3252 if (!aSharedWorkers.IsEmpty()) { |
|
3253 aSharedWorkers.Clear(); |
|
3254 } |
|
3255 |
|
3256 mSharedWorkers.EnumerateRead(Helper::Collect, &aSharedWorkers); |
|
3257 } |
|
3258 |
|
3259 template <class Derived> |
|
3260 void |
|
3261 WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow( |
|
3262 nsPIDOMWindow* aWindow) |
|
3263 { |
|
3264 AssertIsOnMainThread(); |
|
3265 MOZ_ASSERT(IsSharedWorker()); |
|
3266 MOZ_ASSERT(aWindow); |
|
3267 |
|
3268 struct Closure |
|
3269 { |
|
3270 nsPIDOMWindow* mWindow; |
|
3271 nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers; |
|
3272 |
|
3273 Closure(nsPIDOMWindow* aWindow) |
|
3274 : mWindow(aWindow) |
|
3275 { |
|
3276 AssertIsOnMainThread(); |
|
3277 MOZ_ASSERT(aWindow); |
|
3278 } |
|
3279 |
|
3280 static PLDHashOperator |
|
3281 Collect(const uint64_t& aKey, |
|
3282 SharedWorker* aSharedWorker, |
|
3283 void* aClosure) |
|
3284 { |
|
3285 AssertIsOnMainThread(); |
|
3286 MOZ_ASSERT(aSharedWorker); |
|
3287 MOZ_ASSERT(aClosure); |
|
3288 |
|
3289 auto closure = static_cast<Closure*>(aClosure); |
|
3290 MOZ_ASSERT(closure->mWindow); |
|
3291 |
|
3292 if (aSharedWorker->GetOwner() == closure->mWindow) { |
|
3293 closure->mSharedWorkers.AppendElement(aSharedWorker); |
|
3294 } else { |
|
3295 MOZ_ASSERT(!SameCOMIdentity(aSharedWorker->GetOwner(), |
|
3296 closure->mWindow)); |
|
3297 } |
|
3298 |
|
3299 return PL_DHASH_NEXT; |
|
3300 } |
|
3301 }; |
|
3302 |
|
3303 Closure closure(aWindow); |
|
3304 |
|
3305 mSharedWorkers.EnumerateRead(Closure::Collect, &closure); |
|
3306 |
|
3307 for (uint32_t index = 0; index < closure.mSharedWorkers.Length(); index++) { |
|
3308 closure.mSharedWorkers[index]->Close(); |
|
3309 } |
|
3310 } |
|
3311 |
|
3312 template <class Derived> |
|
3313 void |
|
3314 WorkerPrivateParent<Derived>::WorkerScriptLoaded() |
|
3315 { |
|
3316 AssertIsOnMainThread(); |
|
3317 |
|
3318 if (IsSharedWorker()) { |
|
3319 // No longer need to hold references to the window or document we came from. |
|
3320 mLoadInfo.mWindow = nullptr; |
|
3321 mLoadInfo.mScriptContext = nullptr; |
|
3322 } |
|
3323 } |
|
3324 |
|
3325 template <class Derived> |
|
3326 void |
|
3327 WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI) |
|
3328 { |
|
3329 AssertIsOnMainThread(); |
|
3330 |
|
3331 if (!mLoadInfo.mBaseURI) { |
|
3332 NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!"); |
|
3333 mLoadInfo.mResolvedScriptURI = aBaseURI; |
|
3334 } |
|
3335 |
|
3336 mLoadInfo.mBaseURI = aBaseURI; |
|
3337 |
|
3338 if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) { |
|
3339 mLocationInfo.mHref.Truncate(); |
|
3340 } |
|
3341 |
|
3342 if (NS_FAILED(aBaseURI->GetHost(mLocationInfo.mHostname))) { |
|
3343 mLocationInfo.mHostname.Truncate(); |
|
3344 } |
|
3345 |
|
3346 if (NS_FAILED(aBaseURI->GetPath(mLocationInfo.mPathname))) { |
|
3347 mLocationInfo.mPathname.Truncate(); |
|
3348 } |
|
3349 |
|
3350 nsCString temp; |
|
3351 |
|
3352 nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI)); |
|
3353 if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) { |
|
3354 mLocationInfo.mSearch.AssignLiteral("?"); |
|
3355 mLocationInfo.mSearch.Append(temp); |
|
3356 } |
|
3357 |
|
3358 if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) { |
|
3359 nsCOMPtr<nsITextToSubURI> converter = |
|
3360 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID); |
|
3361 if (converter) { |
|
3362 nsCString charset; |
|
3363 nsAutoString unicodeRef; |
|
3364 if (NS_SUCCEEDED(aBaseURI->GetOriginCharset(charset)) && |
|
3365 NS_SUCCEEDED(converter->UnEscapeURIForUI(charset, temp, |
|
3366 unicodeRef))) { |
|
3367 mLocationInfo.mHash.AssignLiteral("#"); |
|
3368 mLocationInfo.mHash.Append(NS_ConvertUTF16toUTF8(unicodeRef)); |
|
3369 } |
|
3370 } |
|
3371 |
|
3372 if (mLocationInfo.mHash.IsEmpty()) { |
|
3373 mLocationInfo.mHash.AssignLiteral("#"); |
|
3374 mLocationInfo.mHash.Append(temp); |
|
3375 } |
|
3376 } |
|
3377 |
|
3378 if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) { |
|
3379 mLocationInfo.mProtocol.AppendLiteral(":"); |
|
3380 } |
|
3381 else { |
|
3382 mLocationInfo.mProtocol.Truncate(); |
|
3383 } |
|
3384 |
|
3385 int32_t port; |
|
3386 if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) { |
|
3387 mLocationInfo.mPort.AppendInt(port); |
|
3388 |
|
3389 nsAutoCString host(mLocationInfo.mHostname); |
|
3390 host.AppendLiteral(":"); |
|
3391 host.Append(mLocationInfo.mPort); |
|
3392 |
|
3393 mLocationInfo.mHost.Assign(host); |
|
3394 } |
|
3395 else { |
|
3396 mLocationInfo.mHost.Assign(mLocationInfo.mHostname); |
|
3397 } |
|
3398 |
|
3399 nsContentUtils::GetUTFNonNullOrigin(aBaseURI, mLocationInfo.mOrigin); |
|
3400 } |
|
3401 |
|
3402 template <class Derived> |
|
3403 void |
|
3404 WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal) |
|
3405 { |
|
3406 AssertIsOnMainThread(); |
|
3407 |
|
3408 mLoadInfo.mPrincipal = aPrincipal; |
|
3409 mLoadInfo.mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal); |
|
3410 uint16_t appStatus = aPrincipal->GetAppStatus(); |
|
3411 mLoadInfo.mIsInPrivilegedApp = |
|
3412 (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED || |
|
3413 appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED); |
|
3414 mLoadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED); |
|
3415 } |
|
3416 |
|
3417 template <class Derived> |
|
3418 JSContext* |
|
3419 WorkerPrivateParent<Derived>::ParentJSContext() const |
|
3420 { |
|
3421 AssertIsOnParentThread(); |
|
3422 |
|
3423 if (mParent) { |
|
3424 return mParent->GetJSContext(); |
|
3425 } |
|
3426 |
|
3427 AssertIsOnMainThread(); |
|
3428 |
|
3429 return mLoadInfo.mScriptContext ? |
|
3430 mLoadInfo.mScriptContext->GetNativeContext() : |
|
3431 nsContentUtils::GetSafeJSContext(); |
|
3432 } |
|
3433 |
|
3434 template <class Derived> |
|
3435 void |
|
3436 WorkerPrivateParent<Derived>::RegisterHostObjectURI(const nsACString& aURI) |
|
3437 { |
|
3438 AssertIsOnMainThread(); |
|
3439 mHostObjectURIs.AppendElement(aURI); |
|
3440 } |
|
3441 |
|
3442 template <class Derived> |
|
3443 void |
|
3444 WorkerPrivateParent<Derived>::UnregisterHostObjectURI(const nsACString& aURI) |
|
3445 { |
|
3446 AssertIsOnMainThread(); |
|
3447 mHostObjectURIs.RemoveElement(aURI); |
|
3448 } |
|
3449 |
|
3450 template <class Derived> |
|
3451 void |
|
3452 WorkerPrivateParent<Derived>::StealHostObjectURIs(nsTArray<nsCString>& aArray) |
|
3453 { |
|
3454 aArray.SwapElements(mHostObjectURIs); |
|
3455 } |
|
3456 |
|
3457 template <class Derived> |
|
3458 NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper) |
|
3459 |
|
3460 template <class Derived> |
|
3461 NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper) |
|
3462 |
|
3463 template <class Derived> |
|
3464 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>) |
|
3465 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
|
3466 |
|
3467 template <class Derived> |
|
3468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>, |
|
3469 DOMEventTargetHelper) |
|
3470 tmp->AssertIsOnParentThread(); |
|
3471 |
|
3472 // The WorkerPrivate::mSelfRef has a reference to itself, which is really |
|
3473 // held by the worker thread. We traverse this reference if and only if our |
|
3474 // busy count is zero and we have not released the main thread reference. |
|
3475 // We do not unlink it. This allows the CC to break cycles involving the |
|
3476 // WorkerPrivate and begin shutting it down (which does happen in unlink) but |
|
3477 // ensures that the WorkerPrivate won't be deleted before we're done shutting |
|
3478 // down the thread. |
|
3479 |
|
3480 if (!tmp->mBusyCount && !tmp->mMainThreadObjectsForgotten) { |
|
3481 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef) |
|
3482 } |
|
3483 |
|
3484 // The various strong references in LoadInfo are managed manually and cannot |
|
3485 // be cycle collected. |
|
3486 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
3487 |
|
3488 template <class Derived> |
|
3489 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>, |
|
3490 DOMEventTargetHelper) |
|
3491 tmp->Terminate(nullptr); |
|
3492 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
3493 |
|
3494 template <class Derived> |
|
3495 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>, |
|
3496 DOMEventTargetHelper) |
|
3497 tmp->AssertIsOnParentThread(); |
|
3498 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
3499 |
|
3500 #ifdef DEBUG |
|
3501 |
|
3502 template <class Derived> |
|
3503 void |
|
3504 WorkerPrivateParent<Derived>::AssertIsOnParentThread() const |
|
3505 { |
|
3506 if (GetParent()) { |
|
3507 GetParent()->AssertIsOnWorkerThread(); |
|
3508 } |
|
3509 else { |
|
3510 AssertIsOnMainThread(); |
|
3511 } |
|
3512 } |
|
3513 |
|
3514 template <class Derived> |
|
3515 void |
|
3516 WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const |
|
3517 { |
|
3518 AssertIsOnParentThread(); |
|
3519 |
|
3520 // Only care about top level workers from windows. |
|
3521 if (mParent || !mLoadInfo.mWindow) { |
|
3522 return; |
|
3523 } |
|
3524 |
|
3525 AssertIsOnMainThread(); |
|
3526 |
|
3527 nsPIDOMWindow* outer = mLoadInfo.mWindow->GetOuterWindow(); |
|
3528 NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow, |
|
3529 "Inner window no longer correct!"); |
|
3530 } |
|
3531 |
|
3532 #endif |
|
3533 WorkerPrivate::WorkerPrivate(JSContext* aCx, |
|
3534 WorkerPrivate* aParent, |
|
3535 const nsAString& aScriptURL, |
|
3536 bool aIsChromeWorker, WorkerType aWorkerType, |
|
3537 const nsACString& aSharedWorkerName, |
|
3538 LoadInfo& aLoadInfo) |
|
3539 : WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL, |
|
3540 aIsChromeWorker, aWorkerType, |
|
3541 aSharedWorkerName, aLoadInfo), |
|
3542 mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1), |
|
3543 mStatus(Pending), mSuspended(false), mTimerRunning(false), |
|
3544 mRunningExpiredTimeouts(false), mCloseHandlerStarted(false), |
|
3545 mCloseHandlerFinished(false), mMemoryReporterRunning(false), |
|
3546 mBlockedForMemoryReporter(false), mCancelAllPendingRunnables(false), |
|
3547 mPeriodicGCTimerRunning(false), mIdleGCTimerRunning(false) |
|
3548 #ifdef DEBUG |
|
3549 , mPRThread(nullptr) |
|
3550 #endif |
|
3551 { |
|
3552 MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid()); |
|
3553 MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty()); |
|
3554 |
|
3555 if (aParent) { |
|
3556 aParent->AssertIsOnWorkerThread(); |
|
3557 aParent->GetAllPreferences(mPreferences); |
|
3558 mOnLine = aParent->OnLine(); |
|
3559 } |
|
3560 else { |
|
3561 AssertIsOnMainThread(); |
|
3562 RuntimeService::GetDefaultPreferences(mPreferences); |
|
3563 mOnLine = !NS_IsOffline(); |
|
3564 } |
|
3565 } |
|
3566 |
|
3567 WorkerPrivate::~WorkerPrivate() |
|
3568 { |
|
3569 } |
|
3570 |
|
3571 // static |
|
3572 already_AddRefed<WorkerPrivate> |
|
3573 WorkerPrivate::Constructor(const GlobalObject& aGlobal, |
|
3574 const nsAString& aScriptURL, |
|
3575 ErrorResult& aRv) |
|
3576 { |
|
3577 return WorkerPrivate::Constructor(aGlobal, aScriptURL, false, |
|
3578 WorkerTypeDedicated, EmptyCString(), |
|
3579 nullptr, aRv); |
|
3580 } |
|
3581 |
|
3582 // static |
|
3583 bool |
|
3584 WorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */) |
|
3585 { |
|
3586 // If we're already on a worker workers are clearly enabled. |
|
3587 if (!NS_IsMainThread()) { |
|
3588 return true; |
|
3589 } |
|
3590 |
|
3591 // If our caller is chrome, workers are always available. |
|
3592 if (nsContentUtils::IsCallerChrome()) { |
|
3593 return true; |
|
3594 } |
|
3595 |
|
3596 // Else check the pref. |
|
3597 return Preferences::GetBool(PREF_WORKERS_ENABLED); |
|
3598 } |
|
3599 |
|
3600 // static |
|
3601 already_AddRefed<ChromeWorkerPrivate> |
|
3602 ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal, |
|
3603 const nsAString& aScriptURL, |
|
3604 ErrorResult& aRv) |
|
3605 { |
|
3606 return WorkerPrivate::Constructor(aGlobal, aScriptURL, true, |
|
3607 WorkerTypeDedicated, EmptyCString(), |
|
3608 nullptr, aRv) |
|
3609 .downcast<ChromeWorkerPrivate>(); |
|
3610 } |
|
3611 |
|
3612 // static |
|
3613 bool |
|
3614 ChromeWorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */) |
|
3615 { |
|
3616 // Chrome is always allowed to use workers, and content is never allowed to |
|
3617 // use ChromeWorker, so all we have to check is the caller. |
|
3618 return nsContentUtils::ThreadsafeIsCallerChrome(); |
|
3619 } |
|
3620 |
|
3621 // static |
|
3622 already_AddRefed<WorkerPrivate> |
|
3623 WorkerPrivate::Constructor(const GlobalObject& aGlobal, |
|
3624 const nsAString& aScriptURL, |
|
3625 bool aIsChromeWorker, WorkerType aWorkerType, |
|
3626 const nsACString& aSharedWorkerName, |
|
3627 LoadInfo* aLoadInfo, ErrorResult& aRv) |
|
3628 { |
|
3629 WorkerPrivate* parent = NS_IsMainThread() ? |
|
3630 nullptr : |
|
3631 GetCurrentThreadWorkerPrivate(); |
|
3632 if (parent) { |
|
3633 parent->AssertIsOnWorkerThread(); |
|
3634 } else { |
|
3635 AssertIsOnMainThread(); |
|
3636 } |
|
3637 |
|
3638 JSContext* cx = aGlobal.GetContext(); |
|
3639 |
|
3640 MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared, |
|
3641 !aSharedWorkerName.IsVoid()); |
|
3642 MOZ_ASSERT_IF(aWorkerType != WorkerTypeShared, |
|
3643 aSharedWorkerName.IsEmpty()); |
|
3644 |
|
3645 Maybe<LoadInfo> stackLoadInfo; |
|
3646 if (!aLoadInfo) { |
|
3647 stackLoadInfo.construct(); |
|
3648 |
|
3649 nsresult rv = GetLoadInfo(cx, nullptr, parent, aScriptURL, |
|
3650 aIsChromeWorker, stackLoadInfo.addr()); |
|
3651 if (NS_FAILED(rv)) { |
|
3652 scriptloader::ReportLoadError(cx, aScriptURL, rv, !parent); |
|
3653 aRv.Throw(rv); |
|
3654 return nullptr; |
|
3655 } |
|
3656 |
|
3657 aLoadInfo = stackLoadInfo.addr(); |
|
3658 } |
|
3659 |
|
3660 // NB: This has to be done before creating the WorkerPrivate, because it will |
|
3661 // attempt to use static variables that are initialized in the RuntimeService |
|
3662 // constructor. |
|
3663 RuntimeService* runtimeService; |
|
3664 |
|
3665 if (!parent) { |
|
3666 runtimeService = RuntimeService::GetOrCreateService(); |
|
3667 if (!runtimeService) { |
|
3668 JS_ReportError(cx, "Failed to create runtime service!"); |
|
3669 aRv.Throw(NS_ERROR_FAILURE); |
|
3670 return nullptr; |
|
3671 } |
|
3672 } |
|
3673 else { |
|
3674 runtimeService = RuntimeService::GetService(); |
|
3675 } |
|
3676 |
|
3677 MOZ_ASSERT(runtimeService); |
|
3678 |
|
3679 nsRefPtr<WorkerPrivate> worker = |
|
3680 new WorkerPrivate(cx, parent, aScriptURL, aIsChromeWorker, |
|
3681 aWorkerType, aSharedWorkerName, *aLoadInfo); |
|
3682 |
|
3683 if (!runtimeService->RegisterWorker(cx, worker)) { |
|
3684 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
3685 return nullptr; |
|
3686 } |
|
3687 |
|
3688 nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker); |
|
3689 if (!compiler->Dispatch(cx)) { |
|
3690 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
3691 return nullptr; |
|
3692 } |
|
3693 |
|
3694 worker->mSelfRef = worker; |
|
3695 |
|
3696 return worker.forget(); |
|
3697 } |
|
3698 |
|
3699 // static |
|
3700 nsresult |
|
3701 WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, |
|
3702 WorkerPrivate* aParent, const nsAString& aScriptURL, |
|
3703 bool aIsChromeWorker, LoadInfo* aLoadInfo) |
|
3704 { |
|
3705 using namespace mozilla::dom::workers::scriptloader; |
|
3706 |
|
3707 MOZ_ASSERT(aCx); |
|
3708 |
|
3709 if (aWindow) { |
|
3710 AssertIsOnMainThread(); |
|
3711 } |
|
3712 |
|
3713 LoadInfo loadInfo; |
|
3714 nsresult rv; |
|
3715 |
|
3716 if (aParent) { |
|
3717 aParent->AssertIsOnWorkerThread(); |
|
3718 |
|
3719 // If the parent is going away give up now. |
|
3720 Status parentStatus; |
|
3721 { |
|
3722 MutexAutoLock lock(aParent->mMutex); |
|
3723 parentStatus = aParent->mStatus; |
|
3724 } |
|
3725 |
|
3726 if (parentStatus > Running) { |
|
3727 NS_WARNING("Cannot create child workers from the close handler!"); |
|
3728 return NS_ERROR_FAILURE; |
|
3729 } |
|
3730 |
|
3731 // StartAssignment() is used instead getter_AddRefs because, getter_AddRefs |
|
3732 // does QI in debug build and, if this worker runs in a child process, |
|
3733 // HttpChannelChild will crash because it's not thread-safe. |
|
3734 rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL, |
|
3735 loadInfo.mChannel.StartAssignment()); |
|
3736 NS_ENSURE_SUCCESS(rv, rv); |
|
3737 |
|
3738 // Now that we've spun the loop there's no guarantee that our parent is |
|
3739 // still alive. We may have received control messages initiating shutdown. |
|
3740 { |
|
3741 MutexAutoLock lock(aParent->mMutex); |
|
3742 parentStatus = aParent->mStatus; |
|
3743 } |
|
3744 |
|
3745 if (parentStatus > Running) { |
|
3746 nsCOMPtr<nsIThread> mainThread; |
|
3747 if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) || |
|
3748 NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) { |
|
3749 NS_WARNING("Failed to proxy release of channel, leaking instead!"); |
|
3750 } |
|
3751 return NS_ERROR_FAILURE; |
|
3752 } |
|
3753 |
|
3754 loadInfo.mDomain = aParent->Domain(); |
|
3755 } else { |
|
3756 AssertIsOnMainThread(); |
|
3757 |
|
3758 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); |
|
3759 MOZ_ASSERT(ssm); |
|
3760 |
|
3761 bool isChrome = nsContentUtils::IsCallerChrome(); |
|
3762 |
|
3763 // First check to make sure the caller has permission to make a privileged |
|
3764 // worker if they called the ChromeWorker/ChromeSharedWorker constructor. |
|
3765 if (aIsChromeWorker && !isChrome) { |
|
3766 return NS_ERROR_DOM_SECURITY_ERR; |
|
3767 } |
|
3768 |
|
3769 // Chrome callers (whether ChromeWorker of Worker) always get the system |
|
3770 // principal here as they're allowed to load anything. The script loader may |
|
3771 // change the principal later depending on the script uri. |
|
3772 if (isChrome) { |
|
3773 rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal)); |
|
3774 NS_ENSURE_SUCCESS(rv, rv); |
|
3775 } |
|
3776 |
|
3777 // See if we're being called from a window. |
|
3778 nsCOMPtr<nsPIDOMWindow> globalWindow = aWindow; |
|
3779 if (!globalWindow) { |
|
3780 nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = |
|
3781 nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx)); |
|
3782 if (scriptGlobal) { |
|
3783 globalWindow = do_QueryInterface(scriptGlobal); |
|
3784 MOZ_ASSERT(globalWindow); |
|
3785 } |
|
3786 } |
|
3787 |
|
3788 nsCOMPtr<nsIDocument> document; |
|
3789 |
|
3790 if (globalWindow) { |
|
3791 // Only use the current inner window, and only use it if the caller can |
|
3792 // access it. |
|
3793 nsPIDOMWindow* outerWindow = globalWindow->GetOuterWindow(); |
|
3794 if (outerWindow) { |
|
3795 loadInfo.mWindow = outerWindow->GetCurrentInnerWindow(); |
|
3796 } |
|
3797 |
|
3798 if (!loadInfo.mWindow || |
|
3799 (globalWindow != loadInfo.mWindow && |
|
3800 !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) { |
|
3801 return NS_ERROR_DOM_SECURITY_ERR; |
|
3802 } |
|
3803 |
|
3804 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow); |
|
3805 MOZ_ASSERT(sgo); |
|
3806 |
|
3807 loadInfo.mScriptContext = sgo->GetContext(); |
|
3808 NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE); |
|
3809 |
|
3810 // If we're called from a window then we can dig out the principal and URI |
|
3811 // from the document. |
|
3812 document = loadInfo.mWindow->GetExtantDoc(); |
|
3813 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE); |
|
3814 |
|
3815 loadInfo.mBaseURI = document->GetDocBaseURI(); |
|
3816 |
|
3817 // Use the document's NodePrincipal as our principal if we're not being |
|
3818 // called from chrome. |
|
3819 if (!loadInfo.mPrincipal) { |
|
3820 loadInfo.mPrincipal = document->NodePrincipal(); |
|
3821 NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE); |
|
3822 |
|
3823 // We use the document's base domain to limit the number of workers |
|
3824 // each domain can create. For sandboxed documents, we use the domain |
|
3825 // of their first non-sandboxed document, walking up until we find |
|
3826 // one. If we can't find one, we fall back to using the GUID of the |
|
3827 // null principal as the base domain. |
|
3828 if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) { |
|
3829 nsCOMPtr<nsIDocument> tmpDoc = document; |
|
3830 do { |
|
3831 tmpDoc = tmpDoc->GetParentDocument(); |
|
3832 } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN); |
|
3833 |
|
3834 if (tmpDoc) { |
|
3835 // There was an unsandboxed ancestor, yay! |
|
3836 nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal(); |
|
3837 rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain); |
|
3838 NS_ENSURE_SUCCESS(rv, rv); |
|
3839 } else { |
|
3840 // No unsandboxed ancestor, use our GUID. |
|
3841 rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain); |
|
3842 NS_ENSURE_SUCCESS(rv, rv); |
|
3843 } |
|
3844 } else { |
|
3845 // Document creating the worker is not sandboxed. |
|
3846 rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain); |
|
3847 NS_ENSURE_SUCCESS(rv, rv); |
|
3848 } |
|
3849 } |
|
3850 |
|
3851 nsCOMPtr<nsIPermissionManager> permMgr = |
|
3852 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); |
|
3853 NS_ENSURE_SUCCESS(rv, rv); |
|
3854 |
|
3855 uint32_t perm; |
|
3856 rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR", |
|
3857 &perm); |
|
3858 NS_ENSURE_SUCCESS(rv, rv); |
|
3859 |
|
3860 loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION; |
|
3861 } else { |
|
3862 // Not a window |
|
3863 MOZ_ASSERT(isChrome); |
|
3864 |
|
3865 // We're being created outside of a window. Need to figure out the script |
|
3866 // that is creating us in order for us to use relative URIs later on. |
|
3867 JS::AutoFilename fileName; |
|
3868 if (JS::DescribeScriptedCaller(aCx, &fileName)) { |
|
3869 // In most cases, fileName is URI. In a few other cases |
|
3870 // (e.g. xpcshell), fileName is a file path. Ideally, we would |
|
3871 // prefer testing whether fileName parses as an URI and fallback |
|
3872 // to file path in case of error, but Windows file paths have |
|
3873 // the interesting property that they can be parsed as bogus |
|
3874 // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C", |
|
3875 // hostname "Windows", path "Tmp"), which defeats this algorithm. |
|
3876 // Therefore, we adopt the opposite convention. |
|
3877 nsCOMPtr<nsIFile> scriptFile = |
|
3878 do_CreateInstance("@mozilla.org/file/local;1", &rv); |
|
3879 if (NS_FAILED(rv)) { |
|
3880 return rv; |
|
3881 } |
|
3882 |
|
3883 rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get())); |
|
3884 if (NS_SUCCEEDED(rv)) { |
|
3885 rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI), |
|
3886 scriptFile); |
|
3887 } |
|
3888 if (NS_FAILED(rv)) { |
|
3889 // As expected, fileName is not a path, so proceed with |
|
3890 // a uri. |
|
3891 rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI), |
|
3892 fileName.get()); |
|
3893 } |
|
3894 if (NS_FAILED(rv)) { |
|
3895 return rv; |
|
3896 } |
|
3897 } |
|
3898 loadInfo.mXHRParamsAllowed = true; |
|
3899 } |
|
3900 |
|
3901 MOZ_ASSERT(loadInfo.mPrincipal); |
|
3902 MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty()); |
|
3903 |
|
3904 // XXXbent Use subject principal here instead of the one we already have? |
|
3905 nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx); |
|
3906 MOZ_ASSERT(subjectPrincipal); |
|
3907 |
|
3908 if (!nsContentUtils::GetContentSecurityPolicy(aCx, |
|
3909 getter_AddRefs(loadInfo.mCSP))) { |
|
3910 NS_WARNING("Failed to get CSP!"); |
|
3911 return NS_ERROR_FAILURE; |
|
3912 } |
|
3913 |
|
3914 if (loadInfo.mCSP) { |
|
3915 rv = loadInfo.mCSP->GetAllowsEval(&loadInfo.mReportCSPViolations, |
|
3916 &loadInfo.mEvalAllowed); |
|
3917 NS_ENSURE_SUCCESS(rv, rv); |
|
3918 } else { |
|
3919 loadInfo.mEvalAllowed = true; |
|
3920 loadInfo.mReportCSPViolations = false; |
|
3921 } |
|
3922 |
|
3923 rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI, |
|
3924 document, aScriptURL, |
|
3925 getter_AddRefs(loadInfo.mChannel)); |
|
3926 NS_ENSURE_SUCCESS(rv, rv); |
|
3927 |
|
3928 rv = NS_GetFinalChannelURI(loadInfo.mChannel, |
|
3929 getter_AddRefs(loadInfo.mResolvedScriptURI)); |
|
3930 NS_ENSURE_SUCCESS(rv, rv); |
|
3931 } |
|
3932 |
|
3933 aLoadInfo->StealFrom(loadInfo); |
|
3934 return NS_OK; |
|
3935 } |
|
3936 |
|
3937 void |
|
3938 WorkerPrivate::DoRunLoop(JSContext* aCx) |
|
3939 { |
|
3940 AssertIsOnWorkerThread(); |
|
3941 MOZ_ASSERT(mThread); |
|
3942 |
|
3943 { |
|
3944 MutexAutoLock lock(mMutex); |
|
3945 mJSContext = aCx; |
|
3946 |
|
3947 MOZ_ASSERT(mStatus == Pending); |
|
3948 mStatus = Running; |
|
3949 } |
|
3950 |
|
3951 EnableMemoryReporter(); |
|
3952 |
|
3953 InitializeGCTimers(); |
|
3954 |
|
3955 Maybe<JSAutoCompartment> workerCompartment; |
|
3956 |
|
3957 for (;;) { |
|
3958 // Workers lazily create a global object in CompileScriptRunnable. We need |
|
3959 // to enter the global's compartment as soon as it has been created. |
|
3960 if (workerCompartment.empty()) { |
|
3961 if (JSObject* global = js::DefaultObjectForContextOrNull(aCx)) { |
|
3962 workerCompartment.construct(aCx, global); |
|
3963 } |
|
3964 } |
|
3965 |
|
3966 Status currentStatus; |
|
3967 bool normalRunnablesPending = false; |
|
3968 |
|
3969 { |
|
3970 MutexAutoLock lock(mMutex); |
|
3971 |
|
3972 while (mControlQueue.IsEmpty() && |
|
3973 !(normalRunnablesPending = NS_HasPendingEvents(mThread))) { |
|
3974 WaitForWorkerEvents(); |
|
3975 } |
|
3976 |
|
3977 ProcessAllControlRunnablesLocked(); |
|
3978 |
|
3979 currentStatus = mStatus; |
|
3980 } |
|
3981 |
|
3982 // If the close handler has finished and all features are done then we can |
|
3983 // kill this thread. |
|
3984 if (currentStatus != Running && !HasActiveFeatures()) { |
|
3985 if (mCloseHandlerFinished && currentStatus != Killing) { |
|
3986 if (!NotifyInternal(aCx, Killing)) { |
|
3987 JS_ReportPendingException(aCx); |
|
3988 } |
|
3989 #ifdef DEBUG |
|
3990 { |
|
3991 MutexAutoLock lock(mMutex); |
|
3992 currentStatus = mStatus; |
|
3993 } |
|
3994 MOZ_ASSERT(currentStatus == Killing); |
|
3995 #else |
|
3996 currentStatus = Killing; |
|
3997 #endif |
|
3998 } |
|
3999 |
|
4000 // If we're supposed to die then we should exit the loop. |
|
4001 if (currentStatus == Killing) { |
|
4002 ShutdownGCTimers(); |
|
4003 |
|
4004 DisableMemoryReporter(); |
|
4005 |
|
4006 { |
|
4007 MutexAutoLock lock(mMutex); |
|
4008 |
|
4009 mStatus = Dead; |
|
4010 mJSContext = nullptr; |
|
4011 } |
|
4012 |
|
4013 // After mStatus is set to Dead there can be no more |
|
4014 // WorkerControlRunnables so no need to lock here. |
|
4015 if (!mControlQueue.IsEmpty()) { |
|
4016 WorkerControlRunnable* runnable; |
|
4017 while (mControlQueue.Pop(runnable)) { |
|
4018 runnable->Cancel(); |
|
4019 runnable->Release(); |
|
4020 } |
|
4021 } |
|
4022 |
|
4023 // Clear away our MessagePorts. |
|
4024 mWorkerPorts.Clear(); |
|
4025 |
|
4026 // Unroot the global |
|
4027 mScope = nullptr; |
|
4028 |
|
4029 return; |
|
4030 } |
|
4031 } |
|
4032 |
|
4033 // Nothing to do here if we don't have any runnables in the main queue. |
|
4034 if (!normalRunnablesPending) { |
|
4035 SetGCTimerMode(IdleTimer); |
|
4036 continue; |
|
4037 } |
|
4038 |
|
4039 MOZ_ASSERT(NS_HasPendingEvents(mThread)); |
|
4040 |
|
4041 // Start the periodic GC timer if it is not already running. |
|
4042 SetGCTimerMode(PeriodicTimer); |
|
4043 |
|
4044 // Process a single runnable from the main queue. |
|
4045 MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false)); |
|
4046 |
|
4047 if (NS_HasPendingEvents(mThread)) { |
|
4048 // Now *might* be a good time to GC. Let the JS engine make the decision. |
|
4049 if (!workerCompartment.empty()) { |
|
4050 JS_MaybeGC(aCx); |
|
4051 } |
|
4052 } |
|
4053 else { |
|
4054 // The normal event queue has been exhausted, cancel the periodic GC timer |
|
4055 // and schedule the idle GC timer. |
|
4056 SetGCTimerMode(IdleTimer); |
|
4057 } |
|
4058 } |
|
4059 |
|
4060 MOZ_ASSUME_UNREACHABLE("Shouldn't get here!"); |
|
4061 } |
|
4062 |
|
4063 void |
|
4064 WorkerPrivate::OnProcessNextEvent(uint32_t aRecursionDepth) |
|
4065 { |
|
4066 AssertIsOnWorkerThread(); |
|
4067 MOZ_ASSERT(aRecursionDepth); |
|
4068 |
|
4069 // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop. |
|
4070 // However, it's possible that non-worker C++ could spin its own nested event |
|
4071 // loop, and in that case we must ensure that we continue to process control |
|
4072 // runnables here. |
|
4073 if (aRecursionDepth > 1 && |
|
4074 mSyncLoopStack.Length() < aRecursionDepth - 1) { |
|
4075 ProcessAllControlRunnables(); |
|
4076 } |
|
4077 } |
|
4078 |
|
4079 void |
|
4080 WorkerPrivate::AfterProcessNextEvent(uint32_t aRecursionDepth) |
|
4081 { |
|
4082 AssertIsOnWorkerThread(); |
|
4083 MOZ_ASSERT(aRecursionDepth); |
|
4084 } |
|
4085 |
|
4086 void |
|
4087 WorkerPrivate::InitializeGCTimers() |
|
4088 { |
|
4089 AssertIsOnWorkerThread(); |
|
4090 |
|
4091 // We need a timer for GC. The basic plan is to run a non-shrinking GC |
|
4092 // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running. |
|
4093 // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to |
|
4094 // run a shrinking GC. If the worker receives more messages then the short |
|
4095 // timer is canceled and the periodic timer resumes. |
|
4096 mGCTimer = do_CreateInstance(NS_TIMER_CONTRACTID); |
|
4097 MOZ_ASSERT(mGCTimer); |
|
4098 |
|
4099 nsRefPtr<GarbageCollectRunnable> runnable = |
|
4100 new GarbageCollectRunnable(this, false, false); |
|
4101 mPeriodicGCTimerTarget = new TimerThreadEventTarget(this, runnable); |
|
4102 |
|
4103 runnable = new GarbageCollectRunnable(this, true, false); |
|
4104 mIdleGCTimerTarget = new TimerThreadEventTarget(this, runnable); |
|
4105 |
|
4106 mPeriodicGCTimerRunning = false; |
|
4107 mIdleGCTimerRunning = false; |
|
4108 } |
|
4109 |
|
4110 void |
|
4111 WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) |
|
4112 { |
|
4113 AssertIsOnWorkerThread(); |
|
4114 MOZ_ASSERT(mGCTimer); |
|
4115 MOZ_ASSERT(mPeriodicGCTimerTarget); |
|
4116 MOZ_ASSERT(mIdleGCTimerTarget); |
|
4117 |
|
4118 if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) || |
|
4119 (aMode == IdleTimer && mIdleGCTimerRunning)) { |
|
4120 return; |
|
4121 } |
|
4122 |
|
4123 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel())); |
|
4124 |
|
4125 mPeriodicGCTimerRunning = false; |
|
4126 mIdleGCTimerRunning = false; |
|
4127 |
|
4128 LOG(("Worker %p canceled GC timer because %s\n", this, |
|
4129 aMode == PeriodicTimer ? |
|
4130 "periodic" : |
|
4131 aMode == IdleTimer ? "idle" : "none")); |
|
4132 |
|
4133 if (aMode == NoTimer) { |
|
4134 return; |
|
4135 } |
|
4136 |
|
4137 MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer); |
|
4138 |
|
4139 nsIEventTarget* target; |
|
4140 uint32_t delay; |
|
4141 int16_t type; |
|
4142 |
|
4143 if (aMode == PeriodicTimer) { |
|
4144 target = mPeriodicGCTimerTarget; |
|
4145 delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000; |
|
4146 type = nsITimer::TYPE_REPEATING_SLACK; |
|
4147 } |
|
4148 else { |
|
4149 target = mIdleGCTimerTarget; |
|
4150 delay = IDLE_GC_TIMER_DELAY_SEC * 1000; |
|
4151 type = nsITimer::TYPE_ONE_SHOT; |
|
4152 } |
|
4153 |
|
4154 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->SetTarget(target))); |
|
4155 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->InitWithFuncCallback(DummyCallback, |
|
4156 nullptr, delay, |
|
4157 type))); |
|
4158 |
|
4159 if (aMode == PeriodicTimer) { |
|
4160 LOG(("Worker %p scheduled periodic GC timer\n", this)); |
|
4161 mPeriodicGCTimerRunning = true; |
|
4162 } |
|
4163 else { |
|
4164 LOG(("Worker %p scheduled idle GC timer\n", this)); |
|
4165 mIdleGCTimerRunning = true; |
|
4166 } |
|
4167 } |
|
4168 |
|
4169 void |
|
4170 WorkerPrivate::ShutdownGCTimers() |
|
4171 { |
|
4172 AssertIsOnWorkerThread(); |
|
4173 |
|
4174 MOZ_ASSERT(mGCTimer); |
|
4175 |
|
4176 // Always make sure the timer is canceled. |
|
4177 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel())); |
|
4178 |
|
4179 LOG(("Worker %p killed the GC timer\n", this)); |
|
4180 |
|
4181 mGCTimer = nullptr; |
|
4182 mPeriodicGCTimerTarget = nullptr; |
|
4183 mIdleGCTimerTarget = nullptr; |
|
4184 mPeriodicGCTimerRunning = false; |
|
4185 mIdleGCTimerRunning = false; |
|
4186 } |
|
4187 |
|
4188 bool |
|
4189 WorkerPrivate::InterruptCallback(JSContext* aCx) |
|
4190 { |
|
4191 AssertIsOnWorkerThread(); |
|
4192 |
|
4193 bool mayContinue = true; |
|
4194 bool scheduledIdleGC = false; |
|
4195 |
|
4196 for (;;) { |
|
4197 // Run all control events now. |
|
4198 mayContinue = ProcessAllControlRunnables(); |
|
4199 |
|
4200 bool maySuspend = mSuspended; |
|
4201 if (maySuspend) { |
|
4202 MutexAutoLock lock(mMutex); |
|
4203 maySuspend = mStatus <= Running; |
|
4204 } |
|
4205 |
|
4206 if (!mayContinue || !maySuspend) { |
|
4207 break; |
|
4208 } |
|
4209 |
|
4210 // Cancel the periodic GC timer here before suspending. The idle GC timer |
|
4211 // will clean everything up once it runs. |
|
4212 if (!scheduledIdleGC) { |
|
4213 SetGCTimerMode(IdleTimer); |
|
4214 scheduledIdleGC = true; |
|
4215 } |
|
4216 |
|
4217 while ((mayContinue = MayContinueRunning())) { |
|
4218 MutexAutoLock lock(mMutex); |
|
4219 if (!mControlQueue.IsEmpty()) { |
|
4220 break; |
|
4221 } |
|
4222 |
|
4223 WaitForWorkerEvents(PR_MillisecondsToInterval(RemainingRunTimeMS())); |
|
4224 } |
|
4225 } |
|
4226 |
|
4227 if (!mayContinue) { |
|
4228 // We want only uncatchable exceptions here. |
|
4229 NS_ASSERTION(!JS_IsExceptionPending(aCx), |
|
4230 "Should not have an exception set here!"); |
|
4231 return false; |
|
4232 } |
|
4233 |
|
4234 // Make sure the periodic timer gets turned back on here. |
|
4235 SetGCTimerMode(PeriodicTimer); |
|
4236 |
|
4237 return true; |
|
4238 } |
|
4239 |
|
4240 nsresult |
|
4241 WorkerPrivate::IsOnCurrentThread(bool* aIsOnCurrentThread) |
|
4242 { |
|
4243 // May be called on any thread! |
|
4244 |
|
4245 MOZ_ASSERT(aIsOnCurrentThread); |
|
4246 |
|
4247 nsCOMPtr<nsIThread> thread; |
|
4248 { |
|
4249 MutexAutoLock lock(mMutex); |
|
4250 thread = mThread; |
|
4251 } |
|
4252 |
|
4253 if (!thread) { |
|
4254 NS_WARNING("Trying to test thread correctness after the worker has " |
|
4255 "released its thread!"); |
|
4256 return NS_ERROR_FAILURE; |
|
4257 } |
|
4258 |
|
4259 nsresult rv = thread->IsOnCurrentThread(aIsOnCurrentThread); |
|
4260 if (NS_WARN_IF(NS_FAILED(rv))) { |
|
4261 return rv; |
|
4262 } |
|
4263 |
|
4264 return NS_OK; |
|
4265 } |
|
4266 |
|
4267 void |
|
4268 WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) |
|
4269 { |
|
4270 AssertIsOnWorkerThread(); |
|
4271 MOZ_ASSERT(mChildWorkers.IsEmpty()); |
|
4272 MOZ_ASSERT(mSyncLoopStack.IsEmpty()); |
|
4273 |
|
4274 ClearMainEventQueue(aRanOrNot); |
|
4275 |
|
4276 if (WorkerPrivate* parent = GetParent()) { |
|
4277 nsRefPtr<WorkerFinishedRunnable> runnable = |
|
4278 new WorkerFinishedRunnable(parent, this); |
|
4279 if (!runnable->Dispatch(nullptr)) { |
|
4280 NS_WARNING("Failed to dispatch runnable!"); |
|
4281 } |
|
4282 } |
|
4283 else { |
|
4284 nsRefPtr<TopLevelWorkerFinishedRunnable> runnable = |
|
4285 new TopLevelWorkerFinishedRunnable(this); |
|
4286 if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { |
|
4287 NS_WARNING("Failed to dispatch runnable!"); |
|
4288 } |
|
4289 } |
|
4290 } |
|
4291 |
|
4292 bool |
|
4293 WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats) |
|
4294 { |
|
4295 AssertIsOnMainThread(); |
|
4296 mMutex.AssertCurrentThreadOwns(); |
|
4297 NS_ASSERTION(aRtStats, "Null RuntimeStats!"); |
|
4298 |
|
4299 NS_ASSERTION(!mMemoryReporterRunning, "How can we get reentered here?!"); |
|
4300 |
|
4301 // This signals the worker that it should block itself as soon as possible. |
|
4302 mMemoryReporterRunning = true; |
|
4303 |
|
4304 NS_ASSERTION(mJSContext, "This must never be null!"); |
|
4305 JSRuntime* rt = JS_GetRuntime(mJSContext); |
|
4306 |
|
4307 // If the worker is not already blocked (e.g. waiting for a worker event or |
|
4308 // currently in a ctypes call) then we need to trigger the interrupt |
|
4309 // callback to trap the worker. |
|
4310 if (!mBlockedForMemoryReporter) { |
|
4311 JS_RequestInterruptCallback(rt); |
|
4312 |
|
4313 // Wait until the worker actually blocks. |
|
4314 while (!mBlockedForMemoryReporter) { |
|
4315 mMemoryReportCondVar.Wait(); |
|
4316 } |
|
4317 } |
|
4318 |
|
4319 bool succeeded = false; |
|
4320 |
|
4321 // If mMemoryReporter is still set then we can do the actual report. Otherwise |
|
4322 // we're trying to shut down and we don't want to do anything but clean up. |
|
4323 if (mMemoryReporter) { |
|
4324 // Don't hold the lock while doing the actual report. |
|
4325 MutexAutoUnlock unlock(mMutex); |
|
4326 succeeded = JS::CollectRuntimeStats(rt, aRtStats, nullptr); |
|
4327 } |
|
4328 |
|
4329 NS_ASSERTION(mMemoryReporterRunning, "This isn't possible!"); |
|
4330 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); |
|
4331 |
|
4332 // Tell the worker that it can now continue its execution. |
|
4333 mMemoryReporterRunning = false; |
|
4334 |
|
4335 // The worker may be waiting so we must notify. |
|
4336 mMemoryReportCondVar.Notify(); |
|
4337 |
|
4338 return succeeded; |
|
4339 } |
|
4340 |
|
4341 void |
|
4342 WorkerPrivate::EnableMemoryReporter() |
|
4343 { |
|
4344 AssertIsOnWorkerThread(); |
|
4345 MOZ_ASSERT(!mMemoryReporter); |
|
4346 |
|
4347 // No need to lock here since the main thread can't race until we've |
|
4348 // successfully registered the reporter. |
|
4349 mMemoryReporter = new MemoryReporter(this); |
|
4350 |
|
4351 if (NS_FAILED(RegisterWeakMemoryReporter(mMemoryReporter))) { |
|
4352 NS_WARNING("Failed to register memory reporter!"); |
|
4353 // No need to lock here since a failed registration means our memory |
|
4354 // reporter can't start running. Just clean up. |
|
4355 mMemoryReporter = nullptr; |
|
4356 } |
|
4357 } |
|
4358 |
|
4359 void |
|
4360 WorkerPrivate::DisableMemoryReporter() |
|
4361 { |
|
4362 AssertIsOnWorkerThread(); |
|
4363 |
|
4364 nsRefPtr<MemoryReporter> memoryReporter; |
|
4365 { |
|
4366 MutexAutoLock lock(mMutex); |
|
4367 |
|
4368 // There is nothing to do here if the memory reporter was never successfully |
|
4369 // registered. |
|
4370 if (!mMemoryReporter) { |
|
4371 return; |
|
4372 } |
|
4373 |
|
4374 // We don't need this set any longer. Swap it out so that we can unregister |
|
4375 // below. |
|
4376 mMemoryReporter.swap(memoryReporter); |
|
4377 |
|
4378 // Next disable the memory reporter so that the main thread stops trying to |
|
4379 // signal us. |
|
4380 memoryReporter->Disable(); |
|
4381 |
|
4382 // If the memory reporter is waiting to start then we need to wait for it to |
|
4383 // finish. |
|
4384 if (mMemoryReporterRunning) { |
|
4385 NS_ASSERTION(!mBlockedForMemoryReporter, |
|
4386 "Can't be blocked in more than one place at the same time!"); |
|
4387 mBlockedForMemoryReporter = true; |
|
4388 |
|
4389 // Tell the main thread that we're blocked. |
|
4390 mMemoryReportCondVar.Notify(); |
|
4391 |
|
4392 // Wait for it the main thread to finish. Since we swapped out |
|
4393 // mMemoryReporter above the main thread should respond quickly. |
|
4394 while (mMemoryReporterRunning) { |
|
4395 mMemoryReportCondVar.Wait(); |
|
4396 } |
|
4397 |
|
4398 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); |
|
4399 mBlockedForMemoryReporter = false; |
|
4400 } |
|
4401 } |
|
4402 |
|
4403 // Finally unregister the memory reporter. |
|
4404 if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) { |
|
4405 NS_WARNING("Failed to unregister memory reporter!"); |
|
4406 } |
|
4407 } |
|
4408 |
|
4409 void |
|
4410 WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval) |
|
4411 { |
|
4412 AssertIsOnWorkerThread(); |
|
4413 mMutex.AssertCurrentThreadOwns(); |
|
4414 |
|
4415 NS_ASSERTION(!mBlockedForMemoryReporter, |
|
4416 "Can't be blocked in more than one place at the same time!"); |
|
4417 |
|
4418 // Let the main thread know that the worker is blocked and that memory |
|
4419 // reporting may proceed. |
|
4420 mBlockedForMemoryReporter = true; |
|
4421 |
|
4422 // The main thread may be waiting so we must notify. |
|
4423 mMemoryReportCondVar.Notify(); |
|
4424 |
|
4425 // Now wait for an actual worker event. |
|
4426 mCondVar.Wait(aInterval); |
|
4427 |
|
4428 // We've gotten some kind of signal but we can't continue until the memory |
|
4429 // reporter has finished. Wait again. |
|
4430 while (mMemoryReporterRunning) { |
|
4431 mMemoryReportCondVar.Wait(); |
|
4432 } |
|
4433 |
|
4434 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); |
|
4435 |
|
4436 // No need to notify here as the main thread isn't watching for this state. |
|
4437 mBlockedForMemoryReporter = false; |
|
4438 } |
|
4439 |
|
4440 bool |
|
4441 WorkerPrivate::ProcessAllControlRunnablesLocked() |
|
4442 { |
|
4443 AssertIsOnWorkerThread(); |
|
4444 mMutex.AssertCurrentThreadOwns(); |
|
4445 |
|
4446 bool result = true; |
|
4447 |
|
4448 for (;;) { |
|
4449 // Block here if the memory reporter is trying to run. |
|
4450 if (mMemoryReporterRunning) { |
|
4451 MOZ_ASSERT(!mBlockedForMemoryReporter); |
|
4452 |
|
4453 // Let the main thread know that we've received the block request and |
|
4454 // that memory reporting may proceed. |
|
4455 mBlockedForMemoryReporter = true; |
|
4456 |
|
4457 // The main thread is almost certainly waiting so we must notify here. |
|
4458 mMemoryReportCondVar.Notify(); |
|
4459 |
|
4460 // Wait for the memory report to finish. |
|
4461 while (mMemoryReporterRunning) { |
|
4462 mMemoryReportCondVar.Wait(); |
|
4463 } |
|
4464 |
|
4465 MOZ_ASSERT(mBlockedForMemoryReporter); |
|
4466 |
|
4467 // No need to notify here as the main thread isn't watching for this |
|
4468 // state. |
|
4469 mBlockedForMemoryReporter = false; |
|
4470 } |
|
4471 |
|
4472 WorkerControlRunnable* event; |
|
4473 if (!mControlQueue.Pop(event)) { |
|
4474 break; |
|
4475 } |
|
4476 |
|
4477 MutexAutoUnlock unlock(mMutex); |
|
4478 |
|
4479 MOZ_ASSERT(event); |
|
4480 if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) { |
|
4481 result = false; |
|
4482 } |
|
4483 |
|
4484 event->Release(); |
|
4485 } |
|
4486 |
|
4487 return result; |
|
4488 } |
|
4489 |
|
4490 void |
|
4491 WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot) |
|
4492 { |
|
4493 AssertIsOnWorkerThread(); |
|
4494 |
|
4495 MOZ_ASSERT(!mCancelAllPendingRunnables); |
|
4496 mCancelAllPendingRunnables = true; |
|
4497 |
|
4498 if (WorkerNeverRan == aRanOrNot) { |
|
4499 for (uint32_t count = mPreStartRunnables.Length(), index = 0; |
|
4500 index < count; |
|
4501 index++) { |
|
4502 nsRefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget(); |
|
4503 static_cast<nsIRunnable*>(runnable.get())->Run(); |
|
4504 } |
|
4505 } else { |
|
4506 nsIThread* currentThread = NS_GetCurrentThread(); |
|
4507 MOZ_ASSERT(currentThread); |
|
4508 |
|
4509 NS_ProcessPendingEvents(currentThread); |
|
4510 MOZ_ASSERT(!NS_HasPendingEvents(currentThread)); |
|
4511 } |
|
4512 |
|
4513 MOZ_ASSERT(mCancelAllPendingRunnables); |
|
4514 mCancelAllPendingRunnables = false; |
|
4515 } |
|
4516 |
|
4517 uint32_t |
|
4518 WorkerPrivate::RemainingRunTimeMS() const |
|
4519 { |
|
4520 if (mKillTime.IsNull()) { |
|
4521 return UINT32_MAX; |
|
4522 } |
|
4523 TimeDuration runtime = mKillTime - TimeStamp::Now(); |
|
4524 double ms = runtime > TimeDuration(0) ? runtime.ToMilliseconds() : 0; |
|
4525 return ms > double(UINT32_MAX) ? UINT32_MAX : uint32_t(ms); |
|
4526 } |
|
4527 |
|
4528 bool |
|
4529 WorkerPrivate::SuspendInternal(JSContext* aCx) |
|
4530 { |
|
4531 AssertIsOnWorkerThread(); |
|
4532 |
|
4533 NS_ASSERTION(!mSuspended, "Already suspended!"); |
|
4534 |
|
4535 mSuspended = true; |
|
4536 return true; |
|
4537 } |
|
4538 |
|
4539 bool |
|
4540 WorkerPrivate::ResumeInternal(JSContext* aCx) |
|
4541 { |
|
4542 AssertIsOnWorkerThread(); |
|
4543 |
|
4544 NS_ASSERTION(mSuspended, "Not yet suspended!"); |
|
4545 |
|
4546 mSuspended = false; |
|
4547 return true; |
|
4548 } |
|
4549 |
|
4550 void |
|
4551 WorkerPrivate::TraceTimeouts(const TraceCallbacks& aCallbacks, |
|
4552 void* aClosure) const |
|
4553 { |
|
4554 AssertIsOnWorkerThread(); |
|
4555 |
|
4556 for (uint32_t index = 0; index < mTimeouts.Length(); index++) { |
|
4557 TimeoutInfo* info = mTimeouts[index]; |
|
4558 |
|
4559 if (info->mTimeoutCallable.isUndefined()) { |
|
4560 continue; |
|
4561 } |
|
4562 |
|
4563 aCallbacks.Trace(&info->mTimeoutCallable, "mTimeoutCallable", aClosure); |
|
4564 for (uint32_t index2 = 0; index2 < info->mExtraArgVals.Length(); index2++) { |
|
4565 aCallbacks.Trace(&info->mExtraArgVals[index2], "mExtraArgVals[i]", aClosure); |
|
4566 } |
|
4567 } |
|
4568 } |
|
4569 |
|
4570 bool |
|
4571 WorkerPrivate::ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease) |
|
4572 { |
|
4573 AssertIsOnWorkerThread(); |
|
4574 |
|
4575 { |
|
4576 MutexAutoLock lock(mMutex); |
|
4577 |
|
4578 // If we're in shutdown then the busy count is no longer being considered so |
|
4579 // just return now. |
|
4580 if (mStatus >= Killing) { |
|
4581 return true; |
|
4582 } |
|
4583 } |
|
4584 |
|
4585 nsRefPtr<ModifyBusyCountRunnable> runnable = |
|
4586 new ModifyBusyCountRunnable(this, aIncrease); |
|
4587 return runnable->Dispatch(aCx); |
|
4588 } |
|
4589 |
|
4590 bool |
|
4591 WorkerPrivate::AddChildWorker(JSContext* aCx, ParentType* aChildWorker) |
|
4592 { |
|
4593 AssertIsOnWorkerThread(); |
|
4594 |
|
4595 #ifdef DEBUG |
|
4596 { |
|
4597 Status currentStatus; |
|
4598 { |
|
4599 MutexAutoLock lock(mMutex); |
|
4600 currentStatus = mStatus; |
|
4601 } |
|
4602 |
|
4603 MOZ_ASSERT(currentStatus == Running); |
|
4604 } |
|
4605 #endif |
|
4606 |
|
4607 NS_ASSERTION(!mChildWorkers.Contains(aChildWorker), |
|
4608 "Already know about this one!"); |
|
4609 mChildWorkers.AppendElement(aChildWorker); |
|
4610 |
|
4611 return mChildWorkers.Length() == 1 ? |
|
4612 ModifyBusyCountFromWorker(aCx, true) : |
|
4613 true; |
|
4614 } |
|
4615 |
|
4616 void |
|
4617 WorkerPrivate::RemoveChildWorker(JSContext* aCx, ParentType* aChildWorker) |
|
4618 { |
|
4619 AssertIsOnWorkerThread(); |
|
4620 |
|
4621 NS_ASSERTION(mChildWorkers.Contains(aChildWorker), |
|
4622 "Didn't know about this one!"); |
|
4623 mChildWorkers.RemoveElement(aChildWorker); |
|
4624 |
|
4625 if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) { |
|
4626 NS_WARNING("Failed to modify busy count!"); |
|
4627 } |
|
4628 } |
|
4629 |
|
4630 bool |
|
4631 WorkerPrivate::AddFeature(JSContext* aCx, WorkerFeature* aFeature) |
|
4632 { |
|
4633 AssertIsOnWorkerThread(); |
|
4634 |
|
4635 { |
|
4636 MutexAutoLock lock(mMutex); |
|
4637 |
|
4638 if (mStatus >= Canceling) { |
|
4639 return false; |
|
4640 } |
|
4641 } |
|
4642 |
|
4643 NS_ASSERTION(!mFeatures.Contains(aFeature), "Already know about this one!"); |
|
4644 mFeatures.AppendElement(aFeature); |
|
4645 |
|
4646 return mFeatures.Length() == 1 ? |
|
4647 ModifyBusyCountFromWorker(aCx, true) : |
|
4648 true; |
|
4649 } |
|
4650 |
|
4651 void |
|
4652 WorkerPrivate::RemoveFeature(JSContext* aCx, WorkerFeature* aFeature) |
|
4653 { |
|
4654 AssertIsOnWorkerThread(); |
|
4655 |
|
4656 NS_ASSERTION(mFeatures.Contains(aFeature), "Didn't know about this one!"); |
|
4657 mFeatures.RemoveElement(aFeature); |
|
4658 |
|
4659 if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) { |
|
4660 NS_WARNING("Failed to modify busy count!"); |
|
4661 } |
|
4662 } |
|
4663 |
|
4664 void |
|
4665 WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus) |
|
4666 { |
|
4667 AssertIsOnWorkerThread(); |
|
4668 |
|
4669 NS_ASSERTION(aStatus > Running, "Bad status!"); |
|
4670 |
|
4671 if (aStatus >= Closing) { |
|
4672 CancelAllTimeouts(aCx); |
|
4673 } |
|
4674 |
|
4675 nsAutoTArray<WorkerFeature*, 30> features; |
|
4676 features.AppendElements(mFeatures); |
|
4677 |
|
4678 for (uint32_t index = 0; index < features.Length(); index++) { |
|
4679 if (!features[index]->Notify(aCx, aStatus)) { |
|
4680 NS_WARNING("Failed to notify feature!"); |
|
4681 } |
|
4682 } |
|
4683 |
|
4684 nsAutoTArray<ParentType*, 10> children; |
|
4685 children.AppendElements(mChildWorkers); |
|
4686 |
|
4687 for (uint32_t index = 0; index < children.Length(); index++) { |
|
4688 if (!children[index]->Notify(aCx, aStatus)) { |
|
4689 NS_WARNING("Failed to notify child worker!"); |
|
4690 } |
|
4691 } |
|
4692 } |
|
4693 |
|
4694 void |
|
4695 WorkerPrivate::CancelAllTimeouts(JSContext* aCx) |
|
4696 { |
|
4697 AssertIsOnWorkerThread(); |
|
4698 |
|
4699 if (mTimerRunning) { |
|
4700 NS_ASSERTION(mTimer, "Huh?!"); |
|
4701 NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!"); |
|
4702 |
|
4703 if (NS_FAILED(mTimer->Cancel())) { |
|
4704 NS_WARNING("Failed to cancel timer!"); |
|
4705 } |
|
4706 |
|
4707 for (uint32_t index = 0; index < mTimeouts.Length(); index++) { |
|
4708 mTimeouts[index]->mCanceled = true; |
|
4709 } |
|
4710 |
|
4711 if (!RunExpiredTimeouts(aCx)) { |
|
4712 JS_ReportPendingException(aCx); |
|
4713 } |
|
4714 |
|
4715 mTimerRunning = false; |
|
4716 } |
|
4717 #ifdef DEBUG |
|
4718 else if (!mRunningExpiredTimeouts) { |
|
4719 NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!"); |
|
4720 } |
|
4721 #endif |
|
4722 |
|
4723 mTimer = nullptr; |
|
4724 } |
|
4725 |
|
4726 already_AddRefed<nsIEventTarget> |
|
4727 WorkerPrivate::CreateNewSyncLoop() |
|
4728 { |
|
4729 AssertIsOnWorkerThread(); |
|
4730 |
|
4731 nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread()); |
|
4732 MOZ_ASSERT(thread); |
|
4733 |
|
4734 nsCOMPtr<nsIEventTarget> realEventTarget; |
|
4735 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->PushEventQueue( |
|
4736 getter_AddRefs(realEventTarget)))); |
|
4737 |
|
4738 nsRefPtr<EventTarget> workerEventTarget = |
|
4739 new EventTarget(this, realEventTarget); |
|
4740 |
|
4741 { |
|
4742 // Modifications must be protected by mMutex in DEBUG builds, see comment |
|
4743 // about mSyncLoopStack in WorkerPrivate.h. |
|
4744 #ifdef DEBUG |
|
4745 MutexAutoLock lock(mMutex); |
|
4746 #endif |
|
4747 |
|
4748 mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget)); |
|
4749 } |
|
4750 |
|
4751 return workerEventTarget.forget(); |
|
4752 } |
|
4753 |
|
4754 bool |
|
4755 WorkerPrivate::RunCurrentSyncLoop() |
|
4756 { |
|
4757 AssertIsOnWorkerThread(); |
|
4758 |
|
4759 JSContext* cx = GetJSContext(); |
|
4760 MOZ_ASSERT(cx); |
|
4761 |
|
4762 // This should not change between now and the time we finish running this sync |
|
4763 // loop. |
|
4764 uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1; |
|
4765 |
|
4766 SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex]; |
|
4767 |
|
4768 MOZ_ASSERT(loopInfo); |
|
4769 MOZ_ASSERT(!loopInfo->mHasRun); |
|
4770 MOZ_ASSERT(!loopInfo->mCompleted); |
|
4771 |
|
4772 #ifdef DEBUG |
|
4773 loopInfo->mHasRun = true; |
|
4774 #endif |
|
4775 |
|
4776 nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread); |
|
4777 MOZ_ASSERT(thread); |
|
4778 |
|
4779 while (!loopInfo->mCompleted) { |
|
4780 bool normalRunnablesPending = false; |
|
4781 |
|
4782 // Don't block with the periodic GC timer running. |
|
4783 if (!NS_HasPendingEvents(thread)) { |
|
4784 SetGCTimerMode(IdleTimer); |
|
4785 } |
|
4786 |
|
4787 // Wait for something to do. |
|
4788 { |
|
4789 MutexAutoLock lock(mMutex); |
|
4790 |
|
4791 for (;;) { |
|
4792 while (mControlQueue.IsEmpty() && |
|
4793 !normalRunnablesPending && |
|
4794 !(normalRunnablesPending = NS_HasPendingEvents(thread))) { |
|
4795 WaitForWorkerEvents(); |
|
4796 } |
|
4797 |
|
4798 ProcessAllControlRunnablesLocked(); |
|
4799 |
|
4800 // NB: If we processed a NotifyRunnable, we might have run non-control |
|
4801 // runnables, one of which may have shut down the sync loop. |
|
4802 if (normalRunnablesPending || loopInfo->mCompleted) { |
|
4803 break; |
|
4804 } |
|
4805 } |
|
4806 } |
|
4807 |
|
4808 if (normalRunnablesPending) { |
|
4809 // Make sure the periodic timer is running before we continue. |
|
4810 SetGCTimerMode(PeriodicTimer); |
|
4811 |
|
4812 MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread, false)); |
|
4813 |
|
4814 // Now *might* be a good time to GC. Let the JS engine make the decision. |
|
4815 JS_MaybeGC(cx); |
|
4816 } |
|
4817 } |
|
4818 |
|
4819 // Make sure that the stack didn't change underneath us. |
|
4820 MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo); |
|
4821 |
|
4822 return DestroySyncLoop(currentLoopIndex); |
|
4823 } |
|
4824 |
|
4825 bool |
|
4826 WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread) |
|
4827 { |
|
4828 MOZ_ASSERT(!mSyncLoopStack.IsEmpty()); |
|
4829 MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex); |
|
4830 |
|
4831 if (!aThread) { |
|
4832 nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread); |
|
4833 MOZ_ASSERT(thread); |
|
4834 |
|
4835 aThread = thread.get(); |
|
4836 } |
|
4837 |
|
4838 // We're about to delete the loop, stash its event target and result. |
|
4839 SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex]; |
|
4840 nsIEventTarget* nestedEventTarget = |
|
4841 loopInfo->mEventTarget->GetWeakNestedEventTarget(); |
|
4842 MOZ_ASSERT(nestedEventTarget); |
|
4843 |
|
4844 bool result = loopInfo->mResult; |
|
4845 |
|
4846 { |
|
4847 // Modifications must be protected by mMutex in DEBUG builds, see comment |
|
4848 // about mSyncLoopStack in WorkerPrivate.h. |
|
4849 #ifdef DEBUG |
|
4850 MutexAutoLock lock(mMutex); |
|
4851 #endif |
|
4852 |
|
4853 // This will delete |loopInfo|! |
|
4854 mSyncLoopStack.RemoveElementAt(aLoopIndex); |
|
4855 } |
|
4856 |
|
4857 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->PopEventQueue(nestedEventTarget))); |
|
4858 |
|
4859 return result; |
|
4860 } |
|
4861 |
|
4862 void |
|
4863 WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult) |
|
4864 { |
|
4865 AssertIsOnWorkerThread(); |
|
4866 AssertValidSyncLoop(aSyncLoopTarget); |
|
4867 |
|
4868 MOZ_ASSERT(!mSyncLoopStack.IsEmpty()); |
|
4869 |
|
4870 for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) { |
|
4871 nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1]; |
|
4872 MOZ_ASSERT(loopInfo); |
|
4873 MOZ_ASSERT(loopInfo->mEventTarget); |
|
4874 |
|
4875 if (loopInfo->mEventTarget == aSyncLoopTarget) { |
|
4876 // Can't assert |loop->mHasRun| here because dispatch failures can cause |
|
4877 // us to bail out early. |
|
4878 MOZ_ASSERT(!loopInfo->mCompleted); |
|
4879 |
|
4880 loopInfo->mResult = aResult; |
|
4881 loopInfo->mCompleted = true; |
|
4882 |
|
4883 loopInfo->mEventTarget->Disable(); |
|
4884 |
|
4885 return; |
|
4886 } |
|
4887 |
|
4888 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget)); |
|
4889 } |
|
4890 |
|
4891 MOZ_CRASH("Unknown sync loop!"); |
|
4892 } |
|
4893 |
|
4894 #ifdef DEBUG |
|
4895 void |
|
4896 WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) |
|
4897 { |
|
4898 MOZ_ASSERT(aSyncLoopTarget); |
|
4899 |
|
4900 EventTarget* workerTarget; |
|
4901 nsresult rv = |
|
4902 aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID, |
|
4903 reinterpret_cast<void**>(&workerTarget)); |
|
4904 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
4905 MOZ_ASSERT(workerTarget); |
|
4906 |
|
4907 bool valid = false; |
|
4908 |
|
4909 { |
|
4910 MutexAutoLock lock(mMutex); |
|
4911 |
|
4912 for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) { |
|
4913 nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index]; |
|
4914 MOZ_ASSERT(loopInfo); |
|
4915 MOZ_ASSERT(loopInfo->mEventTarget); |
|
4916 |
|
4917 if (loopInfo->mEventTarget == aSyncLoopTarget) { |
|
4918 valid = true; |
|
4919 break; |
|
4920 } |
|
4921 |
|
4922 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget)); |
|
4923 } |
|
4924 } |
|
4925 |
|
4926 MOZ_ASSERT(valid); |
|
4927 } |
|
4928 #endif |
|
4929 |
|
4930 void |
|
4931 WorkerPrivate::PostMessageToParentInternal( |
|
4932 JSContext* aCx, |
|
4933 JS::Handle<JS::Value> aMessage, |
|
4934 const Optional<Sequence<JS::Value>>& aTransferable, |
|
4935 bool aToMessagePort, |
|
4936 uint64_t aMessagePortSerial, |
|
4937 ErrorResult& aRv) |
|
4938 { |
|
4939 AssertIsOnWorkerThread(); |
|
4940 |
|
4941 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); |
|
4942 if (aTransferable.WasPassed()) { |
|
4943 const Sequence<JS::Value>& realTransferable = aTransferable.Value(); |
|
4944 |
|
4945 // The input sequence only comes from the generated bindings code, which |
|
4946 // ensures it is rooted. |
|
4947 JS::HandleValueArray elements = |
|
4948 JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(), |
|
4949 realTransferable.Elements()); |
|
4950 |
|
4951 JSObject* array = JS_NewArrayObject(aCx, elements); |
|
4952 if (!array) { |
|
4953 aRv = NS_ERROR_OUT_OF_MEMORY; |
|
4954 return; |
|
4955 } |
|
4956 transferable.setObject(*array); |
|
4957 } |
|
4958 |
|
4959 JSStructuredCloneCallbacks* callbacks = |
|
4960 IsChromeWorker() ? |
|
4961 &gChromeWorkerStructuredCloneCallbacks : |
|
4962 &gWorkerStructuredCloneCallbacks; |
|
4963 |
|
4964 nsTArray<nsCOMPtr<nsISupports>> clonedObjects; |
|
4965 |
|
4966 JSAutoStructuredCloneBuffer buffer; |
|
4967 if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) { |
|
4968 aRv = NS_ERROR_DOM_DATA_CLONE_ERR; |
|
4969 return; |
|
4970 } |
|
4971 |
|
4972 nsRefPtr<MessageEventRunnable> runnable = |
|
4973 new MessageEventRunnable(this, |
|
4974 WorkerRunnable::ParentThreadUnchangedBusyCount, |
|
4975 Move(buffer), clonedObjects, aToMessagePort, |
|
4976 aMessagePortSerial); |
|
4977 if (!runnable->Dispatch(aCx)) { |
|
4978 aRv = NS_ERROR_FAILURE; |
|
4979 } |
|
4980 } |
|
4981 |
|
4982 void |
|
4983 WorkerPrivate::PostMessageToParentMessagePort( |
|
4984 JSContext* aCx, |
|
4985 uint64_t aMessagePortSerial, |
|
4986 JS::Handle<JS::Value> aMessage, |
|
4987 const Optional<Sequence<JS::Value>>& aTransferable, |
|
4988 ErrorResult& aRv) |
|
4989 { |
|
4990 AssertIsOnWorkerThread(); |
|
4991 |
|
4992 if (!mWorkerPorts.GetWeak(aMessagePortSerial)) { |
|
4993 // This port has been closed from the main thread. There's no point in |
|
4994 // sending this message so just bail. |
|
4995 return; |
|
4996 } |
|
4997 |
|
4998 PostMessageToParentInternal(aCx, aMessage, aTransferable, true, |
|
4999 aMessagePortSerial, aRv); |
|
5000 } |
|
5001 |
|
5002 bool |
|
5003 WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus) |
|
5004 { |
|
5005 AssertIsOnWorkerThread(); |
|
5006 |
|
5007 NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!"); |
|
5008 |
|
5009 nsRefPtr<EventTarget> eventTarget; |
|
5010 |
|
5011 // Save the old status and set the new status. |
|
5012 Status previousStatus; |
|
5013 { |
|
5014 MutexAutoLock lock(mMutex); |
|
5015 |
|
5016 if (mStatus >= aStatus) { |
|
5017 MOZ_ASSERT(!mEventTarget); |
|
5018 return true; |
|
5019 } |
|
5020 |
|
5021 previousStatus = mStatus; |
|
5022 mStatus = aStatus; |
|
5023 |
|
5024 mEventTarget.swap(eventTarget); |
|
5025 } |
|
5026 |
|
5027 // Now that mStatus > Running, no-one can create a new WorkerEventTarget or |
|
5028 // WorkerCrossThreadDispatcher if we don't already have one. |
|
5029 if (eventTarget) { |
|
5030 // Since we'll no longer process events, make sure we no longer allow anyone |
|
5031 // to post them. We have to do this without mMutex held, since our mutex |
|
5032 // must be acquired *after* the WorkerEventTarget's mutex when they're both |
|
5033 // held. |
|
5034 eventTarget->Disable(); |
|
5035 eventTarget = nullptr; |
|
5036 } |
|
5037 |
|
5038 if (mCrossThreadDispatcher) { |
|
5039 // Since we'll no longer process events, make sure we no longer allow |
|
5040 // anyone to post them. We have to do this without mMutex held, since our |
|
5041 // mutex must be acquired *after* mCrossThreadDispatcher's mutex when |
|
5042 // they're both held. |
|
5043 mCrossThreadDispatcher->Forget(); |
|
5044 mCrossThreadDispatcher = nullptr; |
|
5045 } |
|
5046 |
|
5047 MOZ_ASSERT(previousStatus != Pending); |
|
5048 |
|
5049 MOZ_ASSERT(previousStatus >= Canceling || mKillTime.IsNull()); |
|
5050 |
|
5051 // Let all our features know the new status. |
|
5052 NotifyFeatures(aCx, aStatus); |
|
5053 |
|
5054 // If this is the first time our status has changed then we need to clear the |
|
5055 // main event queue. |
|
5056 if (previousStatus == Running) { |
|
5057 ClearMainEventQueue(WorkerRan); |
|
5058 } |
|
5059 |
|
5060 // If we've run the close handler, we don't need to do anything else. |
|
5061 if (mCloseHandlerFinished) { |
|
5062 return true; |
|
5063 } |
|
5064 |
|
5065 // If the worker script never ran, or failed to compile, we don't need to do |
|
5066 // anything else, except pretend that we ran the close handler. |
|
5067 if (!JS::CurrentGlobalOrNull(aCx)) { |
|
5068 mCloseHandlerStarted = true; |
|
5069 mCloseHandlerFinished = true; |
|
5070 return true; |
|
5071 } |
|
5072 |
|
5073 // If this is the first time our status has changed we also need to schedule |
|
5074 // the close handler unless we're being shut down. |
|
5075 if (previousStatus == Running && aStatus != Killing) { |
|
5076 MOZ_ASSERT(!mCloseHandlerStarted && !mCloseHandlerFinished); |
|
5077 |
|
5078 nsRefPtr<CloseEventRunnable> closeRunnable = new CloseEventRunnable(this); |
|
5079 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(closeRunnable))); |
|
5080 } |
|
5081 |
|
5082 if (aStatus == Closing) { |
|
5083 // Notify parent to stop sending us messages and balance our busy count. |
|
5084 nsRefPtr<CloseRunnable> runnable = new CloseRunnable(this); |
|
5085 if (!runnable->Dispatch(aCx)) { |
|
5086 return false; |
|
5087 } |
|
5088 |
|
5089 // Don't abort the script. |
|
5090 return true; |
|
5091 } |
|
5092 |
|
5093 if (aStatus == Terminating) { |
|
5094 // Only abort the script if we're not yet running the close handler. |
|
5095 return mCloseHandlerStarted; |
|
5096 } |
|
5097 |
|
5098 if (aStatus == Canceling) { |
|
5099 // We need to enforce a timeout on the close handler. |
|
5100 MOZ_ASSERT(previousStatus >= Running && previousStatus <= Terminating); |
|
5101 |
|
5102 uint32_t killSeconds = IsChromeWorker() ? |
|
5103 RuntimeService::GetChromeCloseHandlerTimeoutSeconds() : |
|
5104 RuntimeService::GetContentCloseHandlerTimeoutSeconds(); |
|
5105 |
|
5106 if (killSeconds) { |
|
5107 mKillTime = TimeStamp::Now() + TimeDuration::FromSeconds(killSeconds); |
|
5108 |
|
5109 if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) { |
|
5110 return false; |
|
5111 } |
|
5112 } |
|
5113 |
|
5114 // Only abort the script if we're not yet running the close handler. |
|
5115 return mCloseHandlerStarted; |
|
5116 } |
|
5117 |
|
5118 MOZ_ASSERT(aStatus == Killing); |
|
5119 |
|
5120 mKillTime = TimeStamp::Now(); |
|
5121 |
|
5122 if (mCloseHandlerStarted && !mCloseHandlerFinished) { |
|
5123 ScheduleKillCloseEventRunnable(aCx); |
|
5124 } |
|
5125 |
|
5126 // Always abort the script. |
|
5127 return false; |
|
5128 } |
|
5129 |
|
5130 bool |
|
5131 WorkerPrivate::ScheduleKillCloseEventRunnable(JSContext* aCx) |
|
5132 { |
|
5133 AssertIsOnWorkerThread(); |
|
5134 MOZ_ASSERT(!mKillTime.IsNull()); |
|
5135 |
|
5136 nsRefPtr<KillCloseEventRunnable> killCloseEventRunnable = |
|
5137 new KillCloseEventRunnable(this); |
|
5138 if (!killCloseEventRunnable->SetTimeout(aCx, RemainingRunTimeMS())) { |
|
5139 return false; |
|
5140 } |
|
5141 |
|
5142 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread( |
|
5143 killCloseEventRunnable))); |
|
5144 |
|
5145 return true; |
|
5146 } |
|
5147 |
|
5148 void |
|
5149 WorkerPrivate::ReportError(JSContext* aCx, const char* aMessage, |
|
5150 JSErrorReport* aReport) |
|
5151 { |
|
5152 AssertIsOnWorkerThread(); |
|
5153 |
|
5154 if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) { |
|
5155 return; |
|
5156 } |
|
5157 |
|
5158 NS_ASSERTION(mErrorHandlerRecursionCount == 0 || |
|
5159 mErrorHandlerRecursionCount == 1, |
|
5160 "Bad recursion logic!"); |
|
5161 |
|
5162 JS_ClearPendingException(aCx); |
|
5163 |
|
5164 nsString message, filename, line; |
|
5165 uint32_t lineNumber, columnNumber, flags, errorNumber; |
|
5166 |
|
5167 if (aReport) { |
|
5168 // ErrorEvent objects don't have a |name| field the way ES |Error| objects |
|
5169 // do. Traditionally (and mostly by accident), the |message| field of |
|
5170 // ErrorEvent has corresponded to |Name: Message| of the original Error |
|
5171 // object. Things have been cleaned up in the JS engine, so now we need to |
|
5172 // format this string explicitly. |
|
5173 JS::Rooted<JSString*> messageStr(aCx, |
|
5174 js::ErrorReportToString(aCx, aReport)); |
|
5175 if (messageStr) { |
|
5176 nsDependentJSString depStr; |
|
5177 if (depStr.init(aCx, messageStr)) { |
|
5178 message = depStr; |
|
5179 } |
|
5180 } |
|
5181 filename = NS_ConvertUTF8toUTF16(aReport->filename); |
|
5182 line = aReport->uclinebuf; |
|
5183 lineNumber = aReport->lineno; |
|
5184 columnNumber = aReport->uctokenptr - aReport->uclinebuf; |
|
5185 flags = aReport->flags; |
|
5186 errorNumber = aReport->errorNumber; |
|
5187 } |
|
5188 else { |
|
5189 lineNumber = columnNumber = errorNumber = 0; |
|
5190 flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag; |
|
5191 } |
|
5192 |
|
5193 if (message.IsEmpty()) { |
|
5194 message = NS_ConvertUTF8toUTF16(aMessage); |
|
5195 } |
|
5196 |
|
5197 mErrorHandlerRecursionCount++; |
|
5198 |
|
5199 // Don't want to run the scope's error handler if this is a recursive error or |
|
5200 // if there was an error in the close handler or if we ran out of memory. |
|
5201 bool fireAtScope = mErrorHandlerRecursionCount == 1 && |
|
5202 !mCloseHandlerStarted && |
|
5203 errorNumber != JSMSG_OUT_OF_MEMORY; |
|
5204 |
|
5205 if (!ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message, |
|
5206 filename, line, lineNumber, |
|
5207 columnNumber, flags, errorNumber, 0)) { |
|
5208 JS_ReportPendingException(aCx); |
|
5209 } |
|
5210 |
|
5211 mErrorHandlerRecursionCount--; |
|
5212 } |
|
5213 |
|
5214 int32_t |
|
5215 WorkerPrivate::SetTimeout(JSContext* aCx, |
|
5216 Function* aHandler, |
|
5217 const nsAString& aStringHandler, |
|
5218 int32_t aTimeout, |
|
5219 const Sequence<JS::Value>& aArguments, |
|
5220 bool aIsInterval, |
|
5221 ErrorResult& aRv) |
|
5222 { |
|
5223 AssertIsOnWorkerThread(); |
|
5224 |
|
5225 const int32_t timerId = mNextTimeoutId++; |
|
5226 |
|
5227 Status currentStatus; |
|
5228 { |
|
5229 MutexAutoLock lock(mMutex); |
|
5230 currentStatus = mStatus; |
|
5231 } |
|
5232 |
|
5233 // It's a script bug if setTimeout/setInterval are called from a close handler |
|
5234 // so throw an exception. |
|
5235 if (currentStatus == Closing) { |
|
5236 JS_ReportError(aCx, "Cannot schedule timeouts from the close handler!"); |
|
5237 } |
|
5238 |
|
5239 // If the worker is trying to call setTimeout/setInterval and the parent |
|
5240 // thread has initiated the close process then just silently fail. |
|
5241 if (currentStatus >= Closing) { |
|
5242 aRv.Throw(NS_ERROR_FAILURE); |
|
5243 return 0; |
|
5244 } |
|
5245 |
|
5246 nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo()); |
|
5247 newInfo->mIsInterval = aIsInterval; |
|
5248 newInfo->mId = timerId; |
|
5249 |
|
5250 if (MOZ_UNLIKELY(timerId == INT32_MAX)) { |
|
5251 NS_WARNING("Timeout ids overflowed!"); |
|
5252 mNextTimeoutId = 1; |
|
5253 } |
|
5254 |
|
5255 // Take care of the main argument. |
|
5256 if (aHandler) { |
|
5257 newInfo->mTimeoutCallable = JS::ObjectValue(*aHandler->Callable()); |
|
5258 } |
|
5259 else if (!aStringHandler.IsEmpty()) { |
|
5260 newInfo->mTimeoutString = aStringHandler; |
|
5261 } |
|
5262 else { |
|
5263 JS_ReportError(aCx, "Useless %s call (missing quotes around argument?)", |
|
5264 aIsInterval ? "setInterval" : "setTimeout"); |
|
5265 return 0; |
|
5266 } |
|
5267 |
|
5268 // See if any of the optional arguments were passed. |
|
5269 aTimeout = std::max(0, aTimeout); |
|
5270 newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout); |
|
5271 |
|
5272 uint32_t argc = aArguments.Length(); |
|
5273 if (argc && !newInfo->mTimeoutCallable.isUndefined()) { |
|
5274 nsTArray<JS::Heap<JS::Value>> extraArgVals(argc); |
|
5275 for (uint32_t index = 0; index < argc; index++) { |
|
5276 extraArgVals.AppendElement(aArguments[index]); |
|
5277 } |
|
5278 newInfo->mExtraArgVals.SwapElements(extraArgVals); |
|
5279 } |
|
5280 |
|
5281 newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval; |
|
5282 |
|
5283 if (!newInfo->mTimeoutString.IsEmpty()) { |
|
5284 const char* filenameChars; |
|
5285 uint32_t lineNumber; |
|
5286 if (nsJSUtils::GetCallingLocation(aCx, &filenameChars, &lineNumber)) { |
|
5287 newInfo->mFilename = filenameChars; |
|
5288 newInfo->mLineNumber = lineNumber; |
|
5289 } |
|
5290 else { |
|
5291 NS_WARNING("Failed to get calling location!"); |
|
5292 } |
|
5293 } |
|
5294 |
|
5295 nsAutoPtr<TimeoutInfo>* insertedInfo = |
|
5296 mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts)); |
|
5297 |
|
5298 // If the timeout we just made is set to fire next then we need to update the |
|
5299 // timer. |
|
5300 if (insertedInfo == mTimeouts.Elements()) { |
|
5301 nsresult rv; |
|
5302 |
|
5303 if (!mTimer) { |
|
5304 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); |
|
5305 if (NS_FAILED(rv)) { |
|
5306 aRv.Throw(rv); |
|
5307 return 0; |
|
5308 } |
|
5309 |
|
5310 nsRefPtr<TimerRunnable> runnable = new TimerRunnable(this); |
|
5311 |
|
5312 nsRefPtr<TimerThreadEventTarget> target = |
|
5313 new TimerThreadEventTarget(this, runnable); |
|
5314 |
|
5315 rv = timer->SetTarget(target); |
|
5316 if (NS_FAILED(rv)) { |
|
5317 aRv.Throw(rv); |
|
5318 return 0; |
|
5319 } |
|
5320 |
|
5321 timer.swap(mTimer); |
|
5322 } |
|
5323 |
|
5324 if (!mTimerRunning) { |
|
5325 if (!ModifyBusyCountFromWorker(aCx, true)) { |
|
5326 aRv.Throw(NS_ERROR_FAILURE); |
|
5327 return 0; |
|
5328 } |
|
5329 mTimerRunning = true; |
|
5330 } |
|
5331 |
|
5332 if (!RescheduleTimeoutTimer(aCx)) { |
|
5333 aRv.Throw(NS_ERROR_FAILURE); |
|
5334 return 0; |
|
5335 } |
|
5336 } |
|
5337 |
|
5338 return timerId; |
|
5339 } |
|
5340 |
|
5341 void |
|
5342 WorkerPrivate::ClearTimeout(int32_t aId) |
|
5343 { |
|
5344 AssertIsOnWorkerThread(); |
|
5345 |
|
5346 if (!mTimeouts.IsEmpty()) { |
|
5347 NS_ASSERTION(mTimerRunning, "Huh?!"); |
|
5348 |
|
5349 for (uint32_t index = 0; index < mTimeouts.Length(); index++) { |
|
5350 nsAutoPtr<TimeoutInfo>& info = mTimeouts[index]; |
|
5351 if (info->mId == aId) { |
|
5352 info->mCanceled = true; |
|
5353 break; |
|
5354 } |
|
5355 } |
|
5356 } |
|
5357 } |
|
5358 |
|
5359 bool |
|
5360 WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) |
|
5361 { |
|
5362 AssertIsOnWorkerThread(); |
|
5363 |
|
5364 // We may be called recursively (e.g. close() inside a timeout) or we could |
|
5365 // have been canceled while this event was pending, bail out if there is |
|
5366 // nothing to do. |
|
5367 if (mRunningExpiredTimeouts || !mTimerRunning) { |
|
5368 return true; |
|
5369 } |
|
5370 |
|
5371 NS_ASSERTION(mTimer, "Must have a timer!"); |
|
5372 NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!"); |
|
5373 |
|
5374 bool retval = true; |
|
5375 |
|
5376 AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts); |
|
5377 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); |
|
5378 |
|
5379 // We want to make sure to run *something*, even if the timer fired a little |
|
5380 // early. Fudge the value of now to at least include the first timeout. |
|
5381 const TimeStamp now = std::max(TimeStamp::Now(), mTimeouts[0]->mTargetTime); |
|
5382 |
|
5383 nsAutoTArray<TimeoutInfo*, 10> expiredTimeouts; |
|
5384 for (uint32_t index = 0; index < mTimeouts.Length(); index++) { |
|
5385 nsAutoPtr<TimeoutInfo>& info = mTimeouts[index]; |
|
5386 if (info->mTargetTime > now) { |
|
5387 break; |
|
5388 } |
|
5389 expiredTimeouts.AppendElement(info); |
|
5390 } |
|
5391 |
|
5392 // Guard against recursion. |
|
5393 mRunningExpiredTimeouts = true; |
|
5394 |
|
5395 // Run expired timeouts. |
|
5396 for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) { |
|
5397 TimeoutInfo*& info = expiredTimeouts[index]; |
|
5398 |
|
5399 if (info->mCanceled) { |
|
5400 continue; |
|
5401 } |
|
5402 |
|
5403 // Always call JS_ReportPendingException if something fails, and if |
|
5404 // JS_ReportPendingException returns false (i.e. uncatchable exception) then |
|
5405 // break out of the loop. |
|
5406 |
|
5407 if (!info->mTimeoutCallable.isUndefined()) { |
|
5408 JS::Rooted<JS::Value> rval(aCx); |
|
5409 JS::HandleValueArray args = |
|
5410 JS::HandleValueArray::fromMarkedLocation(info->mExtraArgVals.Length(), |
|
5411 info->mExtraArgVals.Elements()->address()); |
|
5412 JS::Rooted<JS::Value> callable(aCx, info->mTimeoutCallable); |
|
5413 if (!JS_CallFunctionValue(aCx, global, callable, args, &rval) && |
|
5414 !JS_ReportPendingException(aCx)) { |
|
5415 retval = false; |
|
5416 break; |
|
5417 } |
|
5418 } |
|
5419 else { |
|
5420 nsString expression = info->mTimeoutString; |
|
5421 |
|
5422 JS::CompileOptions options(aCx); |
|
5423 options.setFileAndLine(info->mFilename.get(), info->mLineNumber); |
|
5424 |
|
5425 if ((expression.IsEmpty() || |
|
5426 !JS::Evaluate(aCx, global, options, expression.get(), expression.Length())) && |
|
5427 !JS_ReportPendingException(aCx)) { |
|
5428 retval = false; |
|
5429 break; |
|
5430 } |
|
5431 } |
|
5432 |
|
5433 NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!"); |
|
5434 } |
|
5435 |
|
5436 // No longer possible to be called recursively. |
|
5437 mRunningExpiredTimeouts = false; |
|
5438 |
|
5439 // Now remove canceled and expired timeouts from the main list. |
|
5440 // NB: The timeouts present in expiredTimeouts must have the same order |
|
5441 // with respect to each other in mTimeouts. That is, mTimeouts is just |
|
5442 // expiredTimeouts with extra elements inserted. There may be unexpired |
|
5443 // timeouts that have been inserted between the expired timeouts if the |
|
5444 // timeout event handler called setTimeout/setInterval. |
|
5445 for (uint32_t index = 0, expiredTimeoutIndex = 0, |
|
5446 expiredTimeoutLength = expiredTimeouts.Length(); |
|
5447 index < mTimeouts.Length(); ) { |
|
5448 nsAutoPtr<TimeoutInfo>& info = mTimeouts[index]; |
|
5449 if ((expiredTimeoutIndex < expiredTimeoutLength && |
|
5450 info == expiredTimeouts[expiredTimeoutIndex] && |
|
5451 ++expiredTimeoutIndex) || |
|
5452 info->mCanceled) { |
|
5453 if (info->mIsInterval && !info->mCanceled) { |
|
5454 // Reschedule intervals. |
|
5455 info->mTargetTime = info->mTargetTime + info->mInterval; |
|
5456 // Don't resort the list here, we'll do that at the end. |
|
5457 ++index; |
|
5458 } |
|
5459 else { |
|
5460 mTimeouts.RemoveElement(info); |
|
5461 } |
|
5462 } |
|
5463 else { |
|
5464 // If info did not match the current entry in expiredTimeouts, it |
|
5465 // shouldn't be there at all. |
|
5466 NS_ASSERTION(!expiredTimeouts.Contains(info), |
|
5467 "Our timeouts are out of order!"); |
|
5468 ++index; |
|
5469 } |
|
5470 } |
|
5471 |
|
5472 mTimeouts.Sort(comparator); |
|
5473 |
|
5474 // Either signal the parent that we're no longer using timeouts or reschedule |
|
5475 // the timer. |
|
5476 if (mTimeouts.IsEmpty()) { |
|
5477 if (!ModifyBusyCountFromWorker(aCx, false)) { |
|
5478 retval = false; |
|
5479 } |
|
5480 mTimerRunning = false; |
|
5481 } |
|
5482 else if (retval && !RescheduleTimeoutTimer(aCx)) { |
|
5483 retval = false; |
|
5484 } |
|
5485 |
|
5486 return retval; |
|
5487 } |
|
5488 |
|
5489 bool |
|
5490 WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx) |
|
5491 { |
|
5492 AssertIsOnWorkerThread(); |
|
5493 NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!"); |
|
5494 NS_ASSERTION(mTimer, "Should have a timer!"); |
|
5495 |
|
5496 double delta = |
|
5497 (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds(); |
|
5498 uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0; |
|
5499 |
|
5500 nsresult rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, delay, |
|
5501 nsITimer::TYPE_ONE_SHOT); |
|
5502 if (NS_FAILED(rv)) { |
|
5503 JS_ReportError(aCx, "Failed to start timer!"); |
|
5504 return false; |
|
5505 } |
|
5506 |
|
5507 return true; |
|
5508 } |
|
5509 |
|
5510 void |
|
5511 WorkerPrivate::UpdateRuntimeAndContextOptionsInternal( |
|
5512 JSContext* aCx, |
|
5513 const JS::RuntimeOptions& aRuntimeOptions, |
|
5514 const JS::ContextOptions& aContentCxOptions, |
|
5515 const JS::ContextOptions& aChromeCxOptions) |
|
5516 { |
|
5517 AssertIsOnWorkerThread(); |
|
5518 |
|
5519 JS::RuntimeOptionsRef(aCx) = aRuntimeOptions; |
|
5520 JS::ContextOptionsRef(aCx) = IsChromeWorker() ? aChromeCxOptions : aContentCxOptions; |
|
5521 |
|
5522 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { |
|
5523 mChildWorkers[index]->UpdateRuntimeAndContextOptions(aCx, aRuntimeOptions, |
|
5524 aContentCxOptions, |
|
5525 aChromeCxOptions); |
|
5526 } |
|
5527 } |
|
5528 |
|
5529 void |
|
5530 WorkerPrivate::UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue) |
|
5531 { |
|
5532 AssertIsOnWorkerThread(); |
|
5533 MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT); |
|
5534 |
|
5535 mPreferences[aPref] = aValue; |
|
5536 |
|
5537 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { |
|
5538 mChildWorkers[index]->UpdatePreference(aCx, aPref, aValue); |
|
5539 } |
|
5540 } |
|
5541 |
|
5542 void |
|
5543 WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, |
|
5544 JSGCParamKey aKey, |
|
5545 uint32_t aValue) |
|
5546 { |
|
5547 AssertIsOnWorkerThread(); |
|
5548 |
|
5549 // XXX aValue might be 0 here (telling us to unset a previous value for child |
|
5550 // workers). Calling JS_SetGCParameter with a value of 0 isn't actually |
|
5551 // supported though. We really need some way to revert to a default value |
|
5552 // here. |
|
5553 if (aValue) { |
|
5554 JS_SetGCParameter(JS_GetRuntime(aCx), aKey, aValue); |
|
5555 } |
|
5556 |
|
5557 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { |
|
5558 mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aCx, aKey, aValue); |
|
5559 } |
|
5560 } |
|
5561 |
|
5562 #ifdef JS_GC_ZEAL |
|
5563 void |
|
5564 WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, |
|
5565 uint32_t aFrequency) |
|
5566 { |
|
5567 AssertIsOnWorkerThread(); |
|
5568 |
|
5569 JS_SetGCZeal(aCx, aGCZeal, aFrequency); |
|
5570 |
|
5571 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { |
|
5572 mChildWorkers[index]->UpdateGCZeal(aCx, aGCZeal, aFrequency); |
|
5573 } |
|
5574 } |
|
5575 #endif |
|
5576 |
|
5577 void |
|
5578 WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking, |
|
5579 bool aCollectChildren) |
|
5580 { |
|
5581 AssertIsOnWorkerThread(); |
|
5582 |
|
5583 if (!JS::CurrentGlobalOrNull(aCx)) { |
|
5584 // We haven't compiled anything yet. Just bail out. |
|
5585 return; |
|
5586 } |
|
5587 |
|
5588 if (aShrinking || aCollectChildren) { |
|
5589 JSRuntime* rt = JS_GetRuntime(aCx); |
|
5590 JS::PrepareForFullGC(rt); |
|
5591 |
|
5592 if (aShrinking) { |
|
5593 JS::ShrinkingGC(rt, JS::gcreason::DOM_WORKER); |
|
5594 |
|
5595 if (!aCollectChildren) { |
|
5596 LOG(("Worker %p collected idle garbage\n", this)); |
|
5597 } |
|
5598 } |
|
5599 else { |
|
5600 JS::GCForReason(rt, JS::gcreason::DOM_WORKER); |
|
5601 LOG(("Worker %p collected garbage\n", this)); |
|
5602 } |
|
5603 } |
|
5604 else { |
|
5605 JS_MaybeGC(aCx); |
|
5606 LOG(("Worker %p collected periodic garbage\n", this)); |
|
5607 } |
|
5608 |
|
5609 if (aCollectChildren) { |
|
5610 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { |
|
5611 mChildWorkers[index]->GarbageCollect(aCx, aShrinking); |
|
5612 } |
|
5613 } |
|
5614 } |
|
5615 |
|
5616 void |
|
5617 WorkerPrivate::CycleCollectInternal(JSContext* aCx, bool aCollectChildren) |
|
5618 { |
|
5619 AssertIsOnWorkerThread(); |
|
5620 |
|
5621 nsCycleCollector_collect(nullptr); |
|
5622 |
|
5623 if (aCollectChildren) { |
|
5624 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { |
|
5625 mChildWorkers[index]->CycleCollect(aCx, /* dummy = */ false); |
|
5626 } |
|
5627 } |
|
5628 } |
|
5629 |
|
5630 void |
|
5631 WorkerPrivate::SetThread(nsIThread* aThread) |
|
5632 { |
|
5633 #ifdef DEBUG |
|
5634 if (aThread) { |
|
5635 bool isOnCurrentThread; |
|
5636 MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread))); |
|
5637 MOZ_ASSERT(isOnCurrentThread); |
|
5638 |
|
5639 MOZ_ASSERT(!mPRThread); |
|
5640 mPRThread = PRThreadFromThread(aThread); |
|
5641 MOZ_ASSERT(mPRThread); |
|
5642 } |
|
5643 else { |
|
5644 MOZ_ASSERT(mPRThread); |
|
5645 } |
|
5646 #endif |
|
5647 |
|
5648 nsCOMPtr<nsIThread> doomedThread; |
|
5649 |
|
5650 { // Scope so that |doomedThread| is released without holding the lock. |
|
5651 MutexAutoLock lock(mMutex); |
|
5652 |
|
5653 if (aThread) { |
|
5654 MOZ_ASSERT(!mThread); |
|
5655 MOZ_ASSERT(mStatus == Pending); |
|
5656 |
|
5657 mThread = aThread; |
|
5658 |
|
5659 if (!mPreStartRunnables.IsEmpty()) { |
|
5660 for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) { |
|
5661 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThread->Dispatch( |
|
5662 mPreStartRunnables[index], |
|
5663 NS_DISPATCH_NORMAL))); |
|
5664 } |
|
5665 mPreStartRunnables.Clear(); |
|
5666 } |
|
5667 } |
|
5668 else { |
|
5669 MOZ_ASSERT(mThread); |
|
5670 mThread.swap(doomedThread); |
|
5671 } |
|
5672 } |
|
5673 } |
|
5674 |
|
5675 WorkerCrossThreadDispatcher* |
|
5676 WorkerPrivate::GetCrossThreadDispatcher() |
|
5677 { |
|
5678 MutexAutoLock lock(mMutex); |
|
5679 |
|
5680 if (!mCrossThreadDispatcher && mStatus <= Running) { |
|
5681 mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this); |
|
5682 } |
|
5683 |
|
5684 return mCrossThreadDispatcher; |
|
5685 } |
|
5686 |
|
5687 void |
|
5688 WorkerPrivate::BeginCTypesCall() |
|
5689 { |
|
5690 AssertIsOnWorkerThread(); |
|
5691 |
|
5692 // Don't try to GC while we're blocked in a ctypes call. |
|
5693 SetGCTimerMode(NoTimer); |
|
5694 |
|
5695 MutexAutoLock lock(mMutex); |
|
5696 |
|
5697 NS_ASSERTION(!mBlockedForMemoryReporter, |
|
5698 "Can't be blocked in more than one place at the same time!"); |
|
5699 |
|
5700 // Let the main thread know that the worker is effectively blocked while in |
|
5701 // this ctypes call. It isn't technically true (obviously the call could do |
|
5702 // non-blocking things), but we're assuming that ctypes can't call back into |
|
5703 // JSAPI here and therefore any work the ctypes call does will not alter the |
|
5704 // data structures of this JS runtime. |
|
5705 mBlockedForMemoryReporter = true; |
|
5706 |
|
5707 // The main thread may be waiting on us so it must be notified. |
|
5708 mMemoryReportCondVar.Notify(); |
|
5709 } |
|
5710 |
|
5711 void |
|
5712 WorkerPrivate::EndCTypesCall() |
|
5713 { |
|
5714 AssertIsOnWorkerThread(); |
|
5715 |
|
5716 { |
|
5717 MutexAutoLock lock(mMutex); |
|
5718 |
|
5719 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); |
|
5720 |
|
5721 // Don't continue until the memory reporter has finished. |
|
5722 while (mMemoryReporterRunning) { |
|
5723 mMemoryReportCondVar.Wait(); |
|
5724 } |
|
5725 |
|
5726 // No need to notify the main thread here as it shouldn't be waiting to see |
|
5727 // this state. |
|
5728 mBlockedForMemoryReporter = false; |
|
5729 } |
|
5730 |
|
5731 // Make sure the periodic timer is running before we start running JS again. |
|
5732 SetGCTimerMode(PeriodicTimer); |
|
5733 } |
|
5734 |
|
5735 bool |
|
5736 WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial) |
|
5737 { |
|
5738 AssertIsOnWorkerThread(); |
|
5739 |
|
5740 NS_ASSERTION(!mWorkerPorts.GetWeak(aMessagePortSerial), |
|
5741 "Already have this port registered!"); |
|
5742 |
|
5743 WorkerGlobalScope* globalScope = GlobalScope(); |
|
5744 |
|
5745 JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper()); |
|
5746 MOZ_ASSERT(jsGlobal); |
|
5747 |
|
5748 nsRefPtr<MessagePort> port = new MessagePort(this, aMessagePortSerial); |
|
5749 |
|
5750 GlobalObject globalObject(aCx, jsGlobal); |
|
5751 if (globalObject.Failed()) { |
|
5752 return false; |
|
5753 } |
|
5754 |
|
5755 RootedDictionary<MessageEventInit> init(aCx); |
|
5756 init.mBubbles = false; |
|
5757 init.mCancelable = false; |
|
5758 init.mSource.SetValue().SetAsMessagePort() = port; |
|
5759 |
|
5760 ErrorResult rv; |
|
5761 |
|
5762 nsRefPtr<MessageEvent> event = |
|
5763 MessageEvent::Constructor(globalObject, aCx, |
|
5764 NS_LITERAL_STRING("connect"), init, rv); |
|
5765 |
|
5766 event->SetTrusted(true); |
|
5767 |
|
5768 nsTArray<nsRefPtr<MessagePortBase>> ports; |
|
5769 ports.AppendElement(port); |
|
5770 |
|
5771 nsRefPtr<MessagePortList> portList = |
|
5772 new MessagePortList(static_cast<nsIDOMEventTarget*>(globalScope), ports); |
|
5773 event->SetPorts(portList); |
|
5774 |
|
5775 mWorkerPorts.Put(aMessagePortSerial, port); |
|
5776 |
|
5777 nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event); |
|
5778 |
|
5779 nsEventStatus dummy = nsEventStatus_eIgnore; |
|
5780 globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy); |
|
5781 return true; |
|
5782 } |
|
5783 |
|
5784 void |
|
5785 WorkerPrivate::DisconnectMessagePort(uint64_t aMessagePortSerial) |
|
5786 { |
|
5787 AssertIsOnWorkerThread(); |
|
5788 |
|
5789 mWorkerPorts.Remove(aMessagePortSerial); |
|
5790 } |
|
5791 |
|
5792 workers::MessagePort* |
|
5793 WorkerPrivate::GetMessagePort(uint64_t aMessagePortSerial) |
|
5794 { |
|
5795 AssertIsOnWorkerThread(); |
|
5796 |
|
5797 nsRefPtr<MessagePort> port; |
|
5798 if (mWorkerPorts.Get(aMessagePortSerial, getter_AddRefs(port))) { |
|
5799 return port; |
|
5800 } |
|
5801 |
|
5802 return nullptr; |
|
5803 } |
|
5804 |
|
5805 JSObject* |
|
5806 WorkerPrivate::CreateGlobalScope(JSContext* aCx) |
|
5807 { |
|
5808 AssertIsOnWorkerThread(); |
|
5809 |
|
5810 nsRefPtr<WorkerGlobalScope> globalScope; |
|
5811 if (IsSharedWorker()) { |
|
5812 globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName()); |
|
5813 } |
|
5814 else { |
|
5815 globalScope = new DedicatedWorkerGlobalScope(this); |
|
5816 } |
|
5817 |
|
5818 JS::Rooted<JSObject*> global(aCx, globalScope->WrapGlobalObject(aCx)); |
|
5819 NS_ENSURE_TRUE(global, nullptr); |
|
5820 |
|
5821 JSAutoCompartment ac(aCx, global); |
|
5822 |
|
5823 if (!RegisterBindings(aCx, global)) { |
|
5824 return nullptr; |
|
5825 } |
|
5826 |
|
5827 mScope = globalScope.forget(); |
|
5828 |
|
5829 JS_FireOnNewGlobalObject(aCx, global); |
|
5830 |
|
5831 return global; |
|
5832 } |
|
5833 |
|
5834 #ifdef DEBUG |
|
5835 |
|
5836 void |
|
5837 WorkerPrivate::AssertIsOnWorkerThread() const |
|
5838 { |
|
5839 // This is much more complicated than it needs to be but we can't use mThread |
|
5840 // because it must be protected by mMutex and sometimes this method is called |
|
5841 // when mMutex is already locked. This method should always work. |
|
5842 MOZ_ASSERT(mPRThread, |
|
5843 "AssertIsOnWorkerThread() called before a thread was assigned!"); |
|
5844 |
|
5845 MOZ_ASSERT(nsThreadManager::get()); |
|
5846 |
|
5847 nsCOMPtr<nsIThread> thread; |
|
5848 nsresult rv = |
|
5849 nsThreadManager::get()->GetThreadFromPRThread(mPRThread, |
|
5850 getter_AddRefs(thread)); |
|
5851 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
5852 MOZ_ASSERT(thread); |
|
5853 |
|
5854 bool current; |
|
5855 rv = thread->IsOnCurrentThread(¤t); |
|
5856 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
5857 MOZ_ASSERT(current, "Wrong thread!"); |
|
5858 } |
|
5859 |
|
5860 #endif // DEBUG |
|
5861 |
|
5862 NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable) |
|
5863 |
|
5864 template <class Derived> |
|
5865 NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget) |
|
5866 |
|
5867 template <class Derived> |
|
5868 NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget) |
|
5869 |
|
5870 template <class Derived> |
|
5871 NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget) |
|
5872 NS_INTERFACE_MAP_ENTRY(nsIEventTarget) |
|
5873 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
5874 #ifdef DEBUG |
|
5875 // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its |
|
5876 // result. |
|
5877 if (aIID.Equals(kDEBUGWorkerEventTargetIID)) { |
|
5878 *aInstancePtr = this; |
|
5879 return NS_OK; |
|
5880 } |
|
5881 else |
|
5882 #endif |
|
5883 NS_INTERFACE_MAP_END |
|
5884 |
|
5885 template <class Derived> |
|
5886 NS_IMETHODIMP |
|
5887 WorkerPrivateParent<Derived>:: |
|
5888 EventTarget::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) |
|
5889 { |
|
5890 // May be called on any thread! |
|
5891 |
|
5892 // Workers only support asynchronous dispatch for now. |
|
5893 if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { |
|
5894 return NS_ERROR_UNEXPECTED; |
|
5895 } |
|
5896 |
|
5897 nsRefPtr<WorkerRunnable> workerRunnable; |
|
5898 |
|
5899 MutexAutoLock lock(mMutex); |
|
5900 |
|
5901 if (!mWorkerPrivate) { |
|
5902 NS_WARNING("A runnable was posted to a worker that is already shutting " |
|
5903 "down!"); |
|
5904 return NS_ERROR_UNEXPECTED; |
|
5905 } |
|
5906 |
|
5907 if (aRunnable) { |
|
5908 workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable); |
|
5909 } |
|
5910 |
|
5911 nsresult rv = |
|
5912 mWorkerPrivate->DispatchPrivate(workerRunnable, mNestedEventTarget); |
|
5913 if (NS_WARN_IF(NS_FAILED(rv))) { |
|
5914 return rv; |
|
5915 } |
|
5916 |
|
5917 return NS_OK; |
|
5918 } |
|
5919 |
|
5920 template <class Derived> |
|
5921 NS_IMETHODIMP |
|
5922 WorkerPrivateParent<Derived>:: |
|
5923 EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) |
|
5924 { |
|
5925 // May be called on any thread! |
|
5926 |
|
5927 MOZ_ASSERT(aIsOnCurrentThread); |
|
5928 |
|
5929 MutexAutoLock lock(mMutex); |
|
5930 |
|
5931 if (!mWorkerPrivate) { |
|
5932 NS_WARNING("A worker's event target was used after the worker has !"); |
|
5933 return NS_ERROR_UNEXPECTED; |
|
5934 } |
|
5935 |
|
5936 nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread); |
|
5937 if (NS_WARN_IF(NS_FAILED(rv))) { |
|
5938 return rv; |
|
5939 } |
|
5940 |
|
5941 return NS_OK; |
|
5942 } |
|
5943 |
|
5944 BEGIN_WORKERS_NAMESPACE |
|
5945 |
|
5946 WorkerCrossThreadDispatcher* |
|
5947 GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker) |
|
5948 { |
|
5949 if (!aWorker.isObject()) { |
|
5950 return nullptr; |
|
5951 } |
|
5952 |
|
5953 WorkerPrivate* w = nullptr; |
|
5954 UNWRAP_OBJECT(Worker, &aWorker.toObject(), w); |
|
5955 MOZ_ASSERT(w); |
|
5956 return w->GetCrossThreadDispatcher(); |
|
5957 } |
|
5958 |
|
5959 JSStructuredCloneCallbacks* |
|
5960 WorkerStructuredCloneCallbacks(bool aMainRuntime) |
|
5961 { |
|
5962 return aMainRuntime ? |
|
5963 &gMainThreadWorkerStructuredCloneCallbacks : |
|
5964 &gWorkerStructuredCloneCallbacks; |
|
5965 } |
|
5966 |
|
5967 JSStructuredCloneCallbacks* |
|
5968 ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime) |
|
5969 { |
|
5970 return aMainRuntime ? |
|
5971 &gMainThreadChromeWorkerStructuredCloneCallbacks : |
|
5972 &gChromeWorkerStructuredCloneCallbacks; |
|
5973 } |
|
5974 |
|
5975 // Force instantiation. |
|
5976 template class WorkerPrivateParent<WorkerPrivate>; |
|
5977 |
|
5978 END_WORKERS_NAMESPACE |