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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Web Workers.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * The Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2011
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Ben Turner <bent.mozilla@gmail.com> (Original Author)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "WorkerPrivate.h"
41 :
42 : #include "mozIThirdPartyUtil.h"
43 : #include "nsIClassInfo.h"
44 : #include "nsIConsoleService.h"
45 : #include "nsIDOMFile.h"
46 : #include "nsIDocument.h"
47 : #include "nsIJSContextStack.h"
48 : #include "nsIMemoryReporter.h"
49 : #include "nsIScriptError.h"
50 : #include "nsIScriptGlobalObject.h"
51 : #include "nsIScriptSecurityManager.h"
52 : #include "nsPIDOMWindow.h"
53 : #include "nsITextToSubURI.h"
54 : #include "nsITimer.h"
55 : #include "nsIURI.h"
56 : #include "nsIURL.h"
57 : #include "nsIXPConnect.h"
58 :
59 : #include "jsfriendapi.h"
60 : #include "jsdbgapi.h"
61 : #include "jsfriendapi.h"
62 : #include "jsprf.h"
63 : #include "js/MemoryMetrics.h"
64 :
65 : #include "nsAlgorithm.h"
66 : #include "nsContentUtils.h"
67 : #include "nsDOMClassInfo.h"
68 : #include "nsDOMJSUtils.h"
69 : #include "nsGUIEvent.h"
70 : #include "nsJSEnvironment.h"
71 : #include "nsJSUtils.h"
72 : #include "nsNetUtil.h"
73 : #include "nsThreadUtils.h"
74 : #include "xpcpublic.h"
75 :
76 : #include "Events.h"
77 : #include "Exceptions.h"
78 : #include "File.h"
79 : #include "Principal.h"
80 : #include "RuntimeService.h"
81 : #include "ScriptLoader.h"
82 : #include "Worker.h"
83 : #include "WorkerFeature.h"
84 : #include "WorkerScope.h"
85 : #ifdef ANDROID
86 : #include <android/log.h>
87 : #endif
88 :
89 : #include "WorkerInlines.h"
90 :
91 : #if 0 // Define to run GC more often.
92 : #define EXTRA_GC
93 : #endif
94 :
95 : // GC will run once every thirty seconds during normal execution.
96 : #define NORMAL_GC_TIMER_DELAY_MS 30000
97 :
98 : // GC will run five seconds after the last event is processed.
99 : #define IDLE_GC_TIMER_DELAY_MS 5000
100 :
101 : using mozilla::MutexAutoLock;
102 : using mozilla::TimeDuration;
103 : using mozilla::TimeStamp;
104 : using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
105 : using mozilla::xpconnect::memory::ReportJSRuntimeExplicitTreeStats;
106 :
107 : USING_WORKERS_NAMESPACE
108 :
109 : namespace {
110 :
111 : const char gErrorChars[] = "error";
112 : const char gMessageChars[] = "message";
113 :
114 : template <class T>
115 : class AutoPtrComparator
116 : {
117 : typedef nsAutoPtr<T> A;
118 : typedef T* B;
119 :
120 : public:
121 0 : bool Equals(const A& a, const B& b) const {
122 0 : return a && b ? *a == *b : !a && !b ? true : false;
123 : }
124 0 : bool LessThan(const A& a, const B& b) const {
125 0 : return a && b ? *a < *b : b ? true : false;
126 : }
127 : };
128 :
129 : template <class T>
130 : inline AutoPtrComparator<T>
131 0 : GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
132 : {
133 0 : return AutoPtrComparator<T>();
134 : }
135 :
136 : // Specialize this if there's some class that has multiple nsISupports bases.
137 : template <class T>
138 : struct ISupportsBaseInfo
139 : {
140 : typedef T ISupportsBase;
141 : };
142 :
143 : template <template <class> class SmartPtr, class T>
144 : inline void
145 0 : SwapToISupportsArray(SmartPtr<T>& aSrc,
146 : nsTArray<nsCOMPtr<nsISupports> >& aDest)
147 : {
148 0 : nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
149 :
150 0 : T* raw = nsnull;
151 0 : aSrc.swap(raw);
152 :
153 : nsISupports* rawSupports =
154 0 : static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
155 0 : dest->swap(rawSupports);
156 0 : }
157 :
158 0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
159 :
160 : struct WorkerJSRuntimeStats : public JS::RuntimeStats
161 0 : {
162 0 : WorkerJSRuntimeStats()
163 0 : : JS::RuntimeStats(JsWorkerMallocSizeOf) { }
164 :
165 0 : virtual void initExtraCompartmentStats(JSCompartment *c,
166 : JS::CompartmentStats *cstats) MOZ_OVERRIDE
167 : {
168 0 : MOZ_ASSERT(!cstats->extra);
169 :
170 : // ReportJSRuntimeExplicitTreeStats expects that cstats->extra is a char pointer
171 0 : const char *name = js::IsAtomsCompartment(c) ? "Web Worker Atoms" : "Web Worker";
172 0 : cstats->extra = const_cast<char *>(name);
173 0 : }
174 : };
175 :
176 : class WorkerMemoryReporter : public nsIMemoryMultiReporter
177 0 : {
178 : WorkerPrivate* mWorkerPrivate;
179 : nsCString mAddressString;
180 : nsCString mPathPrefix;
181 :
182 : public:
183 : NS_DECL_ISUPPORTS
184 :
185 0 : WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate)
186 0 : : mWorkerPrivate(aWorkerPrivate)
187 : {
188 0 : aWorkerPrivate->AssertIsOnWorkerThread();
189 :
190 0 : nsCString escapedDomain(aWorkerPrivate->Domain());
191 0 : escapedDomain.ReplaceChar('/', '\\');
192 :
193 0 : NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
194 0 : escapedURL.ReplaceChar('/', '\\');
195 :
196 : {
197 : // 64bit address plus '0x' plus null terminator.
198 : char address[21];
199 : uint32_t addressSize =
200 0 : JS_snprintf(address, sizeof(address), "0x%llx", aWorkerPrivate);
201 0 : if (addressSize != uint32_t(-1)) {
202 0 : mAddressString.Assign(address, addressSize);
203 : }
204 : else {
205 0 : NS_WARNING("JS_snprintf failed!");
206 0 : mAddressString.AssignLiteral("<unknown address>");
207 : }
208 : }
209 :
210 0 : mPathPrefix = NS_LITERAL_CSTRING("explicit/dom/workers(") +
211 0 : escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
212 0 : escapedURL + NS_LITERAL_CSTRING(", ") + mAddressString +
213 0 : NS_LITERAL_CSTRING(")/");
214 0 : }
215 :
216 : nsresult
217 0 : CollectForRuntime(bool aIsQuick, void* aData)
218 : {
219 0 : AssertIsOnMainThread();
220 :
221 0 : if (mWorkerPrivate) {
222 : bool disabled;
223 0 : if (!mWorkerPrivate->BlockAndCollectRuntimeStats(aIsQuick, aData, &disabled)) {
224 0 : return NS_ERROR_FAILURE;
225 : }
226 :
227 : // Don't ever try to talk to the worker again.
228 0 : if (disabled) {
229 : #ifdef DEBUG
230 : {
231 0 : nsCAutoString message("Unable to report memory for ");
232 0 : if (mWorkerPrivate->IsChromeWorker()) {
233 0 : message.AppendLiteral("Chrome");
234 : }
235 0 : message += NS_LITERAL_CSTRING("Worker (") + mAddressString +
236 0 : NS_LITERAL_CSTRING(")! It is either using ctypes or is in "
237 0 : "the process of being destroyed");
238 0 : NS_WARNING(message.get());
239 : }
240 : #endif
241 0 : mWorkerPrivate = nsnull;
242 : }
243 : }
244 0 : return NS_OK;
245 : }
246 :
247 0 : NS_IMETHOD GetName(nsACString &aName)
248 : {
249 0 : aName.AssignLiteral("workers");
250 0 : return NS_OK;
251 : }
252 :
253 : NS_IMETHOD
254 0 : CollectReports(nsIMemoryMultiReporterCallback* aCallback,
255 : nsISupports* aClosure)
256 : {
257 0 : AssertIsOnMainThread();
258 :
259 0 : WorkerJSRuntimeStats rtStats;
260 0 : nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
261 0 : if (NS_FAILED(rv)) {
262 0 : return rv;
263 : }
264 :
265 : // Always report, even if we're disabled, so that we at least get an entry
266 : // in about::memory.
267 0 : rv = ReportJSRuntimeExplicitTreeStats(rtStats, mPathPrefix, aCallback, aClosure);
268 0 : if (NS_FAILED(rv)) {
269 0 : return rv;
270 : }
271 :
272 0 : return NS_OK;
273 : }
274 :
275 : NS_IMETHOD
276 0 : GetExplicitNonHeap(PRInt64 *aAmount)
277 : {
278 0 : AssertIsOnMainThread();
279 :
280 0 : return CollectForRuntime(/* isQuick = */true, aAmount);
281 : }
282 : };
283 :
284 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter)
285 :
286 : struct WorkerStructuredCloneCallbacks
287 : {
288 : static JSObject*
289 0 : Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
290 : uint32_t aData, void* aClosure)
291 : {
292 : // See if object is a nsIDOMFile pointer.
293 0 : if (aTag == DOMWORKER_SCTAG_FILE) {
294 0 : JS_ASSERT(!aData);
295 :
296 : nsIDOMFile* file;
297 0 : if (JS_ReadBytes(aReader, &file, sizeof(file))) {
298 0 : JS_ASSERT(file);
299 :
300 : #ifdef DEBUG
301 : {
302 : // File should not be mutable.
303 0 : nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
304 : bool isMutable;
305 0 : NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
306 : !isMutable,
307 : "Only immutable file should be passed to worker");
308 : }
309 : #endif
310 :
311 : // nsIDOMFiles should be threadsafe, thus we will use the same instance
312 : // in the worker.
313 0 : JSObject* jsFile = file::CreateFile(aCx, file);
314 0 : return jsFile;
315 : }
316 : }
317 : // See if object is a nsIDOMBlob pointer.
318 0 : else if (aTag == DOMWORKER_SCTAG_BLOB) {
319 0 : JS_ASSERT(!aData);
320 :
321 : nsIDOMBlob* blob;
322 0 : if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
323 0 : JS_ASSERT(blob);
324 :
325 : #ifdef DEBUG
326 : {
327 : // Blob should not be mutable.
328 0 : nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
329 : bool isMutable;
330 0 : NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
331 : !isMutable,
332 : "Only immutable blob should be passed to worker");
333 : }
334 : #endif
335 :
336 : // nsIDOMBlob should be threadsafe, thus we will use the same instance
337 : // in the worker.
338 0 : JSObject* jsBlob = file::CreateBlob(aCx, blob);
339 0 : return jsBlob;
340 : }
341 : }
342 :
343 0 : Error(aCx, 0);
344 0 : return nsnull;
345 : }
346 :
347 : static JSBool
348 0 : Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
349 : void* aClosure)
350 : {
351 0 : NS_ASSERTION(aClosure, "Null pointer!");
352 :
353 : // We'll stash any nsISupports pointers that need to be AddRef'd here.
354 : nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
355 0 : static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
356 :
357 : // See if this is a File object.
358 : {
359 0 : nsIDOMFile* file = file::GetDOMFileFromJSObject(aObj);
360 0 : if (file) {
361 0 : if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
362 0 : JS_WriteBytes(aWriter, &file, sizeof(file))) {
363 0 : clonedObjects->AppendElement(file);
364 0 : return true;
365 : }
366 : }
367 : }
368 :
369 : // See if this is a Blob object.
370 : {
371 0 : nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj);
372 0 : if (blob) {
373 0 : nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
374 0 : if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false)) &&
375 0 : JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
376 0 : JS_WriteBytes(aWriter, &blob, sizeof(blob))) {
377 0 : clonedObjects->AppendElement(blob);
378 0 : return true;
379 : }
380 : }
381 : }
382 :
383 0 : Error(aCx, 0);
384 0 : return false;
385 : }
386 :
387 : static void
388 0 : Error(JSContext* aCx, uint32_t /* aErrorId */)
389 : {
390 0 : ThrowDOMExceptionForCode(aCx, DATA_CLONE_ERR);
391 0 : }
392 : };
393 :
394 : JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
395 : WorkerStructuredCloneCallbacks::Read,
396 : WorkerStructuredCloneCallbacks::Write,
397 : WorkerStructuredCloneCallbacks::Error
398 : };
399 :
400 : struct MainThreadWorkerStructuredCloneCallbacks
401 : {
402 : static JSObject*
403 0 : Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
404 : uint32_t aData, void* aClosure)
405 : {
406 0 : AssertIsOnMainThread();
407 :
408 : // See if object is a nsIDOMFile pointer.
409 0 : if (aTag == DOMWORKER_SCTAG_FILE) {
410 0 : JS_ASSERT(!aData);
411 :
412 : nsIDOMFile* file;
413 0 : if (JS_ReadBytes(aReader, &file, sizeof(file))) {
414 0 : JS_ASSERT(file);
415 :
416 : #ifdef DEBUG
417 : {
418 : // File should not be mutable.
419 0 : nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
420 : bool isMutable;
421 0 : NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
422 : !isMutable,
423 : "Only immutable file should be passed to worker");
424 : }
425 : #endif
426 :
427 : // nsIDOMFiles should be threadsafe, thus we will use the same instance
428 : // on the main thread.
429 : jsval wrappedFile;
430 : nsresult rv =
431 : nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
432 0 : &NS_GET_IID(nsIDOMFile), &wrappedFile);
433 0 : if (NS_FAILED(rv)) {
434 0 : Error(aCx, DATA_CLONE_ERR);
435 0 : return nsnull;
436 : }
437 :
438 0 : return JSVAL_TO_OBJECT(wrappedFile);
439 : }
440 : }
441 : // See if object is a nsIDOMBlob pointer.
442 0 : else if (aTag == DOMWORKER_SCTAG_BLOB) {
443 0 : JS_ASSERT(!aData);
444 :
445 : nsIDOMBlob* blob;
446 0 : if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
447 0 : JS_ASSERT(blob);
448 :
449 : #ifdef DEBUG
450 : {
451 : // Blob should not be mutable.
452 0 : nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
453 : bool isMutable;
454 0 : NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
455 : !isMutable,
456 : "Only immutable blob should be passed to worker");
457 : }
458 : #endif
459 :
460 : // nsIDOMBlobs should be threadsafe, thus we will use the same instance
461 : // on the main thread.
462 : jsval wrappedBlob;
463 : nsresult rv =
464 : nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
465 0 : &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
466 0 : if (NS_FAILED(rv)) {
467 0 : Error(aCx, DATA_CLONE_ERR);
468 0 : return nsnull;
469 : }
470 :
471 0 : return JSVAL_TO_OBJECT(wrappedBlob);
472 : }
473 : }
474 :
475 : JSObject* clone =
476 0 : WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, aClosure);
477 0 : if (clone) {
478 0 : return clone;
479 : }
480 :
481 0 : JS_ClearPendingException(aCx);
482 0 : return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull);
483 : }
484 :
485 : static JSBool
486 0 : Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
487 : void* aClosure)
488 : {
489 0 : AssertIsOnMainThread();
490 :
491 0 : NS_ASSERTION(aClosure, "Null pointer!");
492 :
493 : // We'll stash any nsISupports pointers that need to be AddRef'd here.
494 : nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
495 0 : static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
496 :
497 : // See if this is a wrapped native.
498 0 : nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
499 0 : nsContentUtils::XPConnect()->
500 0 : GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
501 :
502 0 : if (wrappedNative) {
503 : // Get the raw nsISupports out of it.
504 0 : nsISupports* wrappedObject = wrappedNative->Native();
505 0 : NS_ASSERTION(wrappedObject, "Null pointer?!");
506 :
507 : // See if the wrapped native is a nsIDOMFile.
508 0 : nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject);
509 0 : if (file) {
510 0 : nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
511 0 : if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(false))) {
512 0 : nsIDOMFile* filePtr = file;
513 0 : if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
514 0 : JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) {
515 0 : clonedObjects->AppendElement(file);
516 0 : return true;
517 : }
518 : }
519 : }
520 :
521 : // See if the wrapped native is a nsIDOMBlob.
522 0 : nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject);
523 0 : if (blob) {
524 0 : nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
525 0 : if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false))) {
526 0 : nsIDOMBlob* blobPtr = blob;
527 0 : if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
528 0 : JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) {
529 0 : clonedObjects->AppendElement(blob);
530 0 : return true;
531 : }
532 : }
533 : }
534 : }
535 :
536 : JSBool ok =
537 0 : WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
538 0 : if (ok) {
539 0 : return ok;
540 : }
541 :
542 0 : JS_ClearPendingException(aCx);
543 0 : return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
544 : }
545 :
546 : static void
547 0 : Error(JSContext* aCx, uint32_t aErrorId)
548 : {
549 0 : AssertIsOnMainThread();
550 :
551 0 : NS_DOMStructuredCloneError(aCx, aErrorId);
552 0 : }
553 : };
554 :
555 : JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
556 : MainThreadWorkerStructuredCloneCallbacks::Read,
557 : MainThreadWorkerStructuredCloneCallbacks::Write,
558 : MainThreadWorkerStructuredCloneCallbacks::Error
559 : };
560 :
561 : struct ChromeWorkerStructuredCloneCallbacks
562 : {
563 : static JSObject*
564 0 : Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
565 : uint32_t aData, void* aClosure)
566 : {
567 : return WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
568 0 : aClosure);
569 : }
570 :
571 : static JSBool
572 0 : Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
573 : void* aClosure)
574 : {
575 0 : return WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
576 : }
577 :
578 : static void
579 0 : Error(JSContext* aCx, uint32_t aErrorId)
580 : {
581 0 : return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId);
582 : }
583 : };
584 :
585 : JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
586 : ChromeWorkerStructuredCloneCallbacks::Read,
587 : ChromeWorkerStructuredCloneCallbacks::Write,
588 : ChromeWorkerStructuredCloneCallbacks::Error
589 : };
590 :
591 : struct MainThreadChromeWorkerStructuredCloneCallbacks
592 : {
593 : static JSObject*
594 0 : Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
595 : uint32_t aData, void* aClosure)
596 : {
597 0 : AssertIsOnMainThread();
598 :
599 : JSObject* clone =
600 : MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
601 0 : aClosure);
602 0 : if (clone) {
603 0 : return clone;
604 : }
605 :
606 : clone =
607 : ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
608 0 : aClosure);
609 0 : if (clone) {
610 0 : return clone;
611 : }
612 :
613 0 : JS_ClearPendingException(aCx);
614 0 : return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull);
615 : }
616 :
617 : static JSBool
618 0 : Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
619 : void* aClosure)
620 : {
621 0 : AssertIsOnMainThread();
622 :
623 0 : if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
624 0 : aClosure) ||
625 : ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
626 0 : aClosure) ||
627 0 : NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull)) {
628 0 : return true;
629 : }
630 :
631 0 : return false;
632 : }
633 :
634 : static void
635 0 : Error(JSContext* aCx, uint32_t aErrorId)
636 : {
637 0 : AssertIsOnMainThread();
638 :
639 0 : NS_DOMStructuredCloneError(aCx, aErrorId);
640 0 : }
641 : };
642 :
643 : JSStructuredCloneCallbacks gMainThreadChromeWorkerStructuredCloneCallbacks = {
644 : MainThreadChromeWorkerStructuredCloneCallbacks::Read,
645 : MainThreadChromeWorkerStructuredCloneCallbacks::Write,
646 : MainThreadChromeWorkerStructuredCloneCallbacks::Error
647 : };
648 :
649 : class WorkerFinishedRunnable : public WorkerControlRunnable
650 0 : {
651 : WorkerPrivate* mFinishedWorker;
652 : nsCOMPtr<nsIThread> mThread;
653 :
654 : class MainThreadReleaseRunnable : public nsRunnable
655 0 : {
656 : nsCOMPtr<nsIThread> mThread;
657 : nsTArray<nsCOMPtr<nsISupports> > mDoomed;
658 :
659 : public:
660 0 : MainThreadReleaseRunnable(nsCOMPtr<nsIThread>& aThread,
661 : nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
662 0 : {
663 0 : mThread.swap(aThread);
664 0 : mDoomed.SwapElements(aDoomed);
665 0 : }
666 :
667 : NS_IMETHOD
668 0 : Run()
669 : {
670 0 : mDoomed.Clear();
671 :
672 0 : if (mThread) {
673 0 : RuntimeService* runtime = RuntimeService::GetService();
674 0 : NS_ASSERTION(runtime, "This should never be null!");
675 :
676 0 : runtime->NoteIdleThread(mThread);
677 : }
678 :
679 0 : return NS_OK;
680 : }
681 : };
682 :
683 : public:
684 0 : WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
685 : WorkerPrivate* aFinishedWorker,
686 : nsIThread* aFinishedThread)
687 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
688 0 : mFinishedWorker(aFinishedWorker), mThread(aFinishedThread)
689 0 : { }
690 :
691 : bool
692 0 : PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
693 : {
694 : // Silence bad assertions.
695 0 : return true;
696 : }
697 :
698 : void
699 0 : PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
700 : bool aDispatchResult)
701 : {
702 : // Silence bad assertions.
703 0 : }
704 :
705 : bool
706 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
707 : {
708 0 : nsTArray<nsCOMPtr<nsISupports> > doomed;
709 0 : mFinishedWorker->ForgetMainThreadObjects(doomed);
710 :
711 : nsRefPtr<MainThreadReleaseRunnable> runnable =
712 0 : new MainThreadReleaseRunnable(mThread, doomed);
713 0 : if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
714 0 : NS_WARNING("Failed to dispatch, going to leak!");
715 : }
716 :
717 0 : mFinishedWorker->FinalizeInstance(aCx, false);
718 :
719 0 : RuntimeService* runtime = RuntimeService::GetService();
720 0 : NS_ASSERTION(runtime, "This should never be null!");
721 :
722 0 : runtime->UnregisterWorker(aCx, mFinishedWorker);
723 :
724 0 : delete mFinishedWorker;
725 0 : return true;
726 : }
727 : };
728 :
729 : class TopLevelWorkerFinishedRunnable : public nsRunnable
730 0 : {
731 : WorkerPrivate* mFinishedWorker;
732 : nsCOMPtr<nsIThread> mThread;
733 :
734 : public:
735 0 : TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker,
736 : nsIThread* aFinishedThread)
737 0 : : mFinishedWorker(aFinishedWorker), mThread(aFinishedThread)
738 : {
739 0 : aFinishedWorker->AssertIsOnWorkerThread();
740 0 : }
741 :
742 : NS_IMETHOD
743 0 : Run()
744 : {
745 0 : AssertIsOnMainThread();
746 :
747 0 : RuntimeService::AutoSafeJSContext cx;
748 :
749 0 : mFinishedWorker->FinalizeInstance(cx, false);
750 :
751 0 : RuntimeService* runtime = RuntimeService::GetService();
752 0 : NS_ASSERTION(runtime, "This should never be null!");
753 :
754 0 : runtime->UnregisterWorker(cx, mFinishedWorker);
755 :
756 0 : if (mThread) {
757 0 : runtime->NoteIdleThread(mThread);
758 : }
759 :
760 0 : delete mFinishedWorker;
761 :
762 0 : return NS_OK;
763 : }
764 : };
765 :
766 : class ModifyBusyCountRunnable : public WorkerControlRunnable
767 0 : {
768 : bool mIncrease;
769 :
770 : public:
771 0 : ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
772 : : WorkerControlRunnable(aWorkerPrivate, ParentThread, UnchangedBusyCount),
773 0 : mIncrease(aIncrease)
774 0 : { }
775 :
776 : bool
777 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
778 : {
779 0 : return aWorkerPrivate->ModifyBusyCount(aCx, mIncrease);
780 : }
781 :
782 : void
783 0 : PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
784 : {
785 0 : if (mIncrease) {
786 0 : WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
787 0 : return;
788 : }
789 : // Don't do anything here as it's possible that aWorkerPrivate has been
790 : // deleted.
791 : }
792 : };
793 :
794 : class CompileScriptRunnable : public WorkerRunnable
795 0 : {
796 : public:
797 0 : CompileScriptRunnable(WorkerPrivate* aWorkerPrivate)
798 : : WorkerRunnable(aWorkerPrivate, WorkerThread, ModifyBusyCount,
799 0 : SkipWhenClearing)
800 0 : { }
801 :
802 : bool
803 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
804 : {
805 0 : JSObject* global = CreateDedicatedWorkerGlobalScope(aCx);
806 0 : if (!global) {
807 0 : NS_WARNING("Failed to make global!");
808 0 : return false;
809 : }
810 :
811 0 : JSAutoEnterCompartment ac;
812 0 : if (!ac.enter(aCx, global)) {
813 0 : NS_WARNING("Failed to enter compartment!");
814 0 : return false;
815 : }
816 :
817 0 : JS_SetGlobalObject(aCx, global);
818 :
819 0 : return scriptloader::LoadWorkerScript(aCx);
820 : }
821 : };
822 :
823 : class CloseEventRunnable : public WorkerRunnable
824 0 : {
825 : public:
826 0 : CloseEventRunnable(WorkerPrivate* aWorkerPrivate)
827 : : WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
828 0 : SkipWhenClearing)
829 0 : { }
830 :
831 : bool
832 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
833 : {
834 0 : JSObject* target = JS_GetGlobalObject(aCx);
835 0 : NS_ASSERTION(target, "This must never be null!");
836 :
837 0 : aWorkerPrivate->CloseHandlerStarted();
838 :
839 0 : JSString* type = JS_InternString(aCx, "close");
840 0 : if (!type) {
841 0 : return false;
842 : }
843 :
844 : JSObject* event = events::CreateGenericEvent(aCx, type, false, false,
845 0 : false);
846 0 : if (!event) {
847 0 : return false;
848 : }
849 :
850 : bool ignored;
851 0 : return events::DispatchEventToTarget(aCx, target, event, &ignored);
852 : }
853 :
854 : void
855 0 : PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
856 : {
857 : // Report errors.
858 0 : WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
859 :
860 : // Match the busy count increase from NotifyRunnable.
861 0 : if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
862 0 : JS_ReportPendingException(aCx);
863 : }
864 :
865 0 : aWorkerPrivate->CloseHandlerFinished();
866 0 : }
867 : };
868 :
869 : class MessageEventRunnable : public WorkerRunnable
870 0 : {
871 : uint64_t* mData;
872 : size_t mDataByteCount;
873 : nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
874 :
875 : public:
876 0 : MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
877 : JSAutoStructuredCloneBuffer& aData,
878 : nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects)
879 : : WorkerRunnable(aWorkerPrivate, aTarget, aTarget == WorkerThread ?
880 : ModifyBusyCount :
881 : UnchangedBusyCount,
882 0 : SkipWhenClearing)
883 : {
884 0 : aData.steal(&mData, &mDataByteCount);
885 :
886 0 : if (!mClonedObjects.SwapElements(aClonedObjects)) {
887 0 : NS_ERROR("This should never fail!");
888 : }
889 0 : }
890 :
891 : bool
892 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
893 : {
894 0 : JSAutoStructuredCloneBuffer buffer;
895 0 : buffer.adopt(mData, mDataByteCount);
896 :
897 0 : mData = nsnull;
898 0 : mDataByteCount = 0;
899 :
900 : bool mainRuntime;
901 : JSObject* target;
902 0 : if (mTarget == ParentThread) {
903 0 : mainRuntime = !aWorkerPrivate->GetParent();
904 :
905 0 : target = aWorkerPrivate->GetJSObject();
906 :
907 : // Don't fire this event if the JS object has ben disconnected from the
908 : // private object.
909 0 : if (!target) {
910 0 : return true;
911 : }
912 :
913 0 : if (aWorkerPrivate->IsSuspended()) {
914 0 : aWorkerPrivate->QueueRunnable(this);
915 0 : buffer.steal(&mData, &mDataByteCount);
916 0 : return true;
917 : }
918 :
919 0 : aWorkerPrivate->AssertInnerWindowIsCorrect();
920 : }
921 : else {
922 0 : NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx),
923 : "Badness!");
924 0 : mainRuntime = false;
925 0 : target = JS_GetGlobalObject(aCx);
926 : }
927 :
928 0 : NS_ASSERTION(target, "This should never be null!");
929 :
930 : JSObject* event = events::CreateMessageEvent(aCx, buffer, mClonedObjects,
931 0 : mainRuntime);
932 0 : if (!event) {
933 0 : return false;
934 : }
935 :
936 : bool dummy;
937 0 : return events::DispatchEventToTarget(aCx, target, event, &dummy);
938 : }
939 : };
940 :
941 : class NotifyRunnable : public WorkerControlRunnable
942 0 : {
943 : bool mFromJSObjectFinalizer;
944 : Status mStatus;
945 :
946 : public:
947 0 : NotifyRunnable(WorkerPrivate* aWorkerPrivate, bool aFromJSObjectFinalizer,
948 : Status aStatus)
949 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
950 0 : mFromJSObjectFinalizer(aFromJSObjectFinalizer), mStatus(aStatus)
951 : {
952 0 : NS_ASSERTION(aStatus == Terminating || aStatus == Canceling ||
953 : aStatus == Killing, "Bad status!");
954 0 : }
955 :
956 : bool
957 0 : PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
958 : {
959 : // Modify here, but not in PostRun! This busy count addition will be matched
960 : // by the CloseEventRunnable. If we're running from a finalizer there is no
961 : // need to modify the count because future changes to the busy count will
962 : // have no effect.
963 : return mFromJSObjectFinalizer ?
964 : true :
965 0 : aWorkerPrivate->ModifyBusyCount(aCx, true);
966 : }
967 :
968 : bool
969 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
970 : {
971 0 : return aWorkerPrivate->NotifyInternal(aCx, mStatus);
972 : }
973 : };
974 :
975 : class CloseRunnable : public WorkerControlRunnable
976 0 : {
977 : public:
978 0 : CloseRunnable(WorkerPrivate* aWorkerPrivate)
979 0 : : WorkerControlRunnable(aWorkerPrivate, ParentThread, UnchangedBusyCount)
980 0 : { }
981 :
982 : bool
983 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
984 : {
985 : // This busy count will be matched by the CloseEventRunnable.
986 0 : return aWorkerPrivate->ModifyBusyCount(aCx, true) &&
987 0 : aWorkerPrivate->Close(aCx);
988 : }
989 : };
990 :
991 : class SuspendRunnable : public WorkerControlRunnable
992 0 : {
993 : public:
994 0 : SuspendRunnable(WorkerPrivate* aWorkerPrivate)
995 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount)
996 0 : { }
997 :
998 : bool
999 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1000 : {
1001 0 : return aWorkerPrivate->SuspendInternal(aCx);
1002 : }
1003 : };
1004 :
1005 : class ResumeRunnable : public WorkerControlRunnable
1006 0 : {
1007 : public:
1008 0 : ResumeRunnable(WorkerPrivate* aWorkerPrivate)
1009 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount)
1010 0 : { }
1011 :
1012 : bool
1013 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1014 : {
1015 0 : return aWorkerPrivate->ResumeInternal(aCx);
1016 : }
1017 : };
1018 :
1019 : class ReportErrorRunnable : public WorkerRunnable
1020 0 : {
1021 : nsString mMessage;
1022 : nsString mFilename;
1023 : nsString mLine;
1024 : PRUint32 mLineNumber;
1025 : PRUint32 mColumnNumber;
1026 : PRUint32 mFlags;
1027 : PRUint32 mErrorNumber;
1028 :
1029 : public:
1030 0 : ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage,
1031 : const nsString& aFilename, const nsString& aLine,
1032 : PRUint32 aLineNumber, PRUint32 aColumnNumber,
1033 : PRUint32 aFlags, PRUint32 aErrorNumber)
1034 : : WorkerRunnable(aWorkerPrivate, ParentThread, UnchangedBusyCount,
1035 : SkipWhenClearing),
1036 : mMessage(aMessage), mFilename(aFilename), mLine(aLine),
1037 : mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags),
1038 0 : mErrorNumber(aErrorNumber)
1039 0 : { }
1040 :
1041 : void
1042 0 : PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1043 : bool aDispatchResult)
1044 : {
1045 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1046 :
1047 : // Dispatch may fail if the worker was canceled, no need to report that as
1048 : // an error, so don't call base class PostDispatch.
1049 0 : }
1050 :
1051 : bool
1052 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1053 : {
1054 0 : JSObject* target = aWorkerPrivate->GetJSObject();
1055 0 : if (target) {
1056 0 : aWorkerPrivate->AssertInnerWindowIsCorrect();
1057 : }
1058 :
1059 : PRUint64 innerWindowId;
1060 :
1061 0 : WorkerPrivate* parent = aWorkerPrivate->GetParent();
1062 0 : if (parent) {
1063 0 : innerWindowId = 0;
1064 : }
1065 : else {
1066 0 : AssertIsOnMainThread();
1067 :
1068 0 : if (aWorkerPrivate->IsSuspended()) {
1069 0 : aWorkerPrivate->QueueRunnable(this);
1070 0 : return true;
1071 : }
1072 :
1073 0 : innerWindowId = aWorkerPrivate->GetInnerWindowId();
1074 : }
1075 :
1076 : return ReportErrorRunnable::ReportError(aCx, parent, true, target, mMessage,
1077 : mFilename, mLine, mLineNumber,
1078 : mColumnNumber, mFlags,
1079 0 : mErrorNumber, innerWindowId);
1080 : }
1081 :
1082 : static bool
1083 0 : ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1084 : bool aFireAtScope, JSObject* aTarget, const nsString& aMessage,
1085 : const nsString& aFilename, const nsString& aLine,
1086 : PRUint32 aLineNumber, PRUint32 aColumnNumber, PRUint32 aFlags,
1087 : PRUint32 aErrorNumber, PRUint64 aInnerWindowId)
1088 : {
1089 0 : if (aWorkerPrivate) {
1090 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1091 : }
1092 : else {
1093 0 : AssertIsOnMainThread();
1094 : }
1095 :
1096 : JSString* message = JS_NewUCStringCopyN(aCx, aMessage.get(),
1097 0 : aMessage.Length());
1098 0 : if (!message) {
1099 0 : return false;
1100 : }
1101 :
1102 : JSString* filename = JS_NewUCStringCopyN(aCx, aFilename.get(),
1103 0 : aFilename.Length());
1104 0 : if (!filename) {
1105 0 : return false;
1106 : }
1107 :
1108 : // First fire an ErrorEvent at the worker.
1109 0 : if (aTarget) {
1110 : JSObject* event = events::CreateErrorEvent(aCx, message, filename,
1111 0 : aLineNumber, !aWorkerPrivate);
1112 0 : if (!event) {
1113 0 : return false;
1114 : }
1115 :
1116 : bool preventDefaultCalled;
1117 0 : if (!events::DispatchEventToTarget(aCx, aTarget, event,
1118 0 : &preventDefaultCalled)) {
1119 0 : return false;
1120 : }
1121 :
1122 0 : if (preventDefaultCalled) {
1123 0 : return true;
1124 : }
1125 : }
1126 :
1127 : // Now fire an event at the global object, but don't do that if the error
1128 : // code is too much recursion and this is the same script threw the error.
1129 0 : if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
1130 0 : aTarget = JS_GetGlobalForScopeChain(aCx);
1131 0 : NS_ASSERTION(aTarget, "This should never be null!");
1132 :
1133 : bool preventDefaultCalled;
1134 : nsIScriptGlobalObject* sgo;
1135 :
1136 0 : if (aWorkerPrivate ||
1137 : !(sgo = nsJSUtils::GetStaticScriptGlobal(aCx, aTarget))) {
1138 : // Fire a normal ErrorEvent if we're running on a worker thread.
1139 : JSObject* event = events::CreateErrorEvent(aCx, message, filename,
1140 0 : aLineNumber, false);
1141 0 : if (!event) {
1142 0 : return false;
1143 : }
1144 :
1145 0 : if (!events::DispatchEventToTarget(aCx, aTarget, event,
1146 0 : &preventDefaultCalled)) {
1147 0 : return false;
1148 : }
1149 : }
1150 : else {
1151 : // Icky, we have to fire an nsScriptErrorEvent...
1152 0 : nsScriptErrorEvent event(true, NS_LOAD_ERROR);
1153 0 : event.lineNr = aLineNumber;
1154 0 : event.errorMsg = aMessage.get();
1155 0 : event.fileName = aFilename.get();
1156 :
1157 : nsEventStatus status;
1158 0 : if (NS_FAILED(sgo->HandleScriptError(&event, &status))) {
1159 0 : NS_WARNING("Failed to dispatch main thread error event!");
1160 0 : status = nsEventStatus_eIgnore;
1161 : }
1162 :
1163 0 : preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
1164 : }
1165 :
1166 0 : if (preventDefaultCalled) {
1167 0 : return true;
1168 : }
1169 : }
1170 :
1171 : // Now fire a runnable to do the same on the parent's thread if we can.
1172 0 : if (aWorkerPrivate) {
1173 : nsRefPtr<ReportErrorRunnable> runnable =
1174 : new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
1175 : aLineNumber, aColumnNumber, aFlags,
1176 0 : aErrorNumber);
1177 0 : return runnable->Dispatch(aCx);
1178 : }
1179 :
1180 : // Otherwise log an error to the error console.
1181 : nsCOMPtr<nsIScriptError> scriptError =
1182 0 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
1183 0 : NS_WARN_IF_FALSE(scriptError, "Failed to create script error!");
1184 :
1185 0 : if (scriptError) {
1186 0 : if (NS_FAILED(scriptError->InitWithWindowID(aMessage.get(),
1187 : aFilename.get(),
1188 : aLine.get(), aLineNumber,
1189 : aColumnNumber, aFlags,
1190 : "Web Worker",
1191 : aInnerWindowId))) {
1192 0 : NS_WARNING("Failed to init script error!");
1193 0 : scriptError = nsnull;
1194 : }
1195 : }
1196 :
1197 : nsCOMPtr<nsIConsoleService> consoleService =
1198 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1199 0 : NS_WARN_IF_FALSE(consoleService, "Failed to get console service!");
1200 :
1201 0 : bool logged = false;
1202 :
1203 0 : if (consoleService) {
1204 0 : if (scriptError) {
1205 0 : if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
1206 0 : logged = true;
1207 : }
1208 : else {
1209 0 : NS_WARNING("Failed to log script error!");
1210 : }
1211 : }
1212 0 : else if (NS_SUCCEEDED(consoleService->LogStringMessage(aMessage.get()))) {
1213 0 : logged = true;
1214 : }
1215 : else {
1216 0 : NS_WARNING("Failed to log script error!");
1217 : }
1218 : }
1219 :
1220 0 : if (!logged) {
1221 0 : NS_ConvertUTF16toUTF8 msg(aMessage);
1222 : #ifdef ANDROID
1223 : __android_log_print(ANDROID_LOG_INFO, "Gecko", msg.get());
1224 : #endif
1225 0 : fputs(msg.get(), stderr);
1226 0 : fflush(stderr);
1227 : }
1228 :
1229 0 : return true;
1230 : }
1231 : };
1232 :
1233 : class TimerRunnable : public WorkerRunnable
1234 0 : {
1235 : public:
1236 0 : TimerRunnable(WorkerPrivate* aWorkerPrivate)
1237 : : WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
1238 0 : SkipWhenClearing)
1239 0 : { }
1240 :
1241 : bool
1242 0 : PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1243 : {
1244 : // Silence bad assertions.
1245 0 : return true;
1246 : }
1247 :
1248 : void
1249 0 : PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1250 : bool aDispatchResult)
1251 : {
1252 : // Silence bad assertions.
1253 0 : }
1254 :
1255 : bool
1256 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1257 : {
1258 0 : return aWorkerPrivate->RunExpiredTimeouts(aCx);
1259 : }
1260 : };
1261 :
1262 : void
1263 0 : DummyCallback(nsITimer* aTimer, void* aClosure)
1264 : {
1265 : // Nothing!
1266 0 : }
1267 :
1268 : class WorkerRunnableEventTarget : public nsIEventTarget
1269 0 : {
1270 : protected:
1271 : nsRefPtr<WorkerRunnable> mWorkerRunnable;
1272 :
1273 : public:
1274 0 : WorkerRunnableEventTarget(WorkerRunnable* aWorkerRunnable)
1275 0 : : mWorkerRunnable(aWorkerRunnable)
1276 0 : { }
1277 :
1278 : NS_DECL_ISUPPORTS
1279 :
1280 : NS_IMETHOD
1281 0 : Dispatch(nsIRunnable* aRunnable, PRUint32 aFlags)
1282 : {
1283 0 : NS_ASSERTION(aFlags == nsIEventTarget::DISPATCH_NORMAL, "Don't call me!");
1284 :
1285 0 : nsRefPtr<WorkerRunnableEventTarget> kungFuDeathGrip = this;
1286 :
1287 : // This can fail if we're racing to terminate or cancel, should be handled
1288 : // by the terminate or cancel code.
1289 0 : mWorkerRunnable->Dispatch(nsnull);
1290 :
1291 : // Run the runnable we're given now (should just call DummyCallback()),
1292 : // otherwise the timer thread will leak it...
1293 0 : return aRunnable->Run();
1294 : }
1295 :
1296 : NS_IMETHOD
1297 0 : IsOnCurrentThread(bool* aIsOnCurrentThread)
1298 : {
1299 0 : *aIsOnCurrentThread = false;
1300 0 : return NS_OK;
1301 : }
1302 : };
1303 :
1304 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerRunnableEventTarget, nsIEventTarget)
1305 :
1306 : class KillCloseEventRunnable : public WorkerRunnable
1307 : {
1308 : nsCOMPtr<nsITimer> mTimer;
1309 :
1310 : class KillScriptRunnable : public WorkerControlRunnable
1311 0 : {
1312 : public:
1313 0 : KillScriptRunnable(WorkerPrivate* aWorkerPrivate)
1314 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount)
1315 0 : { }
1316 :
1317 : bool
1318 0 : PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1319 : {
1320 : // Silence bad assertions.
1321 0 : return true;
1322 : }
1323 :
1324 : void
1325 0 : PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1326 : bool aDispatchResult)
1327 : {
1328 : // Silence bad assertions.
1329 0 : }
1330 :
1331 : bool
1332 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1333 : {
1334 : // Kill running script.
1335 0 : return false;
1336 : }
1337 : };
1338 :
1339 : public:
1340 0 : KillCloseEventRunnable(WorkerPrivate* aWorkerPrivate)
1341 : : WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
1342 0 : SkipWhenClearing)
1343 0 : { }
1344 :
1345 0 : ~KillCloseEventRunnable()
1346 0 : {
1347 0 : if (mTimer) {
1348 0 : mTimer->Cancel();
1349 : }
1350 0 : }
1351 :
1352 : bool
1353 0 : PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1354 : {
1355 0 : NS_NOTREACHED("Not meant to be dispatched!");
1356 0 : return false;
1357 : }
1358 :
1359 : bool
1360 0 : SetTimeout(JSContext* aCx, PRUint32 aDelayMS)
1361 : {
1362 0 : nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
1363 0 : if (!timer) {
1364 0 : JS_ReportError(aCx, "Failed to create timer!");
1365 0 : return false;
1366 : }
1367 :
1368 : nsRefPtr<KillScriptRunnable> runnable =
1369 0 : new KillScriptRunnable(mWorkerPrivate);
1370 :
1371 : nsRefPtr<WorkerRunnableEventTarget> target =
1372 0 : new WorkerRunnableEventTarget(runnable);
1373 :
1374 0 : if (NS_FAILED(timer->SetTarget(target))) {
1375 0 : JS_ReportError(aCx, "Failed to set timer's target!");
1376 0 : return false;
1377 : }
1378 :
1379 0 : if (NS_FAILED(timer->InitWithFuncCallback(DummyCallback, nsnull, aDelayMS,
1380 : nsITimer::TYPE_ONE_SHOT))) {
1381 0 : JS_ReportError(aCx, "Failed to start timer!");
1382 0 : return false;
1383 : }
1384 :
1385 0 : mTimer.swap(timer);
1386 0 : return true;
1387 : }
1388 :
1389 : bool
1390 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1391 : {
1392 0 : if (mTimer) {
1393 0 : mTimer->Cancel();
1394 0 : mTimer = nsnull;
1395 : }
1396 :
1397 0 : return true;
1398 : }
1399 : };
1400 :
1401 : class UpdateJSContextOptionsRunnable : public WorkerControlRunnable
1402 0 : {
1403 : PRUint32 mOptions;
1404 :
1405 : public:
1406 0 : UpdateJSContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
1407 : PRUint32 aOptions)
1408 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
1409 0 : mOptions(aOptions)
1410 0 : { }
1411 :
1412 : bool
1413 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1414 : {
1415 0 : aWorkerPrivate->UpdateJSContextOptionsInternal(aCx, mOptions);
1416 0 : return true;
1417 : }
1418 : };
1419 :
1420 : class UpdateJSRuntimeHeapSizeRunnable : public WorkerControlRunnable
1421 0 : {
1422 : PRUint32 mJSRuntimeHeapSize;
1423 :
1424 : public:
1425 0 : UpdateJSRuntimeHeapSizeRunnable(WorkerPrivate* aWorkerPrivate,
1426 : PRUint32 aJSRuntimeHeapSize)
1427 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
1428 0 : mJSRuntimeHeapSize(aJSRuntimeHeapSize)
1429 0 : { }
1430 :
1431 : bool
1432 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1433 : {
1434 0 : aWorkerPrivate->UpdateJSRuntimeHeapSizeInternal(aCx, mJSRuntimeHeapSize);
1435 0 : return true;
1436 : }
1437 : };
1438 :
1439 : #ifdef JS_GC_ZEAL
1440 : class UpdateGCZealRunnable : public WorkerControlRunnable
1441 0 : {
1442 : PRUint8 mGCZeal;
1443 :
1444 : public:
1445 0 : UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
1446 : PRUint8 aGCZeal)
1447 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
1448 0 : mGCZeal(aGCZeal)
1449 0 : { }
1450 :
1451 : bool
1452 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1453 : {
1454 0 : aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal);
1455 0 : return true;
1456 : }
1457 : };
1458 : #endif
1459 :
1460 : class GarbageCollectRunnable : public WorkerControlRunnable
1461 0 : {
1462 : protected:
1463 : bool mShrinking;
1464 : bool mCollectChildren;
1465 :
1466 : public:
1467 0 : GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
1468 : bool aCollectChildren)
1469 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
1470 0 : mShrinking(aShrinking), mCollectChildren(aCollectChildren)
1471 0 : { }
1472 :
1473 : bool
1474 0 : PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1475 : {
1476 : // Silence bad assertions, this can be dispatched from either the main
1477 : // thread or the timer thread..
1478 0 : return true;
1479 : }
1480 :
1481 : void
1482 0 : PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1483 : bool aDispatchResult)
1484 : {
1485 : // Silence bad assertions, this can be dispatched from either the main
1486 : // thread or the timer thread..
1487 0 : }
1488 :
1489 : bool
1490 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1491 : {
1492 0 : aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
1493 0 : return true;
1494 : }
1495 : };
1496 :
1497 : class CollectRuntimeStatsRunnable : public WorkerControlRunnable
1498 0 : {
1499 : typedef mozilla::Mutex Mutex;
1500 : typedef mozilla::CondVar CondVar;
1501 :
1502 : Mutex mMutex;
1503 : CondVar mCondVar;
1504 : volatile bool mDone;
1505 : bool mIsQuick;
1506 : void* mData;
1507 : bool* mSucceeded;
1508 :
1509 : public:
1510 0 : CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, bool aIsQuick,
1511 : void* aData, bool* aSucceeded)
1512 : : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
1513 : mMutex("CollectRuntimeStatsRunnable::mMutex"),
1514 : mCondVar(mMutex, "CollectRuntimeStatsRunnable::mCondVar"), mDone(false),
1515 0 : mIsQuick(aIsQuick), mData(aData), mSucceeded(aSucceeded)
1516 0 : { }
1517 :
1518 : bool
1519 0 : PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1520 : {
1521 0 : AssertIsOnMainThread();
1522 0 : return true;
1523 : }
1524 :
1525 : void
1526 0 : PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1527 : bool aDispatchResult)
1528 : {
1529 0 : AssertIsOnMainThread();
1530 0 : }
1531 :
1532 : bool
1533 0 : DispatchInternal()
1534 : {
1535 0 : AssertIsOnMainThread();
1536 :
1537 0 : if (!WorkerControlRunnable::DispatchInternal()) {
1538 0 : NS_WARNING("Failed to dispatch runnable!");
1539 0 : return false;
1540 : }
1541 :
1542 : {
1543 0 : MutexAutoLock lock(mMutex);
1544 0 : while (!mDone) {
1545 0 : mCondVar.Wait();
1546 : }
1547 : }
1548 :
1549 0 : return true;
1550 : }
1551 :
1552 : bool
1553 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1554 : {
1555 0 : JSRuntime *rt = JS_GetRuntime(aCx);
1556 0 : if (mIsQuick) {
1557 0 : *static_cast<int64_t*>(mData) = JS::GetExplicitNonHeapForRuntime(rt, JsWorkerMallocSizeOf);
1558 0 : *mSucceeded = true;
1559 : } else {
1560 0 : *mSucceeded = JS::CollectRuntimeStats(rt, static_cast<JS::RuntimeStats*>(mData));
1561 : }
1562 :
1563 : {
1564 0 : MutexAutoLock lock(mMutex);
1565 0 : mDone = true;
1566 0 : mCondVar.Notify();
1567 : }
1568 :
1569 0 : return true;
1570 : }
1571 : };
1572 :
1573 : } /* anonymous namespace */
1574 :
1575 : #ifdef DEBUG
1576 : void
1577 867573 : mozilla::dom::workers::AssertIsOnMainThread()
1578 : {
1579 867573 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1580 867573 : }
1581 :
1582 0 : WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
1583 : BusyBehavior aBusyBehavior,
1584 : ClearingBehavior aClearingBehavior)
1585 : : mWorkerPrivate(aWorkerPrivate), mTarget(aTarget),
1586 0 : mBusyBehavior(aBusyBehavior), mClearingBehavior(aClearingBehavior)
1587 : {
1588 0 : NS_ASSERTION(aWorkerPrivate, "Null worker private!");
1589 0 : }
1590 : #endif
1591 :
1592 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerRunnable, nsIRunnable)
1593 :
1594 : bool
1595 0 : WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1596 : {
1597 : #ifdef DEBUG
1598 0 : if (mBusyBehavior == ModifyBusyCount) {
1599 0 : NS_ASSERTION(mTarget == WorkerThread,
1600 : "Don't set this option unless targeting the worker thread!");
1601 : }
1602 0 : if (mTarget == ParentThread) {
1603 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1604 : }
1605 : else {
1606 0 : aWorkerPrivate->AssertIsOnParentThread();
1607 : }
1608 : #endif
1609 :
1610 0 : if (mBusyBehavior == ModifyBusyCount && aCx) {
1611 0 : return aWorkerPrivate->ModifyBusyCount(aCx, true);
1612 : }
1613 :
1614 0 : return true;
1615 : }
1616 :
1617 : bool
1618 0 : WorkerRunnable::Dispatch(JSContext* aCx)
1619 : {
1620 : bool ok;
1621 :
1622 0 : if (!aCx) {
1623 0 : ok = PreDispatch(nsnull, mWorkerPrivate);
1624 0 : if (ok) {
1625 0 : ok = DispatchInternal();
1626 : }
1627 0 : PostDispatch(nsnull, mWorkerPrivate, ok);
1628 0 : return ok;
1629 : }
1630 :
1631 0 : JSAutoRequest ar(aCx);
1632 :
1633 0 : JSObject* global = JS_GetGlobalObject(aCx);
1634 :
1635 0 : JSAutoEnterCompartment ac;
1636 0 : if (global && !ac.enter(aCx, global)) {
1637 0 : return false;
1638 : }
1639 :
1640 0 : ok = PreDispatch(aCx, mWorkerPrivate);
1641 :
1642 0 : if (ok && !DispatchInternal()) {
1643 0 : ok = false;
1644 : }
1645 :
1646 0 : PostDispatch(aCx, mWorkerPrivate, ok);
1647 :
1648 0 : return ok;
1649 : }
1650 :
1651 : // static
1652 : bool
1653 0 : WorkerRunnable::DispatchToMainThread(nsIRunnable* aRunnable)
1654 : {
1655 0 : nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
1656 0 : NS_ASSERTION(mainThread, "This should never fail!");
1657 :
1658 0 : return NS_SUCCEEDED(mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL));
1659 : }
1660 :
1661 : // These DispatchInternal functions look identical but carry important type
1662 : // informaton so they can't be consolidated...
1663 :
1664 : #define IMPL_DISPATCH_INTERNAL(_class) \
1665 : bool \
1666 : _class ::DispatchInternal() \
1667 : { \
1668 : if (mTarget == WorkerThread) { \
1669 : return mWorkerPrivate->Dispatch(this); \
1670 : } \
1671 : \
1672 : if (mWorkerPrivate->GetParent()) { \
1673 : return mWorkerPrivate->GetParent()->Dispatch(this); \
1674 : } \
1675 : \
1676 : return DispatchToMainThread(this); \
1677 : }
1678 :
1679 0 : IMPL_DISPATCH_INTERNAL(WorkerRunnable)
1680 0 : IMPL_DISPATCH_INTERNAL(WorkerSyncRunnable)
1681 0 : IMPL_DISPATCH_INTERNAL(WorkerControlRunnable)
1682 :
1683 : #undef IMPL_DISPATCH_INTERNAL
1684 :
1685 : void
1686 0 : WorkerRunnable::PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1687 : bool aDispatchResult)
1688 : {
1689 : #ifdef DEBUG
1690 0 : if (mTarget == ParentThread) {
1691 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1692 : }
1693 : else {
1694 0 : aWorkerPrivate->AssertIsOnParentThread();
1695 : }
1696 : #endif
1697 :
1698 0 : if (!aDispatchResult && aCx) {
1699 0 : if (mBusyBehavior == ModifyBusyCount) {
1700 0 : aWorkerPrivate->ModifyBusyCount(aCx, false);
1701 : }
1702 0 : JS_ReportPendingException(aCx);
1703 : }
1704 0 : }
1705 :
1706 : NS_IMETHODIMP
1707 0 : WorkerRunnable::Run()
1708 : {
1709 : JSContext* cx;
1710 : JSObject* targetCompartmentObject;
1711 0 : nsIThreadJSContextStack* contextStack = nsnull;
1712 :
1713 0 : if (mTarget == WorkerThread) {
1714 0 : mWorkerPrivate->AssertIsOnWorkerThread();
1715 0 : cx = mWorkerPrivate->GetJSContext();
1716 0 : targetCompartmentObject = JS_GetGlobalObject(cx);
1717 : } else {
1718 0 : mWorkerPrivate->AssertIsOnParentThread();
1719 0 : cx = mWorkerPrivate->ParentJSContext();
1720 0 : targetCompartmentObject = mWorkerPrivate->GetJSObject();
1721 :
1722 0 : if (!mWorkerPrivate->GetParent()) {
1723 0 : AssertIsOnMainThread();
1724 :
1725 0 : contextStack = nsContentUtils::ThreadJSContextStack();
1726 0 : NS_ASSERTION(contextStack, "This should never be null!");
1727 :
1728 0 : if (NS_FAILED(contextStack->Push(cx))) {
1729 0 : NS_WARNING("Failed to push context!");
1730 0 : contextStack = nsnull;
1731 : }
1732 : }
1733 : }
1734 :
1735 0 : NS_ASSERTION(cx, "Must have a context!");
1736 :
1737 0 : JSAutoRequest ar(cx);
1738 :
1739 0 : JSAutoEnterCompartment ac;
1740 0 : if (targetCompartmentObject && !ac.enter(cx, targetCompartmentObject)) {
1741 0 : return false;
1742 : }
1743 :
1744 0 : bool result = WorkerRun(cx, mWorkerPrivate);
1745 :
1746 0 : PostRun(cx, mWorkerPrivate, result);
1747 :
1748 0 : if (contextStack) {
1749 : JSContext* otherCx;
1750 0 : if (NS_FAILED(contextStack->Pop(&otherCx))) {
1751 0 : NS_WARNING("Failed to pop context!");
1752 : }
1753 0 : else if (otherCx != cx) {
1754 0 : NS_WARNING("Popped a different context!");
1755 : }
1756 : }
1757 :
1758 0 : return result ? NS_OK : NS_ERROR_FAILURE;
1759 : }
1760 :
1761 : void
1762 0 : WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
1763 : bool aRunResult)
1764 : {
1765 : #ifdef DEBUG
1766 0 : if (mTarget == ParentThread) {
1767 0 : mWorkerPrivate->AssertIsOnParentThread();
1768 : }
1769 : else {
1770 0 : mWorkerPrivate->AssertIsOnWorkerThread();
1771 : }
1772 : #endif
1773 :
1774 0 : if (mBusyBehavior == ModifyBusyCount) {
1775 0 : if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
1776 0 : aRunResult = false;
1777 : }
1778 : }
1779 :
1780 0 : if (!aRunResult) {
1781 0 : JS_ReportPendingException(aCx);
1782 : }
1783 0 : }
1784 :
1785 : struct WorkerPrivate::TimeoutInfo
1786 : {
1787 0 : TimeoutInfo()
1788 : : mTimeoutVal(JSVAL_VOID), mLineNumber(0), mId(0), mIsInterval(false),
1789 0 : mCanceled(false)
1790 : {
1791 0 : MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
1792 0 : }
1793 :
1794 0 : ~TimeoutInfo()
1795 0 : {
1796 0 : MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
1797 0 : }
1798 :
1799 0 : bool operator==(const TimeoutInfo& aOther)
1800 : {
1801 0 : return mTargetTime == aOther.mTargetTime;
1802 : }
1803 :
1804 0 : bool operator<(const TimeoutInfo& aOther)
1805 : {
1806 0 : return mTargetTime < aOther.mTargetTime;
1807 : }
1808 :
1809 : jsval mTimeoutVal;
1810 : nsTArray<jsval> mExtraArgVals;
1811 : mozilla::TimeStamp mTargetTime;
1812 : mozilla::TimeDuration mInterval;
1813 : nsCString mFilename;
1814 : PRUint32 mLineNumber;
1815 : PRUint32 mId;
1816 : bool mIsInterval;
1817 : bool mCanceled;
1818 : };
1819 :
1820 : template <class Derived>
1821 : WorkerPrivateParent<Derived>::WorkerPrivateParent(
1822 : JSContext* aCx, JSObject* aObject,
1823 : WorkerPrivate* aParent,
1824 : JSContext* aParentJSContext,
1825 : const nsAString& aScriptURL,
1826 : bool aIsChromeWorker,
1827 : const nsACString& aDomain,
1828 : nsCOMPtr<nsPIDOMWindow>& aWindow,
1829 : nsCOMPtr<nsIScriptContext>& aScriptContext,
1830 : nsCOMPtr<nsIURI>& aBaseURI,
1831 : nsCOMPtr<nsIPrincipal>& aPrincipal,
1832 : nsCOMPtr<nsIDocument>& aDocument)
1833 : : mMutex("WorkerPrivateParent Mutex"),
1834 : mCondVar(mMutex, "WorkerPrivateParent CondVar"),
1835 : mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext),
1836 : mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0),
1837 : mParentStatus(Pending), mJSContextOptions(0), mJSRuntimeHeapSize(0),
1838 : mGCZeal(0), mJSObjectRooted(false), mParentSuspended(false),
1839 0 : mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false)
1840 : {
1841 0 : MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
1842 :
1843 0 : if (aWindow) {
1844 0 : NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!");
1845 : }
1846 :
1847 0 : mWindow.swap(aWindow);
1848 0 : mScriptContext.swap(aScriptContext);
1849 0 : mBaseURI.swap(aBaseURI);
1850 0 : mPrincipal.swap(aPrincipal);
1851 0 : mDocument.swap(aDocument);
1852 :
1853 0 : if (aParent) {
1854 0 : aParent->AssertIsOnWorkerThread();
1855 :
1856 0 : NS_ASSERTION(JS_GetOptions(aCx) == aParent->GetJSContextOptions(),
1857 : "Options mismatch!");
1858 0 : mJSContextOptions = aParent->GetJSContextOptions();
1859 :
1860 0 : NS_ASSERTION(JS_GetGCParameter(JS_GetRuntime(aCx), JSGC_MAX_BYTES) ==
1861 : aParent->GetJSRuntimeHeapSize(),
1862 : "Runtime heap size mismatch!");
1863 0 : mJSRuntimeHeapSize = aParent->GetJSRuntimeHeapSize();
1864 :
1865 : #ifdef JS_GC_ZEAL
1866 0 : mGCZeal = aParent->GetGCZeal();
1867 : #endif
1868 : }
1869 : else {
1870 0 : AssertIsOnMainThread();
1871 :
1872 0 : mJSContextOptions = RuntimeService::GetDefaultJSContextOptions();
1873 0 : mJSRuntimeHeapSize = RuntimeService::GetDefaultJSRuntimeHeapSize();
1874 : #ifdef JS_GC_ZEAL
1875 0 : mGCZeal = RuntimeService::GetDefaultGCZeal();
1876 : #endif
1877 : }
1878 0 : }
1879 :
1880 : template <class Derived>
1881 : WorkerPrivateParent<Derived>::~WorkerPrivateParent()
1882 : {
1883 0 : MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivateParent);
1884 0 : }
1885 :
1886 : template <class Derived>
1887 : bool
1888 : WorkerPrivateParent<Derived>::Start()
1889 : {
1890 : // May be called on any thread!
1891 : {
1892 0 : MutexAutoLock lock(mMutex);
1893 :
1894 0 : NS_ASSERTION(mParentStatus != Running, "How can this be?!");
1895 :
1896 0 : if (mParentStatus == Pending) {
1897 0 : mParentStatus = Running;
1898 0 : return true;
1899 : }
1900 : }
1901 :
1902 0 : return false;
1903 : }
1904 :
1905 : template <class Derived>
1906 : bool
1907 : WorkerPrivateParent<Derived>::NotifyPrivate(JSContext* aCx, Status aStatus,
1908 : bool aFromJSObjectFinalizer)
1909 : {
1910 0 : AssertIsOnParentThread();
1911 :
1912 : bool pending;
1913 : {
1914 0 : MutexAutoLock lock(mMutex);
1915 :
1916 0 : if (mParentStatus >= aStatus) {
1917 0 : return true;
1918 : }
1919 :
1920 0 : pending = mParentStatus == Pending;
1921 0 : mParentStatus = aStatus;
1922 : }
1923 :
1924 0 : FinalizeInstance(aCx, false);
1925 :
1926 0 : if (pending) {
1927 0 : WorkerPrivate* self = ParentAsWorkerPrivate();
1928 : #ifdef DEBUG
1929 : {
1930 : // Silence useless assertions in debug builds.
1931 0 : nsIThread* currentThread = NS_GetCurrentThread();
1932 0 : NS_ASSERTION(currentThread, "This should never be null!");
1933 :
1934 0 : self->SetThread(currentThread);
1935 : }
1936 : #endif
1937 : // Worker never got a chance to run, go ahead and delete it.
1938 0 : self->ScheduleDeletion(true);
1939 0 : return true;
1940 : }
1941 :
1942 0 : NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
1943 : "Shouldn't have anything queued!");
1944 :
1945 : // Anything queued will be discarded.
1946 0 : mQueuedRunnables.Clear();
1947 :
1948 : nsRefPtr<NotifyRunnable> runnable =
1949 : new NotifyRunnable(ParentAsWorkerPrivate(), aFromJSObjectFinalizer,
1950 0 : aStatus);
1951 0 : return runnable->Dispatch(aFromJSObjectFinalizer ? nsnull : aCx);
1952 : }
1953 :
1954 : template <class Derived>
1955 : bool
1956 : WorkerPrivateParent<Derived>::Suspend(JSContext* aCx)
1957 : {
1958 0 : AssertIsOnParentThread();
1959 0 : NS_ASSERTION(!mParentSuspended, "Suspended more than once!");
1960 :
1961 0 : mParentSuspended = true;
1962 :
1963 : {
1964 0 : MutexAutoLock lock(mMutex);
1965 :
1966 0 : if (mParentStatus >= Terminating) {
1967 0 : return true;
1968 : }
1969 : }
1970 :
1971 : nsRefPtr<SuspendRunnable> runnable =
1972 0 : new SuspendRunnable(ParentAsWorkerPrivate());
1973 0 : return runnable->Dispatch(aCx);
1974 : }
1975 :
1976 : template <class Derived>
1977 : bool
1978 : WorkerPrivateParent<Derived>::Resume(JSContext* aCx)
1979 : {
1980 0 : AssertIsOnParentThread();
1981 0 : NS_ASSERTION(mParentSuspended, "Not yet suspended!");
1982 :
1983 0 : mParentSuspended = false;
1984 :
1985 : {
1986 0 : MutexAutoLock lock(mMutex);
1987 :
1988 0 : if (mParentStatus >= Terminating) {
1989 0 : return true;
1990 : }
1991 : }
1992 :
1993 : // Dispatch queued runnables before waking up the worker, otherwise the worker
1994 : // could post new messages before we run those that have been queued.
1995 0 : if (!mQueuedRunnables.IsEmpty()) {
1996 0 : AssertIsOnMainThread();
1997 :
1998 0 : nsTArray<nsRefPtr<WorkerRunnable> > runnables;
1999 0 : mQueuedRunnables.SwapElements(runnables);
2000 :
2001 0 : for (PRUint32 index = 0; index < runnables.Length(); index++) {
2002 0 : nsRefPtr<WorkerRunnable>& runnable = runnables[index];
2003 0 : if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
2004 0 : NS_WARNING("Failed to dispatch queued runnable!");
2005 : }
2006 : }
2007 : }
2008 :
2009 : nsRefPtr<ResumeRunnable> runnable =
2010 0 : new ResumeRunnable(ParentAsWorkerPrivate());
2011 0 : if (!runnable->Dispatch(aCx)) {
2012 0 : return false;
2013 : }
2014 :
2015 0 : return true;
2016 : }
2017 :
2018 : template <class Derived>
2019 : void
2020 : WorkerPrivateParent<Derived>::FinalizeInstance(JSContext* aCx,
2021 : bool aFromJSFinalizer)
2022 : {
2023 0 : AssertIsOnParentThread();
2024 :
2025 0 : if (mJSObject) {
2026 : // Make sure we're in the right compartment, but only enter one if this is
2027 : // not running from a finalizer.
2028 0 : JSAutoEnterCompartment ac;
2029 0 : if (!aFromJSFinalizer && !ac.enter(aCx, mJSObject)) {
2030 0 : NS_ERROR("How can this fail?!");
2031 : return;
2032 : }
2033 :
2034 : // Decouple the object from the private now.
2035 0 : worker::ClearPrivateSlot(aCx, mJSObject, !aFromJSFinalizer);
2036 :
2037 : // Clear the JS object.
2038 0 : mJSObject = nsnull;
2039 :
2040 : // Unroot.
2041 0 : RootJSObject(aCx, false);
2042 :
2043 0 : if (!TerminatePrivate(aCx, aFromJSFinalizer)) {
2044 0 : NS_WARNING("Failed to terminate!");
2045 : }
2046 :
2047 0 : events::EventTarget::FinalizeInstance(aCx);
2048 : }
2049 : }
2050 :
2051 : template <class Derived>
2052 : bool
2053 : WorkerPrivateParent<Derived>::Close(JSContext* aCx)
2054 : {
2055 0 : AssertIsOnParentThread();
2056 :
2057 : {
2058 0 : MutexAutoLock lock(mMutex);
2059 :
2060 0 : if (mParentStatus < Closing) {
2061 0 : mParentStatus = Closing;
2062 : }
2063 : }
2064 :
2065 0 : return true;
2066 : }
2067 :
2068 : template <class Derived>
2069 : bool
2070 : WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease)
2071 : {
2072 0 : AssertIsOnParentThread();
2073 :
2074 0 : NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
2075 :
2076 0 : if (aIncrease) {
2077 0 : if (mBusyCount++ == 0) {
2078 0 : if (!RootJSObject(aCx, true)) {
2079 0 : return false;
2080 : }
2081 : }
2082 0 : return true;
2083 : }
2084 :
2085 0 : if (--mBusyCount == 0) {
2086 0 : if (!RootJSObject(aCx, false)) {
2087 0 : return false;
2088 : }
2089 :
2090 : bool shouldCancel;
2091 : {
2092 0 : MutexAutoLock lock(mMutex);
2093 0 : shouldCancel = mParentStatus == Terminating;
2094 : }
2095 :
2096 0 : if (shouldCancel && !Cancel(aCx)) {
2097 0 : return false;
2098 : }
2099 : }
2100 :
2101 0 : return true;
2102 : }
2103 :
2104 : template <class Derived>
2105 : bool
2106 : WorkerPrivateParent<Derived>::RootJSObject(JSContext* aCx, bool aRoot)
2107 : {
2108 0 : AssertIsOnParentThread();
2109 :
2110 0 : if (aRoot) {
2111 0 : if (mJSObjectRooted || !mJSObject) {
2112 0 : return true;
2113 : }
2114 :
2115 0 : if (!JS_AddNamedObjectRoot(aCx, &mJSObject, "Worker root")) {
2116 0 : NS_WARNING("JS_AddNamedObjectRoot failed!");
2117 0 : return false;
2118 : }
2119 : }
2120 : else {
2121 0 : if (!mJSObjectRooted) {
2122 0 : return true;
2123 : }
2124 :
2125 0 : if (!JS_RemoveObjectRoot(aCx, &mJSObject)) {
2126 0 : NS_WARNING("JS_RemoveObjectRoot failed!");
2127 0 : return false;
2128 : }
2129 : }
2130 :
2131 0 : mJSObjectRooted = aRoot;
2132 0 : return true;
2133 : }
2134 :
2135 : template <class Derived>
2136 : void
2137 : WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
2138 : nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
2139 : {
2140 0 : AssertIsOnParentThread();
2141 :
2142 0 : aDoomed.SetCapacity(6);
2143 :
2144 0 : SwapToISupportsArray(mWindow, aDoomed);
2145 0 : SwapToISupportsArray(mScriptContext, aDoomed);
2146 0 : SwapToISupportsArray(mBaseURI, aDoomed);
2147 0 : SwapToISupportsArray(mScriptURI, aDoomed);
2148 0 : SwapToISupportsArray(mPrincipal, aDoomed);
2149 0 : SwapToISupportsArray(mDocument, aDoomed);
2150 0 : }
2151 :
2152 : template <class Derived>
2153 : bool
2154 : WorkerPrivateParent<Derived>::PostMessage(JSContext* aCx, jsval aMessage)
2155 : {
2156 0 : AssertIsOnParentThread();
2157 :
2158 : JSStructuredCloneCallbacks* callbacks;
2159 0 : if (GetParent()) {
2160 0 : if (IsChromeWorker()) {
2161 0 : callbacks = &gChromeWorkerStructuredCloneCallbacks;
2162 : }
2163 : else {
2164 0 : callbacks = &gWorkerStructuredCloneCallbacks;
2165 : }
2166 : }
2167 : else {
2168 0 : AssertIsOnMainThread();
2169 :
2170 0 : if (IsChromeWorker()) {
2171 0 : callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks;
2172 : }
2173 : else {
2174 0 : callbacks = &gMainThreadWorkerStructuredCloneCallbacks;
2175 : }
2176 : }
2177 :
2178 0 : nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
2179 :
2180 0 : JSAutoStructuredCloneBuffer buffer;
2181 0 : if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
2182 0 : return false;
2183 : }
2184 :
2185 : nsRefPtr<MessageEventRunnable> runnable =
2186 : new MessageEventRunnable(ParentAsWorkerPrivate(),
2187 : WorkerRunnable::WorkerThread, buffer,
2188 0 : clonedObjects);
2189 0 : return runnable->Dispatch(aCx);
2190 : }
2191 :
2192 : template <class Derived>
2193 : PRUint64
2194 : WorkerPrivateParent<Derived>::GetInnerWindowId()
2195 : {
2196 0 : AssertIsOnMainThread();
2197 0 : return mDocument ? mDocument->InnerWindowID() : 0;
2198 : }
2199 :
2200 : template <class Derived>
2201 : void
2202 : WorkerPrivateParent<Derived>::UpdateJSContextOptions(JSContext* aCx,
2203 : PRUint32 aOptions)
2204 : {
2205 0 : AssertIsOnParentThread();
2206 :
2207 0 : mJSContextOptions = aOptions;
2208 :
2209 : nsRefPtr<UpdateJSContextOptionsRunnable> runnable =
2210 0 : new UpdateJSContextOptionsRunnable(ParentAsWorkerPrivate(), aOptions);
2211 0 : if (!runnable->Dispatch(aCx)) {
2212 0 : NS_WARNING("Failed to update worker context options!");
2213 0 : JS_ClearPendingException(aCx);
2214 : }
2215 0 : }
2216 :
2217 : template <class Derived>
2218 : void
2219 : WorkerPrivateParent<Derived>::UpdateJSRuntimeHeapSize(JSContext* aCx,
2220 : PRUint32 aMaxBytes)
2221 : {
2222 0 : AssertIsOnParentThread();
2223 :
2224 0 : mJSRuntimeHeapSize = aMaxBytes;
2225 :
2226 : nsRefPtr<UpdateJSRuntimeHeapSizeRunnable> runnable =
2227 0 : new UpdateJSRuntimeHeapSizeRunnable(ParentAsWorkerPrivate(), aMaxBytes);
2228 0 : if (!runnable->Dispatch(aCx)) {
2229 0 : NS_WARNING("Failed to update worker heap size!");
2230 0 : JS_ClearPendingException(aCx);
2231 : }
2232 0 : }
2233 :
2234 : #ifdef JS_GC_ZEAL
2235 : template <class Derived>
2236 : void
2237 : WorkerPrivateParent<Derived>::UpdateGCZeal(JSContext* aCx, PRUint8 aGCZeal)
2238 : {
2239 0 : AssertIsOnParentThread();
2240 :
2241 0 : mGCZeal = aGCZeal;
2242 :
2243 : nsRefPtr<UpdateGCZealRunnable> runnable =
2244 0 : new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal);
2245 0 : if (!runnable->Dispatch(aCx)) {
2246 0 : NS_WARNING("Failed to update worker gczeal!");
2247 0 : JS_ClearPendingException(aCx);
2248 : }
2249 0 : }
2250 : #endif
2251 :
2252 : template <class Derived>
2253 : void
2254 : WorkerPrivateParent<Derived>::GarbageCollect(JSContext* aCx, bool aShrinking)
2255 : {
2256 : nsRefPtr<GarbageCollectRunnable> runnable =
2257 0 : new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking, true);
2258 0 : if (!runnable->Dispatch(aCx)) {
2259 0 : NS_WARNING("Failed to update worker heap size!");
2260 0 : JS_ClearPendingException(aCx);
2261 : }
2262 0 : }
2263 :
2264 : template <class Derived>
2265 : void
2266 : WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
2267 : {
2268 0 : AssertIsOnMainThread();
2269 :
2270 0 : mBaseURI = aBaseURI;
2271 :
2272 0 : if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
2273 0 : mLocationInfo.mHref.Truncate();
2274 : }
2275 :
2276 0 : if (NS_FAILED(aBaseURI->GetHost(mLocationInfo.mHostname))) {
2277 0 : mLocationInfo.mHostname.Truncate();
2278 : }
2279 :
2280 0 : if (NS_FAILED(aBaseURI->GetPath(mLocationInfo.mPathname))) {
2281 0 : mLocationInfo.mPathname.Truncate();
2282 : }
2283 :
2284 0 : nsCString temp;
2285 :
2286 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
2287 0 : if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
2288 0 : mLocationInfo.mSearch.AssignLiteral("?");
2289 0 : mLocationInfo.mSearch.Append(temp);
2290 : }
2291 :
2292 0 : if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
2293 : nsCOMPtr<nsITextToSubURI> converter =
2294 0 : do_GetService(NS_ITEXTTOSUBURI_CONTRACTID);
2295 0 : if (converter) {
2296 0 : nsCString charset;
2297 0 : nsAutoString unicodeRef;
2298 0 : if (NS_SUCCEEDED(aBaseURI->GetOriginCharset(charset)) &&
2299 : NS_SUCCEEDED(converter->UnEscapeURIForUI(charset, temp,
2300 : unicodeRef))) {
2301 0 : mLocationInfo.mHash.AssignLiteral("#");
2302 0 : mLocationInfo.mHash.Append(NS_ConvertUTF16toUTF8(unicodeRef));
2303 : }
2304 : }
2305 :
2306 0 : if (mLocationInfo.mHash.IsEmpty()) {
2307 0 : mLocationInfo.mHash.AssignLiteral("#");
2308 0 : mLocationInfo.mHash.Append(temp);
2309 : }
2310 : }
2311 :
2312 0 : if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
2313 0 : mLocationInfo.mProtocol.AppendLiteral(":");
2314 : }
2315 : else {
2316 0 : mLocationInfo.mProtocol.Truncate();
2317 : }
2318 :
2319 : PRInt32 port;
2320 0 : if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
2321 0 : mLocationInfo.mPort.AppendInt(port);
2322 :
2323 0 : nsCAutoString host(mLocationInfo.mHostname);
2324 0 : host.AppendLiteral(":");
2325 0 : host.Append(mLocationInfo.mPort);
2326 :
2327 0 : mLocationInfo.mHost.Assign(host);
2328 : }
2329 : else {
2330 0 : mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
2331 : }
2332 0 : }
2333 :
2334 : template <class Derived>
2335 : void
2336 : WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal)
2337 : {
2338 0 : AssertIsOnMainThread();
2339 :
2340 0 : mPrincipal = aPrincipal;
2341 0 : mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
2342 0 : }
2343 :
2344 : template <class Derived>
2345 : JSContext*
2346 : WorkerPrivateParent<Derived>::ParentJSContext() const
2347 : {
2348 0 : AssertIsOnParentThread();
2349 :
2350 0 : if (!mParent) {
2351 0 : AssertIsOnMainThread();
2352 :
2353 0 : if (!mScriptContext) {
2354 0 : NS_ASSERTION(!mParentJSContext, "Shouldn't have a parent context!");
2355 0 : return RuntimeService::AutoSafeJSContext::GetSafeContext();
2356 : }
2357 :
2358 0 : NS_ASSERTION(mParentJSContext == mScriptContext->GetNativeContext(),
2359 : "Native context has changed!");
2360 : }
2361 :
2362 0 : return mParentJSContext;
2363 : }
2364 :
2365 0 : WorkerPrivate::WorkerPrivate(JSContext* aCx, JSObject* aObject,
2366 : WorkerPrivate* aParent,
2367 : JSContext* aParentJSContext,
2368 : const nsAString& aScriptURL, bool aIsChromeWorker,
2369 : const nsACString& aDomain,
2370 : nsCOMPtr<nsPIDOMWindow>& aWindow,
2371 : nsCOMPtr<nsIScriptContext>& aParentScriptContext,
2372 : nsCOMPtr<nsIURI>& aBaseURI,
2373 : nsCOMPtr<nsIPrincipal>& aPrincipal,
2374 : nsCOMPtr<nsIDocument>& aDocument)
2375 : : WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aParentJSContext,
2376 : aScriptURL, aIsChromeWorker, aDomain,
2377 : aWindow, aParentScriptContext, aBaseURI,
2378 : aPrincipal, aDocument),
2379 : mJSContext(nsnull), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
2380 : mStatus(Pending), mSuspended(false), mTimerRunning(false),
2381 : mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
2382 : mCloseHandlerFinished(false), mMemoryReporterRunning(false),
2383 0 : mMemoryReporterDisabled(false)
2384 : {
2385 0 : MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
2386 0 : }
2387 :
2388 0 : WorkerPrivate::~WorkerPrivate()
2389 : {
2390 0 : MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate);
2391 0 : }
2392 :
2393 : // static
2394 : WorkerPrivate*
2395 0 : WorkerPrivate::Create(JSContext* aCx, JSObject* aObj, WorkerPrivate* aParent,
2396 : JSString* aScriptURL, bool aIsChromeWorker)
2397 : {
2398 0 : nsCString domain;
2399 0 : nsCOMPtr<nsIURI> baseURI;
2400 0 : nsCOMPtr<nsIPrincipal> principal;
2401 0 : nsCOMPtr<nsIScriptContext> scriptContext;
2402 0 : nsCOMPtr<nsIDocument> document;
2403 0 : nsCOMPtr<nsPIDOMWindow> window;
2404 :
2405 : JSContext* parentContext;
2406 :
2407 0 : if (aParent) {
2408 0 : aParent->AssertIsOnWorkerThread();
2409 :
2410 0 : parentContext = aCx;
2411 :
2412 : // Domain is the only thing we can touch here. The rest will be handled by
2413 : // the ScriptLoader.
2414 0 : domain = aParent->Domain();
2415 : }
2416 : else {
2417 0 : AssertIsOnMainThread();
2418 :
2419 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
2420 0 : NS_ASSERTION(ssm, "This should never be null!");
2421 :
2422 : bool isChrome;
2423 0 : if (NS_FAILED(ssm->IsCapabilityEnabled("UniversalXPConnect", &isChrome))) {
2424 0 : NS_WARNING("IsCapabilityEnabled failed!");
2425 0 : isChrome = false;
2426 : }
2427 :
2428 : // First check to make sure the caller has permission to make a
2429 : // ChromeWorker if they called the ChromeWorker constructor.
2430 0 : if (aIsChromeWorker && !isChrome) {
2431 0 : nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_SECURITY_ERR);
2432 0 : return nsnull;
2433 : }
2434 :
2435 : // Chrome callers (whether ChromeWorker of Worker) always get the system
2436 : // principal here as they're allowed to load anything. The script loader may
2437 : // change the principal later depending on the script uri.
2438 0 : if (isChrome &&
2439 0 : NS_FAILED(ssm->GetSystemPrincipal(getter_AddRefs(principal)))) {
2440 0 : JS_ReportError(aCx, "Could not get system principal!");
2441 0 : return nsnull;
2442 : }
2443 :
2444 : // See if we're being called from a window or from somewhere else.
2445 : nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
2446 0 : nsJSUtils::GetStaticScriptGlobal(aCx, JS_GetGlobalForScopeChain(aCx));
2447 0 : if (scriptGlobal) {
2448 : // Window!
2449 0 : nsCOMPtr<nsPIDOMWindow> globalWindow = do_QueryInterface(scriptGlobal);
2450 :
2451 : // Only use the current inner window, and only use it if the caller can
2452 : // access it.
2453 : nsPIDOMWindow* outerWindow = globalWindow ?
2454 0 : globalWindow->GetOuterWindow() :
2455 0 : nsnull;
2456 0 : window = outerWindow ? outerWindow->GetCurrentInnerWindow() : nsnull;
2457 0 : if (!window ||
2458 0 : (globalWindow != window &&
2459 0 : !nsContentUtils::CanCallerAccess(window))) {
2460 0 : nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_SECURITY_ERR);
2461 0 : return nsnull;
2462 : }
2463 :
2464 0 : scriptContext = scriptGlobal->GetContext();
2465 0 : if (!scriptContext) {
2466 0 : JS_ReportError(aCx, "Couldn't get script context for this worker!");
2467 0 : return nsnull;
2468 : }
2469 :
2470 0 : parentContext = scriptContext->GetNativeContext();
2471 :
2472 : // If we're called from a window then we can dig out the principal and URI
2473 : // from the document.
2474 0 : document = do_QueryInterface(window->GetExtantDocument());
2475 0 : if (!document) {
2476 0 : JS_ReportError(aCx, "No document in this window!");
2477 0 : return nsnull;
2478 : }
2479 :
2480 0 : baseURI = document->GetDocBaseURI();
2481 :
2482 : // Use the document's NodePrincipal as our principal if we're not being
2483 : // called from chrome.
2484 0 : if (!principal) {
2485 0 : if (!(principal = document->NodePrincipal())) {
2486 0 : JS_ReportError(aCx, "Could not get document principal!");
2487 0 : return nsnull;
2488 : }
2489 :
2490 0 : nsCOMPtr<nsIURI> codebase;
2491 0 : if (NS_FAILED(principal->GetURI(getter_AddRefs(codebase)))) {
2492 0 : JS_ReportError(aCx, "Could not determine codebase!");
2493 0 : return nsnull;
2494 : }
2495 :
2496 0 : NS_NAMED_LITERAL_CSTRING(file, "file");
2497 :
2498 : bool isFile;
2499 0 : if (NS_FAILED(codebase->SchemeIs(file.get(), &isFile))) {
2500 0 : JS_ReportError(aCx, "Could not determine if codebase is file!");
2501 0 : return nsnull;
2502 : }
2503 :
2504 0 : if (isFile) {
2505 : // XXX Fix this, need a real domain here.
2506 0 : domain = file;
2507 : }
2508 : else {
2509 : nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
2510 0 : do_GetService(THIRDPARTYUTIL_CONTRACTID);
2511 0 : if (!thirdPartyUtil) {
2512 0 : JS_ReportError(aCx, "Could not get third party helper service!");
2513 0 : return nsnull;
2514 : }
2515 :
2516 0 : if (NS_FAILED(thirdPartyUtil->GetBaseDomain(codebase, domain))) {
2517 0 : JS_ReportError(aCx, "Could not get domain!");
2518 0 : return nsnull;
2519 : }
2520 : }
2521 : }
2522 : }
2523 : else {
2524 : // Not a window
2525 0 : NS_ASSERTION(isChrome, "Should be chrome only!");
2526 :
2527 0 : parentContext = nsnull;
2528 :
2529 : // We're being created outside of a window. Need to figure out the script
2530 : // that is creating us in order for us to use relative URIs later on.
2531 : JSScript *script;
2532 0 : if (JS_DescribeScriptedCaller(aCx, &script, nsnull)) {
2533 0 : if (NS_FAILED(NS_NewURI(getter_AddRefs(baseURI),
2534 : JS_GetScriptFilename(aCx, script)))) {
2535 0 : JS_ReportError(aCx, "Failed to construct base URI!");
2536 0 : return nsnull;
2537 : }
2538 : }
2539 : }
2540 :
2541 0 : NS_ASSERTION(principal, "Must have a principal now!");
2542 :
2543 0 : if (!isChrome && domain.IsEmpty()) {
2544 0 : NS_ERROR("Must be chrome or have an domain!");
2545 0 : return nsnull;
2546 : }
2547 : }
2548 :
2549 : size_t urlLength;
2550 : const jschar* urlChars = JS_GetStringCharsZAndLength(aCx, aScriptURL,
2551 0 : &urlLength);
2552 0 : if (!urlChars) {
2553 0 : return nsnull;
2554 : }
2555 :
2556 0 : nsDependentString scriptURL(urlChars, urlLength);
2557 :
2558 : nsAutoPtr<WorkerPrivate> worker(
2559 : new WorkerPrivate(aCx, aObj, aParent, parentContext, scriptURL,
2560 : aIsChromeWorker, domain, window, scriptContext, baseURI,
2561 0 : principal, document));
2562 :
2563 0 : nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
2564 0 : if (!compiler->Dispatch(aCx)) {
2565 0 : return nsnull;
2566 : }
2567 :
2568 0 : return worker.forget();
2569 : }
2570 :
2571 : void
2572 0 : WorkerPrivate::DoRunLoop(JSContext* aCx)
2573 : {
2574 0 : AssertIsOnWorkerThread();
2575 :
2576 : {
2577 0 : MutexAutoLock lock(mMutex);
2578 0 : mJSContext = aCx;
2579 :
2580 0 : NS_ASSERTION(mStatus == Pending, "Huh?!");
2581 0 : mStatus = Running;
2582 : }
2583 :
2584 : // We need a timer for GC. The basic plan is to run a normal (non-shrinking)
2585 : // GC periodically (NORMAL_GC_TIMER_DELAY_MS) while the worker is running.
2586 : // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_MS) timer to
2587 : // run a shrinking GC. If the worker receives more messages then the short
2588 : // timer is canceled and the periodic timer resumes.
2589 0 : nsCOMPtr<nsITimer> gcTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
2590 0 : if (!gcTimer) {
2591 0 : JS_ReportError(aCx, "Failed to create GC timer!");
2592 : return;
2593 : }
2594 :
2595 0 : bool normalGCTimerRunning = false;
2596 :
2597 : // We need to swap event targets below to get different types of GC behavior.
2598 0 : nsCOMPtr<nsIEventTarget> normalGCEventTarget;
2599 0 : nsCOMPtr<nsIEventTarget> idleGCEventTarget;
2600 :
2601 : // We also need to track the idle GC event so that we don't confuse it with a
2602 : // generic event that should re-trigger the idle GC timer.
2603 0 : nsCOMPtr<nsIRunnable> idleGCEvent;
2604 : {
2605 : nsRefPtr<GarbageCollectRunnable> runnable =
2606 0 : new GarbageCollectRunnable(this, false, false);
2607 0 : normalGCEventTarget = new WorkerRunnableEventTarget(runnable);
2608 :
2609 0 : runnable = new GarbageCollectRunnable(this, true, false);
2610 0 : idleGCEventTarget = new WorkerRunnableEventTarget(runnable);
2611 :
2612 0 : idleGCEvent = runnable;
2613 : }
2614 :
2615 0 : mMemoryReporter = new WorkerMemoryReporter(this);
2616 :
2617 0 : if (NS_FAILED(NS_RegisterMemoryMultiReporter(mMemoryReporter))) {
2618 0 : NS_WARNING("Failed to register memory reporter!");
2619 0 : mMemoryReporter = nsnull;
2620 : }
2621 :
2622 0 : for (;;) {
2623 : Status currentStatus;
2624 : bool scheduleIdleGC;
2625 :
2626 : WorkerRunnable* event;
2627 : {
2628 0 : MutexAutoLock lock(mMutex);
2629 :
2630 0 : while (!mControlQueue.Pop(event) && !mQueue.Pop(event)) {
2631 0 : mCondVar.Wait();
2632 : }
2633 :
2634 : bool eventIsNotIdleGCEvent;
2635 0 : currentStatus = mStatus;
2636 :
2637 : {
2638 0 : MutexAutoUnlock unlock(mMutex);
2639 :
2640 0 : if (!normalGCTimerRunning &&
2641 0 : event != idleGCEvent &&
2642 : currentStatus <= Terminating) {
2643 : // Must always cancel before changing the timer's target.
2644 0 : if (NS_FAILED(gcTimer->Cancel())) {
2645 0 : NS_WARNING("Failed to cancel GC timer!");
2646 : }
2647 :
2648 0 : if (NS_SUCCEEDED(gcTimer->SetTarget(normalGCEventTarget)) &&
2649 0 : NS_SUCCEEDED(gcTimer->InitWithFuncCallback(
2650 : DummyCallback, nsnull,
2651 : NORMAL_GC_TIMER_DELAY_MS,
2652 : nsITimer::TYPE_REPEATING_SLACK))) {
2653 0 : normalGCTimerRunning = true;
2654 : }
2655 : else {
2656 0 : JS_ReportError(aCx, "Failed to start normal GC timer!");
2657 : }
2658 : }
2659 :
2660 : #ifdef EXTRA_GC
2661 : // Find GC bugs...
2662 : JS_GC(aCx);
2663 : #endif
2664 :
2665 : // Keep track of whether or not this is the idle GC event.
2666 0 : eventIsNotIdleGCEvent = event != idleGCEvent;
2667 :
2668 0 : static_cast<nsIRunnable*>(event)->Run();
2669 0 : NS_RELEASE(event);
2670 : }
2671 :
2672 0 : currentStatus = mStatus;
2673 0 : scheduleIdleGC = mControlQueue.IsEmpty() &&
2674 0 : mQueue.IsEmpty() &&
2675 0 : eventIsNotIdleGCEvent;
2676 : }
2677 :
2678 : // Take care of the GC timer. If we're starting the close sequence then we
2679 : // kill the timer once and for all. Otherwise we schedule the idle timeout
2680 : // if there are no more events.
2681 0 : if (currentStatus > Terminating || scheduleIdleGC) {
2682 0 : if (NS_SUCCEEDED(gcTimer->Cancel())) {
2683 0 : normalGCTimerRunning = false;
2684 : }
2685 : else {
2686 0 : NS_WARNING("Failed to cancel GC timer!");
2687 : }
2688 : }
2689 :
2690 0 : if (scheduleIdleGC) {
2691 0 : if (NS_SUCCEEDED(gcTimer->SetTarget(idleGCEventTarget)) &&
2692 0 : NS_SUCCEEDED(gcTimer->InitWithFuncCallback(
2693 : DummyCallback, nsnull,
2694 : IDLE_GC_TIMER_DELAY_MS,
2695 : nsITimer::TYPE_ONE_SHOT))) {
2696 : }
2697 : else {
2698 0 : JS_ReportError(aCx, "Failed to start idle GC timer!");
2699 : }
2700 : }
2701 :
2702 : #ifdef EXTRA_GC
2703 : // Find GC bugs...
2704 : JS_GC(aCx);
2705 : #endif
2706 :
2707 0 : if (currentStatus != Running && !HasActiveFeatures()) {
2708 : // If the close handler has finished and all features are done then we can
2709 : // kill this thread.
2710 0 : if (mCloseHandlerFinished && currentStatus != Killing) {
2711 0 : if (!NotifyInternal(aCx, Killing)) {
2712 0 : JS_ReportPendingException(aCx);
2713 : }
2714 : #ifdef DEBUG
2715 : {
2716 0 : MutexAutoLock lock(mMutex);
2717 0 : currentStatus = mStatus;
2718 : }
2719 0 : NS_ASSERTION(currentStatus == Killing, "Should have changed status!");
2720 : #else
2721 : currentStatus = Killing;
2722 : #endif
2723 : }
2724 :
2725 : // If we're supposed to die then we should exit the loop.
2726 0 : if (currentStatus == Killing) {
2727 : // Always make sure the timer is canceled.
2728 0 : if (NS_FAILED(gcTimer->Cancel())) {
2729 0 : NS_WARNING("Failed to cancel the GC timer!");
2730 : }
2731 :
2732 : // Call this before unregistering the reporter as we may be racing with
2733 : // the main thread.
2734 0 : DisableMemoryReporter();
2735 :
2736 0 : if (mMemoryReporter) {
2737 0 : if (NS_FAILED(NS_UnregisterMemoryMultiReporter(mMemoryReporter))) {
2738 0 : NS_WARNING("Failed to unregister memory reporter!");
2739 : }
2740 0 : mMemoryReporter = nsnull;
2741 : }
2742 :
2743 0 : StopAcceptingEvents();
2744 : return;
2745 : }
2746 : }
2747 : }
2748 :
2749 0 : NS_NOTREACHED("Shouldn't get here!");
2750 : }
2751 :
2752 : bool
2753 0 : WorkerPrivate::OperationCallback(JSContext* aCx)
2754 : {
2755 0 : AssertIsOnWorkerThread();
2756 :
2757 0 : bool mayContinue = true;
2758 :
2759 0 : for (;;) {
2760 : // Run all control events now.
2761 0 : mayContinue = ProcessAllControlRunnables();
2762 :
2763 0 : if (!mayContinue || !mSuspended) {
2764 : break;
2765 : }
2766 :
2767 : // Clean up before suspending.
2768 0 : JS_FlushCaches(aCx);
2769 0 : JS_GC(aCx);
2770 :
2771 0 : while ((mayContinue = MayContinueRunning())) {
2772 0 : MutexAutoLock lock(mMutex);
2773 0 : if (!mControlQueue.IsEmpty()) {
2774 : break;
2775 : }
2776 :
2777 0 : mCondVar.Wait(PR_MillisecondsToInterval(RemainingRunTimeMS()));
2778 : }
2779 : }
2780 :
2781 0 : if (!mayContinue) {
2782 : // We want only uncatchable exceptions here.
2783 0 : NS_ASSERTION(!JS_IsExceptionPending(aCx),
2784 : "Should not have an exception set here!");
2785 0 : return false;
2786 : }
2787 :
2788 0 : return true;
2789 : }
2790 :
2791 : void
2792 0 : WorkerPrivate::ScheduleDeletion(bool aWasPending)
2793 : {
2794 0 : AssertIsOnWorkerThread();
2795 0 : NS_ASSERTION(mChildWorkers.IsEmpty(), "Live child workers!");
2796 0 : NS_ASSERTION(mSyncQueues.IsEmpty(), "Should have no sync queues here!");
2797 :
2798 0 : StopAcceptingEvents();
2799 :
2800 : nsIThread* currentThread;
2801 0 : if (aWasPending) {
2802 : // Don't want to close down this thread since we never got to run!
2803 0 : currentThread = nsnull;
2804 : }
2805 : else {
2806 0 : currentThread = NS_GetCurrentThread();
2807 0 : NS_ASSERTION(currentThread, "This should never be null!");
2808 : }
2809 :
2810 0 : WorkerPrivate* parent = GetParent();
2811 0 : if (parent) {
2812 : nsRefPtr<WorkerFinishedRunnable> runnable =
2813 0 : new WorkerFinishedRunnable(parent, this, currentThread);
2814 0 : if (!runnable->Dispatch(nsnull)) {
2815 0 : NS_WARNING("Failed to dispatch runnable!");
2816 : }
2817 : }
2818 : else {
2819 : nsRefPtr<TopLevelWorkerFinishedRunnable> runnable =
2820 0 : new TopLevelWorkerFinishedRunnable(this, currentThread);
2821 0 : if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
2822 0 : NS_WARNING("Failed to dispatch runnable!");
2823 : }
2824 : }
2825 0 : }
2826 :
2827 : bool
2828 0 : WorkerPrivate::BlockAndCollectRuntimeStats(bool aIsQuick, void* aData, bool* aDisabled)
2829 : {
2830 0 : AssertIsOnMainThread();
2831 0 : NS_ASSERTION(aData, "Null data!");
2832 :
2833 : {
2834 0 : MutexAutoLock lock(mMutex);
2835 :
2836 0 : if (mMemoryReporterDisabled) {
2837 0 : *aDisabled = true;
2838 0 : return true;
2839 : }
2840 :
2841 0 : *aDisabled = false;
2842 0 : mMemoryReporterRunning = true;
2843 : }
2844 :
2845 : bool succeeded;
2846 :
2847 : nsRefPtr<CollectRuntimeStatsRunnable> runnable =
2848 0 : new CollectRuntimeStatsRunnable(this, aIsQuick, aData, &succeeded);
2849 0 : if (!runnable->Dispatch(nsnull)) {
2850 0 : NS_WARNING("Failed to dispatch runnable!");
2851 0 : succeeded = false;
2852 : }
2853 :
2854 : {
2855 0 : MutexAutoLock lock(mMutex);
2856 0 : mMemoryReporterRunning = false;
2857 : }
2858 :
2859 0 : return succeeded;
2860 : }
2861 :
2862 : bool
2863 0 : WorkerPrivate::DisableMemoryReporter()
2864 : {
2865 0 : AssertIsOnWorkerThread();
2866 :
2867 0 : bool result = true;
2868 :
2869 : {
2870 0 : MutexAutoLock lock(mMutex);
2871 :
2872 0 : mMemoryReporterDisabled = true;
2873 :
2874 0 : while (mMemoryReporterRunning) {
2875 0 : MutexAutoUnlock unlock(mMutex);
2876 0 : result = ProcessAllControlRunnables() && result;
2877 : }
2878 : }
2879 :
2880 0 : return result;
2881 : }
2882 :
2883 : bool
2884 0 : WorkerPrivate::ProcessAllControlRunnables()
2885 : {
2886 0 : AssertIsOnWorkerThread();
2887 :
2888 0 : bool result = true;
2889 :
2890 0 : for (;;) {
2891 : WorkerRunnable* event;
2892 : {
2893 0 : MutexAutoLock lock(mMutex);
2894 0 : if (!mControlQueue.Pop(event)) {
2895 : break;
2896 : }
2897 : }
2898 :
2899 0 : if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
2900 0 : result = false;
2901 : }
2902 :
2903 0 : NS_RELEASE(event);
2904 : }
2905 0 : return result;
2906 : }
2907 :
2908 : bool
2909 0 : WorkerPrivate::Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue)
2910 : {
2911 0 : nsRefPtr<WorkerRunnable> event(aEvent);
2912 :
2913 : {
2914 0 : MutexAutoLock lock(mMutex);
2915 :
2916 0 : if (mStatus == Dead) {
2917 : // Nothing may be added after we've set Dead.
2918 0 : return false;
2919 : }
2920 :
2921 0 : if (aQueue == &mQueue) {
2922 : // Check parent status.
2923 0 : Status parentStatus = ParentStatus();
2924 0 : if (parentStatus >= Terminating) {
2925 : // Throw.
2926 0 : return false;
2927 : }
2928 :
2929 : // Check inner status too.
2930 0 : if (parentStatus >= Closing || mStatus >= Closing) {
2931 : // Silently eat this one.
2932 0 : return true;
2933 : }
2934 : }
2935 :
2936 0 : if (!aQueue->Push(event)) {
2937 0 : return false;
2938 : }
2939 :
2940 0 : if (aQueue == &mControlQueue && mJSContext) {
2941 0 : JS_TriggerOperationCallback(JS_GetRuntime(mJSContext));
2942 : }
2943 :
2944 0 : mCondVar.Notify();
2945 : }
2946 :
2947 0 : event.forget();
2948 0 : return true;
2949 : }
2950 :
2951 : bool
2952 0 : WorkerPrivate::DispatchToSyncQueue(WorkerSyncRunnable* aEvent)
2953 : {
2954 0 : nsRefPtr<WorkerRunnable> event(aEvent);
2955 :
2956 : {
2957 0 : MutexAutoLock lock(mMutex);
2958 :
2959 0 : NS_ASSERTION(mSyncQueues.Length() > aEvent->mSyncQueueKey, "Bad event!");
2960 :
2961 0 : if (!mSyncQueues[aEvent->mSyncQueueKey]->mQueue.Push(event)) {
2962 0 : return false;
2963 : }
2964 :
2965 0 : mCondVar.Notify();
2966 : }
2967 :
2968 0 : event.forget();
2969 0 : return true;
2970 : }
2971 :
2972 : void
2973 0 : WorkerPrivate::ClearQueue(EventQueue* aQueue)
2974 : {
2975 0 : AssertIsOnWorkerThread();
2976 0 : mMutex.AssertCurrentThreadOwns();
2977 :
2978 : WorkerRunnable* event;
2979 0 : while (aQueue->Pop(event)) {
2980 0 : if (event->WantsToRunDuringClear()) {
2981 0 : MutexAutoUnlock unlock(mMutex);
2982 :
2983 0 : static_cast<nsIRunnable*>(event)->Run();
2984 : }
2985 0 : event->Release();
2986 : }
2987 0 : }
2988 :
2989 : PRUint32
2990 0 : WorkerPrivate::RemainingRunTimeMS() const
2991 : {
2992 0 : if (mKillTime.IsNull()) {
2993 0 : return PR_UINT32_MAX;
2994 : }
2995 0 : TimeDuration runtime = mKillTime - TimeStamp::Now();
2996 0 : double ms = runtime > TimeDuration(0) ? runtime.ToMilliseconds() : 0;
2997 0 : return ms > double(PR_UINT32_MAX) ? PR_UINT32_MAX : PRUint32(ms);
2998 : }
2999 :
3000 : bool
3001 0 : WorkerPrivate::SuspendInternal(JSContext* aCx)
3002 : {
3003 0 : AssertIsOnWorkerThread();
3004 :
3005 0 : NS_ASSERTION(!mSuspended, "Already suspended!");
3006 :
3007 0 : mSuspended = true;
3008 0 : return true;
3009 : }
3010 :
3011 : bool
3012 0 : WorkerPrivate::ResumeInternal(JSContext* aCx)
3013 : {
3014 0 : AssertIsOnWorkerThread();
3015 :
3016 0 : NS_ASSERTION(mSuspended, "Not yet suspended!");
3017 :
3018 0 : mSuspended = false;
3019 0 : return true;
3020 : }
3021 :
3022 : void
3023 0 : WorkerPrivate::TraceInternal(JSTracer* aTrc)
3024 : {
3025 0 : AssertIsOnWorkerThread();
3026 :
3027 0 : for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
3028 0 : TimeoutInfo* info = mTimeouts[index];
3029 0 : JS_CALL_VALUE_TRACER(aTrc, info->mTimeoutVal,
3030 : "WorkerPrivate timeout value");
3031 0 : for (PRUint32 index2 = 0; index2 < info->mExtraArgVals.Length(); index2++) {
3032 0 : JS_CALL_VALUE_TRACER(aTrc, info->mExtraArgVals[index2],
3033 : "WorkerPrivate timeout extra argument value");
3034 : }
3035 : }
3036 0 : }
3037 :
3038 : bool
3039 0 : WorkerPrivate::ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease)
3040 : {
3041 0 : AssertIsOnWorkerThread();
3042 :
3043 : {
3044 0 : MutexAutoLock lock(mMutex);
3045 :
3046 : // If we're in shutdown then the busy count is no longer being considered so
3047 : // just return now.
3048 0 : if (mStatus >= Killing) {
3049 0 : return true;
3050 : }
3051 : }
3052 :
3053 : nsRefPtr<ModifyBusyCountRunnable> runnable =
3054 0 : new ModifyBusyCountRunnable(this, aIncrease);
3055 0 : return runnable->Dispatch(aCx);
3056 : }
3057 :
3058 : bool
3059 0 : WorkerPrivate::AddChildWorker(JSContext* aCx, ParentType* aChildWorker)
3060 : {
3061 0 : AssertIsOnWorkerThread();
3062 :
3063 : Status currentStatus;
3064 : {
3065 0 : MutexAutoLock lock(mMutex);
3066 0 : currentStatus = mStatus;
3067 : }
3068 :
3069 0 : if (currentStatus > Running) {
3070 0 : JS_ReportError(aCx, "Cannot create child workers from the close handler!");
3071 0 : return false;
3072 : }
3073 :
3074 0 : NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
3075 : "Already know about this one!");
3076 0 : mChildWorkers.AppendElement(aChildWorker);
3077 :
3078 0 : return mChildWorkers.Length() == 1 ?
3079 : ModifyBusyCountFromWorker(aCx, true) :
3080 0 : true;
3081 : }
3082 :
3083 : void
3084 0 : WorkerPrivate::RemoveChildWorker(JSContext* aCx, ParentType* aChildWorker)
3085 : {
3086 0 : AssertIsOnWorkerThread();
3087 :
3088 0 : NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
3089 : "Didn't know about this one!");
3090 0 : mChildWorkers.RemoveElement(aChildWorker);
3091 :
3092 0 : if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
3093 0 : NS_WARNING("Failed to modify busy count!");
3094 : }
3095 0 : }
3096 :
3097 : bool
3098 0 : WorkerPrivate::AddFeature(JSContext* aCx, WorkerFeature* aFeature)
3099 : {
3100 0 : AssertIsOnWorkerThread();
3101 :
3102 : {
3103 0 : MutexAutoLock lock(mMutex);
3104 :
3105 0 : if (mStatus >= Canceling) {
3106 0 : return false;
3107 : }
3108 : }
3109 :
3110 0 : NS_ASSERTION(!mFeatures.Contains(aFeature), "Already know about this one!");
3111 0 : mFeatures.AppendElement(aFeature);
3112 :
3113 0 : return mFeatures.Length() == 1 ?
3114 : ModifyBusyCountFromWorker(aCx, true) :
3115 0 : true;
3116 : }
3117 :
3118 : void
3119 0 : WorkerPrivate::RemoveFeature(JSContext* aCx, WorkerFeature* aFeature)
3120 : {
3121 0 : AssertIsOnWorkerThread();
3122 :
3123 0 : NS_ASSERTION(mFeatures.Contains(aFeature), "Didn't know about this one!");
3124 0 : mFeatures.RemoveElement(aFeature);
3125 :
3126 0 : if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
3127 0 : NS_WARNING("Failed to modify busy count!");
3128 : }
3129 0 : }
3130 :
3131 : void
3132 0 : WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus)
3133 : {
3134 0 : AssertIsOnWorkerThread();
3135 :
3136 0 : NS_ASSERTION(aStatus > Running, "Bad status!");
3137 :
3138 0 : if (aStatus >= Closing) {
3139 0 : CancelAllTimeouts(aCx);
3140 : }
3141 :
3142 0 : nsAutoTArray<WorkerFeature*, 30> features;
3143 0 : features.AppendElements(mFeatures);
3144 :
3145 0 : for (PRUint32 index = 0; index < features.Length(); index++) {
3146 0 : if (!features[index]->Notify(aCx, aStatus)) {
3147 0 : NS_WARNING("Failed to notify feature!");
3148 : }
3149 : }
3150 :
3151 0 : nsAutoTArray<ParentType*, 10> children;
3152 0 : children.AppendElements(mChildWorkers);
3153 :
3154 0 : for (PRUint32 index = 0; index < children.Length(); index++) {
3155 0 : if (!children[index]->Notify(aCx, aStatus)) {
3156 0 : NS_WARNING("Failed to notify child worker!");
3157 : }
3158 : }
3159 0 : }
3160 :
3161 : void
3162 0 : WorkerPrivate::CancelAllTimeouts(JSContext* aCx)
3163 : {
3164 0 : AssertIsOnWorkerThread();
3165 :
3166 0 : if (mTimerRunning) {
3167 0 : NS_ASSERTION(mTimer, "Huh?!");
3168 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
3169 :
3170 0 : if (NS_FAILED(mTimer->Cancel())) {
3171 0 : NS_WARNING("Failed to cancel timer!");
3172 : }
3173 :
3174 0 : for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
3175 0 : mTimeouts[index]->mCanceled = true;
3176 : }
3177 :
3178 0 : if (!RunExpiredTimeouts(aCx)) {
3179 0 : JS_ReportPendingException(aCx);
3180 : }
3181 :
3182 0 : mTimerRunning = false;
3183 : }
3184 : #ifdef DEBUG
3185 0 : else if (!mRunningExpiredTimeouts) {
3186 0 : NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
3187 : }
3188 : #endif
3189 :
3190 0 : mTimer = nsnull;
3191 0 : }
3192 :
3193 : PRUint32
3194 0 : WorkerPrivate::CreateNewSyncLoop()
3195 : {
3196 0 : AssertIsOnWorkerThread();
3197 :
3198 0 : NS_ASSERTION(mSyncQueues.Length() < PR_UINT32_MAX,
3199 : "Should have bailed by now!");
3200 :
3201 0 : mSyncQueues.AppendElement(new SyncQueue());
3202 0 : return mSyncQueues.Length() - 1;
3203 : }
3204 :
3205 : bool
3206 0 : WorkerPrivate::RunSyncLoop(JSContext* aCx, PRUint32 aSyncLoopKey)
3207 : {
3208 0 : AssertIsOnWorkerThread();
3209 :
3210 0 : NS_ASSERTION(!mSyncQueues.IsEmpty() ||
3211 : (aSyncLoopKey != mSyncQueues.Length() - 1),
3212 : "Forgot to call CreateNewSyncLoop!");
3213 0 : if (aSyncLoopKey != mSyncQueues.Length() - 1) {
3214 0 : return false;
3215 : }
3216 :
3217 0 : SyncQueue* syncQueue = mSyncQueues[aSyncLoopKey].get();
3218 :
3219 0 : for (;;) {
3220 : WorkerRunnable* event;
3221 : {
3222 0 : MutexAutoLock lock(mMutex);
3223 :
3224 0 : while (!mControlQueue.Pop(event) && !syncQueue->mQueue.Pop(event)) {
3225 0 : mCondVar.Wait();
3226 : }
3227 : }
3228 :
3229 : #ifdef EXTRA_GC
3230 : // Find GC bugs...
3231 : JS_GC(mJSContext);
3232 : #endif
3233 :
3234 0 : static_cast<nsIRunnable*>(event)->Run();
3235 0 : NS_RELEASE(event);
3236 :
3237 : #ifdef EXTRA_GC
3238 : // Find GC bugs...
3239 : JS_GC(mJSContext);
3240 : #endif
3241 :
3242 0 : if (syncQueue->mComplete) {
3243 0 : NS_ASSERTION(mSyncQueues.Length() - 1 == aSyncLoopKey,
3244 : "Mismatched calls!");
3245 0 : NS_ASSERTION(syncQueue->mQueue.IsEmpty(), "Unprocessed sync events!");
3246 :
3247 0 : bool result = syncQueue->mResult;
3248 0 : mSyncQueues.RemoveElementAt(aSyncLoopKey);
3249 :
3250 : #ifdef DEBUG
3251 0 : syncQueue = nsnull;
3252 : #endif
3253 :
3254 0 : return result;
3255 : }
3256 : }
3257 :
3258 : NS_NOTREACHED("Shouldn't get here!");
3259 : return false;
3260 : }
3261 :
3262 : void
3263 0 : WorkerPrivate::StopSyncLoop(PRUint32 aSyncLoopKey, bool aSyncResult)
3264 : {
3265 0 : AssertIsOnWorkerThread();
3266 :
3267 0 : NS_ASSERTION(mSyncQueues.IsEmpty() ||
3268 : (aSyncLoopKey == mSyncQueues.Length() - 1),
3269 : "Forgot to call CreateNewSyncLoop!");
3270 0 : if (aSyncLoopKey != mSyncQueues.Length() - 1) {
3271 0 : return;
3272 : }
3273 :
3274 0 : SyncQueue* syncQueue = mSyncQueues[aSyncLoopKey].get();
3275 :
3276 0 : NS_ASSERTION(!syncQueue->mComplete, "Already called StopSyncLoop?!");
3277 :
3278 0 : syncQueue->mResult = aSyncResult;
3279 0 : syncQueue->mComplete = true;
3280 : }
3281 :
3282 : bool
3283 0 : WorkerPrivate::PostMessageToParent(JSContext* aCx, jsval aMessage)
3284 : {
3285 0 : AssertIsOnWorkerThread();
3286 :
3287 : JSStructuredCloneCallbacks* callbacks =
3288 0 : IsChromeWorker() ?
3289 : &gChromeWorkerStructuredCloneCallbacks :
3290 0 : &gWorkerStructuredCloneCallbacks;
3291 :
3292 0 : nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
3293 :
3294 0 : JSAutoStructuredCloneBuffer buffer;
3295 0 : if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
3296 0 : return false;
3297 : }
3298 :
3299 : nsRefPtr<MessageEventRunnable> runnable =
3300 : new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer,
3301 0 : clonedObjects);
3302 0 : return runnable->Dispatch(aCx);
3303 : }
3304 :
3305 : bool
3306 0 : WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
3307 : {
3308 0 : AssertIsOnWorkerThread();
3309 :
3310 0 : NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
3311 :
3312 : // Save the old status and set the new status.
3313 : Status previousStatus;
3314 : {
3315 0 : MutexAutoLock lock(mMutex);
3316 :
3317 0 : if (mStatus >= aStatus) {
3318 0 : return true;
3319 : }
3320 :
3321 0 : previousStatus = mStatus;
3322 0 : mStatus = aStatus;
3323 : }
3324 :
3325 : // Now that status > Running, no-one can create a new mCrossThreadDispatcher
3326 : // if we don't already have one.
3327 0 : if (mCrossThreadDispatcher) {
3328 : // Since we'll no longer process events, make sure we no longer allow
3329 : // anyone to post them.
3330 : // We have to do this without mMutex held, since our mutex must be
3331 : // acquired *after* mCrossThreadDispatcher's mutex when they're both held.
3332 0 : mCrossThreadDispatcher->Forget();
3333 : }
3334 :
3335 0 : NS_ASSERTION(previousStatus != Pending, "How is this possible?!");
3336 :
3337 0 : NS_ASSERTION(previousStatus >= Canceling || mKillTime.IsNull(),
3338 : "Bad kill time set!");
3339 :
3340 : // Let all our features know the new status.
3341 0 : NotifyFeatures(aCx, aStatus);
3342 :
3343 : // There's nothing to do here if we never succeeded in running the worker
3344 : // script or if the close handler has already run.
3345 0 : if (!JS_GetGlobalObject(aCx) || mCloseHandlerFinished) {
3346 0 : return true;
3347 : }
3348 :
3349 : // If this is the first time our status has changed then we need to clear the
3350 : // main event queue. We also need to schedule the close handler unless we're
3351 : // being shut down.
3352 0 : if (previousStatus == Running) {
3353 0 : NS_ASSERTION(!mCloseHandlerStarted && !mCloseHandlerFinished,
3354 : "This is impossible!");
3355 :
3356 : {
3357 0 : MutexAutoLock lock(mMutex);
3358 0 : ClearQueue(&mQueue);
3359 : }
3360 :
3361 0 : if (aStatus != Killing) {
3362 0 : nsRefPtr<CloseEventRunnable> closeRunnable = new CloseEventRunnable(this);
3363 :
3364 0 : MutexAutoLock lock(mMutex);
3365 :
3366 0 : if (!mQueue.Push(closeRunnable)) {
3367 0 : NS_WARNING("Failed to push closeRunnable!");
3368 0 : return false;
3369 : }
3370 :
3371 0 : closeRunnable.forget();
3372 : }
3373 : }
3374 :
3375 0 : if (aStatus == Closing) {
3376 : // Notify parent to stop sending us messages and balance our busy count.
3377 0 : nsRefPtr<CloseRunnable> runnable = new CloseRunnable(this);
3378 0 : if (!runnable->Dispatch(aCx)) {
3379 0 : return false;
3380 : }
3381 :
3382 : // Don't abort the script.
3383 0 : return true;
3384 : }
3385 :
3386 0 : if (aStatus == Terminating) {
3387 : // Only abort the script if we're not yet running the close handler.
3388 0 : return mCloseHandlerStarted;
3389 : }
3390 :
3391 0 : if (aStatus == Canceling) {
3392 : // We need to enforce a timeout on the close handler.
3393 0 : NS_ASSERTION(previousStatus == Running || previousStatus == Closing ||
3394 : previousStatus == Terminating,
3395 : "Bad previous status!");
3396 :
3397 0 : PRUint32 killSeconds = RuntimeService::GetCloseHandlerTimeoutSeconds();
3398 0 : if (killSeconds) {
3399 0 : mKillTime = TimeStamp::Now() + TimeDuration::FromSeconds(killSeconds);
3400 :
3401 0 : if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) {
3402 0 : return false;
3403 : }
3404 : }
3405 :
3406 : // Only abort the script if we're not yet running the close handler.
3407 0 : return mCloseHandlerStarted;
3408 : }
3409 :
3410 0 : if (aStatus == Killing) {
3411 0 : mKillTime = TimeStamp::Now();
3412 :
3413 0 : if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) {
3414 0 : return false;
3415 : }
3416 :
3417 : // Always abort the script.
3418 0 : return false;
3419 : }
3420 :
3421 0 : NS_NOTREACHED("Should never get here!");
3422 0 : return false;
3423 : }
3424 :
3425 : bool
3426 0 : WorkerPrivate::ScheduleKillCloseEventRunnable(JSContext* aCx)
3427 : {
3428 0 : AssertIsOnWorkerThread();
3429 0 : NS_ASSERTION(!mKillTime.IsNull(), "Must have a kill time!");
3430 :
3431 : nsRefPtr<KillCloseEventRunnable> killCloseEventRunnable =
3432 0 : new KillCloseEventRunnable(this);
3433 0 : if (!killCloseEventRunnable->SetTimeout(aCx, RemainingRunTimeMS())) {
3434 0 : return false;
3435 : }
3436 :
3437 0 : MutexAutoLock lock(mMutex);
3438 :
3439 0 : if (!mQueue.Push(killCloseEventRunnable)) {
3440 0 : NS_WARNING("Failed to push killCloseEventRunnable!");
3441 0 : return false;
3442 : }
3443 :
3444 0 : killCloseEventRunnable.forget();
3445 0 : return true;
3446 : }
3447 :
3448 : void
3449 0 : WorkerPrivate::ReportError(JSContext* aCx, const char* aMessage,
3450 : JSErrorReport* aReport)
3451 : {
3452 0 : AssertIsOnWorkerThread();
3453 :
3454 0 : if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
3455 0 : return;
3456 : }
3457 :
3458 0 : NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
3459 : mErrorHandlerRecursionCount == 1,
3460 : "Bad recursion logic!");
3461 :
3462 0 : JS_ClearPendingException(aCx);
3463 :
3464 0 : nsString message, filename, line;
3465 : PRUint32 lineNumber, columnNumber, flags, errorNumber;
3466 :
3467 0 : if (aReport) {
3468 0 : if (aReport->ucmessage) {
3469 0 : message = aReport->ucmessage;
3470 : }
3471 0 : filename = NS_ConvertUTF8toUTF16(aReport->filename);
3472 0 : line = aReport->uclinebuf;
3473 0 : lineNumber = aReport->lineno;
3474 0 : columnNumber = aReport->uctokenptr - aReport->uclinebuf;
3475 0 : flags = aReport->flags;
3476 0 : errorNumber = aReport->errorNumber;
3477 : }
3478 : else {
3479 0 : lineNumber = columnNumber = errorNumber = 0;
3480 0 : flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
3481 : }
3482 :
3483 0 : if (message.IsEmpty()) {
3484 0 : message = NS_ConvertUTF8toUTF16(aMessage);
3485 : }
3486 :
3487 0 : mErrorHandlerRecursionCount++;
3488 :
3489 : // Don't want to run the scope's error handler if this is a recursive error or
3490 : // if there was an error in the close handler or if we ran out of memory.
3491 : bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
3492 0 : !mCloseHandlerStarted &&
3493 0 : errorNumber != JSMSG_OUT_OF_MEMORY;
3494 :
3495 0 : if (!ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nsnull, message,
3496 : filename, line, lineNumber,
3497 0 : columnNumber, flags, errorNumber, 0)) {
3498 0 : JS_ReportPendingException(aCx);
3499 : }
3500 :
3501 0 : mErrorHandlerRecursionCount--;
3502 : }
3503 :
3504 : bool
3505 0 : WorkerPrivate::SetTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp,
3506 : bool aIsInterval)
3507 : {
3508 0 : AssertIsOnWorkerThread();
3509 0 : NS_ASSERTION(aArgc, "Huh?!");
3510 :
3511 0 : const PRUint32 timerId = mNextTimeoutId++;
3512 :
3513 : Status currentStatus;
3514 : {
3515 0 : MutexAutoLock lock(mMutex);
3516 0 : currentStatus = mStatus;
3517 : }
3518 :
3519 : // It's a script bug if setTimeout/setInterval are called from a close handler
3520 : // so throw an exception.
3521 0 : if (currentStatus == Closing) {
3522 0 : JS_ReportError(aCx, "Cannot schedule timeouts from the close handler!");
3523 : }
3524 :
3525 : // If the worker is trying to call setTimeout/setInterval and the parent
3526 : // thread has initiated the close process then just silently fail.
3527 0 : if (currentStatus >= Closing) {
3528 0 : return false;
3529 : }
3530 :
3531 0 : nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
3532 0 : newInfo->mIsInterval = aIsInterval;
3533 0 : newInfo->mId = timerId;
3534 :
3535 0 : if (NS_UNLIKELY(timerId == PR_UINT32_MAX)) {
3536 0 : NS_WARNING("Timeout ids overflowed!");
3537 0 : mNextTimeoutId = 1;
3538 : }
3539 :
3540 0 : jsval* argv = JS_ARGV(aCx, aVp);
3541 :
3542 : // Take care of the main argument.
3543 0 : if (JSVAL_IS_OBJECT(argv[0])) {
3544 0 : if (JS_ObjectIsCallable(aCx, JSVAL_TO_OBJECT(argv[0]))) {
3545 0 : newInfo->mTimeoutVal = argv[0];
3546 : }
3547 : else {
3548 0 : JSString* timeoutStr = JS_ValueToString(aCx, argv[0]);
3549 0 : if (!timeoutStr) {
3550 0 : return false;
3551 : }
3552 0 : newInfo->mTimeoutVal = STRING_TO_JSVAL(timeoutStr);
3553 : }
3554 : }
3555 0 : else if (JSVAL_IS_STRING(argv[0])) {
3556 0 : newInfo->mTimeoutVal = argv[0];
3557 : }
3558 : else {
3559 : JS_ReportError(aCx, "Useless %s call (missing quotes around argument?)",
3560 0 : aIsInterval ? "setInterval" : "setTimeout");
3561 0 : return false;
3562 : }
3563 :
3564 : // See if any of the optional arguments were passed.
3565 0 : if (aArgc > 1) {
3566 0 : double intervalMS = 0;
3567 0 : if (!JS_ValueToNumber(aCx, argv[1], &intervalMS)) {
3568 0 : return false;
3569 : }
3570 0 : newInfo->mInterval = TimeDuration::FromMilliseconds(intervalMS);
3571 :
3572 0 : if (aArgc > 2 && JSVAL_IS_OBJECT(newInfo->mTimeoutVal)) {
3573 0 : nsTArray<jsval> extraArgVals(aArgc - 2);
3574 0 : for (unsigned index = 2; index < aArgc; index++) {
3575 0 : extraArgVals.AppendElement(argv[index]);
3576 : }
3577 0 : newInfo->mExtraArgVals.SwapElements(extraArgVals);
3578 : }
3579 : }
3580 :
3581 0 : newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
3582 :
3583 0 : if (JSVAL_IS_STRING(newInfo->mTimeoutVal)) {
3584 : const char* filenameChars;
3585 : PRUint32 lineNumber;
3586 0 : if (nsJSUtils::GetCallingLocation(aCx, &filenameChars, &lineNumber)) {
3587 0 : newInfo->mFilename = filenameChars;
3588 0 : newInfo->mLineNumber = lineNumber;
3589 : }
3590 : else {
3591 0 : NS_WARNING("Failed to get calling location!");
3592 : }
3593 : }
3594 :
3595 0 : mTimeouts.InsertElementSorted(newInfo.get(), GetAutoPtrComparator(mTimeouts));
3596 :
3597 : // If the timeout we just made is set to fire next then we need to update the
3598 : // timer.
3599 0 : if (mTimeouts[0] == newInfo) {
3600 : nsresult rv;
3601 :
3602 0 : if (!mTimer) {
3603 0 : mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
3604 0 : if (NS_FAILED(rv)) {
3605 0 : JS_ReportError(aCx, "Failed to create timer!");
3606 0 : return false;
3607 : }
3608 :
3609 0 : nsRefPtr<TimerRunnable> timerRunnable = new TimerRunnable(this);
3610 :
3611 : nsCOMPtr<nsIEventTarget> target =
3612 0 : new WorkerRunnableEventTarget(timerRunnable);
3613 0 : rv = mTimer->SetTarget(target);
3614 0 : if (NS_FAILED(rv)) {
3615 0 : JS_ReportError(aCx, "Failed to set timer's target!");
3616 0 : return false;
3617 : }
3618 : }
3619 :
3620 0 : if (!mTimerRunning) {
3621 0 : if (!ModifyBusyCountFromWorker(aCx, true)) {
3622 0 : return false;
3623 : }
3624 0 : mTimerRunning = true;
3625 : }
3626 :
3627 0 : if (!RescheduleTimeoutTimer(aCx)) {
3628 0 : return false;
3629 : }
3630 : }
3631 :
3632 0 : JS_SET_RVAL(aCx, aVp, INT_TO_JSVAL(timerId));
3633 :
3634 0 : newInfo.forget();
3635 0 : return true;
3636 : }
3637 :
3638 : bool
3639 0 : WorkerPrivate::ClearTimeout(JSContext* aCx, uint32 aId)
3640 : {
3641 0 : AssertIsOnWorkerThread();
3642 :
3643 0 : if (!mTimeouts.IsEmpty()) {
3644 0 : NS_ASSERTION(mTimerRunning, "Huh?!");
3645 :
3646 0 : for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
3647 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
3648 0 : if (info->mId == aId) {
3649 0 : info->mCanceled = true;
3650 0 : break;
3651 : }
3652 : }
3653 : }
3654 :
3655 0 : return true;
3656 : }
3657 :
3658 : bool
3659 0 : WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
3660 : {
3661 0 : AssertIsOnWorkerThread();
3662 :
3663 : // We may be called recursively (e.g. close() inside a timeout) or we could
3664 : // have been canceled while this event was pending, bail out if there is
3665 : // nothing to do.
3666 0 : if (mRunningExpiredTimeouts || !mTimerRunning) {
3667 0 : return true;
3668 : }
3669 :
3670 0 : NS_ASSERTION(mTimer, "Must have a timer!");
3671 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
3672 :
3673 0 : bool retval = true;
3674 :
3675 0 : AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
3676 0 : JSObject* global = JS_GetGlobalObject(aCx);
3677 0 : JSPrincipals* principal = GetWorkerPrincipal();
3678 :
3679 : // We want to make sure to run *something*, even if the timer fired a little
3680 : // early. Fudge the value of now to at least include the first timeout.
3681 0 : const TimeStamp now = NS_MAX(TimeStamp::Now(), mTimeouts[0]->mTargetTime);
3682 :
3683 0 : nsAutoTArray<TimeoutInfo*, 10> expiredTimeouts;
3684 0 : for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
3685 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
3686 0 : if (info->mTargetTime > now) {
3687 0 : break;
3688 : }
3689 0 : expiredTimeouts.AppendElement(info);
3690 : }
3691 :
3692 : // Guard against recursion.
3693 0 : mRunningExpiredTimeouts = true;
3694 :
3695 : // Run expired timeouts.
3696 0 : for (PRUint32 index = 0; index < expiredTimeouts.Length(); index++) {
3697 0 : TimeoutInfo*& info = expiredTimeouts[index];
3698 :
3699 0 : if (info->mCanceled) {
3700 0 : continue;
3701 : }
3702 :
3703 : // Always call JS_ReportPendingException if something fails, and if
3704 : // JS_ReportPendingException returns false (i.e. uncatchable exception) then
3705 : // break out of the loop.
3706 :
3707 0 : if (JSVAL_IS_STRING(info->mTimeoutVal)) {
3708 0 : JSString* expression = JSVAL_TO_STRING(info->mTimeoutVal);
3709 :
3710 : size_t stringLength;
3711 : const jschar* string = JS_GetStringCharsAndLength(aCx, expression,
3712 0 : &stringLength);
3713 :
3714 0 : if ((!string ||
3715 : !JS_EvaluateUCScriptForPrincipals(aCx, global, principal, string,
3716 : stringLength,
3717 : info->mFilename.get(),
3718 0 : info->mLineNumber, nsnull)) &&
3719 0 : !JS_ReportPendingException(aCx)) {
3720 0 : retval = false;
3721 0 : break;
3722 : }
3723 : }
3724 : else {
3725 : jsval rval;
3726 0 : if (!JS_CallFunctionValue(aCx, global, info->mTimeoutVal,
3727 : info->mExtraArgVals.Length(),
3728 0 : info->mExtraArgVals.Elements(), &rval) &&
3729 0 : !JS_ReportPendingException(aCx)) {
3730 0 : retval = false;
3731 0 : break;
3732 : }
3733 : }
3734 :
3735 0 : NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
3736 :
3737 : // Reschedule intervals.
3738 0 : if (info->mIsInterval && !info->mCanceled) {
3739 0 : PRUint32 timeoutIndex = mTimeouts.IndexOf(info);
3740 0 : NS_ASSERTION(timeoutIndex != PRUint32(-1),
3741 : "Should still be in the main list!");
3742 :
3743 : // This is nasty but we have to keep the old nsAutoPtr from deleting the
3744 : // info we're about to re-add.
3745 0 : mTimeouts[timeoutIndex].forget();
3746 0 : mTimeouts.RemoveElementAt(timeoutIndex);
3747 :
3748 0 : NS_ASSERTION(!mTimeouts.Contains(info), "Shouldn't have duplicates!");
3749 :
3750 : // NB: We must ensure that info->mTargetTime > now (where now is the
3751 : // now above, not literally TimeStamp::Now()) or we will remove the
3752 : // interval in the next loop below.
3753 0 : info->mTargetTime = NS_MAX(info->mTargetTime + info->mInterval,
3754 0 : now + TimeDuration::FromMilliseconds(1));
3755 0 : mTimeouts.InsertElementSorted(info, comparator);
3756 : }
3757 : }
3758 :
3759 : // No longer possible to be called recursively.
3760 0 : mRunningExpiredTimeouts = false;
3761 :
3762 : // Now remove canceled and expired timeouts from the main list.
3763 0 : for (PRUint32 index = 0; index < mTimeouts.Length(); ) {
3764 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
3765 0 : if (info->mTargetTime <= now || info->mCanceled) {
3766 0 : NS_ASSERTION(!info->mIsInterval || info->mCanceled,
3767 : "Interval timers can only be removed when canceled!");
3768 0 : mTimeouts.RemoveElement(info);
3769 : }
3770 : else {
3771 0 : index++;
3772 : }
3773 : }
3774 :
3775 : // Either signal the parent that we're no longer using timeouts or reschedule
3776 : // the timer.
3777 0 : if (mTimeouts.IsEmpty()) {
3778 0 : if (!ModifyBusyCountFromWorker(aCx, false)) {
3779 0 : retval = false;
3780 : }
3781 0 : mTimerRunning = false;
3782 : }
3783 0 : else if (retval && !RescheduleTimeoutTimer(aCx)) {
3784 0 : retval = false;
3785 : }
3786 :
3787 0 : return retval;
3788 : }
3789 :
3790 : bool
3791 0 : WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
3792 : {
3793 0 : AssertIsOnWorkerThread();
3794 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
3795 0 : NS_ASSERTION(mTimer, "Should have a timer!");
3796 :
3797 : double delta =
3798 0 : (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
3799 0 : PRUint32 delay = delta > 0 ? NS_MIN(delta, double(PR_UINT32_MAX)) : 0;
3800 :
3801 0 : nsresult rv = mTimer->InitWithFuncCallback(DummyCallback, nsnull, delay,
3802 0 : nsITimer::TYPE_ONE_SHOT);
3803 0 : if (NS_FAILED(rv)) {
3804 0 : JS_ReportError(aCx, "Failed to start timer!");
3805 0 : return false;
3806 : }
3807 :
3808 0 : return true;
3809 : }
3810 :
3811 : void
3812 0 : WorkerPrivate::UpdateJSContextOptionsInternal(JSContext* aCx, PRUint32 aOptions)
3813 : {
3814 0 : AssertIsOnWorkerThread();
3815 :
3816 0 : JS_SetOptions(aCx, aOptions);
3817 :
3818 0 : for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
3819 0 : mChildWorkers[index]->UpdateJSContextOptions(aCx, aOptions);
3820 : }
3821 0 : }
3822 :
3823 : void
3824 0 : WorkerPrivate::UpdateJSRuntimeHeapSizeInternal(JSContext* aCx,
3825 : PRUint32 aMaxBytes)
3826 : {
3827 0 : AssertIsOnWorkerThread();
3828 :
3829 0 : JS_SetGCParameter(JS_GetRuntime(aCx), JSGC_MAX_BYTES, aMaxBytes);
3830 :
3831 0 : for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
3832 0 : mChildWorkers[index]->UpdateJSRuntimeHeapSize(aCx, aMaxBytes);
3833 : }
3834 0 : }
3835 :
3836 : #ifdef JS_GC_ZEAL
3837 : void
3838 0 : WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, PRUint8 aGCZeal)
3839 : {
3840 0 : AssertIsOnWorkerThread();
3841 :
3842 0 : PRUint32 frequency = aGCZeal <= 2 ? JS_DEFAULT_ZEAL_FREQ : 1;
3843 0 : JS_SetGCZeal(aCx, aGCZeal, frequency, false);
3844 :
3845 0 : for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
3846 0 : mChildWorkers[index]->UpdateGCZeal(aCx, aGCZeal);
3847 : }
3848 0 : }
3849 : #endif
3850 :
3851 : void
3852 0 : WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
3853 : bool aCollectChildren)
3854 : {
3855 0 : AssertIsOnWorkerThread();
3856 :
3857 0 : if (aShrinking) {
3858 0 : js::ShrinkingGC(aCx, js::gcreason::DOM_WORKER);
3859 : }
3860 : else {
3861 0 : js::GCForReason(aCx, js::gcreason::DOM_WORKER);
3862 : }
3863 :
3864 0 : if (aCollectChildren) {
3865 0 : for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
3866 0 : mChildWorkers[index]->GarbageCollect(aCx, aShrinking);
3867 : }
3868 : }
3869 0 : }
3870 :
3871 : #ifdef DEBUG
3872 : template <class Derived>
3873 : void
3874 : WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
3875 : {
3876 0 : if (GetParent()) {
3877 0 : GetParent()->AssertIsOnWorkerThread();
3878 : }
3879 : else {
3880 0 : AssertIsOnMainThread();
3881 : }
3882 0 : }
3883 :
3884 : template <class Derived>
3885 : void
3886 : WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
3887 : {
3888 0 : AssertIsOnParentThread();
3889 :
3890 : // Only care about top level workers from windows.
3891 0 : if (mParent || !mWindow) {
3892 0 : return;
3893 : }
3894 :
3895 0 : AssertIsOnMainThread();
3896 :
3897 0 : nsPIDOMWindow* outer = mWindow->GetOuterWindow();
3898 0 : NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mWindow,
3899 : "Inner window no longer correct!");
3900 : }
3901 :
3902 : void
3903 0 : WorkerPrivate::AssertIsOnWorkerThread() const
3904 : {
3905 0 : if (mThread) {
3906 : bool current;
3907 0 : if (NS_FAILED(mThread->IsOnCurrentThread(¤t)) || !current) {
3908 0 : NS_ERROR("Wrong thread!");
3909 : }
3910 : }
3911 : else {
3912 : NS_ERROR("Trying to assert thread identity after thread has been "
3913 0 : "shutdown!");
3914 : }
3915 0 : }
3916 : #endif
3917 :
3918 : WorkerCrossThreadDispatcher*
3919 0 : WorkerPrivate::GetCrossThreadDispatcher()
3920 : {
3921 0 : mozilla::MutexAutoLock lock(mMutex);
3922 0 : if (!mCrossThreadDispatcher && mStatus <= Running) {
3923 0 : mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
3924 : }
3925 0 : return mCrossThreadDispatcher;
3926 : }
3927 :
3928 : BEGIN_WORKERS_NAMESPACE
3929 :
3930 : // Force instantiation.
3931 : template class WorkerPrivateParent<WorkerPrivate>;
3932 :
3933 : WorkerPrivate*
3934 0 : GetWorkerPrivateFromContext(JSContext* aCx)
3935 : {
3936 0 : NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3937 0 : return static_cast<WorkerPrivate*>(JS_GetContextPrivate(aCx));
3938 : }
3939 :
3940 : JSStructuredCloneCallbacks*
3941 0 : WorkerStructuredCloneCallbacks(bool aMainRuntime)
3942 : {
3943 : return aMainRuntime ?
3944 : &gMainThreadWorkerStructuredCloneCallbacks :
3945 0 : &gWorkerStructuredCloneCallbacks;
3946 : }
3947 :
3948 : JSStructuredCloneCallbacks*
3949 0 : ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime)
3950 : {
3951 : return aMainRuntime ?
3952 : &gMainThreadChromeWorkerStructuredCloneCallbacks :
3953 0 : &gChromeWorkerStructuredCloneCallbacks;
3954 : }
3955 :
3956 : END_WORKERS_NAMESPACE
|