2015-05-03 22:32:37 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
#include "WorkerPrivate.h"
|
|
|
|
|
2013-06-15 06:48:28 +04:00
|
|
|
#include "amIAddonManager.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsIClassInfo.h"
|
2012-09-15 22:51:55 +04:00
|
|
|
#include "nsIContentSecurityPolicy.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsIConsoleService.h"
|
2013-09-09 07:29:21 +04:00
|
|
|
#include "nsIDOMDOMException.h"
|
2013-06-05 18:04:23 +04:00
|
|
|
#include "nsIDOMEvent.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsIDocument.h"
|
2012-08-20 22:34:32 +04:00
|
|
|
#include "nsIDocShell.h"
|
2015-02-21 18:09:17 +03:00
|
|
|
#include "nsIInterfaceRequestor.h"
|
2011-09-09 04:03:03 +04:00
|
|
|
#include "nsIMemoryReporter.h"
|
2015-07-02 15:54:00 +03:00
|
|
|
#include "nsINetworkInterceptController.h"
|
2012-09-17 04:20:16 +04:00
|
|
|
#include "nsIPermissionManager.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsIScriptError.h"
|
|
|
|
#include "nsIScriptGlobalObject.h"
|
|
|
|
#include "nsIScriptSecurityManager.h"
|
2016-08-16 09:10:30 +03:00
|
|
|
#include "nsIScriptTimeoutHandler.h"
|
2015-02-21 18:09:17 +03:00
|
|
|
#include "nsITabChild.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsITextToSubURI.h"
|
2013-10-23 17:16:49 +04:00
|
|
|
#include "nsIThreadInternal.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsITimer.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsIURL.h"
|
2015-02-21 18:09:17 +03:00
|
|
|
#include "nsIWeakReferenceUtils.h"
|
2014-10-27 20:00:05 +03:00
|
|
|
#include "nsIWorkerDebugger.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsIXPConnect.h"
|
2014-12-17 09:26:15 +03:00
|
|
|
#include "nsPIDOMWindow.h"
|
2016-10-10 22:09:00 +03:00
|
|
|
#include "nsGlobalWindow.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-05-17 02:49:43 +04:00
|
|
|
#include <algorithm>
|
2015-07-30 21:50:00 +03:00
|
|
|
#include "ImageContainer.h"
|
2012-01-15 12:13:08 +04:00
|
|
|
#include "jsfriendapi.h"
|
2012-01-11 12:23:08 +04:00
|
|
|
#include "js/MemoryMetrics.h"
|
2013-10-23 17:16:49 +04:00
|
|
|
#include "mozilla/Assertions.h"
|
2014-09-02 02:26:43 +04:00
|
|
|
#include "mozilla/Attributes.h"
|
2013-09-25 15:21:20 +04:00
|
|
|
#include "mozilla/ContentEvents.h"
|
2014-03-18 08:48:21 +04:00
|
|
|
#include "mozilla/EventDispatcher.h"
|
2013-05-17 02:49:43 +04:00
|
|
|
#include "mozilla/Likely.h"
|
2014-12-12 19:06:00 +03:00
|
|
|
#include "mozilla/LoadContext.h"
|
2016-08-08 23:33:39 +03:00
|
|
|
#include "mozilla/Move.h"
|
2013-08-04 03:55:40 +04:00
|
|
|
#include "mozilla/dom/BindingUtils.h"
|
2016-03-24 00:55:07 +03:00
|
|
|
#include "mozilla/dom/Console.h"
|
2016-11-07 23:30:17 +03:00
|
|
|
#include "mozilla/dom/DocGroup.h"
|
2013-06-05 18:04:23 +04:00
|
|
|
#include "mozilla/dom/ErrorEvent.h"
|
2013-11-05 18:16:24 +04:00
|
|
|
#include "mozilla/dom/ErrorEventBinding.h"
|
2013-10-23 17:16:49 +04:00
|
|
|
#include "mozilla/dom/Exceptions.h"
|
2016-03-16 21:51:11 +03:00
|
|
|
#include "mozilla/dom/ExtendableMessageEventBinding.h"
|
2013-11-05 18:16:26 +04:00
|
|
|
#include "mozilla/dom/FunctionBinding.h"
|
2016-03-16 21:51:11 +03:00
|
|
|
#include "mozilla/dom/IndexedDatabaseManager.h"
|
2014-02-27 14:51:14 +04:00
|
|
|
#include "mozilla/dom/MessageEvent.h"
|
2013-11-05 18:16:26 +04:00
|
|
|
#include "mozilla/dom/MessageEventBinding.h"
|
2015-06-17 13:44:27 +03:00
|
|
|
#include "mozilla/dom/MessagePort.h"
|
|
|
|
#include "mozilla/dom/MessagePortBinding.h"
|
2017-02-14 18:06:38 +03:00
|
|
|
#include "mozilla/dom/nsCSPUtils.h"
|
2016-06-09 20:04:42 +03:00
|
|
|
#include "mozilla/dom/Performance.h"
|
2015-09-16 06:27:56 +03:00
|
|
|
#include "mozilla/dom/PMessagePort.h"
|
2014-10-28 15:08:19 +03:00
|
|
|
#include "mozilla/dom/Promise.h"
|
2015-04-10 18:27:57 +03:00
|
|
|
#include "mozilla/dom/PromiseDebugging.h"
|
2016-07-27 17:32:12 +03:00
|
|
|
#include "mozilla/dom/PromiseNativeHandler.h"
|
2016-05-11 03:57:29 +03:00
|
|
|
#include "mozilla/dom/SimpleGlobalObject.h"
|
2014-07-01 14:25:27 +04:00
|
|
|
#include "mozilla/dom/ScriptSettings.h"
|
2015-09-30 15:22:08 +03:00
|
|
|
#include "mozilla/dom/StructuredCloneHolder.h"
|
2015-08-19 03:25:02 +03:00
|
|
|
#include "mozilla/dom/TabChild.h"
|
2013-11-05 18:16:24 +04:00
|
|
|
#include "mozilla/dom/WorkerBinding.h"
|
2015-03-04 02:51:53 +03:00
|
|
|
#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
|
|
|
|
#include "mozilla/dom/WorkerGlobalScopeBinding.h"
|
2013-11-05 18:16:24 +04:00
|
|
|
#include "mozilla/Preferences.h"
|
2016-12-16 06:16:31 +03:00
|
|
|
#include "mozilla/SizePrintfMacros.h"
|
2016-11-07 23:30:17 +03:00
|
|
|
#include "mozilla/ThrottledEventQueue.h"
|
2015-10-22 00:10:05 +03:00
|
|
|
#include "mozilla/TimelineConsumers.h"
|
|
|
|
#include "mozilla/WorkerTimelineMarker.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsAlgorithm.h"
|
|
|
|
#include "nsContentUtils.h"
|
2015-03-18 21:36:03 +03:00
|
|
|
#include "nsCycleCollector.h"
|
2012-07-27 18:03:27 +04:00
|
|
|
#include "nsError.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsDOMJSUtils.h"
|
2013-06-05 18:04:23 +04:00
|
|
|
#include "nsHostObjectProtocolHandler.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "nsJSEnvironment.h"
|
|
|
|
#include "nsJSUtils.h"
|
|
|
|
#include "nsNetUtil.h"
|
2013-06-05 18:04:23 +04:00
|
|
|
#include "nsPrintfCString.h"
|
2013-03-13 00:33:40 +04:00
|
|
|
#include "nsProxyRelease.h"
|
2015-04-15 19:47:03 +03:00
|
|
|
#include "nsQueryObject.h"
|
2012-08-20 22:34:32 +04:00
|
|
|
#include "nsSandboxFlags.h"
|
2017-02-15 17:53:07 +03:00
|
|
|
#include "nsScriptError.h"
|
2016-08-14 14:39:31 +03:00
|
|
|
#include "nsUTF8Utils.h"
|
2014-11-17 22:55:37 +03:00
|
|
|
#include "prthread.h"
|
2011-08-07 05:03:46 +04:00
|
|
|
#include "xpcpublic.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2012-03-31 08:42:20 +04:00
|
|
|
#ifdef ANDROID
|
|
|
|
#include <android/log.h>
|
|
|
|
#endif
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
#include "nsThreadManager.h"
|
|
|
|
#endif
|
|
|
|
|
2013-11-20 03:08:50 +04:00
|
|
|
#include "Navigator.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "Principal.h"
|
|
|
|
#include "RuntimeService.h"
|
|
|
|
#include "ScriptLoader.h"
|
2016-03-16 21:51:11 +03:00
|
|
|
#include "ServiceWorkerEvents.h"
|
2014-07-03 04:48:35 +04:00
|
|
|
#include "ServiceWorkerManager.h"
|
2013-06-05 18:04:23 +04:00
|
|
|
#include "SharedWorker.h"
|
2014-10-27 20:00:05 +03:00
|
|
|
#include "WorkerDebuggerManager.h"
|
2016-06-23 11:53:14 +03:00
|
|
|
#include "WorkerHolder.h"
|
2016-04-06 23:27:22 +03:00
|
|
|
#include "WorkerNavigator.h"
|
2013-10-23 17:16:49 +04:00
|
|
|
#include "WorkerRunnable.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
#include "WorkerScope.h"
|
2014-11-17 22:55:37 +03:00
|
|
|
#include "WorkerThread.h"
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// JS_MaybeGC will run once every second during normal execution.
|
|
|
|
#define PERIODIC_GC_TIMER_DELAY_SEC 1
|
2012-01-18 00:05:25 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// A shrinking GC will run five seconds after the last event is processed.
|
|
|
|
#define IDLE_GC_TIMER_DELAY_SEC 5
|
2012-01-18 00:05:25 +04:00
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
#define PREF_WORKERS_ENABLED "dom.workers.enabled"
|
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
|
|
|
|
static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
|
|
|
|
|
|
|
|
mozilla::LogModule*
|
|
|
|
WorkerLog()
|
|
|
|
{
|
|
|
|
return sWorkerPrivateLog;
|
|
|
|
}
|
|
|
|
|
|
|
|
mozilla::LogModule*
|
|
|
|
TimeoutsLog()
|
|
|
|
{
|
|
|
|
return sWorkerTimeoutsLog;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
using namespace mozilla;
|
2012-04-24 14:58:08 +04:00
|
|
|
using namespace mozilla::dom;
|
2014-12-17 09:26:15 +03:00
|
|
|
using namespace mozilla::ipc;
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
USING_WORKERS_NAMESPACE
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-12-08 09:38:32 +04:00
|
|
|
MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
|
2012-07-13 10:51:01 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
BEGIN_WORKERS_NAMESPACE
|
|
|
|
|
|
|
|
void
|
|
|
|
AssertIsOnMainThread()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
|
|
|
}
|
|
|
|
|
|
|
|
END_WORKERS_NAMESPACE
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
namespace {
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
const nsIID kDEBUGWorkerEventTargetIID = {
|
|
|
|
0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb }
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
template <class T>
|
|
|
|
class AutoPtrComparator
|
|
|
|
{
|
|
|
|
typedef nsAutoPtr<T> A;
|
|
|
|
typedef T* B;
|
|
|
|
|
|
|
|
public:
|
2011-09-29 10:19:26 +04:00
|
|
|
bool Equals(const A& a, const B& b) const {
|
2011-07-17 23:09:13 +04:00
|
|
|
return a && b ? *a == *b : !a && !b ? true : false;
|
|
|
|
}
|
2011-09-29 10:19:26 +04:00
|
|
|
bool LessThan(const A& a, const B& b) const {
|
2011-07-17 23:09:13 +04:00
|
|
|
return a && b ? *a < *b : b ? true : false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
inline AutoPtrComparator<T>
|
|
|
|
GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
|
|
|
|
{
|
|
|
|
return AutoPtrComparator<T>();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Specialize this if there's some class that has multiple nsISupports bases.
|
|
|
|
template <class T>
|
|
|
|
struct ISupportsBaseInfo
|
|
|
|
{
|
|
|
|
typedef T ISupportsBase;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <template <class> class SmartPtr, class T>
|
|
|
|
inline void
|
|
|
|
SwapToISupportsArray(SmartPtr<T>& aSrc,
|
|
|
|
nsTArray<nsCOMPtr<nsISupports> >& aDest)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
T* raw = nullptr;
|
2011-07-17 23:09:13 +04:00
|
|
|
aSrc.swap(raw);
|
|
|
|
|
|
|
|
nsISupports* rawSupports =
|
|
|
|
static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
|
|
|
|
dest->swap(rawSupports);
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// This class is used to wrap any runnables that the worker receives via the
|
|
|
|
// nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
|
|
|
|
// from the worker's EventTarget).
|
2015-03-21 19:28:04 +03:00
|
|
|
class ExternalRunnableWrapper final : public WorkerRunnable
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
2016-04-11 21:40:06 +03:00
|
|
|
nsCOMPtr<nsIRunnable> mWrappedRunnable;
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
public:
|
|
|
|
ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
|
2016-04-11 21:40:06 +03:00
|
|
|
nsIRunnable* aWrappedRunnable)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
|
|
|
mWrappedRunnable(aWrappedRunnable)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
|
|
MOZ_ASSERT(aWrappedRunnable);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
|
|
|
|
|
|
|
private:
|
|
|
|
~ExternalRunnableWrapper()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
nsresult rv = mWrappedRunnable->Run();
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
if (!JS_IsExceptionPending(aCx)) {
|
|
|
|
Throw(aCx, rv);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-04-11 21:40:06 +03:00
|
|
|
nsresult
|
2015-03-21 19:28:04 +03:00
|
|
|
Cancel() override
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
2016-04-11 21:40:06 +03:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsICancelableRunnable> cancelable =
|
|
|
|
do_QueryInterface(mWrappedRunnable);
|
|
|
|
MOZ_ASSERT(cancelable); // We checked this earlier!
|
|
|
|
rv = cancelable->Cancel();
|
2013-10-23 17:16:49 +04:00
|
|
|
nsresult rv2 = WorkerRunnable::Cancel();
|
|
|
|
return NS_FAILED(rv) ? rv : rv2;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
struct WindowAction
|
|
|
|
{
|
2016-01-30 20:05:36 +03:00
|
|
|
nsPIDOMWindowInner* mWindow;
|
2013-06-05 18:04:23 +04:00
|
|
|
bool mDefaultAction;
|
|
|
|
|
2016-01-30 20:05:36 +03:00
|
|
|
MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
|
2014-07-02 14:26:49 +04:00
|
|
|
: mWindow(aWindow), mDefaultAction(true)
|
2013-06-05 18:04:23 +04:00
|
|
|
{ }
|
|
|
|
|
|
|
|
bool
|
|
|
|
operator==(const WindowAction& aOther) const
|
|
|
|
{
|
|
|
|
return mWindow == aOther.mWindow;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
2017-02-15 17:53:07 +03:00
|
|
|
LogErrorToConsole(const WorkerErrorReport& aReport, uint64_t aInnerWindowId)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
RefPtr<nsScriptErrorBase> scriptError = new nsScriptError();
|
2016-09-01 08:01:16 +03:00
|
|
|
NS_WARNING_ASSERTION(scriptError, "Failed to create script error!");
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
if (scriptError) {
|
2017-02-15 17:53:07 +03:00
|
|
|
nsAutoCString category("Web Worker");
|
|
|
|
if (NS_FAILED(scriptError->InitWithWindowID(aReport.mMessage,
|
|
|
|
aReport.mFilename,
|
|
|
|
aReport.mLine,
|
|
|
|
aReport.mLineNumber,
|
|
|
|
aReport.mColumnNumber,
|
|
|
|
aReport.mFlags,
|
|
|
|
category,
|
2013-06-05 18:04:23 +04:00
|
|
|
aInnerWindowId))) {
|
|
|
|
NS_WARNING("Failed to init script error!");
|
|
|
|
scriptError = nullptr;
|
|
|
|
}
|
2017-02-15 17:53:07 +03:00
|
|
|
|
|
|
|
for (size_t i = 0, len = aReport.mNotes.Length(); i < len; i++) {
|
|
|
|
const WorkerErrorNote& note = aReport.mNotes.ElementAt(i);
|
|
|
|
|
|
|
|
nsScriptErrorNote* noteObject = new nsScriptErrorNote();
|
|
|
|
noteObject->Init(note.mMessage, note.mFilename,
|
|
|
|
note.mLineNumber, note.mColumnNumber);
|
|
|
|
scriptError->AddNote(noteObject);
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIConsoleService> consoleService =
|
|
|
|
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
2016-09-01 08:01:16 +03:00
|
|
|
NS_WARNING_ASSERTION(consoleService, "Failed to get console service!");
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
if (consoleService) {
|
|
|
|
if (scriptError) {
|
|
|
|
if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
NS_WARNING("LogMessage failed!");
|
|
|
|
} else if (NS_SUCCEEDED(consoleService->LogStringMessage(
|
2017-02-15 17:53:07 +03:00
|
|
|
aReport.mMessage.BeginReading()))) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
NS_WARNING("LogStringMessage failed!");
|
|
|
|
}
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
NS_ConvertUTF16toUTF8 msg(aReport.mMessage);
|
|
|
|
NS_ConvertUTF16toUTF8 filename(aReport.mFilename);
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]";
|
|
|
|
|
|
|
|
#ifdef ANDROID
|
|
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
|
2017-02-15 17:53:07 +03:00
|
|
|
filename.get(), aReport.mLineNumber);
|
2013-06-05 18:04:23 +04:00
|
|
|
#endif
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
fprintf(stderr, kErrorString, msg.get(), filename.get(), aReport.mLineNumber);
|
2013-06-05 18:04:23 +04:00
|
|
|
fflush(stderr);
|
|
|
|
}
|
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
class MainThreadReleaseRunnable final : public Runnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
nsTArray<nsCOMPtr<nsISupports>> mDoomed;
|
2015-02-21 18:09:17 +03:00
|
|
|
nsCOMPtr<nsILoadGroup> mLoadGroupToCancel;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2012-03-31 08:42:20 +04:00
|
|
|
public:
|
2013-10-23 17:16:49 +04:00
|
|
|
MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>& aDoomed,
|
2015-02-21 18:09:17 +03:00
|
|
|
nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2012-03-31 08:42:20 +04:00
|
|
|
mDoomed.SwapElements(aDoomed);
|
2015-02-21 18:09:17 +03:00
|
|
|
mLoadGroupToCancel.swap(aLoadGroupToCancel);
|
2012-03-31 08:42:20 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
2011-07-26 05:49:16 +04:00
|
|
|
|
2012-03-31 08:42:20 +04:00
|
|
|
NS_IMETHOD
|
2015-03-21 19:28:04 +03:00
|
|
|
Run() override
|
2012-03-31 08:42:20 +04:00
|
|
|
{
|
2015-02-21 18:09:17 +03:00
|
|
|
if (mLoadGroupToCancel) {
|
|
|
|
mLoadGroupToCancel->Cancel(NS_BINDING_ABORTED);
|
|
|
|
mLoadGroupToCancel = nullptr;
|
|
|
|
}
|
|
|
|
|
2012-03-31 08:42:20 +04:00
|
|
|
mDoomed.Clear();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
private:
|
|
|
|
~MainThreadReleaseRunnable()
|
|
|
|
{ }
|
2012-03-31 08:42:20 +04:00
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class WorkerFinishedRunnable final : public WorkerControlRunnable
|
2012-03-31 08:42:20 +04:00
|
|
|
{
|
|
|
|
WorkerPrivate* mFinishedWorker;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
public:
|
|
|
|
WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate* aFinishedWorker)
|
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
|
|
|
mFinishedWorker(aFinishedWorker)
|
2011-07-17 23:09:13 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2016-02-26 00:05:39 +03:00
|
|
|
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
// Silence bad assertions.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual void
|
2016-02-26 23:23:12 +03:00
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
// Silence bad assertions.
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2017-02-07 18:28:39 +03:00
|
|
|
if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_WARNING("Failed to dispatch, going to leak!");
|
|
|
|
}
|
|
|
|
|
|
|
|
RuntimeService* runtime = RuntimeService::GetService();
|
|
|
|
NS_ASSERTION(runtime, "This should never be null!");
|
|
|
|
|
2014-10-27 20:00:05 +03:00
|
|
|
mFinishedWorker->DisableDebugger();
|
|
|
|
|
2016-03-28 20:28:14 +03:00
|
|
|
runtime->UnregisterWorker(mFinishedWorker);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2014-02-02 22:08:50 +04:00
|
|
|
mFinishedWorker->ClearSelfRef();
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
class TopLevelWorkerFinishedRunnable final : public Runnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
WorkerPrivate* mFinishedWorker;
|
|
|
|
|
|
|
|
public:
|
2014-09-02 02:26:43 +04:00
|
|
|
explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
|
2013-10-23 17:16:49 +04:00
|
|
|
: mFinishedWorker(aFinishedWorker)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
aFinishedWorker->AssertIsOnWorkerThread();
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
|
|
|
|
|
|
|
private:
|
2014-07-09 01:23:17 +04:00
|
|
|
~TopLevelWorkerFinishedRunnable() {}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_IMETHOD
|
2015-03-21 19:28:04 +03:00
|
|
|
Run() override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2014-02-02 22:08:50 +04:00
|
|
|
RuntimeService* runtime = RuntimeService::GetService();
|
|
|
|
MOZ_ASSERT(runtime);
|
|
|
|
|
2014-10-27 20:00:05 +03:00
|
|
|
mFinishedWorker->DisableDebugger();
|
|
|
|
|
2016-03-28 20:28:14 +03:00
|
|
|
runtime->UnregisterWorker(mFinishedWorker);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
|
2012-03-31 08:42:20 +04:00
|
|
|
NS_WARNING("Failed to dispatch, going to leak!");
|
|
|
|
}
|
|
|
|
|
2014-02-02 22:08:50 +04:00
|
|
|
mFinishedWorker->ClearSelfRef();
|
2011-07-26 05:49:16 +04:00
|
|
|
return NS_OK;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class ModifyBusyCountRunnable final : public WorkerControlRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
bool mIncrease;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
|
2011-07-17 23:09:13 +04:00
|
|
|
mIncrease(aIncrease)
|
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-02-26 00:05:39 +03:00
|
|
|
return aWorkerPrivate->ModifyBusyCount(mIncrease);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual void
|
2011-07-17 23:09:13 +04:00
|
|
|
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
|
2015-03-21 19:28:04 +03:00
|
|
|
override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
if (mIncrease) {
|
|
|
|
WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Don't do anything here as it's possible that aWorkerPrivate has been
|
|
|
|
// deleted.
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-01-28 17:39:24 +03:00
|
|
|
class ReportCompileErrorRunnable final : public WorkerRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static void
|
|
|
|
CreateAndDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
RefPtr<ReportCompileErrorRunnable> runnable =
|
|
|
|
new ReportCompileErrorRunnable(aCx, aWorkerPrivate);
|
|
|
|
runnable->Dispatch();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ReportCompileErrorRunnable(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
|
|
: WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
|
|
|
|
{
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
|
|
|
{
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
// Dispatch may fail if the worker was canceled, no need to report that as
|
|
|
|
// an error, so don't call base class PostDispatch.
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
|
|
|
{
|
|
|
|
if (aWorkerPrivate->IsFrozen() ||
|
|
|
|
aWorkerPrivate->IsParentWindowPaused()) {
|
|
|
|
MOZ_ASSERT(!IsDebuggerRunnable());
|
|
|
|
aWorkerPrivate->QueueRunnable(this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aWorkerPrivate->IsSharedWorker()) {
|
2017-02-15 17:53:07 +03:00
|
|
|
aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, nullptr,
|
2017-01-28 17:39:24 +03:00
|
|
|
/* isErrorEvent */ false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aWorkerPrivate->IsServiceWorker()) {
|
|
|
|
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
|
|
|
if (swm) {
|
|
|
|
swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
|
2017-03-25 02:56:48 +03:00
|
|
|
aWorkerPrivate->ServiceWorkerScope(),
|
2017-01-28 17:39:24 +03:00
|
|
|
aWorkerPrivate->ScriptURL(),
|
|
|
|
EmptyString(), EmptyString(), EmptyString(),
|
|
|
|
0, 0, JSREPORT_ERROR, JSEXN_ERR);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aWorkerPrivate->IsAcceptingEvents()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Event> event =
|
|
|
|
Event::Constructor(aWorkerPrivate, NS_LITERAL_STRING("error"),
|
|
|
|
EventInit());
|
|
|
|
event->SetTrusted(true);
|
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
|
|
aWorkerPrivate->DispatchDOMEvent(nullptr, event, nullptr, &status);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class CompileScriptRunnable final : public WorkerRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2015-03-17 13:15:19 +03:00
|
|
|
nsString mScriptURL;
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
public:
|
2015-03-17 13:15:19 +03:00
|
|
|
explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
const nsAString& aScriptURL)
|
2016-06-28 20:28:13 +03:00
|
|
|
: WorkerRunnable(aWorkerPrivate),
|
2015-03-17 13:15:19 +03:00
|
|
|
mScriptURL(aScriptURL)
|
2011-07-17 23:09:13 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
2016-03-02 00:52:26 +03:00
|
|
|
// We can't implement PreRun effectively, because at the point when that would
|
|
|
|
// run we have not yet done our load so don't know things like our final
|
|
|
|
// principal and whatnot.
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-02-24 18:38:31 +03:00
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
2015-10-15 15:06:55 +03:00
|
|
|
ErrorResult rv;
|
2016-02-29 22:52:42 +03:00
|
|
|
scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
|
2016-02-24 18:38:31 +03:00
|
|
|
rv.WouldReportJSException();
|
2016-02-24 18:38:31 +03:00
|
|
|
// Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
|
|
|
|
// return false and don't SetWorkerScriptExecutedSuccessfully() in that
|
|
|
|
// case, but don't throw anything on aCx. The idea is to not dispatch error
|
|
|
|
// events if our load is canceled with that error code.
|
|
|
|
if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
|
|
|
|
rv.SuppressException();
|
|
|
|
return false;
|
|
|
|
}
|
2016-05-14 03:09:50 +03:00
|
|
|
|
|
|
|
WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
|
|
|
|
if (NS_WARN_IF(!globalScope)) {
|
|
|
|
// We never got as far as calling GetOrCreateGlobalScope, or it failed.
|
|
|
|
// We have no way to enter a compartment, hence no sane way to report this
|
|
|
|
// error. :(
|
|
|
|
rv.SuppressException();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-02 00:52:26 +03:00
|
|
|
// Make sure to propagate exceptions from rv onto aCx, so that they will get
|
2017-01-28 17:39:24 +03:00
|
|
|
// reported after we return. We want to propagate just JS exceptions,
|
|
|
|
// because all the other errors are handled when the script is loaded.
|
|
|
|
// See: https://dom.spec.whatwg.org/#concept-event-fire
|
|
|
|
if (rv.Failed() && !rv.IsJSException()) {
|
|
|
|
ReportCompileErrorRunnable::CreateAndDispatch(aCx, aWorkerPrivate);
|
|
|
|
rv.SuppressException();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-02-24 18:38:31 +03:00
|
|
|
// This is a little dumb, but aCx is in the null compartment here because we
|
|
|
|
// set it up that way in our Run(), since we had not created the global at
|
|
|
|
// that point yet. So we need to enter the compartment of our global,
|
|
|
|
// because setting a pending exception on aCx involves wrapping into its
|
2016-05-14 03:09:50 +03:00
|
|
|
// current compartment. Luckily we have a global now.
|
|
|
|
JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
|
2016-02-24 18:38:31 +03:00
|
|
|
if (rv.MaybeSetPendingException(aCx)) {
|
2011-07-17 23:09:13 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-11 00:12:55 +03:00
|
|
|
aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
|
|
|
|
return true;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable
|
2015-03-17 13:15:19 +03:00
|
|
|
{
|
|
|
|
nsString mScriptURL;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
const nsAString& aScriptURL)
|
|
|
|
: WorkerDebuggerRunnable(aWorkerPrivate),
|
|
|
|
mScriptURL(aScriptURL)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2015-03-17 13:15:19 +03:00
|
|
|
{
|
2016-02-24 18:38:31 +03:00
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
2015-03-17 13:15:19 +03:00
|
|
|
WorkerDebuggerGlobalScope* globalScope =
|
|
|
|
aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
|
|
|
|
if (!globalScope) {
|
|
|
|
NS_WARNING("Failed to make global!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
|
|
|
|
|
2015-10-15 15:06:55 +03:00
|
|
|
ErrorResult rv;
|
2015-03-17 13:15:19 +03:00
|
|
|
JSAutoCompartment ac(aCx, global);
|
2016-02-29 22:52:42 +03:00
|
|
|
scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL,
|
|
|
|
DebuggerScript, rv);
|
2016-02-24 18:38:31 +03:00
|
|
|
rv.WouldReportJSException();
|
2016-02-24 18:38:31 +03:00
|
|
|
// Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
|
|
|
|
// return false and don't SetWorkerScriptExecutedSuccessfully() in that
|
|
|
|
// case, but don't throw anything on aCx. The idea is to not dispatch error
|
|
|
|
// events if our load is canceled with that error code.
|
|
|
|
if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
|
|
|
|
rv.SuppressException();
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-02 00:52:26 +03:00
|
|
|
// Make sure to propagate exceptions from rv onto aCx, so that they will get
|
|
|
|
// reported after we return. We do this for all failures on rv, because now
|
|
|
|
// we're using rv to track all the state we care about.
|
2016-02-24 18:38:31 +03:00
|
|
|
if (rv.MaybeSetPendingException(aCx)) {
|
2015-10-15 15:06:55 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2015-03-17 13:15:19 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class MessageEventRunnable final : public WorkerRunnable
|
2015-09-30 15:22:08 +03:00
|
|
|
, public StructuredCloneHolder
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
public:
|
2013-10-23 17:16:49 +04:00
|
|
|
MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
|
2015-09-16 06:27:56 +03:00
|
|
|
TargetAndBusyBehavior aBehavior)
|
2014-02-01 06:50:07 +04:00
|
|
|
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
2015-09-30 15:22:08 +03:00
|
|
|
, StructuredCloneHolder(CloningSupported, TransferringSupported,
|
2016-07-21 16:29:42 +03:00
|
|
|
StructuredCloneScope::SameProcessDifferentThread)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2015-06-17 13:44:27 +03:00
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
bool
|
|
|
|
DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
2014-04-01 10:13:50 +04:00
|
|
|
DOMEventTargetHelper* aTarget, bool aIsMainThread)
|
2013-11-05 18:16:26 +04:00
|
|
|
{
|
2016-03-01 17:21:11 +03:00
|
|
|
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(aTarget->GetParentObject());
|
|
|
|
|
|
|
|
// For some workers without window, parent is null and we try to find it
|
|
|
|
// from the JS Context.
|
|
|
|
if (!parent) {
|
|
|
|
JS::Rooted<JSObject*> globalObject(aCx, JS::CurrentGlobalOrNull(aCx));
|
|
|
|
if (NS_WARN_IF(!globalObject)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent = xpc::NativeGlobal(globalObject);
|
|
|
|
if (NS_WARN_IF(!parent)) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-24 22:54:35 +03:00
|
|
|
}
|
2013-11-05 18:16:26 +04:00
|
|
|
|
2016-03-01 17:21:11 +03:00
|
|
|
MOZ_ASSERT(parent);
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
JS::Rooted<JS::Value> messageData(aCx);
|
2015-08-27 19:19:13 +03:00
|
|
|
ErrorResult rv;
|
2015-10-22 00:10:05 +03:00
|
|
|
|
|
|
|
UniquePtr<AbstractTimelineMarker> start;
|
|
|
|
UniquePtr<AbstractTimelineMarker> end;
|
|
|
|
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
|
|
|
bool isTimelineRecording = timelines && !timelines->IsEmpty();
|
|
|
|
|
|
|
|
if (isTimelineRecording) {
|
|
|
|
start = MakeUnique<WorkerTimelineMarker>(aIsMainThread
|
|
|
|
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
|
|
|
|
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
|
|
|
|
MarkerTracingType::START);
|
|
|
|
}
|
|
|
|
|
2015-08-27 19:19:13 +03:00
|
|
|
Read(parent, aCx, &messageData, rv);
|
2015-10-22 00:10:05 +03:00
|
|
|
|
|
|
|
if (isTimelineRecording) {
|
|
|
|
end = MakeUnique<WorkerTimelineMarker>(aIsMainThread
|
|
|
|
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
|
|
|
|
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
|
|
|
|
MarkerTracingType::END);
|
|
|
|
timelines->AddMarkerForAllObservedDocShells(start);
|
|
|
|
timelines->AddMarkerForAllObservedDocShells(end);
|
|
|
|
}
|
|
|
|
|
2015-08-27 19:19:13 +03:00
|
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
|
|
xpc::Throw(aCx, rv.StealNSResult());
|
2013-11-05 18:16:26 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-10-26 23:00:17 +03:00
|
|
|
Sequence<OwningNonNull<MessagePort>> ports;
|
|
|
|
if (!TakeTransferredPortsAsSequence(ports)) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-11-10 09:31:41 +03:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMEvent> domEvent;
|
2016-11-21 05:14:53 +03:00
|
|
|
RefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
|
|
|
|
event->InitMessageEvent(nullptr,
|
|
|
|
NS_LITERAL_STRING("message"),
|
|
|
|
false /* non-bubbling */,
|
|
|
|
false /* cancelable */,
|
|
|
|
messageData,
|
|
|
|
EmptyString(),
|
|
|
|
EmptyString(),
|
|
|
|
nullptr,
|
|
|
|
ports);
|
|
|
|
domEvent = do_QueryObject(event);
|
2015-11-10 09:31:41 +03:00
|
|
|
|
|
|
|
domEvent->SetTrusted(true);
|
2013-11-05 18:16:26 +04:00
|
|
|
|
|
|
|
nsEventStatus dummy = nsEventStatus_eIgnore;
|
|
|
|
aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
|
2016-07-27 17:32:12 +03:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
if (mBehavior == ParentThreadUnchangedBusyCount) {
|
2012-03-31 08:42:20 +04:00
|
|
|
// Don't fire this event if the JS object has been disconnected from the
|
2011-07-17 23:09:13 +04:00
|
|
|
// private object.
|
2012-03-31 08:42:20 +04:00
|
|
|
if (!aWorkerPrivate->IsAcceptingEvents()) {
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-04 13:14:07 +03:00
|
|
|
if (aWorkerPrivate->IsFrozen() ||
|
|
|
|
aWorkerPrivate->IsParentWindowPaused()) {
|
2015-10-07 13:20:59 +03:00
|
|
|
MOZ_ASSERT(!IsDebuggerRunnable());
|
2011-07-17 23:09:13 +04:00
|
|
|
aWorkerPrivate->QueueRunnable(this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
aWorkerPrivate->AssertInnerWindowIsCorrect();
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate,
|
|
|
|
!aWorkerPrivate->GetParent());
|
2013-11-05 18:16:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(),
|
|
|
|
false);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-20 14:15:59 +03:00
|
|
|
class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable {
|
|
|
|
nsString mMessage;
|
|
|
|
|
|
|
|
public:
|
|
|
|
DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
const nsAString& aMessage)
|
|
|
|
: WorkerDebuggerRunnable(aWorkerPrivate),
|
|
|
|
mMessage(aMessage)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2015-03-20 14:15:59 +03:00
|
|
|
{
|
|
|
|
WorkerDebuggerGlobalScope* globalScope = aWorkerPrivate->DebuggerGlobalScope();
|
|
|
|
MOZ_ASSERT(globalScope);
|
|
|
|
|
|
|
|
JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, mMessage.get(),
|
|
|
|
mMessage.Length()));
|
|
|
|
if (!message) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
JS::Rooted<JS::Value> data(aCx, JS::StringValue(message));
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<MessageEvent> event = new MessageEvent(globalScope, nullptr,
|
2015-03-20 14:15:59 +03:00
|
|
|
nullptr);
|
2016-01-30 20:05:36 +03:00
|
|
|
event->InitMessageEvent(nullptr,
|
|
|
|
NS_LITERAL_STRING("message"),
|
|
|
|
false, // canBubble
|
|
|
|
true, // cancelable
|
|
|
|
data,
|
|
|
|
EmptyString(),
|
|
|
|
EmptyString(),
|
|
|
|
nullptr,
|
2016-10-25 08:48:05 +03:00
|
|
|
Sequence<OwningNonNull<MessagePort>>());
|
2015-03-20 14:15:59 +03:00
|
|
|
event->SetTrusted(true);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
|
|
globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &status);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class NotifyRunnable final : public WorkerControlRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
Status mStatus;
|
|
|
|
|
|
|
|
public:
|
2012-08-08 01:38:46 +04:00
|
|
|
NotifyRunnable(WorkerPrivate* aWorkerPrivate, Status aStatus)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
2012-08-08 01:38:46 +04:00
|
|
|
mStatus(aStatus)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2014-01-11 04:37:47 +04:00
|
|
|
MOZ_ASSERT(aStatus == Closing || aStatus == Terminating ||
|
|
|
|
aStatus == Canceling || aStatus == Killing);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2016-02-26 00:05:39 +03:00
|
|
|
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-02-26 00:05:39 +03:00
|
|
|
aWorkerPrivate->AssertIsOnParentThread();
|
2016-02-26 00:05:39 +03:00
|
|
|
return aWorkerPrivate->ModifyBusyCount(true);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual void
|
2016-02-26 23:23:12 +03:00
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
2013-11-05 18:16:24 +04:00
|
|
|
{
|
2016-02-26 00:05:39 +03:00
|
|
|
aWorkerPrivate->AssertIsOnParentThread();
|
2013-11-05 18:16:24 +04:00
|
|
|
if (!aDispatchResult) {
|
|
|
|
// We couldn't dispatch to the worker, which means it's already dead.
|
|
|
|
// Undo the busy count modification.
|
2016-02-26 00:05:39 +03:00
|
|
|
aWorkerPrivate->ModifyBusyCount(false);
|
2013-11-05 18:16:24 +04:00
|
|
|
}
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2016-09-01 07:33:05 +03:00
|
|
|
virtual void
|
|
|
|
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
|
|
|
|
override
|
|
|
|
{
|
|
|
|
aWorkerPrivate->ModifyBusyCountFromWorker(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
2016-02-27 05:15:56 +03:00
|
|
|
bool ok = aWorkerPrivate->NotifyInternal(aCx, mStatus);
|
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
|
|
|
|
return ok;
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
};
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
class FreezeRunnable final : public WorkerControlRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
public:
|
2015-04-01 12:00:19 +03:00
|
|
|
explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
2011-07-17 23:09:13 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-02-29 22:52:43 +03:00
|
|
|
return aWorkerPrivate->FreezeInternal();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
class ThawRunnable final : public WorkerControlRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
public:
|
2015-04-01 12:00:19 +03:00
|
|
|
explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
2011-07-17 23:09:13 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-02-29 22:52:43 +03:00
|
|
|
return aWorkerPrivate->ThawInternal();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-04-12 02:41:00 +03:00
|
|
|
class ReportErrorToConsoleRunnable final : public WorkerRunnable
|
|
|
|
{
|
|
|
|
const char* mMessage;
|
|
|
|
|
|
|
|
public:
|
|
|
|
// aWorkerPrivate is the worker thread we're on (or the main thread, if null)
|
|
|
|
static void
|
|
|
|
Report(WorkerPrivate* aWorkerPrivate, const char* aMessage)
|
|
|
|
{
|
|
|
|
if (aWorkerPrivate) {
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
} else {
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now fire a runnable to do the same on the parent's thread if we can.
|
|
|
|
if (aWorkerPrivate) {
|
|
|
|
RefPtr<ReportErrorToConsoleRunnable> runnable =
|
|
|
|
new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage);
|
|
|
|
runnable->Dispatch();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log a warning to the console.
|
|
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
|
|
|
NS_LITERAL_CSTRING("DOM"),
|
|
|
|
nullptr,
|
|
|
|
nsContentUtils::eDOM_PROPERTIES,
|
|
|
|
aMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate, const char* aMessage)
|
|
|
|
: WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
|
|
|
|
mMessage(aMessage)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
|
|
|
{
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
// Dispatch may fail if the worker was canceled, no need to report that as
|
|
|
|
// an error, so don't call base class PostDispatch.
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
|
|
|
{
|
|
|
|
WorkerPrivate* parent = aWorkerPrivate->GetParent();
|
|
|
|
MOZ_ASSERT_IF(!parent, NS_IsMainThread());
|
|
|
|
Report(parent, mMessage);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class ReportErrorRunnable final : public WorkerRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2017-02-15 17:53:07 +03:00
|
|
|
WorkerErrorReport mReport;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
public:
|
2013-11-05 18:16:24 +04:00
|
|
|
// aWorkerPrivate is the worker thread we're on (or the main thread, if null)
|
|
|
|
// aTarget is the worker object that we are going to fire an error at
|
|
|
|
// (if any).
|
2016-02-26 23:23:12 +03:00
|
|
|
static void
|
2011-07-17 23:09:13 +04:00
|
|
|
ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
2013-11-05 18:16:24 +04:00
|
|
|
bool aFireAtScope, WorkerPrivate* aTarget,
|
2017-02-15 17:53:07 +03:00
|
|
|
const WorkerErrorReport& aReport, uint64_t aInnerWindowId,
|
2016-08-29 19:30:51 +03:00
|
|
|
JS::Handle<JS::Value> aException = JS::NullHandleValue)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
if (aWorkerPrivate) {
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
2015-10-15 15:06:55 +03:00
|
|
|
} else {
|
2011-07-17 23:09:13 +04:00
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
|
|
|
|
2012-12-08 04:35:44 +04:00
|
|
|
// We should not fire error events for warnings but instead make sure that
|
|
|
|
// they show up in the error console.
|
2017-02-15 17:53:07 +03:00
|
|
|
if (!JSREPORT_IS_WARNING(aReport.mFlags)) {
|
2012-12-08 04:35:44 +04:00
|
|
|
// First fire an ErrorEvent at the worker.
|
2014-01-03 05:04:15 +04:00
|
|
|
RootedDictionary<ErrorEventInit> init(aCx);
|
2015-10-15 15:06:55 +03:00
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
if (aReport.mMutedError) {
|
2015-10-15 15:06:55 +03:00
|
|
|
init.mMessage.AssignLiteral("Script error.");
|
|
|
|
} else {
|
2017-02-15 17:53:07 +03:00
|
|
|
init.mMessage = aReport.mMessage;
|
|
|
|
init.mFilename = aReport.mFilename;
|
|
|
|
init.mLineno = aReport.mLineNumber;
|
2016-08-29 19:30:51 +03:00
|
|
|
init.mError = aException;
|
2015-10-15 15:06:55 +03:00
|
|
|
}
|
|
|
|
|
2014-02-23 09:01:12 +04:00
|
|
|
init.mCancelable = true;
|
2015-07-29 01:59:55 +03:00
|
|
|
init.mBubbles = false;
|
2013-10-14 07:10:43 +04:00
|
|
|
|
2014-02-23 09:01:12 +04:00
|
|
|
if (aTarget) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ErrorEvent> event =
|
2013-11-05 18:16:24 +04:00
|
|
|
ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
|
2013-11-05 18:16:26 +04:00
|
|
|
event->SetTrusted(true);
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
|
|
aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
|
|
|
|
|
|
|
|
if (status == nsEventStatus_eConsumeNoDefault) {
|
2016-02-26 23:23:12 +03:00
|
|
|
return;
|
2012-12-08 04:35:44 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2012-12-08 04:35:44 +04:00
|
|
|
|
|
|
|
// Now fire an event at the global object, but don't do that if the error
|
|
|
|
// code is too much recursion and this is the same script threw the error.
|
2016-05-11 03:57:29 +03:00
|
|
|
// XXXbz the interaction of this with worker errors seems kinda broken.
|
|
|
|
// An overrecursion in the debugger or debugger sandbox will get turned
|
|
|
|
// into an error event on our parent worker!
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks making this
|
|
|
|
// better.
|
2017-02-15 17:53:07 +03:00
|
|
|
if (aFireAtScope &&
|
|
|
|
(aTarget || aReport.mErrorNumber != JSMSG_OVER_RECURSED)) {
|
2015-03-04 02:51:53 +03:00
|
|
|
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
|
|
|
NS_ASSERTION(global, "This should never be null!");
|
2012-12-08 04:35:44 +04:00
|
|
|
|
2013-12-12 09:13:24 +04:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2012-12-08 04:35:44 +04:00
|
|
|
nsIScriptGlobalObject* sgo;
|
|
|
|
|
2013-12-10 01:59:15 +04:00
|
|
|
if (aWorkerPrivate) {
|
2015-03-04 02:51:53 +03:00
|
|
|
WorkerGlobalScope* globalScope = nullptr;
|
2016-06-28 20:47:23 +03:00
|
|
|
UNWRAP_OBJECT(WorkerGlobalScope, global, globalScope);
|
2015-03-26 22:09:45 +03:00
|
|
|
|
|
|
|
if (!globalScope) {
|
2015-03-04 02:51:53 +03:00
|
|
|
WorkerDebuggerGlobalScope* globalScope = nullptr;
|
|
|
|
UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalScope);
|
2015-03-31 13:22:40 +03:00
|
|
|
|
|
|
|
MOZ_ASSERT_IF(globalScope, globalScope->GetWrapperPreserveColor() == global);
|
2016-05-11 03:57:29 +03:00
|
|
|
if (globalScope || IsDebuggerSandbox(global)) {
|
2017-02-15 17:53:07 +03:00
|
|
|
aWorkerPrivate->ReportErrorToDebugger(aReport.mFilename, aReport.mLineNumber,
|
|
|
|
aReport.mMessage);
|
2016-05-11 03:57:29 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(SimpleGlobalObject::SimpleGlobalType(global) ==
|
|
|
|
SimpleGlobalObject::GlobalType::BindingDetail);
|
|
|
|
// XXXbz We should really log this to console, but unwinding out of
|
|
|
|
// this stuff without ending up firing any events is ... hard. Just
|
|
|
|
// return for now.
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks
|
|
|
|
// making this better.
|
2016-02-26 23:23:12 +03:00
|
|
|
return;
|
2015-03-04 02:51:53 +03:00
|
|
|
}
|
2012-12-08 04:35:44 +04:00
|
|
|
|
2015-03-31 13:22:40 +03:00
|
|
|
MOZ_ASSERT(globalScope->GetWrapperPreserveColor() == global);
|
2015-03-26 22:09:45 +03:00
|
|
|
nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalScope);
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ErrorEvent> event =
|
2014-02-23 09:01:12 +04:00
|
|
|
ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
|
|
|
|
event->SetTrusted(true);
|
2013-11-05 18:16:26 +04:00
|
|
|
|
2014-03-18 08:48:21 +04:00
|
|
|
if (NS_FAILED(EventDispatcher::DispatchDOMEvent(target, nullptr,
|
|
|
|
event, nullptr,
|
|
|
|
&status))) {
|
2013-11-05 18:16:26 +04:00
|
|
|
NS_WARNING("Failed to dispatch worker thread error event!");
|
|
|
|
status = nsEventStatus_eIgnore;
|
2012-12-08 04:35:44 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2015-03-04 02:51:53 +03:00
|
|
|
else if ((sgo = nsJSUtils::GetStaticScriptGlobal(global))) {
|
2013-12-21 04:22:13 +04:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2012-12-08 04:35:44 +04:00
|
|
|
|
2014-02-23 09:01:12 +04:00
|
|
|
if (NS_FAILED(sgo->HandleScriptError(init, &status))) {
|
2012-12-08 04:35:44 +04:00
|
|
|
NS_WARNING("Failed to dispatch main thread error event!");
|
|
|
|
status = nsEventStatus_eIgnore;
|
|
|
|
}
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-12-12 09:13:24 +04:00
|
|
|
// Was preventDefault() called?
|
|
|
|
if (status == nsEventStatus_eConsumeNoDefault) {
|
2016-02-26 23:23:12 +03:00
|
|
|
return;
|
2012-12-08 04:35:44 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now fire a runnable to do the same on the parent's thread if we can.
|
|
|
|
if (aWorkerPrivate) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ReportErrorRunnable> runnable =
|
2017-02-15 17:53:07 +03:00
|
|
|
new ReportErrorRunnable(aWorkerPrivate, aReport);
|
2016-02-26 23:23:12 +03:00
|
|
|
runnable->Dispatch();
|
|
|
|
return;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise log an error to the error console.
|
2017-02-15 17:53:07 +03:00
|
|
|
LogErrorToConsole(aReport, aInnerWindowId);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
private:
|
2017-02-15 17:53:07 +03:00
|
|
|
ReportErrorRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
const WorkerErrorReport& aReport)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
|
2017-02-15 17:53:07 +03:00
|
|
|
mReport(aReport)
|
2013-10-23 17:16:49 +04:00
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void
|
2016-02-26 23:23:12 +03:00
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
// Dispatch may fail if the worker was canceled, no need to report that as
|
|
|
|
// an error, so don't call base class PostDispatch.
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper());
|
|
|
|
|
|
|
|
uint64_t innerWindowId;
|
|
|
|
bool fireAtScope = true;
|
|
|
|
|
2014-07-11 22:59:39 +04:00
|
|
|
bool workerIsAcceptingEvents = aWorkerPrivate->IsAcceptingEvents();
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate* parent = aWorkerPrivate->GetParent();
|
|
|
|
if (parent) {
|
|
|
|
innerWindowId = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2016-10-04 13:14:07 +03:00
|
|
|
if (aWorkerPrivate->IsFrozen() ||
|
|
|
|
aWorkerPrivate->IsParentWindowPaused()) {
|
2015-10-07 13:20:59 +03:00
|
|
|
MOZ_ASSERT(!IsDebuggerRunnable());
|
2013-10-23 17:16:49 +04:00
|
|
|
aWorkerPrivate->QueueRunnable(this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-16 19:04:11 +03:00
|
|
|
if (aWorkerPrivate->IsSharedWorker()) {
|
2017-02-15 17:53:07 +03:00
|
|
|
aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, &mReport,
|
2017-01-28 17:39:24 +03:00
|
|
|
/* isErrorEvent */ true);
|
2013-10-23 17:16:49 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-16 19:04:11 +03:00
|
|
|
// Service workers do not have a main thread parent global, so normal
|
|
|
|
// worker error reporting will crash. Instead, pass the error to
|
|
|
|
// the ServiceWorkerManager to report on any controlled documents.
|
|
|
|
if (aWorkerPrivate->IsServiceWorker()) {
|
|
|
|
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
2017-01-13 01:00:36 +03:00
|
|
|
if (swm) {
|
|
|
|
swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
|
2017-03-25 02:56:48 +03:00
|
|
|
aWorkerPrivate->ServiceWorkerScope(),
|
2017-01-13 01:00:36 +03:00
|
|
|
aWorkerPrivate->ScriptURL(),
|
2017-02-15 17:53:07 +03:00
|
|
|
mReport.mMessage,
|
|
|
|
mReport.mFilename, mReport.mLine, mReport.mLineNumber,
|
|
|
|
mReport.mColumnNumber, mReport.mFlags,
|
|
|
|
mReport.mExnType);
|
2017-01-13 01:00:36 +03:00
|
|
|
}
|
2015-11-16 19:04:11 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-11 22:59:39 +04:00
|
|
|
// The innerWindowId is only required if we are going to ReportError
|
|
|
|
// below, which is gated on this condition. The inner window correctness
|
|
|
|
// check is only going to succeed when the worker is accepting events.
|
|
|
|
if (workerIsAcceptingEvents) {
|
|
|
|
aWorkerPrivate->AssertInnerWindowIsCorrect();
|
2014-12-17 09:26:15 +03:00
|
|
|
innerWindowId = aWorkerPrivate->WindowID();
|
2014-07-11 22:59:39 +04:00
|
|
|
}
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2014-07-11 22:59:39 +04:00
|
|
|
// Don't fire this event if the JS object has been disconnected from the
|
|
|
|
// private object.
|
|
|
|
if (!workerIsAcceptingEvents) {
|
|
|
|
return true;
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mReport,
|
|
|
|
innerWindowId);
|
2016-02-26 23:23:12 +03:00
|
|
|
return true;
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
};
|
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
class TimerRunnable final : public WorkerRunnable,
|
|
|
|
public nsITimerCallback
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
public:
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
|
|
|
|
2014-09-02 02:26:43 +04:00
|
|
|
explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
2011-07-17 23:09:13 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
~TimerRunnable() {}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual bool
|
2016-02-26 00:05:39 +03:00
|
|
|
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
// Silence bad assertions.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual void
|
2016-02-26 23:23:12 +03:00
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
// Silence bad assertions.
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
return aWorkerPrivate->RunExpiredTimeouts(aCx);
|
|
|
|
}
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
Notify(nsITimer* aTimer) override
|
|
|
|
{
|
|
|
|
return Run();
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
};
|
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback)
|
|
|
|
|
2015-03-30 14:54:38 +03:00
|
|
|
class DebuggerImmediateRunnable : public WorkerRunnable
|
|
|
|
{
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<dom::Function> mHandler;
|
2015-03-30 14:54:38 +03:00
|
|
|
|
|
|
|
public:
|
|
|
|
explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
|
2015-09-12 00:25:23 +03:00
|
|
|
dom::Function& aHandler)
|
2015-03-30 14:54:38 +03:00
|
|
|
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
|
|
|
mHandler(&aHandler)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-30 20:18:07 +03:00
|
|
|
IsDebuggerRunnable() const override
|
2015-03-30 14:54:38 +03:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
2016-02-26 00:05:39 +03:00
|
|
|
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
2015-03-30 14:54:38 +03:00
|
|
|
{
|
|
|
|
// Silence bad assertions.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
2016-02-26 23:23:12 +03:00
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
2015-03-30 14:54:38 +03:00
|
|
|
{
|
|
|
|
// Silence bad assertions.
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
|
|
|
{
|
|
|
|
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
2016-11-15 08:25:37 +03:00
|
|
|
JS::Rooted<JS::Value> callable(aCx, JS::ObjectOrNullValue(mHandler->CallableOrNull()));
|
2015-03-30 14:54:38 +03:00
|
|
|
JS::HandleValueArray args = JS::HandleValueArray::empty();
|
|
|
|
JS::Rooted<JS::Value> rval(aCx);
|
2016-03-02 00:52:27 +03:00
|
|
|
if (!JS_CallFunctionValue(aCx, global, callable, args, &rval)) {
|
|
|
|
// Just return false; WorkerRunnable::Run will report the exception.
|
2015-03-30 14:54:38 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
void
|
2017-01-26 19:01:33 +03:00
|
|
|
PeriodicGCTimerCallback(nsITimer* aTimer, void* aClosure)
|
|
|
|
{
|
|
|
|
auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
|
|
|
|
workerPrivate->AssertIsOnWorkerThread();
|
|
|
|
workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
|
|
|
|
false /* shrinking */,
|
|
|
|
false /* collect children */);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IdleGCTimerCallback(nsITimer* aTimer, void* aClosure)
|
|
|
|
{
|
|
|
|
auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
|
|
|
|
workerPrivate->AssertIsOnWorkerThread();
|
|
|
|
workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
|
|
|
|
true /* shrinking */,
|
|
|
|
false /* collect children */);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-07-07 09:15:15 +03:00
|
|
|
class UpdateContextOptionsRunnable final : public WorkerControlRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-07-07 09:15:15 +03:00
|
|
|
JS::ContextOptions mContextOptions;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
public:
|
2016-07-07 09:15:15 +03:00
|
|
|
UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
const JS::ContextOptions& aContextOptions)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
2016-07-07 09:15:15 +03:00
|
|
|
mContextOptions(aContextOptions)
|
2011-07-17 23:09:13 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-07-07 09:15:15 +03:00
|
|
|
aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class UpdatePreferenceRunnable final : public WorkerControlRunnable
|
2013-11-24 23:27:15 +04:00
|
|
|
{
|
|
|
|
WorkerPreference mPref;
|
|
|
|
bool mValue;
|
|
|
|
|
|
|
|
public:
|
|
|
|
UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
WorkerPreference aPref,
|
|
|
|
bool aValue)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
2013-11-24 23:27:15 +04:00
|
|
|
mPref(aPref),
|
|
|
|
mValue(aValue)
|
2013-10-23 17:16:49 +04:00
|
|
|
{ }
|
2013-11-24 23:27:15 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2013-11-24 23:27:15 +04:00
|
|
|
{
|
2016-02-26 23:23:12 +03:00
|
|
|
aWorkerPrivate->UpdatePreferenceInternal(mPref, mValue);
|
2013-11-24 23:27:15 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class UpdateLanguagesRunnable final : public WorkerRunnable
|
2014-09-05 18:26:34 +04:00
|
|
|
{
|
|
|
|
nsTArray<nsString> mLanguages;
|
|
|
|
|
|
|
|
public:
|
|
|
|
UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
const nsTArray<nsString>& aLanguages)
|
2016-06-28 20:28:13 +03:00
|
|
|
: WorkerRunnable(aWorkerPrivate),
|
2014-09-05 18:26:34 +04:00
|
|
|
mLanguages(aLanguages)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2014-09-05 18:26:34 +04:00
|
|
|
{
|
2016-02-26 23:23:12 +03:00
|
|
|
aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
|
2014-09-05 18:26:34 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class UpdateJSWorkerMemoryParameterRunnable final :
|
2013-10-23 17:16:49 +04:00
|
|
|
public WorkerControlRunnable
|
2012-01-04 23:11:32 +04:00
|
|
|
{
|
2013-01-11 02:50:40 +04:00
|
|
|
uint32_t mValue;
|
|
|
|
JSGCParamKey mKey;
|
2012-01-04 23:11:32 +04:00
|
|
|
|
|
|
|
public:
|
2013-01-11 02:50:40 +04:00
|
|
|
UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
JSGCParamKey aKey,
|
|
|
|
uint32_t aValue)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
2013-01-11 02:50:40 +04:00
|
|
|
mValue(aValue), mKey(aKey)
|
2012-01-04 23:11:32 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2012-01-04 23:11:32 +04:00
|
|
|
{
|
2013-02-16 02:12:19 +04:00
|
|
|
aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
|
2012-01-04 23:11:32 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
#ifdef JS_GC_ZEAL
|
2015-03-21 19:28:04 +03:00
|
|
|
class UpdateGCZealRunnable final : public WorkerControlRunnable
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2012-08-22 19:56:38 +04:00
|
|
|
uint8_t mGCZeal;
|
2013-05-17 02:49:43 +04:00
|
|
|
uint32_t mFrequency;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
public:
|
|
|
|
UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
|
2013-05-17 02:49:43 +04:00
|
|
|
uint8_t aGCZeal,
|
|
|
|
uint32_t aFrequency)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
2013-05-17 02:49:43 +04:00
|
|
|
mGCZeal(aGCZeal), mFrequency(aFrequency)
|
2011-07-17 23:09:13 +04:00
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-05-17 02:49:43 +04:00
|
|
|
aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class GarbageCollectRunnable final : public WorkerControlRunnable
|
2012-01-18 00:05:25 +04:00
|
|
|
{
|
|
|
|
bool mShrinking;
|
|
|
|
bool mCollectChildren;
|
|
|
|
|
|
|
|
public:
|
|
|
|
GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
|
|
|
|
bool aCollectChildren)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
2012-01-18 00:05:25 +04:00
|
|
|
mShrinking(aShrinking), mCollectChildren(aCollectChildren)
|
|
|
|
{ }
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
virtual bool
|
2016-02-26 00:05:39 +03:00
|
|
|
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
2012-01-18 00:05:25 +04:00
|
|
|
{
|
|
|
|
// Silence bad assertions, this can be dispatched from either the main
|
|
|
|
// thread or the timer thread..
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual void
|
2016-02-26 23:23:12 +03:00
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
2012-01-18 00:05:25 +04:00
|
|
|
{
|
|
|
|
// Silence bad assertions, this can be dispatched from either the main
|
|
|
|
// thread or the timer thread..
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2012-01-18 00:05:25 +04:00
|
|
|
{
|
|
|
|
aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-12-03 08:07:02 +04:00
|
|
|
class CycleCollectRunnable : public WorkerControlRunnable
|
|
|
|
{
|
|
|
|
bool mCollectChildren;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
|
2013-10-23 17:16:49 +04:00
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
2013-12-03 08:07:02 +04:00
|
|
|
mCollectChildren(aCollectChildren)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
2016-02-26 23:23:12 +03:00
|
|
|
aWorkerPrivate->CycleCollectInternal(mCollectChildren);
|
2013-12-03 08:07:02 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-11-20 03:08:50 +04:00
|
|
|
class OfflineStatusChangeRunnable : public WorkerRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
|
2016-06-28 20:28:13 +03:00
|
|
|
: WorkerRunnable(aWorkerPrivate),
|
2013-11-20 03:08:50 +04:00
|
|
|
mIsOffline(aIsOffline)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
2016-02-26 23:23:12 +03:00
|
|
|
aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
|
2013-11-20 03:08:50 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool mIsOffline;
|
|
|
|
};
|
|
|
|
|
2016-03-24 00:55:07 +03:00
|
|
|
class MemoryPressureRunnable : public WorkerControlRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
|
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
|
|
|
aWorkerPrivate->MemoryPressureInternal();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-01-23 08:26:21 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
static bool
|
|
|
|
StartsWithExplicit(nsACString& s)
|
|
|
|
{
|
|
|
|
return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class MessagePortRunnable final : public WorkerRunnable
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
2015-09-16 06:27:56 +03:00
|
|
|
MessagePortIdentifier mPortIdentifier;
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
public:
|
2016-06-28 20:28:13 +03:00
|
|
|
MessagePortRunnable(WorkerPrivate* aWorkerPrivate, MessagePort* aPort)
|
|
|
|
: WorkerRunnable(aWorkerPrivate)
|
2015-09-16 06:27:56 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(aPort);
|
|
|
|
// In order to move the port from one thread to another one, we have to
|
|
|
|
// close and disentangle it. The output will be a MessagePortIdentifier that
|
|
|
|
// will be used to recreate a new MessagePort on the other thread.
|
|
|
|
aPort->CloneAndDisentangle(mPortIdentifier);
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
private:
|
|
|
|
~MessagePortRunnable()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
2015-09-16 06:27:56 +03:00
|
|
|
return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier);
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
2016-01-07 21:01:56 +03:00
|
|
|
|
2016-04-11 21:40:06 +03:00
|
|
|
nsresult
|
2016-01-07 21:45:09 +03:00
|
|
|
Cancel() override
|
2016-01-07 21:01:56 +03:00
|
|
|
{
|
|
|
|
MessagePort::ForceClose(mPortIdentifier);
|
|
|
|
return WorkerRunnable::Cancel();
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class DummyRunnable final
|
2014-12-17 09:26:15 +03:00
|
|
|
: public WorkerRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit
|
|
|
|
DummyRunnable(WorkerPrivate* aWorkerPrivate)
|
|
|
|
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
|
|
|
{
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~DummyRunnable()
|
|
|
|
{
|
|
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
2016-02-26 00:05:39 +03:00
|
|
|
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
2014-12-17 09:26:15 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Should never call Dispatch on this!");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
2016-02-26 23:23:12 +03:00
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
2014-12-17 09:26:15 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Should never call Dispatch on this!");
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
2015-03-21 19:28:04 +03:00
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
2014-12-17 09:26:15 +03:00
|
|
|
{
|
|
|
|
// Do nothing.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
PRThread*
|
|
|
|
PRThreadFromThread(nsIThread* aThread)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(aThread);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
PRThread* result;
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(result);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
return result;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2017-05-25 10:04:25 +03:00
|
|
|
class SimpleWorkerHolder final : public WorkerHolder
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual bool Notify(Status aStatus) { return true; }
|
|
|
|
};
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
} /* anonymous namespace */
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, Runnable)
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, Runnable)
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-01-26 19:01:32 +03:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class WrappedControlRunnable final : public WorkerControlRunnable
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> mInner;
|
|
|
|
|
|
|
|
~WrappedControlRunnable()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
WrappedControlRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
already_AddRefed<nsIRunnable>&& aInner)
|
|
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
|
|
|
, mInner(aInner)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool
|
|
|
|
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
|
|
|
{
|
|
|
|
// Silence bad assertions, this can be dispatched from any thread.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
|
|
|
{
|
|
|
|
// Silence bad assertions, this can be dispatched from any thread.
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
|
|
|
{
|
|
|
|
mInner->Run();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
Cancel() override
|
|
|
|
{
|
|
|
|
// First run the default cancelation code
|
|
|
|
WorkerControlRunnable::Cancel();
|
|
|
|
|
|
|
|
// Attempt to cancel the inner runnable as well
|
|
|
|
nsCOMPtr<nsICancelableRunnable> cr = do_QueryInterface(mInner);
|
|
|
|
if (cr) {
|
|
|
|
return cr->Cancel();
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
BEGIN_WORKERS_NAMESPACE
|
|
|
|
|
|
|
|
class WorkerControlEventTarget final : public nsIEventTarget
|
|
|
|
{
|
|
|
|
mozilla::Mutex mMutex;
|
|
|
|
WorkerPrivate* mWorkerPrivate;
|
|
|
|
|
|
|
|
~WorkerControlEventTarget() = default;
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit WorkerControlEventTarget(WorkerPrivate* aWorkerPrivate)
|
|
|
|
: mMutex("WorkerControlEventTarget")
|
|
|
|
, mWorkerPrivate(aWorkerPrivate)
|
|
|
|
{
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ForgetWorkerPrivate(WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate == aWorkerPrivate);
|
|
|
|
mWorkerPrivate = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
|
|
|
return Dispatch(runnable.forget(), aFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
2017-05-20 06:58:07 +03:00
|
|
|
Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags = NS_DISPATCH_NORMAL) override
|
2017-01-26 19:01:32 +03:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<WorkerControlRunnable> r = new WrappedControlRunnable(mWorkerPrivate,
|
|
|
|
Move(aRunnable));
|
|
|
|
if (!r->Dispatch()) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override
|
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
IsOnCurrentThread(bool* aIsOnCurrentThread) override
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aIsOnCurrentThread);
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
*aIsOnCurrentThread = false;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(WorkerControlEventTarget, nsIEventTarget)
|
|
|
|
|
|
|
|
END_WORKERS_NAMESPACE
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo::WorkerLoadInfo()
|
2017-01-04 12:09:00 +03:00
|
|
|
: mLoadFlags(nsIRequest::LOAD_NORMAL)
|
|
|
|
, mWindowID(UINT64_MAX)
|
2015-05-14 22:41:42 +03:00
|
|
|
, mServiceWorkerID(0)
|
2017-01-05 06:30:07 +03:00
|
|
|
, mReferrerPolicy(net::RP_Unset)
|
2014-12-17 09:26:15 +03:00
|
|
|
, mFromWindow(false)
|
|
|
|
, mEvalAllowed(false)
|
|
|
|
, mReportCSPViolations(false)
|
|
|
|
, mXHRParamsAllowed(false)
|
|
|
|
, mPrincipalIsSystem(false)
|
2015-07-16 00:01:02 +03:00
|
|
|
, mStorageAllowed(false)
|
2015-06-28 06:19:24 +03:00
|
|
|
, mServiceWorkersTestingInWindow(false)
|
2014-12-17 09:26:15 +03:00
|
|
|
{
|
2015-02-12 12:50:05 +03:00
|
|
|
MOZ_COUNT_CTOR(WorkerLoadInfo);
|
2014-12-17 09:26:15 +03:00
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo::~WorkerLoadInfo()
|
2015-02-12 12:50:05 +03:00
|
|
|
{
|
2015-02-12 12:50:05 +03:00
|
|
|
MOZ_COUNT_DTOR(WorkerLoadInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mBaseURI);
|
|
|
|
aOther.mBaseURI.swap(mBaseURI);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mResolvedScriptURI);
|
|
|
|
aOther.mResolvedScriptURI.swap(mResolvedScriptURI);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mPrincipal);
|
|
|
|
aOther.mPrincipal.swap(mPrincipal);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mScriptContext);
|
|
|
|
aOther.mScriptContext.swap(mScriptContext);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mWindow);
|
|
|
|
aOther.mWindow.swap(mWindow);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mCSP);
|
|
|
|
aOther.mCSP.swap(mCSP);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mChannel);
|
|
|
|
aOther.mChannel.swap(mChannel);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mLoadGroup);
|
|
|
|
aOther.mLoadGroup.swap(mLoadGroup);
|
|
|
|
|
2015-07-15 22:21:40 +03:00
|
|
|
MOZ_ASSERT(!mLoadFailedAsyncRunnable);
|
|
|
|
aOther.mLoadFailedAsyncRunnable.swap(mLoadFailedAsyncRunnable);
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
MOZ_ASSERT(!mInterfaceRequestor);
|
|
|
|
aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mPrincipalInfo);
|
|
|
|
mPrincipalInfo = aOther.mPrincipalInfo.forget();
|
|
|
|
|
|
|
|
mDomain = aOther.mDomain;
|
2017-02-14 00:06:47 +03:00
|
|
|
mOrigin = aOther.mOrigin;
|
2015-03-19 21:41:42 +03:00
|
|
|
mServiceWorkerCacheName = aOther.mServiceWorkerCacheName;
|
2017-01-04 12:09:00 +03:00
|
|
|
mLoadFlags = aOther.mLoadFlags;
|
2015-02-12 12:50:05 +03:00
|
|
|
mWindowID = aOther.mWindowID;
|
2015-05-14 22:41:42 +03:00
|
|
|
mServiceWorkerID = aOther.mServiceWorkerID;
|
2016-07-05 06:47:13 +03:00
|
|
|
mReferrerPolicy = aOther.mReferrerPolicy;
|
2015-02-12 12:50:05 +03:00
|
|
|
mFromWindow = aOther.mFromWindow;
|
|
|
|
mEvalAllowed = aOther.mEvalAllowed;
|
|
|
|
mReportCSPViolations = aOther.mReportCSPViolations;
|
|
|
|
mXHRParamsAllowed = aOther.mXHRParamsAllowed;
|
|
|
|
mPrincipalIsSystem = aOther.mPrincipalIsSystem;
|
2015-07-16 00:01:02 +03:00
|
|
|
mStorageAllowed = aOther.mStorageAllowed;
|
2015-06-28 06:19:24 +03:00
|
|
|
mServiceWorkersTestingInWindow = aOther.mServiceWorkersTestingInWindow;
|
2016-09-20 04:13:00 +03:00
|
|
|
mOriginAttributes = aOther.mOriginAttributes;
|
2015-03-03 21:13:23 +03:00
|
|
|
}
|
2015-02-12 12:50:05 +03:00
|
|
|
|
2017-02-10 21:34:38 +03:00
|
|
|
nsresult
|
2017-02-07 18:28:38 +03:00
|
|
|
WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
|
|
|
|
nsILoadGroup* aLoadGroup)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
|
|
|
|
|
|
|
|
mPrincipal = aPrincipal;
|
|
|
|
mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
|
|
|
|
|
2017-02-10 21:34:38 +03:00
|
|
|
nsresult rv = aPrincipal->GetCsp(getter_AddRefs(mCSP));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-02-07 18:28:38 +03:00
|
|
|
|
|
|
|
if (mCSP) {
|
|
|
|
mCSP->GetAllowsEval(&mReportCSPViolations, &mEvalAllowed);
|
|
|
|
// Set ReferrerPolicy
|
|
|
|
bool hasReferrerPolicy = false;
|
|
|
|
uint32_t rp = mozilla::net::RP_Unset;
|
|
|
|
|
2017-02-10 21:34:38 +03:00
|
|
|
rv = mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-02-07 18:28:38 +03:00
|
|
|
|
|
|
|
if (hasReferrerPolicy) {
|
|
|
|
mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mEvalAllowed = true;
|
|
|
|
mReportCSPViolations = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mLoadGroup = aLoadGroup;
|
|
|
|
|
|
|
|
mPrincipalInfo = new PrincipalInfo();
|
|
|
|
mOriginAttributes = nsContentUtils::GetOriginAttributes(aLoadGroup);
|
|
|
|
|
2017-02-10 21:34:38 +03:00
|
|
|
rv = PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-02-14 00:06:47 +03:00
|
|
|
|
|
|
|
rv = nsContentUtils::GetUTFOrigin(aPrincipal, mOrigin);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-02-10 21:34:38 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
2017-02-07 18:28:38 +03:00
|
|
|
}
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
nsresult
|
2017-02-07 18:28:39 +03:00
|
|
|
WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
|
|
|
|
nsIPrincipal** aPrincipalOut,
|
|
|
|
nsILoadGroup** aLoadGroupOut)
|
2017-02-07 18:28:39 +03:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aChannel);
|
2017-02-07 18:28:39 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aPrincipalOut);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut);
|
2017-02-07 18:28:39 +03:00
|
|
|
|
|
|
|
// Initial triggering principal should be set
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mPrincipal);
|
|
|
|
|
|
|
|
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(ssm);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrincipal> channelPrincipal;
|
|
|
|
nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsILoadGroup> channelLoadGroup;
|
|
|
|
rv = aChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
MOZ_ASSERT(channelLoadGroup);
|
|
|
|
|
|
|
|
// If the load principal is the system principal then the channel
|
|
|
|
// principal must also be the system principal (we do not allow chrome
|
|
|
|
// code to create workers with non-chrome scripts, and if we ever decide
|
|
|
|
// to change this we need to make sure we don't always set
|
|
|
|
// mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
|
|
|
|
// this channel principal must be same origin with the load principal (we
|
|
|
|
// check again here in case redirects changed the location of the script).
|
|
|
|
if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
|
|
|
|
if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
|
|
|
|
nsCOMPtr<nsIURI> finalURI;
|
|
|
|
rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// See if this is a resource URI. Since JSMs usually come from
|
|
|
|
// resource:// URIs we're currently considering all URIs with the
|
|
|
|
// URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
|
|
|
|
bool isResource;
|
|
|
|
rv = NS_URIChainHasFlags(finalURI,
|
|
|
|
nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
|
|
|
&isResource);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
if (isResource) {
|
|
|
|
// Assign the system principal to the resource:// worker only if it
|
|
|
|
// was loaded from code using the system principal.
|
|
|
|
channelPrincipal = mPrincipal;
|
|
|
|
} else {
|
|
|
|
return NS_ERROR_DOM_BAD_URI;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The principal can change, but it should still match the original
|
|
|
|
// load group's appId and browser element flag.
|
|
|
|
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup, channelPrincipal));
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
channelPrincipal.forget(aPrincipalOut);
|
|
|
|
channelLoadGroup.forget(aLoadGroupOut);
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
nsresult
|
|
|
|
WorkerLoadInfo::SetPrincipalFromChannel(nsIChannel* aChannel)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
|
|
nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
|
|
|
|
getter_AddRefs(principal),
|
|
|
|
getter_AddRefs(loadGroup));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2017-02-10 21:34:38 +03:00
|
|
|
return SetPrincipalOnMainThread(principal, loadGroup);
|
2017-02-07 18:28:39 +03:00
|
|
|
}
|
|
|
|
|
2017-06-06 00:43:34 +03:00
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-02-07 18:28:39 +03:00
|
|
|
bool
|
|
|
|
WorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
|
|
nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
|
|
|
|
getter_AddRefs(principal),
|
|
|
|
getter_AddRefs(loadGroup));
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
|
|
|
|
|
|
|
// Verify that the channel is still a null principal. We don't care
|
|
|
|
// if these are the exact same null principal object, though. From
|
|
|
|
// the worker's perspective its the same effect.
|
|
|
|
if (principal->GetIsNullPrincipal() && mPrincipal->GetIsNullPrincipal()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise we require exact equality. Redirects can happen, but they
|
|
|
|
// are not allowed to change our principal.
|
|
|
|
if (principal->Equals(mPrincipal)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2017-02-14 18:06:39 +03:00
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerLoadInfo::PrincipalIsValid() const
|
|
|
|
{
|
|
|
|
return mPrincipal && mPrincipalInfo &&
|
|
|
|
mPrincipalInfo->type() != PrincipalInfo::T__None &&
|
|
|
|
mPrincipalInfo->type() <= PrincipalInfo::T__Last;
|
|
|
|
}
|
2017-02-23 18:54:41 +03:00
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerLoadInfo::PrincipalURIMatchesScriptURL()
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
nsAutoCString scheme;
|
|
|
|
nsresult rv = mBaseURI->GetScheme(scheme);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
|
|
|
// A system principal must either be a blob URL or a resource JSM.
|
|
|
|
if (mPrincipal->GetIsSystemPrincipal()) {
|
|
|
|
if (scheme == NS_LITERAL_CSTRING("blob")) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isResource = false;
|
|
|
|
nsresult rv = NS_URIChainHasFlags(mBaseURI,
|
|
|
|
nsIProtocolHandler::URI_IS_UI_RESOURCE,
|
|
|
|
&isResource);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
|
|
|
return isResource;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A null principal can occur for a data URL worker script or a blob URL
|
|
|
|
// worker script from a sandboxed iframe.
|
|
|
|
if (mPrincipal->GetIsNullPrincipal()) {
|
|
|
|
return scheme == NS_LITERAL_CSTRING("data") ||
|
|
|
|
scheme == NS_LITERAL_CSTRING("blob");
|
|
|
|
}
|
|
|
|
|
|
|
|
// The principal for a blob: URL worker script does not have a matching URL.
|
|
|
|
// This is likely a bug in our referer setting logic, but exempt it for now.
|
|
|
|
// This is another reason we should fix bug 1340694 so that referer does not
|
|
|
|
// depend on the principal URI.
|
|
|
|
if (scheme == NS_LITERAL_CSTRING("blob")) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> principalURI;
|
|
|
|
rv = mPrincipal->GetURI(getter_AddRefs(principalURI));
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
NS_ENSURE_TRUE(principalURI, false);
|
|
|
|
|
|
|
|
bool equal = false;
|
|
|
|
rv = principalURI->Equals(mBaseURI, &equal);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
|
|
|
return equal;
|
|
|
|
}
|
2017-06-06 00:43:34 +03:00
|
|
|
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-02-07 18:28:39 +03:00
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
bool
|
|
|
|
WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsILoadGroup> nullLoadGroup;
|
|
|
|
return ProxyReleaseMainThreadObjects(aWorkerPrivate, nullLoadGroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
|
|
|
|
nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
|
2017-02-07 18:28:39 +03:00
|
|
|
{
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
static const uint32_t kDoomedCount = 10;
|
|
|
|
nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
|
|
|
|
|
|
|
|
SwapToISupportsArray(mWindow, doomed);
|
|
|
|
SwapToISupportsArray(mScriptContext, doomed);
|
|
|
|
SwapToISupportsArray(mBaseURI, doomed);
|
|
|
|
SwapToISupportsArray(mResolvedScriptURI, doomed);
|
|
|
|
SwapToISupportsArray(mPrincipal, doomed);
|
|
|
|
SwapToISupportsArray(mChannel, doomed);
|
|
|
|
SwapToISupportsArray(mCSP, doomed);
|
|
|
|
SwapToISupportsArray(mLoadGroup, doomed);
|
|
|
|
SwapToISupportsArray(mLoadFailedAsyncRunnable, doomed);
|
|
|
|
SwapToISupportsArray(mInterfaceRequestor, doomed);
|
2017-02-07 18:28:39 +03:00
|
|
|
// Before adding anything here update kDoomedCount above!
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
MOZ_ASSERT(doomed.Length() == kDoomedCount);
|
|
|
|
|
|
|
|
RefPtr<MainThreadReleaseRunnable> runnable =
|
|
|
|
new MainThreadReleaseRunnable(doomed, aLoadGroupToCancel);
|
|
|
|
return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
|
2017-02-07 18:28:39 +03:00
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
template <class Derived>
|
2015-03-21 19:28:04 +03:00
|
|
|
class WorkerPrivateParent<Derived>::EventTarget final
|
2015-02-12 12:50:05 +03:00
|
|
|
: public nsIEventTarget
|
|
|
|
{
|
|
|
|
// This mutex protects mWorkerPrivate and must be acquired *before* the
|
|
|
|
// WorkerPrivate's mutex whenever they must both be held.
|
|
|
|
mozilla::Mutex mMutex;
|
|
|
|
WorkerPrivate* mWorkerPrivate;
|
|
|
|
nsIEventTarget* mWeakNestedEventTarget;
|
|
|
|
nsCOMPtr<nsIEventTarget> mNestedEventTarget;
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit EventTarget(WorkerPrivate* aWorkerPrivate)
|
|
|
|
: mMutex("WorkerPrivateParent::EventTarget::mMutex"),
|
|
|
|
mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
|
|
}
|
|
|
|
|
|
|
|
EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
|
|
|
|
: mMutex("WorkerPrivateParent::EventTarget::mMutex"),
|
|
|
|
mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
|
|
|
|
mNestedEventTarget(aNestedEventTarget)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
|
|
MOZ_ASSERT(aNestedEventTarget);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Disable()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIEventTarget> nestedEventTarget;
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
MOZ_ASSERT(mWorkerPrivate);
|
|
|
|
mWorkerPrivate = nullptr;
|
|
|
|
mNestedEventTarget.swap(nestedEventTarget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIEventTarget*
|
|
|
|
GetWeakNestedEventTarget() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mWeakNestedEventTarget);
|
|
|
|
return mWeakNestedEventTarget;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
2017-05-20 06:58:07 +03:00
|
|
|
NS_DECL_NSIEVENTTARGET_FULL
|
2015-02-12 12:50:05 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
~EventTarget()
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
WorkerLoadInfo::
|
|
|
|
InterfaceRequestor::InterfaceRequestor(nsIPrincipal* aPrincipal,
|
|
|
|
nsILoadGroup* aLoadGroup)
|
2015-02-21 18:09:17 +03:00
|
|
|
{
|
2015-02-12 12:50:05 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(aPrincipal);
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
// Look for an existing LoadContext. This is optional and it's ok if
|
|
|
|
// we don't find one.
|
|
|
|
nsCOMPtr<nsILoadContext> baseContext;
|
|
|
|
if (aLoadGroup) {
|
2015-02-21 18:09:17 +03:00
|
|
|
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
|
|
|
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
|
2015-02-12 12:50:05 +03:00
|
|
|
if (callbacks) {
|
|
|
|
callbacks->GetInterface(NS_GET_IID(nsILoadContext),
|
|
|
|
getter_AddRefs(baseContext));
|
2015-02-21 18:09:17 +03:00
|
|
|
}
|
2015-03-29 20:43:34 +03:00
|
|
|
mOuterRequestor = callbacks;
|
2015-02-21 18:09:17 +03:00
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
mLoadContext = new LoadContext(aPrincipal, baseContext);
|
|
|
|
}
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
void
|
|
|
|
WorkerLoadInfo::
|
|
|
|
InterfaceRequestor::MaybeAddTabChild(nsILoadGroup* aLoadGroup)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
if (!aLoadGroup) {
|
|
|
|
return;
|
|
|
|
}
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
|
|
|
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
|
|
|
|
if (!callbacks) {
|
|
|
|
return;
|
2015-02-21 18:09:17 +03:00
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
nsCOMPtr<nsITabChild> tabChild;
|
|
|
|
callbacks->GetInterface(NS_GET_IID(nsITabChild), getter_AddRefs(tabChild));
|
|
|
|
if (!tabChild) {
|
|
|
|
return;
|
|
|
|
}
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
// Use weak references to the tab child. Holding a strong reference will
|
|
|
|
// not prevent an ActorDestroy() from being called on the TabChild.
|
|
|
|
// Therefore, we should let the TabChild destroy itself as soon as possible.
|
|
|
|
mTabChildList.AppendElement(do_GetWeakReference(tabChild));
|
|
|
|
}
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerLoadInfo::
|
|
|
|
InterfaceRequestor::GetInterface(const nsIID& aIID, void** aSink)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(mLoadContext);
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
|
|
|
|
nsCOMPtr<nsILoadContext> ref = mLoadContext;
|
|
|
|
ref.forget(aSink);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
// If we still have an active nsITabChild, then return it. Its possible,
|
|
|
|
// though, that all of the TabChild objects have been destroyed. In that
|
|
|
|
// case we return NS_NOINTERFACE.
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsITabChild))) {
|
|
|
|
nsCOMPtr<nsITabChild> tabChild = GetAnyLiveTabChild();
|
|
|
|
if (!tabChild) {
|
|
|
|
return NS_NOINTERFACE;
|
2015-02-21 18:09:17 +03:00
|
|
|
}
|
2015-02-12 12:50:05 +03:00
|
|
|
tabChild.forget(aSink);
|
|
|
|
return NS_OK;
|
2015-02-21 18:09:17 +03:00
|
|
|
}
|
|
|
|
|
2015-03-29 20:43:34 +03:00
|
|
|
if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
|
|
|
|
mOuterRequestor) {
|
|
|
|
// If asked for the network intercept controller, ask the outer requestor,
|
|
|
|
// which could be the docshell.
|
|
|
|
return mOuterRequestor->GetInterface(aIID, aSink);
|
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
return NS_NOINTERFACE;
|
|
|
|
}
|
2015-03-03 21:13:23 +03:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
already_AddRefed<nsITabChild>
|
|
|
|
WorkerLoadInfo::
|
|
|
|
InterfaceRequestor::GetAnyLiveTabChild()
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
2015-02-12 12:50:05 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
// Search our list of known TabChild objects for one that still exists.
|
|
|
|
while (!mTabChildList.IsEmpty()) {
|
|
|
|
nsCOMPtr<nsITabChild> tabChild =
|
|
|
|
do_QueryReferent(mTabChildList.LastElement());
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-08-19 03:25:02 +03:00
|
|
|
// Does this tab child still exist? If so, return it. We are done. If the
|
|
|
|
// PBrowser actor is no longer useful, don't bother returning this tab.
|
|
|
|
if (tabChild && !static_cast<TabChild*>(tabChild.get())->IsDestroyed()) {
|
2015-02-12 12:50:05 +03:00
|
|
|
return tabChild.forget();
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
// Otherwise remove the stale weak reference and check the next one
|
|
|
|
mTabChildList.RemoveElementAt(mTabChildList.Length() - 1);
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)
|
|
|
|
NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)
|
|
|
|
NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor, nsIInterfaceRequestor)
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
struct WorkerPrivate::TimeoutInfo
|
|
|
|
{
|
|
|
|
TimeoutInfo()
|
2016-08-16 09:10:30 +03:00
|
|
|
: mId(0), mIsInterval(false), mCanceled(false)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
~TimeoutInfo()
|
|
|
|
{
|
|
|
|
MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const TimeoutInfo& aOther)
|
|
|
|
{
|
|
|
|
return mTargetTime == aOther.mTargetTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator<(const TimeoutInfo& aOther)
|
|
|
|
{
|
|
|
|
return mTargetTime < aOther.mTargetTime;
|
|
|
|
}
|
|
|
|
|
2016-08-16 09:10:30 +03:00
|
|
|
nsCOMPtr<nsIScriptTimeoutHandler> mHandler;
|
2011-07-17 23:09:13 +04:00
|
|
|
mozilla::TimeStamp mTargetTime;
|
|
|
|
mozilla::TimeDuration mInterval;
|
2013-11-05 18:16:26 +04:00
|
|
|
int32_t mId;
|
2011-07-17 23:09:13 +04:00
|
|
|
bool mIsInterval;
|
|
|
|
bool mCanceled;
|
|
|
|
};
|
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
class WorkerJSContextStats final : public JS::RuntimeStats
|
|
|
|
{
|
|
|
|
const nsCString mRtPath;
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit WorkerJSContextStats(const nsACString& aRtPath)
|
|
|
|
: JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
~WorkerJSContextStats()
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i != zoneStatsVector.length(); i++) {
|
|
|
|
delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
|
|
|
|
delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const nsCString& Path() const
|
|
|
|
{
|
|
|
|
return mRtPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
initExtraZoneStats(JS::Zone* aZone,
|
|
|
|
JS::ZoneStats* aZoneStats)
|
|
|
|
override
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!aZoneStats->extra);
|
|
|
|
|
|
|
|
// ReportJSRuntimeExplicitTreeStats expects that
|
|
|
|
// aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
|
|
|
|
xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
|
|
|
|
extras->pathPrefix = mRtPath;
|
|
|
|
extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone);
|
|
|
|
|
|
|
|
MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
|
|
|
|
|
|
|
|
aZoneStats->extra = extras;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
initExtraCompartmentStats(JSCompartment* aCompartment,
|
|
|
|
JS::CompartmentStats* aCompartmentStats)
|
|
|
|
override
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!aCompartmentStats->extra);
|
|
|
|
|
|
|
|
// ReportJSRuntimeExplicitTreeStats expects that
|
|
|
|
// aCompartmentStats->extra is a xpc::CompartmentStatsExtras pointer.
|
|
|
|
xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras;
|
|
|
|
|
|
|
|
// This is the |jsPathPrefix|. Each worker has exactly two compartments:
|
|
|
|
// one for atoms, and one for everything else.
|
|
|
|
extras->jsPathPrefix.Assign(mRtPath);
|
|
|
|
extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
|
|
|
|
(void *)js::GetCompartmentZone(aCompartment));
|
|
|
|
extras->jsPathPrefix += js::IsAtomsCompartment(aCompartment)
|
|
|
|
? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
|
|
|
|
: NS_LITERAL_CSTRING("compartment(web-worker)/");
|
|
|
|
|
|
|
|
// This should never be used when reporting with workers (hence the "?!").
|
|
|
|
extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
|
|
|
|
|
|
|
|
MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
|
|
|
|
MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
|
|
|
|
|
|
|
|
extras->location = nullptr;
|
|
|
|
|
|
|
|
aCompartmentStats->extra = extras;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter
|
2013-02-08 15:50:00 +04:00
|
|
|
{
|
2013-12-08 09:39:47 +04:00
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
|
2013-02-08 15:50:00 +04:00
|
|
|
friend class WorkerPrivate;
|
|
|
|
|
|
|
|
SharedMutex mMutex;
|
|
|
|
WorkerPrivate* mWorkerPrivate;
|
2013-06-15 06:48:28 +04:00
|
|
|
bool mAlreadyMappedToAddon;
|
2013-02-08 15:50:00 +04:00
|
|
|
|
|
|
|
public:
|
2014-09-02 02:26:43 +04:00
|
|
|
explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
|
2013-12-04 08:01:24 +04:00
|
|
|
: mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate),
|
2013-06-15 06:48:28 +04:00
|
|
|
mAlreadyMappedToAddon(false)
|
2013-02-08 15:50:00 +04:00
|
|
|
{
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
2016-08-24 08:23:45 +03:00
|
|
|
CollectReports(nsIHandleReportCallback* aHandleReport,
|
2016-10-28 12:50:16 +03:00
|
|
|
nsISupports* aData, bool aAnonymize) override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
class FinishCollectRunnable;
|
|
|
|
|
|
|
|
class CollectReportsRunnable final : public MainThreadWorkerControlRunnable
|
2013-02-08 15:50:00 +04:00
|
|
|
{
|
2016-10-28 12:50:16 +03:00
|
|
|
RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
|
|
|
|
const bool mAnonymize;
|
2013-02-08 15:50:00 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
public:
|
|
|
|
CollectReportsRunnable(
|
|
|
|
WorkerPrivate* aWorkerPrivate,
|
|
|
|
nsIHandleReportCallback* aHandleReport,
|
|
|
|
nsISupports* aHandlerData,
|
|
|
|
bool aAnonymize,
|
|
|
|
const nsACString& aPath);
|
2013-02-08 15:50:00 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
private:
|
|
|
|
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
|
2013-02-08 15:50:00 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
~CollectReportsRunnable()
|
|
|
|
{
|
|
|
|
if (NS_IsMainThread()) {
|
|
|
|
mFinishCollectRunnable->Run();
|
|
|
|
return;
|
2015-01-16 07:12:20 +03:00
|
|
|
}
|
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
|
|
MOZ_ASSERT(workerPrivate);
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
|
|
workerPrivate->DispatchToMainThread(mFinishCollectRunnable.forget()));
|
|
|
|
}
|
|
|
|
};
|
2014-05-21 10:06:54 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
class FinishCollectRunnable final : public Runnable
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIHandleReportCallback> mHandleReport;
|
|
|
|
nsCOMPtr<nsISupports> mHandlerData;
|
|
|
|
const bool mAnonymize;
|
|
|
|
bool mSuccess;
|
2015-01-23 08:26:21 +03:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
public:
|
|
|
|
WorkerJSContextStats mCxStats;
|
|
|
|
|
|
|
|
explicit FinishCollectRunnable(
|
|
|
|
nsIHandleReportCallback* aHandleReport,
|
|
|
|
nsISupports* aHandlerData,
|
|
|
|
bool aAnonymize,
|
|
|
|
const nsACString& aPath);
|
|
|
|
|
|
|
|
NS_IMETHOD Run() override;
|
|
|
|
|
|
|
|
void SetSuccess(bool success)
|
|
|
|
{
|
|
|
|
mSuccess = success;
|
2013-02-08 15:50:00 +04:00
|
|
|
}
|
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
private:
|
|
|
|
~FinishCollectRunnable()
|
|
|
|
{
|
|
|
|
// mHandleReport and mHandlerData are released on the main thread.
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
FinishCollectRunnable(const FinishCollectRunnable&) = delete;
|
|
|
|
FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
|
|
|
|
FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
|
|
|
|
};
|
2013-02-08 15:50:00 +04:00
|
|
|
|
|
|
|
~MemoryReporter()
|
2016-10-28 12:50:16 +03:00
|
|
|
{
|
|
|
|
}
|
2013-02-08 15:50:00 +04:00
|
|
|
|
|
|
|
void
|
|
|
|
Disable()
|
|
|
|
{
|
|
|
|
// Called from WorkerPrivate::DisableMemoryReporter.
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
|
|
|
|
|
|
|
NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
|
|
|
|
mWorkerPrivate = nullptr;
|
|
|
|
}
|
2013-06-15 06:48:28 +04:00
|
|
|
|
|
|
|
// Only call this from the main thread and under mMutex lock.
|
|
|
|
void
|
2016-10-28 12:50:16 +03:00
|
|
|
TryToMapAddon(nsACString &path);
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerPrivate::MemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
|
|
|
|
nsISupports* aData,
|
|
|
|
bool aAnonymize)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
RefPtr<CollectReportsRunnable> runnable;
|
|
|
|
|
2013-06-15 06:48:28 +04:00
|
|
|
{
|
2016-10-28 12:50:16 +03:00
|
|
|
MutexAutoLock lock(mMutex);
|
2013-06-15 06:48:28 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
// This will effectively report 0 memory.
|
|
|
|
nsCOMPtr<nsIMemoryReporterManager> manager =
|
|
|
|
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
|
|
|
if (manager) {
|
|
|
|
manager->EndReport();
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2013-06-15 06:48:28 +04:00
|
|
|
}
|
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
nsAutoCString path;
|
|
|
|
path.AppendLiteral("explicit/workers/workers(");
|
|
|
|
if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
|
|
|
|
path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
|
|
|
|
} else {
|
|
|
|
nsAutoCString escapedDomain(mWorkerPrivate->Domain());
|
|
|
|
if (escapedDomain.IsEmpty()) {
|
|
|
|
escapedDomain += "chrome";
|
|
|
|
} else {
|
|
|
|
escapedDomain.ReplaceChar('/', '\\');
|
|
|
|
}
|
|
|
|
path.Append(escapedDomain);
|
|
|
|
path.AppendLiteral(")/worker(");
|
|
|
|
NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
|
|
|
|
escapedURL.ReplaceChar('/', '\\');
|
|
|
|
path.Append(escapedURL);
|
2013-06-15 06:48:28 +04:00
|
|
|
}
|
2016-10-28 12:50:16 +03:00
|
|
|
path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
|
2013-06-15 06:48:28 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
TryToMapAddon(path);
|
2013-06-15 06:48:28 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
runnable =
|
|
|
|
new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData, aAnonymize, path);
|
|
|
|
}
|
2013-10-28 08:53:00 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
2013-06-15 06:48:28 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerPrivate::MemoryReporter::TryToMapAddon(nsACString &path)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
2013-06-15 06:48:28 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
if (mAlreadyMappedToAddon || !mWorkerPrivate) {
|
|
|
|
return;
|
2013-06-15 06:48:28 +04:00
|
|
|
}
|
2013-02-08 15:50:00 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
nsCOMPtr<nsIURI> scriptURI;
|
|
|
|
if (NS_FAILED(NS_NewURI(getter_AddRefs(scriptURI),
|
|
|
|
mWorkerPrivate->ScriptURL()))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mAlreadyMappedToAddon = true;
|
|
|
|
|
|
|
|
if (!XRE_IsParentProcess()) {
|
|
|
|
// Only try to access the service from the main process.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoCString addonId;
|
|
|
|
bool ok;
|
|
|
|
nsCOMPtr<amIAddonManager> addonManager =
|
|
|
|
do_GetService("@mozilla.org/addons/integration;1");
|
|
|
|
|
|
|
|
if (!addonManager ||
|
|
|
|
NS_FAILED(addonManager->MapURIToAddonID(scriptURI, addonId, &ok)) ||
|
|
|
|
!ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const size_t explicitLength = strlen("explicit/");
|
|
|
|
addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
|
|
|
|
addonId += "/";
|
|
|
|
path.Insert(addonId, explicitLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
|
|
|
|
WorkerPrivate* aWorkerPrivate,
|
|
|
|
nsIHandleReportCallback* aHandleReport,
|
|
|
|
nsISupports* aHandlerData,
|
|
|
|
bool aAnonymize,
|
|
|
|
const nsACString& aPath)
|
|
|
|
: MainThreadWorkerControlRunnable(aWorkerPrivate),
|
|
|
|
mFinishCollectRunnable(
|
|
|
|
new FinishCollectRunnable(aHandleReport, aHandlerData, aAnonymize, aPath)),
|
|
|
|
mAnonymize(aAnonymize)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(JSContext* aCx,
|
|
|
|
WorkerPrivate* aWorkerPrivate)
|
|
|
|
{
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
mFinishCollectRunnable->SetSuccess(
|
|
|
|
aWorkerPrivate->CollectRuntimeStats(&mFinishCollectRunnable->mCxStats, mAnonymize));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
|
|
|
|
nsIHandleReportCallback* aHandleReport,
|
|
|
|
nsISupports* aHandlerData,
|
|
|
|
bool aAnonymize,
|
|
|
|
const nsACString& aPath)
|
|
|
|
: mHandleReport(aHandleReport),
|
|
|
|
mHandlerData(aHandlerData),
|
|
|
|
mAnonymize(aAnonymize),
|
|
|
|
mSuccess(false),
|
|
|
|
mCxStats(aPath)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run()
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIMemoryReporterManager> manager =
|
|
|
|
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
|
|
|
|
|
|
|
if (!manager)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
if (mSuccess) {
|
|
|
|
xpc::ReportJSRuntimeExplicitTreeStats(mCxStats, mCxStats.Path(),
|
|
|
|
mHandleReport, mHandlerData,
|
|
|
|
mAnonymize);
|
|
|
|
}
|
|
|
|
|
|
|
|
manager->EndReport();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2013-12-08 09:39:47 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
|
|
|
|
: mEventTarget(aEventTarget), mCompleted(false), mResult(false)
|
|
|
|
#ifdef DEBUG
|
|
|
|
, mHasRun(false)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-04-07 19:19:19 +03:00
|
|
|
template <class Derived>
|
|
|
|
nsIDocument*
|
|
|
|
WorkerPrivateParent<Derived>::GetDocument() const
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
if (mLoadInfo.mWindow) {
|
|
|
|
return mLoadInfo.mWindow->GetExtantDoc();
|
|
|
|
}
|
|
|
|
// if we don't have a document, we should query the document
|
|
|
|
// from the parent in case of a nested worker
|
|
|
|
WorkerPrivate* parent = mParent;
|
|
|
|
while (parent) {
|
|
|
|
if (parent->mLoadInfo.mWindow) {
|
|
|
|
return parent->mLoadInfo.mWindow->GetExtantDoc();
|
|
|
|
}
|
|
|
|
parent = parent->GetParent();
|
|
|
|
}
|
|
|
|
// couldn't query a document, give up and return nullptr
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-03-29 05:20:32 +03:00
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::SetCSP(nsIContentSecurityPolicy* aCSP)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
if (!aCSP) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
|
|
|
aCSP->EnsureEventTarget(self->mMainThreadEventTarget);
|
|
|
|
mLoadInfo.mCSP = aCSP;
|
|
|
|
}
|
|
|
|
|
2017-02-14 18:06:38 +03:00
|
|
|
template <class Derived>
|
|
|
|
nsresult
|
|
|
|
WorkerPrivateParent<Derived>::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
|
|
|
|
const nsACString& aCSPReportOnlyHeaderValue)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
|
|
|
|
|
|
|
|
NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
|
|
|
|
NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
|
|
|
nsresult rv = mLoadInfo.mPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
|
|
|
|
if (!csp) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-03-29 05:20:32 +03:00
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
|
|
|
csp->EnsureEventTarget(self->mMainThreadEventTarget);
|
|
|
|
|
2017-02-14 18:06:38 +03:00
|
|
|
// If there's a CSP header, apply it.
|
|
|
|
if (!cspHeaderValue.IsEmpty()) {
|
|
|
|
rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
// If there's a report-only CSP header, apply it.
|
|
|
|
if (!cspROHeaderValue.IsEmpty()) {
|
|
|
|
rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set evalAllowed, default value is set in GetAllowsEval
|
|
|
|
bool evalAllowed = false;
|
|
|
|
bool reportEvalViolations = false;
|
|
|
|
rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// Set ReferrerPolicy, default value is set in GetReferrerPolicy
|
|
|
|
bool hasReferrerPolicy = false;
|
|
|
|
uint32_t rp = mozilla::net::RP_Unset;
|
|
|
|
rv = csp->GetReferrerPolicy(&rp, &hasReferrerPolicy);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
mLoadInfo.mCSP = csp;
|
|
|
|
mLoadInfo.mEvalAllowed = evalAllowed;
|
|
|
|
mLoadInfo.mReportCSPViolations = reportEvalViolations;
|
|
|
|
|
|
|
|
if (hasReferrerPolicy) {
|
|
|
|
mLoadInfo.mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-02-23 18:54:42 +03:00
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::SetReferrerPolicyFromHeaderValue(
|
|
|
|
const nsACString& aReferrerPolicyHeaderValue)
|
|
|
|
{
|
|
|
|
NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
|
|
|
|
|
|
|
|
if (headerValue.IsEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
net::ReferrerPolicy policy =
|
|
|
|
nsContentUtils::GetReferrerPolicyFromHeader(headerValue);
|
|
|
|
if (policy == net::RP_Unset) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SetReferrerPolicy(policy);
|
|
|
|
}
|
|
|
|
|
2015-04-07 19:19:19 +03:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
|
|
|
|
// templates.
|
|
|
|
template <class Derived>
|
|
|
|
typename WorkerPrivateParent<Derived>::cycleCollection
|
|
|
|
WorkerPrivateParent<Derived>::_cycleCollectorGlobal =
|
|
|
|
WorkerPrivateParent<Derived>::cycleCollection();
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
template <class Derived>
|
|
|
|
WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
2014-02-06 18:39:10 +04:00
|
|
|
WorkerPrivate* aParent,
|
|
|
|
const nsAString& aScriptURL,
|
|
|
|
bool aIsChromeWorker,
|
|
|
|
WorkerType aWorkerType,
|
2017-05-17 17:49:34 +03:00
|
|
|
const nsAString& aWorkerName,
|
|
|
|
const nsACString& aServiceWorkerScope,
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo& aLoadInfo)
|
2013-11-05 18:16:24 +04:00
|
|
|
: mMutex("WorkerPrivateParent Mutex"),
|
2011-07-17 23:09:13 +04:00
|
|
|
mCondVar(mMutex, "WorkerPrivateParent CondVar"),
|
2013-11-05 18:16:24 +04:00
|
|
|
mParent(aParent), mScriptURL(aScriptURL),
|
2017-05-17 17:49:34 +03:00
|
|
|
mWorkerName(aWorkerName), mServiceWorkerScope(aServiceWorkerScope),
|
|
|
|
mLoadingWorkerScript(false), mBusyCount(0), mParentWindowPausedDepth(0),
|
|
|
|
mParentStatus(Pending), mParentFrozen(false),
|
|
|
|
mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
|
|
|
|
mIsSecureContext(false), mWorkerType(aWorkerType),
|
2015-05-25 19:53:07 +03:00
|
|
|
mCreationTimeStamp(TimeStamp::Now()),
|
2016-01-12 17:48:53 +03:00
|
|
|
mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2017-05-17 17:48:54 +03:00
|
|
|
MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
if (aLoadInfo.mWindow) {
|
2013-11-05 18:16:24 +04:00
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
|
|
|
|
"Should have inner window here!");
|
|
|
|
BindToOwner(aLoadInfo.mWindow);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
mLoadInfo.StealFrom(aLoadInfo);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
if (aParent) {
|
|
|
|
aParent->AssertIsOnWorkerThread();
|
|
|
|
|
2016-10-10 22:09:00 +03:00
|
|
|
// Note that this copies our parent's secure context state into mJSSettings.
|
2013-05-17 02:49:43 +04:00
|
|
|
aParent->CopyJSSettings(mJSSettings);
|
2014-08-30 03:50:06 +04:00
|
|
|
|
2016-10-10 22:09:00 +03:00
|
|
|
// And manually set our mIsSecureContext, though it's not really relevant to
|
|
|
|
// dedicated workers...
|
|
|
|
mIsSecureContext = aParent->IsSecureContext();
|
|
|
|
MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
|
|
|
|
|
2014-08-30 03:50:06 +04:00
|
|
|
MOZ_ASSERT(IsDedicatedWorker());
|
2016-10-26 20:20:15 +03:00
|
|
|
|
|
|
|
if (aParent->mParentFrozen) {
|
|
|
|
Freeze(nullptr);
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2013-05-17 02:49:43 +04:00
|
|
|
RuntimeService::GetDefaultJSSettings(mJSSettings);
|
2016-02-02 00:48:04 +03:00
|
|
|
|
2016-10-10 22:09:00 +03:00
|
|
|
// Our secure context state depends on the kind of worker we have.
|
|
|
|
if (UsesSystemPrincipal() || IsServiceWorker()) {
|
|
|
|
mIsSecureContext = true;
|
|
|
|
} else if (mLoadInfo.mWindow) {
|
|
|
|
// Shared and dedicated workers both inherit the loading window's secure
|
|
|
|
// context state. Shared workers then prevent windows with a different
|
|
|
|
// secure context state from attaching to them.
|
|
|
|
mIsSecureContext = mLoadInfo.mWindow->IsSecureContext();
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
|
|
|
|
"that has no parent and no associated window");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mIsSecureContext) {
|
|
|
|
mJSSettings.chrome.compartmentOptions
|
|
|
|
.creationOptions().setSecureContext(true);
|
|
|
|
mJSSettings.content.compartmentOptions
|
|
|
|
.creationOptions().setSecureContext(true);
|
|
|
|
}
|
|
|
|
|
2016-10-26 20:20:15 +03:00
|
|
|
// Our parent can get suspended after it initiates the async creation
|
|
|
|
// of a new worker thread. In this case suspend the new worker as well.
|
2016-10-26 20:20:16 +03:00
|
|
|
if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
|
2016-10-26 20:20:15 +03:00
|
|
|
ParentWindowPaused();
|
|
|
|
}
|
|
|
|
|
2016-10-26 20:20:16 +03:00
|
|
|
if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
|
2016-10-26 20:20:15 +03:00
|
|
|
Freeze(mLoadInfo.mWindow);
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2013-10-14 15:58:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
2013-10-14 22:38:54 +04:00
|
|
|
WorkerPrivateParent<Derived>::~WorkerPrivateParent()
|
2013-10-14 15:58:05 +04:00
|
|
|
{
|
2013-11-05 18:16:24 +04:00
|
|
|
DropJSObjects(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
2013-10-23 17:16:49 +04:00
|
|
|
JSObject*
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 17:13:33 +03:00
|
|
|
WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(!IsSharedWorker(),
|
|
|
|
"We should never wrap a WorkerPrivate for a SharedWorker");
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
AssertIsOnParentThread();
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2014-03-06 05:58:03 +04:00
|
|
|
// XXXkhuey this should not need to be rooted, the analysis is dumb.
|
|
|
|
// See bug 980181.
|
|
|
|
JS::Rooted<JSObject*> wrapper(aCx,
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 17:13:33 +03:00
|
|
|
WorkerBinding::Wrap(aCx, ParentAsWorkerPrivate(), aGivenProto));
|
2014-03-06 05:58:03 +04:00
|
|
|
if (wrapper) {
|
|
|
|
MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
|
|
|
|
}
|
|
|
|
|
|
|
|
return wrapper;
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2013-11-05 18:16:24 +04:00
|
|
|
|
|
|
|
template <class Derived>
|
2013-10-23 17:16:49 +04:00
|
|
|
nsresult
|
2016-06-01 03:04:54 +03:00
|
|
|
WorkerPrivateParent<Derived>::DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable,
|
2013-10-23 17:16:49 +04:00
|
|
|
nsIEventTarget* aSyncLoopTarget)
|
|
|
|
{
|
|
|
|
// May be called on any thread!
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerRunnable> runnable(aRunnable);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
MOZ_ASSERT_IF(aSyncLoopTarget, self->mThread);
|
|
|
|
|
|
|
|
if (!self->mThread) {
|
|
|
|
if (ParentStatus() == Pending || self->mStatus == Pending) {
|
2015-07-10 06:21:46 +03:00
|
|
|
mPreStartRunnables.AppendElement(runnable);
|
2013-10-23 17:16:49 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_WARNING("Using a worker event target after the thread has already"
|
|
|
|
"been released!");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->mStatus == Dead ||
|
|
|
|
(!aSyncLoopTarget && ParentStatus() > Running)) {
|
|
|
|
NS_WARNING("A runnable was posted to a worker that is already shutting "
|
|
|
|
"down!");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2014-11-17 22:55:37 +03:00
|
|
|
nsresult rv;
|
2013-10-23 17:16:49 +04:00
|
|
|
if (aSyncLoopTarget) {
|
2015-07-10 06:21:46 +03:00
|
|
|
rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
2014-11-17 22:55:37 +03:00
|
|
|
} else {
|
2015-07-10 06:21:46 +03:00
|
|
|
rv = self->mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
mCondVar.Notify();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2014-10-27 20:00:05 +03:00
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::EnableDebugger()
|
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
|
|
|
|
2016-01-07 15:35:31 +03:00
|
|
|
if (NS_FAILED(RegisterWorkerDebugger(self))) {
|
2014-10-27 20:00:05 +03:00
|
|
|
NS_WARNING("Failed to register worker debugger!");
|
2016-01-07 15:35:31 +03:00
|
|
|
return;
|
2014-10-27 20:00:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::DisableDebugger()
|
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
|
|
|
|
2016-01-07 15:35:31 +03:00
|
|
|
if (NS_FAILED(UnregisterWorkerDebugger(self))) {
|
2014-10-27 20:00:05 +03:00
|
|
|
NS_WARNING("Failed to unregister worker debugger!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
template <class Derived>
|
2013-10-23 17:16:49 +04:00
|
|
|
nsresult
|
|
|
|
WorkerPrivateParent<Derived>::DispatchControlRunnable(
|
2016-06-01 03:04:54 +03:00
|
|
|
already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable)
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
// May be called on any thread!
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable);
|
2015-07-10 06:21:46 +03:00
|
|
|
MOZ_ASSERT(runnable);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (self->mStatus == Dead) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transfer ownership to the control queue.
|
2014-03-15 23:00:15 +04:00
|
|
|
self->mControlQueue.Push(runnable.forget().take());
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
if (JSContext* cx = self->mJSContext) {
|
|
|
|
MOZ_ASSERT(self->mThread);
|
2016-07-23 20:52:20 +03:00
|
|
|
JS_RequestInterruptCallback(cx);
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
mCondVar.Notify();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2015-03-04 17:11:32 +03:00
|
|
|
template <class Derived>
|
|
|
|
nsresult
|
|
|
|
WorkerPrivateParent<Derived>::DispatchDebuggerRunnable(
|
2016-06-01 03:04:54 +03:00
|
|
|
already_AddRefed<WorkerRunnable> aDebuggerRunnable)
|
2015-03-04 17:11:32 +03:00
|
|
|
{
|
|
|
|
// May be called on any thread!
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
|
2015-03-04 17:11:32 +03:00
|
|
|
|
2015-07-10 06:21:46 +03:00
|
|
|
MOZ_ASSERT(runnable);
|
2015-03-04 17:11:32 +03:00
|
|
|
|
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (self->mStatus == Dead) {
|
|
|
|
NS_WARNING("A debugger runnable was posted to a worker that is already "
|
|
|
|
"shutting down!");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transfer ownership to the debugger queue.
|
|
|
|
self->mDebuggerQueue.Push(runnable.forget().take());
|
|
|
|
|
|
|
|
mCondVar.Notify();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
template <class Derived>
|
2013-10-23 17:16:49 +04:00
|
|
|
already_AddRefed<WorkerRunnable>
|
2016-06-01 03:04:54 +03:00
|
|
|
WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable)
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
// May be called on any thread!
|
|
|
|
|
2015-07-10 06:21:46 +03:00
|
|
|
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
|
|
|
MOZ_ASSERT(runnable);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerRunnable> workerRunnable =
|
2015-07-10 06:21:46 +03:00
|
|
|
WorkerRunnable::FromRunnable(runnable);
|
2013-10-23 17:16:49 +04:00
|
|
|
if (workerRunnable) {
|
|
|
|
return workerRunnable.forget();
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:21:46 +03:00
|
|
|
nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
|
2013-10-23 17:16:49 +04:00
|
|
|
if (!cancelable) {
|
|
|
|
MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
|
|
|
|
}
|
|
|
|
|
|
|
|
workerRunnable =
|
2016-04-11 21:40:06 +03:00
|
|
|
new ExternalRunnableWrapper(ParentAsWorkerPrivate(), runnable);
|
2013-10-23 17:16:49 +04:00
|
|
|
return workerRunnable.forget();
|
|
|
|
}
|
2013-11-05 18:16:24 +04:00
|
|
|
|
|
|
|
template <class Derived>
|
2013-10-23 17:16:49 +04:00
|
|
|
already_AddRefed<nsIEventTarget>
|
|
|
|
WorkerPrivateParent<Derived>::GetEventTarget()
|
2013-11-05 18:16:24 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
nsCOMPtr<nsIEventTarget> target;
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
if (!mEventTarget &&
|
|
|
|
ParentStatus() <= Running &&
|
|
|
|
self->mStatus <= Running) {
|
|
|
|
mEventTarget = new EventTarget(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
target = mEventTarget;
|
2013-11-05 18:16:24 +04:00
|
|
|
}
|
|
|
|
|
2016-09-01 08:01:16 +03:00
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
target,
|
|
|
|
"Requested event target for a worker that is already shutting down!");
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
return target.forget();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
bool
|
|
|
|
WorkerPrivateParent<Derived>::Start()
|
|
|
|
{
|
|
|
|
// May be called on any thread!
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
NS_ASSERTION(mParentStatus != Running, "How can this be?!");
|
|
|
|
|
|
|
|
if (mParentStatus == Pending) {
|
|
|
|
mParentStatus = Running;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-19 18:34:58 +04:00
|
|
|
// aCx is null when called from the finalizer
|
2011-07-17 23:09:13 +04:00
|
|
|
template <class Derived>
|
|
|
|
bool
|
2016-02-26 00:05:39 +03:00
|
|
|
WorkerPrivateParent<Derived>::NotifyPrivate(Status aStatus)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
|
|
|
bool pending;
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mParentStatus >= aStatus) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pending = mParentStatus == Pending;
|
|
|
|
mParentStatus = aStatus;
|
|
|
|
}
|
|
|
|
|
2015-10-01 02:11:03 +03:00
|
|
|
if (IsSharedWorker()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
RuntimeService* runtime = RuntimeService::GetService();
|
|
|
|
MOZ_ASSERT(runtime);
|
|
|
|
|
|
|
|
runtime->ForgetSharedWorker(ParentAsWorkerPrivate());
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
if (pending) {
|
|
|
|
WorkerPrivate* self = ParentAsWorkerPrivate();
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
// Fake a thread here just so that our assertions don't go off for no
|
|
|
|
// reason.
|
2011-07-17 23:09:13 +04:00
|
|
|
nsIThread* currentThread = NS_GetCurrentThread();
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(currentThread);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(!self->mPRThread);
|
|
|
|
self->mPRThread = PRThreadFromThread(currentThread);
|
|
|
|
MOZ_ASSERT(self->mPRThread);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
#endif
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
// Worker never got a chance to run, go ahead and delete it.
|
2014-03-05 03:09:23 +04:00
|
|
|
self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
|
|
|
|
"Shouldn't have anything queued!");
|
|
|
|
|
|
|
|
// Anything queued will be discarded.
|
|
|
|
mQueuedRunnables.Clear();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<NotifyRunnable> runnable =
|
2012-08-08 01:38:46 +04:00
|
|
|
new NotifyRunnable(ParentAsWorkerPrivate(), aStatus);
|
2016-02-26 00:05:39 +03:00
|
|
|
return runnable->Dispatch();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
bool
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::Freeze(nsPIDOMWindowInner* aWindow)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
// Shared workers are only frozen if all of their owning documents are
|
|
|
|
// frozen. It can happen that mSharedWorkers is empty but this thread has
|
2014-09-30 14:43:19 +04:00
|
|
|
// not been unregistered yet.
|
2015-09-16 06:27:56 +03:00
|
|
|
if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2016-10-11 11:33:31 +03:00
|
|
|
bool allFrozen = true;
|
2015-09-15 21:08:09 +03:00
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
|
|
|
|
if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
|
|
|
|
// Calling Freeze() may change the refcount, ensure that the worker
|
|
|
|
// outlives this call.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
|
2015-09-15 21:08:09 +03:00
|
|
|
|
2016-08-24 21:12:09 +03:00
|
|
|
kungFuDeathGrip->Freeze();
|
2015-09-16 06:27:56 +03:00
|
|
|
} else {
|
|
|
|
MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
|
|
|
|
!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
|
|
|
|
aWindow));
|
|
|
|
if (!mSharedWorkers[i]->IsFrozen()) {
|
|
|
|
allFrozen = false;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
}
|
2015-09-16 06:27:56 +03:00
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
if (!allFrozen || mParentFrozen) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
mParentFrozen = true;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mParentStatus >= Terminating) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:48:26 +03:00
|
|
|
DisableDebugger();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<FreezeRunnable> runnable =
|
2015-04-01 12:00:19 +03:00
|
|
|
new FreezeRunnable(ParentAsWorkerPrivate());
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
2013-06-05 18:04:23 +04:00
|
|
|
bool
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::Thaw(nsPIDOMWindowInner* aWindow)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2016-10-26 20:20:15 +03:00
|
|
|
MOZ_ASSERT(mParentFrozen);
|
2015-04-01 12:00:19 +03:00
|
|
|
|
|
|
|
// Shared workers are resumed if any of their owning documents are thawed.
|
2014-09-30 14:43:19 +04:00
|
|
|
// It can happen that mSharedWorkers is empty but this thread has not been
|
|
|
|
// unregistered yet.
|
2015-09-16 06:27:56 +03:00
|
|
|
if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
bool anyRunning = false;
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
|
|
|
|
if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
|
|
|
|
// Calling Thaw() may change the refcount, ensure that the worker
|
|
|
|
// outlives this call.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
|
2015-09-15 21:08:09 +03:00
|
|
|
|
2016-08-24 21:12:09 +03:00
|
|
|
kungFuDeathGrip->Thaw();
|
2015-09-16 06:27:56 +03:00
|
|
|
anyRunning = true;
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
|
|
|
|
!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
|
|
|
|
aWindow));
|
|
|
|
if (!mSharedWorkers[i]->IsFrozen()) {
|
|
|
|
anyRunning = true;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
}
|
2015-09-16 06:27:56 +03:00
|
|
|
}
|
2015-09-15 21:08:09 +03:00
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
if (!anyRunning || !mParentFrozen) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
MOZ_ASSERT(mParentFrozen);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
mParentFrozen = false;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mParentStatus >= Terminating) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return true;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-16 19:48:26 +03:00
|
|
|
EnableDebugger();
|
|
|
|
|
2013-06-06 21:28:47 +04:00
|
|
|
// Execute queued runnables before waking up the worker, otherwise the worker
|
2011-07-17 23:09:13 +04:00
|
|
|
// could post new messages before we run those that have been queued.
|
2016-10-04 13:14:07 +03:00
|
|
|
if (!IsParentWindowPaused() && !mQueuedRunnables.IsEmpty()) {
|
2013-10-31 03:40:16 +04:00
|
|
|
MOZ_ASSERT(IsDedicatedWorker());
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
nsTArray<nsCOMPtr<nsIRunnable>> runnables;
|
2011-07-17 23:09:13 +04:00
|
|
|
mQueuedRunnables.SwapElements(runnables);
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < runnables.Length(); index++) {
|
2013-10-23 17:16:49 +04:00
|
|
|
runnables[index]->Run();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ThawRunnable> runnable =
|
2015-04-01 12:00:19 +03:00
|
|
|
new ThawRunnable(ParentAsWorkerPrivate());
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2013-06-06 21:28:47 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-10-07 13:20:59 +03:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-10-04 13:14:07 +03:00
|
|
|
WorkerPrivateParent<Derived>::ParentWindowPaused()
|
2015-10-07 13:20:59 +03:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
2016-10-25 16:26:04 +03:00
|
|
|
MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 0);
|
|
|
|
mParentWindowPausedDepth += 1;
|
2015-10-07 13:20:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-10-04 13:14:07 +03:00
|
|
|
WorkerPrivateParent<Derived>::ParentWindowResumed()
|
2015-10-07 13:20:59 +03:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2016-10-25 16:26:04 +03:00
|
|
|
MOZ_ASSERT(mParentWindowPausedDepth > 0);
|
|
|
|
MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 1);
|
|
|
|
mParentWindowPausedDepth -= 1;
|
|
|
|
if (mParentWindowPausedDepth > 0) {
|
|
|
|
return;
|
|
|
|
}
|
2015-10-07 13:20:59 +03:00
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mParentStatus >= Terminating) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute queued runnables before waking up, otherwise the worker could post
|
|
|
|
// new messages before we run those that have been queued.
|
|
|
|
if (!IsFrozen() && !mQueuedRunnables.IsEmpty()) {
|
|
|
|
MOZ_ASSERT(IsDedicatedWorker());
|
|
|
|
|
|
|
|
nsTArray<nsCOMPtr<nsIRunnable>> runnables;
|
|
|
|
mQueuedRunnables.SwapElements(runnables);
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < runnables.Length(); index++) {
|
|
|
|
runnables[index]->Run();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
template <class Derived>
|
|
|
|
bool
|
2015-04-15 05:35:01 +03:00
|
|
|
WorkerPrivateParent<Derived>::Close()
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2017-05-03 17:42:43 +03:00
|
|
|
mMutex.AssertCurrentThreadOwns();
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-05-03 17:42:43 +03:00
|
|
|
if (mParentStatus < Closing) {
|
|
|
|
mParentStatus = Closing;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
bool
|
2016-02-26 00:05:39 +03:00
|
|
|
WorkerPrivateParent<Derived>::ModifyBusyCount(bool aIncrease)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
|
|
|
NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
|
|
|
|
|
|
|
|
if (aIncrease) {
|
2014-02-02 22:08:50 +04:00
|
|
|
mBusyCount++;
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
if (--mBusyCount == 0) {
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
bool shouldCancel;
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
shouldCancel = mParentStatus == Terminating;
|
|
|
|
}
|
|
|
|
|
2016-02-26 00:05:39 +03:00
|
|
|
if (shouldCancel && !Cancel()) {
|
2011-07-17 23:09:13 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-21 18:09:17 +03:00
|
|
|
template <class Derived>
|
2017-02-07 18:28:39 +03:00
|
|
|
bool
|
|
|
|
WorkerPrivateParent<Derived>::ProxyReleaseMainThreadObjects()
|
2015-02-21 18:09:17 +03:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
2017-02-07 18:28:39 +03:00
|
|
|
MOZ_ASSERT(!mMainThreadObjectsForgotten);
|
2015-02-21 18:09:17 +03:00
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
nsCOMPtr<nsILoadGroup> loadGroupToCancel;
|
2015-02-21 18:09:17 +03:00
|
|
|
// If we're not overriden, then do nothing here. Let the load group get
|
|
|
|
// handled in ForgetMainThreadObjects().
|
2017-02-07 18:28:39 +03:00
|
|
|
if (mLoadInfo.mInterfaceRequestor) {
|
|
|
|
mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
|
2015-02-21 18:09:17 +03:00
|
|
|
}
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
bool result = mLoadInfo.ProxyReleaseMainThreadObjects(ParentAsWorkerPrivate(),
|
|
|
|
loadGroupToCancel);
|
2012-03-31 08:42:20 +04:00
|
|
|
|
|
|
|
mMainThreadObjectsForgotten = true;
|
2017-02-07 18:28:39 +03:00
|
|
|
|
|
|
|
return result;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
2013-11-05 18:16:24 +04:00
|
|
|
void
|
2017-02-03 13:00:38 +03:00
|
|
|
WorkerPrivateParent<Derived>::PostMessageInternal(JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aMessage,
|
|
|
|
const Sequence<JSObject*>& aTransferable,
|
|
|
|
ErrorResult& aRv)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
2012-03-31 08:42:20 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
2012-12-14 20:16:11 +04:00
|
|
|
if (mParentStatus > Running) {
|
2013-11-05 18:16:24 +04:00
|
|
|
return;
|
2012-03-31 08:42:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
2017-02-03 13:00:38 +03:00
|
|
|
|
|
|
|
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
|
|
|
|
&transferable);
|
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return;
|
2013-11-05 18:16:24 +04:00
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<MessageEventRunnable> runnable =
|
2015-06-17 13:44:27 +03:00
|
|
|
new MessageEventRunnable(ParentAsWorkerPrivate(),
|
2015-09-16 06:27:56 +03:00
|
|
|
WorkerRunnable::WorkerThreadModifyBusyCount);
|
2011-08-16 07:40:38 +04:00
|
|
|
|
2015-10-22 00:10:05 +03:00
|
|
|
UniquePtr<AbstractTimelineMarker> start;
|
|
|
|
UniquePtr<AbstractTimelineMarker> end;
|
|
|
|
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
|
|
|
bool isTimelineRecording = timelines && !timelines->IsEmpty();
|
|
|
|
|
|
|
|
if (isTimelineRecording) {
|
|
|
|
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
|
|
|
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
|
|
|
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
|
|
|
MarkerTracingType::START);
|
|
|
|
}
|
|
|
|
|
2016-10-24 16:14:45 +03:00
|
|
|
runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
|
2015-10-22 00:10:05 +03:00
|
|
|
|
|
|
|
if (isTimelineRecording) {
|
|
|
|
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
|
|
|
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
|
|
|
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
|
|
|
MarkerTracingType::END);
|
|
|
|
timelines->AddMarkerForAllObservedDocShells(start);
|
|
|
|
timelines->AddMarkerForAllObservedDocShells(end);
|
|
|
|
}
|
|
|
|
|
2015-08-27 19:19:13 +03:00
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
2013-11-05 18:16:24 +04:00
|
|
|
return;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-11-05 18:16:24 +04:00
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2015-10-01 02:11:03 +03:00
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::PostMessage(
|
|
|
|
JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
2017-02-03 13:00:38 +03:00
|
|
|
const Sequence<JSObject*>& aTransferable,
|
2015-10-01 02:11:03 +03:00
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2016-11-21 05:14:53 +03:00
|
|
|
PostMessageInternal(aCx, aMessage, aTransferable, aRv);
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-07-07 09:15:15 +03:00
|
|
|
WorkerPrivateParent<Derived>::UpdateContextOptions(
|
|
|
|
const JS::ContextOptions& aContextOptions)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
2016-07-07 09:15:15 +03:00
|
|
|
mJSSettings.contextOptions = aContextOptions;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2016-07-07 09:15:15 +03:00
|
|
|
RefPtr<UpdateContextOptionsRunnable> runnable =
|
|
|
|
new UpdateContextOptionsRunnable(ParentAsWorkerPrivate(), aContextOptions);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
NS_WARNING("Failed to update worker context options!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-24 23:27:15 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::UpdatePreference(WorkerPreference aPref, bool aValue)
|
2013-11-24 23:27:15 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<UpdatePreferenceRunnable> runnable =
|
2013-11-24 23:27:15 +04:00
|
|
|
new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-11-24 23:27:15 +04:00
|
|
|
NS_WARNING("Failed to update worker preferences!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-05 18:26:34 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::UpdateLanguages(const nsTArray<nsString>& aLanguages)
|
2014-09-05 18:26:34 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<UpdateLanguagesRunnable> runnable =
|
2014-09-05 18:26:34 +04:00
|
|
|
new UpdateLanguagesRunnable(ParentAsWorkerPrivate(), aLanguages);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2014-09-05 18:26:34 +04:00
|
|
|
NS_WARNING("Failed to update worker languages!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,
|
2013-06-05 18:04:23 +04:00
|
|
|
uint32_t aValue)
|
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
found = mJSSettings.ApplyGCSetting(aKey, aValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
|
2013-06-05 18:04:23 +04:00
|
|
|
new UpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(), aKey,
|
|
|
|
aValue);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
NS_WARNING("Failed to update memory parameter!");
|
2013-05-17 02:49:43 +04:00
|
|
|
}
|
2012-01-04 23:11:32 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
#ifdef JS_GC_ZEAL
|
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
2013-05-17 02:49:43 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
mJSSettings.gcZeal = aGCZeal;
|
|
|
|
mJSSettings.gcZealFrequency = aFrequency;
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<UpdateGCZealRunnable> runnable =
|
2013-05-17 02:49:43 +04:00
|
|
|
new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_WARNING("Failed to update worker gczeal!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-01-18 00:05:25 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::GarbageCollect(bool aShrinking)
|
2012-01-18 00:05:25 +04:00
|
|
|
{
|
2013-06-05 18:04:23 +04:00
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<GarbageCollectRunnable> runnable =
|
2013-12-03 08:07:02 +04:00
|
|
|
new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking,
|
|
|
|
/* collectChildren = */ true);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-12-03 08:07:02 +04:00
|
|
|
NS_WARNING("Failed to GC worker!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::CycleCollect(bool aDummy)
|
2013-12-03 08:07:02 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<CycleCollectRunnable> runnable =
|
2013-12-03 08:07:02 +04:00
|
|
|
new CycleCollectRunnable(ParentAsWorkerPrivate(),
|
|
|
|
/* collectChildren = */ true);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-12-03 08:07:02 +04:00
|
|
|
NS_WARNING("Failed to CC worker!");
|
2012-01-18 00:05:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-20 03:08:50 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(bool aIsOffline)
|
2013-11-20 03:08:50 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<OfflineStatusChangeRunnable> runnable =
|
2013-11-20 03:08:50 +04:00
|
|
|
new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-11-20 03:08:50 +04:00
|
|
|
NS_WARNING("Failed to dispatch offline status change event!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline)
|
2013-11-20 03:08:50 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2014-08-27 06:42:13 +04:00
|
|
|
// The worker is already in this state. No need to dispatch an event.
|
|
|
|
if (mOnLine == !aIsOffline) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-20 03:08:50 +04:00
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) {
|
2016-02-26 23:23:12 +03:00
|
|
|
mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);
|
2013-11-20 03:08:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
mOnLine = !aIsOffline;
|
|
|
|
WorkerGlobalScope* globalScope = GlobalScope();
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
|
2013-11-20 03:08:50 +04:00
|
|
|
if (nav) {
|
|
|
|
nav->SetOnLine(mOnLine);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsString eventType;
|
|
|
|
if (aIsOffline) {
|
|
|
|
eventType.AssignLiteral("offline");
|
|
|
|
} else {
|
|
|
|
eventType.AssignLiteral("online");
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
|
2013-11-20 03:08:50 +04:00
|
|
|
|
2015-11-13 03:09:42 +03:00
|
|
|
event->InitEvent(eventType, false, false);
|
2013-11-20 03:08:50 +04:00
|
|
|
event->SetTrusted(true);
|
|
|
|
|
|
|
|
globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
|
|
|
}
|
|
|
|
|
2016-03-24 00:55:07 +03:00
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::MemoryPressure(bool aDummy)
|
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
|
|
|
RefPtr<MemoryPressureRunnable> runnable =
|
|
|
|
new MemoryPressureRunnable(ParentAsWorkerPrivate());
|
2016-09-02 10:12:24 +03:00
|
|
|
Unused << NS_WARN_IF(!runnable->Dispatch());
|
2016-03-24 00:55:07 +03:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
template <class Derived>
|
|
|
|
bool
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivateParent<Derived>::RegisterSharedWorker(SharedWorker* aSharedWorker,
|
2015-09-16 06:27:56 +03:00
|
|
|
MessagePort* aPort)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_ASSERT(aSharedWorker);
|
2015-10-01 02:11:03 +03:00
|
|
|
MOZ_ASSERT(IsSharedWorker());
|
2015-09-16 06:27:56 +03:00
|
|
|
MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2014-05-12 20:11:15 +04:00
|
|
|
if (IsSharedWorker()) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<MessagePortRunnable> runnable =
|
2015-09-16 06:27:56 +03:00
|
|
|
new MessagePortRunnable(ParentAsWorkerPrivate(), aPort);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2014-05-12 20:11:15 +04:00
|
|
|
return false;
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
mSharedWorkers.AppendElement(aSharedWorker);
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
// If there were other SharedWorker objects attached to this worker then they
|
2015-04-01 12:00:19 +03:00
|
|
|
// may all have been frozen and this worker would need to be thawed.
|
2016-10-26 20:20:15 +03:00
|
|
|
if (mSharedWorkers.Length() > 1 && IsFrozen() && !Thaw(nullptr)) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
|
|
|
|
JSContext* aCx,
|
2017-02-15 17:53:07 +03:00
|
|
|
const WorkerErrorReport* aReport,
|
2017-01-28 17:39:24 +03:00
|
|
|
bool aIsErrorEvent)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
if (aIsErrorEvent && JSREPORT_IS_WARNING(aReport->mFlags)) {
|
2016-03-02 00:53:26 +03:00
|
|
|
// Don't fire any events anywhere. Just log to console.
|
|
|
|
// XXXbz should we log to all the consoles of all the relevant windows?
|
2017-02-15 17:53:07 +03:00
|
|
|
MOZ_ASSERT(aReport);
|
|
|
|
LogErrorToConsole(*aReport, 0);
|
2016-03-02 00:53:26 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
|
2013-06-05 18:04:23 +04:00
|
|
|
GetAllSharedWorkers(sharedWorkers);
|
|
|
|
|
|
|
|
if (sharedWorkers.IsEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<WindowAction, 10> windowActions;
|
2013-06-05 18:04:23 +04:00
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
// First fire the error event at all SharedWorker objects. This may include
|
|
|
|
// multiple objects in a single window as well as objects in different
|
|
|
|
// windows.
|
2014-05-09 05:03:35 +04:00
|
|
|
for (size_t index = 0; index < sharedWorkers.Length(); index++) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
// May be null.
|
2016-01-30 20:05:36 +03:00
|
|
|
nsPIDOMWindowInner* window = sharedWorker->GetOwner();
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2017-01-28 17:39:24 +03:00
|
|
|
RefPtr<Event> event;
|
|
|
|
|
|
|
|
if (aIsErrorEvent) {
|
|
|
|
RootedDictionary<ErrorEventInit> errorInit(aCx);
|
|
|
|
errorInit.mBubbles = false;
|
|
|
|
errorInit.mCancelable = true;
|
2017-02-15 17:53:07 +03:00
|
|
|
errorInit.mMessage = aReport->mMessage;
|
|
|
|
errorInit.mFilename = aReport->mFilename;
|
|
|
|
errorInit.mLineno = aReport->mLineNumber;
|
|
|
|
errorInit.mColno = aReport->mColumnNumber;
|
2017-01-28 17:39:24 +03:00
|
|
|
|
|
|
|
event = ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
|
|
|
|
errorInit);
|
|
|
|
} else {
|
|
|
|
event = Event::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
|
|
|
|
EventInit());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!event) {
|
2014-07-02 14:26:49 +04:00
|
|
|
ThrowAndReport(window, NS_ERROR_UNEXPECTED);
|
2013-06-05 18:04:23 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-01-28 17:39:24 +03:00
|
|
|
event->SetTrusted(true);
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
bool defaultActionEnabled;
|
2017-01-28 17:39:24 +03:00
|
|
|
nsresult rv = sharedWorker->DispatchEvent(event, &defaultActionEnabled);
|
2013-06-05 18:04:23 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
2014-07-02 14:26:49 +04:00
|
|
|
ThrowAndReport(window, rv);
|
2013-06-05 18:04:23 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-01-28 17:39:24 +03:00
|
|
|
if (!aIsErrorEvent) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
if (defaultActionEnabled) {
|
|
|
|
// Add the owning window to our list so that we will fire an error event
|
|
|
|
// at it later.
|
|
|
|
if (!windowActions.Contains(window)) {
|
2014-07-02 14:26:49 +04:00
|
|
|
windowActions.AppendElement(WindowAction(window));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
|
|
|
|
if (actionsIndex != windowActions.NoIndex) {
|
|
|
|
// Any listener that calls preventDefault() will prevent the window from
|
|
|
|
// receiving the error event.
|
|
|
|
windowActions[actionsIndex].mDefaultAction = false;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are no windows to consider further then we're done.
|
|
|
|
if (windowActions.IsEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool shouldLogErrorToConsole = true;
|
|
|
|
|
|
|
|
// Now fire error events at all the windows remaining.
|
|
|
|
for (uint32_t index = 0; index < windowActions.Length(); index++) {
|
|
|
|
WindowAction& windowAction = windowActions[index];
|
|
|
|
|
|
|
|
// If there is no window or the script already called preventDefault then
|
|
|
|
// skip this window.
|
|
|
|
if (!windowAction.mWindow || !windowAction.mDefaultAction) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIScriptGlobalObject> sgo =
|
|
|
|
do_QueryInterface(windowAction.mWindow);
|
|
|
|
MOZ_ASSERT(sgo);
|
|
|
|
|
2013-12-21 04:22:13 +04:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2014-01-03 05:04:15 +04:00
|
|
|
RootedDictionary<ErrorEventInit> init(aCx);
|
2017-02-15 17:53:07 +03:00
|
|
|
init.mLineno = aReport->mLineNumber;
|
|
|
|
init.mFilename = aReport->mFilename;
|
|
|
|
init.mMessage = aReport->mMessage;
|
2014-02-23 09:01:12 +04:00
|
|
|
init.mCancelable = true;
|
|
|
|
init.mBubbles = true;
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2014-02-23 09:01:12 +04:00
|
|
|
rv = sgo->HandleScriptError(init, &status);
|
2013-06-05 18:04:23 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
2014-07-02 14:26:49 +04:00
|
|
|
ThrowAndReport(windowAction.mWindow, rv);
|
2013-06-05 18:04:23 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status == nsEventStatus_eConsumeNoDefault) {
|
|
|
|
shouldLogErrorToConsole = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally log a warning in the console if no window tried to prevent it.
|
|
|
|
if (shouldLogErrorToConsole) {
|
2017-02-15 17:53:07 +03:00
|
|
|
MOZ_ASSERT(aReport);
|
|
|
|
LogErrorToConsole(*aReport, 0);
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::GetAllSharedWorkers(
|
2015-10-18 08:24:48 +03:00
|
|
|
nsTArray<RefPtr<SharedWorker>>& aSharedWorkers)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
2014-05-12 20:11:15 +04:00
|
|
|
MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
if (!aSharedWorkers.IsEmpty()) {
|
|
|
|
aSharedWorkers.Clear();
|
|
|
|
}
|
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
|
|
|
|
aSharedWorkers.AppendElement(mSharedWorkers[i]);
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
|
2016-01-30 20:05:36 +03:00
|
|
|
nsPIDOMWindowInner* aWindow)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
2014-05-12 20:11:15 +04:00
|
|
|
MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
|
2013-06-05 18:04:23 +04:00
|
|
|
MOZ_ASSERT(aWindow);
|
|
|
|
|
2015-09-23 08:54:29 +03:00
|
|
|
bool someRemoved = false;
|
2015-09-15 21:08:09 +03:00
|
|
|
|
2015-09-23 08:54:29 +03:00
|
|
|
for (uint32_t i = 0; i < mSharedWorkers.Length();) {
|
2015-09-16 06:27:56 +03:00
|
|
|
if (mSharedWorkers[i]->GetOwner() == aWindow) {
|
2015-09-23 08:54:29 +03:00
|
|
|
mSharedWorkers[i]->Close();
|
|
|
|
mSharedWorkers.RemoveElementAt(i);
|
|
|
|
someRemoved = true;
|
2015-09-16 06:27:56 +03:00
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
|
|
|
|
aWindow));
|
2015-09-23 08:54:29 +03:00
|
|
|
++i;
|
2015-09-15 21:08:09 +03:00
|
|
|
}
|
2015-09-16 06:27:56 +03:00
|
|
|
}
|
2015-09-15 21:08:09 +03:00
|
|
|
|
2015-09-23 08:54:29 +03:00
|
|
|
if (!someRemoved) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are still SharedWorker objects attached to this worker then they
|
|
|
|
// may all be frozen and this worker would need to be frozen. Otherwise,
|
|
|
|
// if that was the last SharedWorker then it's time to cancel this worker.
|
|
|
|
|
|
|
|
if (!mSharedWorkers.IsEmpty()) {
|
2016-02-26 23:23:12 +03:00
|
|
|
Freeze(nullptr);
|
2016-02-26 00:05:39 +03:00
|
|
|
} else {
|
|
|
|
Cancel();
|
2015-09-23 08:54:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::CloseAllSharedWorkers()
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
|
|
|
|
mSharedWorkers[i]->Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
mSharedWorkers.Clear();
|
|
|
|
|
2016-02-26 00:05:39 +03:00
|
|
|
Cancel();
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::WorkerScriptLoaded()
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2014-05-12 20:11:15 +04:00
|
|
|
if (IsSharedWorker() || IsServiceWorker()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
// No longer need to hold references to the window or document we came from.
|
|
|
|
mLoadInfo.mWindow = nullptr;
|
|
|
|
mLoadInfo.mScriptContext = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
template <class Derived>
|
2011-11-04 20:32:17 +04:00
|
|
|
void
|
2011-07-17 23:09:13 +04:00
|
|
|
WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
if (!mLoadInfo.mBaseURI) {
|
|
|
|
NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
|
|
|
|
mLoadInfo.mResolvedScriptURI = aBaseURI;
|
|
|
|
}
|
|
|
|
|
|
|
|
mLoadInfo.mBaseURI = aBaseURI;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2011-11-04 20:32:17 +04:00
|
|
|
if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
|
|
|
|
mLocationInfo.mHref.Truncate();
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-04-15 23:32:53 +03:00
|
|
|
mLocationInfo.mHostname.Truncate();
|
|
|
|
nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-04-15 23:32:53 +03:00
|
|
|
nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
|
|
|
|
if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) {
|
2011-11-04 20:32:17 +04:00
|
|
|
mLocationInfo.mPathname.Truncate();
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
nsCString temp;
|
|
|
|
|
2011-11-04 20:32:17 +04:00
|
|
|
if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
|
2014-05-26 22:55:08 +04:00
|
|
|
mLocationInfo.mSearch.Assign('?');
|
2011-07-17 23:09:13 +04:00
|
|
|
mLocationInfo.mSearch.Append(temp);
|
|
|
|
}
|
|
|
|
|
2011-11-04 20:32:17 +04:00
|
|
|
if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
|
2013-10-23 17:16:49 +04:00
|
|
|
if (mLocationInfo.mHash.IsEmpty()) {
|
2014-05-26 22:55:08 +04:00
|
|
|
mLocationInfo.mHash.Assign('#');
|
2013-10-23 17:16:49 +04:00
|
|
|
mLocationInfo.mHash.Append(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
|
2014-05-22 07:48:51 +04:00
|
|
|
mLocationInfo.mProtocol.Append(':');
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
mLocationInfo.mProtocol.Truncate();
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t port;
|
|
|
|
if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
|
|
|
|
mLocationInfo.mPort.AppendInt(port);
|
|
|
|
|
|
|
|
nsAutoCString host(mLocationInfo.mHostname);
|
2014-05-22 07:48:51 +04:00
|
|
|
host.Append(':');
|
2013-10-23 17:16:49 +04:00
|
|
|
host.Append(mLocationInfo.mPort);
|
|
|
|
|
|
|
|
mLocationInfo.mHost.Assign(host);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
|
|
|
|
}
|
2014-01-31 22:22:52 +04:00
|
|
|
|
2014-08-31 14:40:11 +04:00
|
|
|
nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
2017-02-10 21:34:38 +03:00
|
|
|
nsresult
|
2017-02-07 18:28:39 +03:00
|
|
|
WorkerPrivateParent<Derived>::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
|
|
|
|
nsILoadGroup* aLoadGroup)
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
2017-02-10 21:34:38 +03:00
|
|
|
return mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
2017-02-07 18:28:39 +03:00
|
|
|
template <class Derived>
|
|
|
|
nsresult
|
|
|
|
WorkerPrivateParent<Derived>::SetPrincipalFromChannel(nsIChannel* aChannel)
|
|
|
|
{
|
|
|
|
return mLoadInfo.SetPrincipalFromChannel(aChannel);
|
|
|
|
}
|
|
|
|
|
2017-06-06 00:43:34 +03:00
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-02-07 18:28:39 +03:00
|
|
|
template <class Derived>
|
|
|
|
bool
|
|
|
|
WorkerPrivateParent<Derived>::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
|
|
|
|
{
|
|
|
|
return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
|
|
|
|
}
|
2017-02-23 18:54:41 +03:00
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
bool
|
|
|
|
WorkerPrivateParent<Derived>::PrincipalURIMatchesScriptURL()
|
|
|
|
{
|
|
|
|
return mLoadInfo.PrincipalURIMatchesScriptURL();
|
|
|
|
}
|
2017-02-07 18:28:39 +03:00
|
|
|
#endif
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2015-02-21 18:09:17 +03:00
|
|
|
WorkerPrivateParent<Derived>::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
// The load group should have been overriden at init time.
|
|
|
|
mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup);
|
|
|
|
}
|
|
|
|
|
2016-05-23 09:56:46 +03:00
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::FlushReportsToSharedWorkers(
|
|
|
|
nsIConsoleReportCollector* aReporter)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
|
|
|
|
AutoTArray<WindowAction, 10> windowActions;
|
|
|
|
GetAllSharedWorkers(sharedWorkers);
|
|
|
|
|
|
|
|
// First find out all the shared workers' window.
|
|
|
|
for (size_t index = 0; index < sharedWorkers.Length(); index++) {
|
|
|
|
RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
|
|
|
|
|
|
|
|
// May be null.
|
|
|
|
nsPIDOMWindowInner* window = sharedWorker->GetOwner();
|
|
|
|
|
|
|
|
// Add the owning window to our list so that we will flush the reports later.
|
|
|
|
if (window && !windowActions.Contains(window)) {
|
|
|
|
windowActions.AppendElement(WindowAction(window));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool reportErrorToBrowserConsole = true;
|
|
|
|
|
|
|
|
// Flush the reports.
|
|
|
|
for (uint32_t index = 0; index < windowActions.Length(); index++) {
|
|
|
|
WindowAction& windowAction = windowActions[index];
|
|
|
|
|
2017-02-06 04:19:34 +03:00
|
|
|
aReporter->FlushReportsToConsole(
|
|
|
|
windowAction.mWindow->WindowID(),
|
|
|
|
nsIConsoleReportCollector::ReportAction::Save);
|
2016-05-23 09:56:46 +03:00
|
|
|
reportErrorToBrowserConsole = false;
|
|
|
|
}
|
|
|
|
|
2017-04-04 01:26:11 +03:00
|
|
|
// Finally report to browser console if there is no any window or shared
|
2016-05-23 09:56:46 +03:00
|
|
|
// worker.
|
|
|
|
if (reportErrorToBrowserConsole) {
|
2017-02-06 04:19:34 +03:00
|
|
|
aReporter->FlushReportsToConsole(0);
|
2016-05-23 09:56:46 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aReporter->ClearConsoleReports();
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
template <class Derived>
|
2014-04-01 10:13:50 +04:00
|
|
|
NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
template <class Derived>
|
2014-04-01 10:13:50 +04:00
|
|
|
NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)
|
2014-04-01 10:13:50 +04:00
|
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
|
2014-04-01 10:13:50 +04:00
|
|
|
DOMEventTargetHelper)
|
2014-02-02 22:08:50 +04:00
|
|
|
tmp->AssertIsOnParentThread();
|
|
|
|
|
|
|
|
// The WorkerPrivate::mSelfRef has a reference to itself, which is really
|
|
|
|
// held by the worker thread. We traverse this reference if and only if our
|
|
|
|
// busy count is zero and we have not released the main thread reference.
|
|
|
|
// We do not unlink it. This allows the CC to break cycles involving the
|
|
|
|
// WorkerPrivate and begin shutting it down (which does happen in unlink) but
|
|
|
|
// ensures that the WorkerPrivate won't be deleted before we're done shutting
|
|
|
|
// down the thread.
|
|
|
|
|
|
|
|
if (!tmp->mBusyCount && !tmp->mMainThreadObjectsForgotten) {
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef)
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// The various strong references in LoadInfo are managed manually and cannot
|
|
|
|
// be cycle collected.
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
template <class Derived>
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
|
2014-04-01 10:13:50 +04:00
|
|
|
DOMEventTargetHelper)
|
2016-03-12 00:43:30 +03:00
|
|
|
tmp->Terminate();
|
2013-10-23 17:16:49 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
template <class Derived>
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
|
2014-04-01 10:13:50 +04:00
|
|
|
DOMEventTargetHelper)
|
2013-10-23 17:16:49 +04:00
|
|
|
tmp->AssertIsOnParentThread();
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
#ifdef DEBUG
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
|
|
|
WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
|
|
|
|
{
|
|
|
|
if (GetParent()) {
|
|
|
|
GetParent()->AssertIsOnWorkerThread();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
else {
|
2013-10-23 17:16:49 +04:00
|
|
|
AssertIsOnMainThread();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-16 07:40:38 +04:00
|
|
|
template <class Derived>
|
|
|
|
void
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnParentThread();
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Only care about top level workers from windows.
|
|
|
|
if (mParent || !mLoadInfo.mWindow) {
|
|
|
|
return;
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
AssertIsOnMainThread();
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-01-30 20:05:36 +03:00
|
|
|
nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
|
2013-10-23 17:16:49 +04:00
|
|
|
NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
|
|
|
|
"Inner window no longer correct!");
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
#endif
|
2014-10-27 20:00:05 +03:00
|
|
|
|
2017-06-06 00:43:34 +03:00
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
2017-02-14 18:06:39 +03:00
|
|
|
template <class Derived>
|
|
|
|
bool
|
|
|
|
WorkerPrivateParent<Derived>::PrincipalIsValid() const
|
|
|
|
{
|
|
|
|
return mLoadInfo.PrincipalIsValid();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
class PostDebuggerMessageRunnable final : public Runnable
|
2016-01-07 15:35:31 +03:00
|
|
|
{
|
|
|
|
WorkerDebugger *mDebugger;
|
|
|
|
nsString mMessage;
|
|
|
|
|
|
|
|
public:
|
|
|
|
PostDebuggerMessageRunnable(WorkerDebugger* aDebugger,
|
|
|
|
const nsAString& aMessage)
|
|
|
|
: mDebugger(aDebugger),
|
|
|
|
mMessage(aMessage)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~PostDebuggerMessageRunnable()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
Run() override
|
|
|
|
{
|
|
|
|
mDebugger->PostMessageToDebuggerOnMainThread(mMessage);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-04-26 03:23:21 +03:00
|
|
|
class ReportDebuggerErrorRunnable final : public Runnable
|
2015-03-26 22:09:45 +03:00
|
|
|
{
|
2016-01-07 15:35:31 +03:00
|
|
|
WorkerDebugger *mDebugger;
|
2015-03-26 22:09:45 +03:00
|
|
|
nsString mFilename;
|
|
|
|
uint32_t mLineno;
|
|
|
|
nsString mMessage;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger,
|
2016-01-07 15:35:31 +03:00
|
|
|
const nsAString& aFilename, uint32_t aLineno,
|
|
|
|
const nsAString& aMessage)
|
2015-03-26 22:09:45 +03:00
|
|
|
: mDebugger(aDebugger),
|
|
|
|
mFilename(aFilename),
|
|
|
|
mLineno(aLineno),
|
|
|
|
mMessage(aMessage)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~ReportDebuggerErrorRunnable()
|
|
|
|
{ }
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
Run() override
|
|
|
|
{
|
|
|
|
mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-10-27 20:00:05 +03:00
|
|
|
WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
|
2016-01-07 15:35:31 +03:00
|
|
|
: mWorkerPrivate(aWorkerPrivate),
|
2015-10-16 19:48:26 +03:00
|
|
|
mIsInitialized(false)
|
2014-10-27 20:00:05 +03:00
|
|
|
{
|
2016-01-07 15:35:31 +03:00
|
|
|
AssertIsOnMainThread();
|
2014-10-27 20:00:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
WorkerDebugger::~WorkerDebugger()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mWorkerPrivate);
|
2014-10-29 23:11:33 +03:00
|
|
|
|
|
|
|
if (!NS_IsMainThread()) {
|
|
|
|
for (size_t index = 0; index < mListeners.Length(); ++index) {
|
2016-02-10 10:23:00 +03:00
|
|
|
NS_ReleaseOnMainThread(mListeners[index].forget());
|
2014-10-29 23:11:33 +03:00
|
|
|
}
|
|
|
|
}
|
2014-10-27 20:00:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetIsClosed(bool* aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
*aResult = !mWorkerPrivate;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-29 23:11:33 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetIsChrome(bool* aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = mWorkerPrivate->IsChromeWorker();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-06-08 10:08:20 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetIsInitialized(bool* aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = mIsInitialized;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-29 23:11:33 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetParent(nsIWorkerDebugger** aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkerPrivate* parent = mWorkerPrivate->GetParent();
|
|
|
|
if (!parent) {
|
|
|
|
*aResult = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker());
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWorkerDebugger> debugger = parent->Debugger();
|
|
|
|
debugger.forget(aResult);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetType(uint32_t* aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = mWorkerPrivate->Type();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-27 20:00:05 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetUrl(nsAString& aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
aResult = mWorkerPrivate->ScriptURL();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-29 23:11:33 +03:00
|
|
|
NS_IMETHODIMP
|
2016-01-30 20:05:36 +03:00
|
|
|
WorkerDebugger::GetWindow(mozIDOMWindow** aResult)
|
2014-10-29 23:11:33 +03:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mWorkerPrivate->GetParent() || !mWorkerPrivate->IsDedicatedWorker()) {
|
|
|
|
*aResult = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-01-30 20:05:36 +03:00
|
|
|
nsCOMPtr<nsPIDOMWindowInner> window = mWorkerPrivate->GetWindow();
|
2014-10-29 23:11:33 +03:00
|
|
|
window.forget(aResult);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-12-15 14:10:53 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetPrincipal(nsIPrincipal** aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_ASSERT(aResult);
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrincipal> prin = mWorkerPrivate->GetPrincipal();
|
|
|
|
prin.forget(aResult);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::GetServiceWorkerID(uint32_t* aResult)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_ASSERT(aResult);
|
|
|
|
|
|
|
|
if (!mWorkerPrivate || !mWorkerPrivate->IsServiceWorker()) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = mWorkerPrivate->ServiceWorkerID();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-03-17 13:15:19 +03:00
|
|
|
NS_IMETHODIMP
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerDebugger::Initialize(const nsAString& aURL)
|
2015-03-17 13:15:19 +03:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2015-06-02 17:52:17 +03:00
|
|
|
if (!mWorkerPrivate) {
|
2015-03-17 13:15:19 +03:00
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2015-06-02 17:52:17 +03:00
|
|
|
if (!mIsInitialized) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<CompileDebuggerScriptRunnable> runnable =
|
2015-06-02 17:52:17 +03:00
|
|
|
new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2015-06-02 17:52:17 +03:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2015-03-17 13:15:19 +03:00
|
|
|
|
2015-06-02 17:52:17 +03:00
|
|
|
mIsInitialized = true;
|
|
|
|
}
|
2015-03-17 13:15:19 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2015-03-20 14:15:59 +03:00
|
|
|
NS_IMETHODIMP
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerDebugger::PostMessageMoz(const nsAString& aMessage)
|
2015-03-20 14:15:59 +03:00
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mWorkerPrivate || !mIsInitialized) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<DebuggerMessageEventRunnable> runnable =
|
2015-03-20 14:15:59 +03:00
|
|
|
new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2015-03-20 14:15:59 +03:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-29 23:11:33 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (mListeners.Contains(aListener)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
mListeners.AppendElement(aListener);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
if (!mListeners.Contains(aListener)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
mListeners.RemoveElement(aListener);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-27 20:00:05 +03:00
|
|
|
void
|
2016-01-07 15:35:31 +03:00
|
|
|
WorkerDebugger::Close()
|
2015-12-22 16:11:27 +03:00
|
|
|
{
|
2014-10-27 20:00:05 +03:00
|
|
|
MOZ_ASSERT(mWorkerPrivate);
|
|
|
|
mWorkerPrivate = nullptr;
|
|
|
|
|
2016-01-07 15:35:31 +03:00
|
|
|
nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
|
|
|
|
for (size_t index = 0; index < listeners.Length(); ++index) {
|
2014-10-29 23:11:33 +03:00
|
|
|
listeners[index]->OnClose();
|
|
|
|
}
|
2014-10-27 20:00:05 +03:00
|
|
|
}
|
|
|
|
|
2015-03-20 14:15:59 +03:00
|
|
|
void
|
|
|
|
WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
|
|
|
|
{
|
|
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
2016-01-07 15:35:31 +03:00
|
|
|
RefPtr<PostDebuggerMessageRunnable> runnable =
|
|
|
|
new PostDebuggerMessageRunnable(this, aMessage);
|
2016-09-14 06:14:02 +03:00
|
|
|
if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
|
2016-01-07 15:35:31 +03:00
|
|
|
NS_WARNING("Failed to post message to debugger on main thread!");
|
|
|
|
}
|
2015-03-20 14:15:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2016-01-07 15:35:31 +03:00
|
|
|
nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
|
2015-03-20 14:15:59 +03:00
|
|
|
for (size_t index = 0; index < listeners.Length(); ++index) {
|
|
|
|
listeners[index]->OnMessage(aMessage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-26 22:09:45 +03:00
|
|
|
void
|
|
|
|
WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename,
|
|
|
|
uint32_t aLineno,
|
|
|
|
const nsAString& aMessage)
|
|
|
|
{
|
|
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
2016-01-07 15:35:31 +03:00
|
|
|
RefPtr<ReportDebuggerErrorRunnable> runnable =
|
2015-03-26 22:09:45 +03:00
|
|
|
new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
|
2016-09-14 06:14:02 +03:00
|
|
|
if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
|
2015-03-26 22:09:45 +03:00
|
|
|
NS_WARNING("Failed to report error to debugger on main thread!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
|
|
|
|
uint32_t aLineno,
|
|
|
|
const nsAString& aMessage)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2016-01-07 15:35:31 +03:00
|
|
|
nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
|
2015-03-26 22:09:45 +03:00
|
|
|
for (size_t index = 0; index < listeners.Length(); ++index) {
|
|
|
|
listeners[index]->OnError(aFilename, aLineno, aMessage);
|
|
|
|
}
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
WorkerErrorReport report;
|
|
|
|
report.mMessage = aMessage;
|
|
|
|
report.mFilename = aFilename;
|
|
|
|
LogErrorToConsole(report, 0);
|
2015-03-26 22:09:45 +03:00
|
|
|
}
|
|
|
|
|
2016-03-12 00:43:30 +03:00
|
|
|
WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
|
2013-06-05 18:04:23 +04:00
|
|
|
const nsAString& aScriptURL,
|
2013-10-31 03:40:16 +04:00
|
|
|
bool aIsChromeWorker, WorkerType aWorkerType,
|
2017-05-17 17:49:34 +03:00
|
|
|
const nsAString& aWorkerName,
|
|
|
|
const nsACString& aServiceWorkerScope,
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo& aLoadInfo)
|
2016-03-12 00:43:30 +03:00
|
|
|
: WorkerPrivateParent<WorkerPrivate>(aParent, aScriptURL,
|
2014-11-17 22:55:37 +03:00
|
|
|
aIsChromeWorker, aWorkerType,
|
2017-05-17 17:49:34 +03:00
|
|
|
aWorkerName, aServiceWorkerScope,
|
|
|
|
aLoadInfo)
|
2016-01-07 15:35:31 +03:00
|
|
|
, mDebuggerRegistered(false)
|
|
|
|
, mDebugger(nullptr)
|
2014-11-17 22:55:37 +03:00
|
|
|
, mJSContext(nullptr)
|
2013-10-23 17:16:49 +04:00
|
|
|
, mPRThread(nullptr)
|
2017-05-10 19:27:10 +03:00
|
|
|
, mNumHoldersPreventingShutdownStart(0)
|
2015-03-27 09:17:16 +03:00
|
|
|
, mDebuggerEventLoopLevel(0)
|
2016-09-14 06:14:02 +03:00
|
|
|
, mMainThreadEventTarget(do_GetMainThread())
|
2017-01-26 19:01:32 +03:00
|
|
|
, mWorkerControlEventTarget(new WorkerControlEventTarget(this))
|
2014-11-17 22:55:37 +03:00
|
|
|
, mErrorHandlerRecursionCount(0)
|
|
|
|
, mNextTimeoutId(1)
|
|
|
|
, mStatus(Pending)
|
2015-04-01 12:00:19 +03:00
|
|
|
, mFrozen(false)
|
2014-11-17 22:55:37 +03:00
|
|
|
, mTimerRunning(false)
|
|
|
|
, mRunningExpiredTimeouts(false)
|
2015-09-29 00:34:28 +03:00
|
|
|
, mPendingEventQueueClearing(false)
|
2014-11-17 22:55:37 +03:00
|
|
|
, mCancelAllPendingRunnables(false)
|
|
|
|
, mPeriodicGCTimerRunning(false)
|
|
|
|
, mIdleGCTimerRunning(false)
|
|
|
|
, mWorkerScriptExecutedSuccessfully(false)
|
2016-12-19 05:38:53 +03:00
|
|
|
, mFetchHandlerWasAdded(false)
|
2016-01-12 21:16:59 +03:00
|
|
|
, mOnLine(false)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-11-24 23:27:15 +04:00
|
|
|
if (aParent) {
|
|
|
|
aParent->AssertIsOnWorkerThread();
|
|
|
|
aParent->GetAllPreferences(mPreferences);
|
2013-11-20 03:08:50 +04:00
|
|
|
mOnLine = aParent->OnLine();
|
2013-11-24 23:27:15 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
RuntimeService::GetDefaultPreferences(mPreferences);
|
2016-10-17 04:58:54 +03:00
|
|
|
mOnLine = !NS_IsOffline();
|
2013-11-24 23:27:15 +04:00
|
|
|
}
|
2016-09-14 06:14:02 +03:00
|
|
|
|
|
|
|
nsCOMPtr<nsIEventTarget> target;
|
|
|
|
|
2016-11-07 23:30:17 +03:00
|
|
|
// A child worker just inherits the parent workers ThrottledEventQueue
|
|
|
|
// and main thread target for now. This is mainly due to the restriction
|
|
|
|
// that ThrottledEventQueue can only be created on the main thread at the
|
|
|
|
// moment.
|
2016-09-14 06:14:02 +03:00
|
|
|
if (aParent) {
|
2016-11-07 23:30:17 +03:00
|
|
|
mMainThreadThrottledEventQueue = aParent->mMainThreadThrottledEventQueue;
|
|
|
|
mMainThreadEventTarget = aParent->mMainThreadEventTarget;
|
|
|
|
return;
|
2016-09-14 06:14:02 +03:00
|
|
|
}
|
|
|
|
|
2016-11-07 23:30:17 +03:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2016-11-27 22:24:34 +03:00
|
|
|
target = GetWindow() ? GetWindow()->EventTargetFor(TaskCategory::Worker) : nullptr;
|
2016-09-14 06:14:02 +03:00
|
|
|
|
|
|
|
if (!target) {
|
|
|
|
nsCOMPtr<nsIThread> mainThread;
|
|
|
|
NS_GetMainThread(getter_AddRefs(mainThread));
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mainThread);
|
|
|
|
target = mainThread;
|
|
|
|
}
|
|
|
|
|
2016-11-07 23:30:17 +03:00
|
|
|
// Throttle events to the main thread using a ThrottledEventQueue specific to
|
|
|
|
// this worker thread. This may return nullptr during shutdown.
|
|
|
|
mMainThreadThrottledEventQueue = ThrottledEventQueue::Create(target);
|
2016-09-14 06:14:02 +03:00
|
|
|
|
2016-11-07 23:30:17 +03:00
|
|
|
// If we were able to creat the throttled event queue, then use it for
|
|
|
|
// dispatching our main thread runnables. Otherwise use our underlying
|
|
|
|
// base target.
|
|
|
|
if (mMainThreadThrottledEventQueue) {
|
|
|
|
mMainThreadEventTarget = mMainThreadThrottledEventQueue;
|
|
|
|
} else {
|
|
|
|
mMainThreadEventTarget = target.forget();
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
WorkerPrivate::~WorkerPrivate()
|
|
|
|
{
|
2017-01-26 19:01:32 +03:00
|
|
|
mWorkerControlEventTarget->ForgetWorkerPrivate(this);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
2012-03-31 08:42:20 +04:00
|
|
|
already_AddRefed<WorkerPrivate>
|
2013-11-05 18:16:24 +04:00
|
|
|
WorkerPrivate::Constructor(const GlobalObject& aGlobal,
|
|
|
|
const nsAString& aScriptURL,
|
2017-05-17 17:48:54 +03:00
|
|
|
const WorkerOptions& aOptions,
|
2013-11-05 18:16:24 +04:00
|
|
|
ErrorResult& aRv)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
2014-02-06 18:39:10 +04:00
|
|
|
return WorkerPrivate::Constructor(aGlobal, aScriptURL, false,
|
2017-05-17 17:48:54 +03:00
|
|
|
WorkerTypeDedicated,
|
2017-05-17 17:49:34 +03:00
|
|
|
aOptions.mName, nullptr, aRv);
|
2013-11-05 18:16:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
bool
|
2017-02-01 23:43:38 +03:00
|
|
|
WorkerPrivate::WorkerAvailable(JSContext* aCx, JSObject* /* unused */)
|
2013-11-05 18:16:24 +04:00
|
|
|
{
|
|
|
|
// If we're already on a worker workers are clearly enabled.
|
|
|
|
if (!NS_IsMainThread()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If our caller is chrome, workers are always available.
|
2017-02-01 23:43:38 +03:00
|
|
|
if (nsContentUtils::IsSystemCaller(aCx)) {
|
2013-11-05 18:16:24 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else check the pref.
|
|
|
|
return Preferences::GetBool(PREF_WORKERS_ENABLED);
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
already_AddRefed<ChromeWorkerPrivate>
|
|
|
|
ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal,
|
|
|
|
const nsAString& aScriptURL,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2014-02-06 18:39:10 +04:00
|
|
|
return WorkerPrivate::Constructor(aGlobal, aScriptURL, true,
|
2017-05-17 17:49:34 +03:00
|
|
|
WorkerTypeDedicated, EmptyString(),
|
2014-02-06 18:39:10 +04:00
|
|
|
nullptr, aRv)
|
|
|
|
.downcast<ChromeWorkerPrivate>();
|
2013-11-05 18:16:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
bool
|
2014-08-05 06:20:35 +04:00
|
|
|
ChromeWorkerPrivate::WorkerAvailable(JSContext* aCx, JSObject* /* unused */)
|
2013-11-05 18:16:24 +04:00
|
|
|
{
|
2014-08-05 06:20:35 +04:00
|
|
|
// Chrome is always allowed to use workers, and content is never
|
|
|
|
// allowed to use ChromeWorker, so all we have to check is the
|
|
|
|
// caller. However, chrome workers apparently might not have a
|
|
|
|
// system principal, so we have to check for them manually.
|
|
|
|
if (NS_IsMainThread()) {
|
2017-02-01 23:43:38 +03:00
|
|
|
return nsContentUtils::IsSystemCaller(aCx);
|
2014-08-05 06:20:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
|
2013-11-05 18:16:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
already_AddRefed<WorkerPrivate>
|
|
|
|
WorkerPrivate::Constructor(const GlobalObject& aGlobal,
|
|
|
|
const nsAString& aScriptURL,
|
|
|
|
bool aIsChromeWorker, WorkerType aWorkerType,
|
2017-05-17 17:49:34 +03:00
|
|
|
const nsAString& aWorkerName,
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
|
2014-05-29 20:19:00 +04:00
|
|
|
{
|
2014-06-16 22:08:00 +04:00
|
|
|
JSContext* cx = aGlobal.Context();
|
2014-05-29 20:19:00 +04:00
|
|
|
return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType,
|
2017-05-17 17:49:34 +03:00
|
|
|
aWorkerName, NullCString(), aLoadInfo, aRv);
|
2014-05-29 20:19:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
already_AddRefed<WorkerPrivate>
|
|
|
|
WorkerPrivate::Constructor(JSContext* aCx,
|
|
|
|
const nsAString& aScriptURL,
|
|
|
|
bool aIsChromeWorker, WorkerType aWorkerType,
|
2017-05-17 17:49:34 +03:00
|
|
|
const nsAString& aWorkerName,
|
|
|
|
const nsACString& aServiceWorkerScope,
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
|
2013-11-05 18:16:24 +04:00
|
|
|
{
|
2017-05-25 10:04:25 +03:00
|
|
|
// If this is a sub-worker, we need to keep the parent worker alive until this
|
|
|
|
// one is registered.
|
|
|
|
UniquePtr<SimpleWorkerHolder> holder;
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
WorkerPrivate* parent = NS_IsMainThread() ?
|
|
|
|
nullptr :
|
|
|
|
GetCurrentThreadWorkerPrivate();
|
|
|
|
if (parent) {
|
|
|
|
parent->AssertIsOnWorkerThread();
|
2017-05-25 10:04:25 +03:00
|
|
|
|
|
|
|
holder.reset(new SimpleWorkerHolder());
|
|
|
|
if (!holder->HoldWorker(parent, Canceling)) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
} else {
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
Maybe<WorkerLoadInfo> stackLoadInfo;
|
2013-06-05 18:04:23 +04:00
|
|
|
if (!aLoadInfo) {
|
2014-08-14 02:39:41 +04:00
|
|
|
stackLoadInfo.emplace();
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2014-05-29 20:19:00 +04:00
|
|
|
nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL,
|
2015-02-21 18:09:17 +03:00
|
|
|
aIsChromeWorker, InheritLoadGroup,
|
2015-06-17 04:21:08 +03:00
|
|
|
aWorkerType, stackLoadInfo.ptr());
|
2016-02-24 18:38:31 +03:00
|
|
|
aRv.MightThrowJSException();
|
2013-06-05 18:04:23 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
2016-02-29 22:52:42 +03:00
|
|
|
scriptloader::ReportLoadError(aRv, rv, aScriptURL);
|
2013-06-05 18:04:23 +04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-08-14 02:39:41 +04:00
|
|
|
aLoadInfo = stackLoadInfo.ptr();
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
// NB: This has to be done before creating the WorkerPrivate, because it will
|
|
|
|
// attempt to use static variables that are initialized in the RuntimeService
|
|
|
|
// constructor.
|
|
|
|
RuntimeService* runtimeService;
|
|
|
|
|
|
|
|
if (!parent) {
|
|
|
|
runtimeService = RuntimeService::GetOrCreateService();
|
|
|
|
if (!runtimeService) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
runtimeService = RuntimeService::GetService();
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(runtimeService);
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerPrivate> worker =
|
2016-03-12 00:43:30 +03:00
|
|
|
new WorkerPrivate(parent, aScriptURL, aIsChromeWorker,
|
2017-05-17 17:49:34 +03:00
|
|
|
aWorkerType, aWorkerName, aServiceWorkerScope,
|
|
|
|
*aLoadInfo);
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2016-08-08 23:33:39 +03:00
|
|
|
// Gecko contexts always have an explicitly-set default locale (set by
|
|
|
|
// XPJSRuntime::Initialize for the main thread, set by
|
|
|
|
// WorkerThreadPrimaryRunnable::Run for workers just before running worker
|
|
|
|
// code), so this is never SpiderMonkey's builtin default locale.
|
|
|
|
JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx);
|
|
|
|
if (NS_WARN_IF(!defaultLocale)) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
worker->mDefaultLocale = Move(defaultLocale);
|
|
|
|
|
2016-03-28 20:28:14 +03:00
|
|
|
if (!runtimeService->RegisterWorker(worker)) {
|
2013-11-05 18:16:24 +04:00
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-10-27 20:00:05 +03:00
|
|
|
worker->EnableDebugger();
|
|
|
|
|
2017-02-14 18:06:39 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<CompileScriptRunnable> compiler =
|
2015-03-17 13:15:19 +03:00
|
|
|
new CompileScriptRunnable(worker, aScriptURL);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!compiler->Dispatch()) {
|
2013-11-05 18:16:24 +04:00
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
2013-06-05 18:04:23 +04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-02-02 22:08:50 +04:00
|
|
|
worker->mSelfRef = worker;
|
2013-11-05 18:16:24 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
return worker.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsresult
|
2016-01-30 20:05:36 +03:00
|
|
|
WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
2013-06-05 18:04:23 +04:00
|
|
|
WorkerPrivate* aParent, const nsAString& aScriptURL,
|
2015-02-21 18:09:17 +03:00
|
|
|
bool aIsChromeWorker,
|
|
|
|
LoadGroupBehavior aLoadGroupBehavior,
|
2015-06-17 04:21:08 +03:00
|
|
|
WorkerType aWorkerType,
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo* aLoadInfo)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-06-05 18:04:23 +04:00
|
|
|
using namespace mozilla::dom::workers::scriptloader;
|
2012-09-15 22:51:55 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
MOZ_ASSERT(aCx);
|
2014-05-07 02:43:02 +04:00
|
|
|
MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext());
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
if (aWindow) {
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerLoadInfo loadInfo;
|
2013-06-05 18:04:23 +04:00
|
|
|
nsresult rv;
|
2012-09-17 04:20:16 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
if (aParent) {
|
|
|
|
aParent->AssertIsOnWorkerThread();
|
|
|
|
|
2013-03-13 00:33:40 +04:00
|
|
|
// If the parent is going away give up now.
|
2013-06-05 18:04:23 +04:00
|
|
|
Status parentStatus;
|
2013-03-13 00:33:40 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(aParent->mMutex);
|
2013-06-05 18:04:23 +04:00
|
|
|
parentStatus = aParent->mStatus;
|
2013-03-13 00:33:40 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
if (parentStatus > Running) {
|
|
|
|
return NS_ERROR_FAILURE;
|
2013-03-13 00:33:40 +04:00
|
|
|
}
|
|
|
|
|
2017-02-07 18:28:38 +03:00
|
|
|
// Passing a pointer to our stack loadInfo is safe here because this
|
|
|
|
// method uses a sync runnable to get the channel from the main thread.
|
2013-06-05 18:04:23 +04:00
|
|
|
rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
|
2017-02-07 18:28:38 +03:00
|
|
|
loadInfo);
|
2017-02-07 18:28:39 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
2017-02-07 18:28:39 +03:00
|
|
|
MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
|
2017-02-07 18:28:39 +03:00
|
|
|
return rv;
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
// Now that we've spun the loop there's no guarantee that our parent is
|
|
|
|
// still alive. We may have received control messages initiating shutdown.
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(aParent->mMutex);
|
|
|
|
parentStatus = aParent->mStatus;
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
|
|
|
|
if (parentStatus > Running) {
|
2017-02-07 18:28:39 +03:00
|
|
|
MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
|
2013-06-05 18:04:23 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
loadInfo.mDomain = aParent->Domain();
|
2014-12-17 09:26:15 +03:00
|
|
|
loadInfo.mFromWindow = aParent->IsFromWindow();
|
|
|
|
loadInfo.mWindowID = aParent->WindowID();
|
2015-07-16 00:01:02 +03:00
|
|
|
loadInfo.mStorageAllowed = aParent->IsStorageAllowed();
|
2016-09-20 04:13:00 +03:00
|
|
|
loadInfo.mOriginAttributes = aParent->GetOriginAttributes();
|
2015-06-28 06:19:24 +03:00
|
|
|
loadInfo.mServiceWorkersTestingInWindow =
|
|
|
|
aParent->ServiceWorkersTestingInWindow();
|
2013-06-05 18:04:23 +04:00
|
|
|
} else {
|
2011-07-17 23:09:13 +04:00
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
2015-07-16 00:01:02 +03:00
|
|
|
// Make sure that the IndexedDatabaseManager is set up
|
2016-09-02 10:12:24 +03:00
|
|
|
Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
|
2015-07-16 00:01:02 +03:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
2013-06-05 18:04:23 +04:00
|
|
|
MOZ_ASSERT(ssm);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-02-01 23:43:38 +03:00
|
|
|
bool isChrome = nsContentUtils::IsSystemCaller(aCx);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
// First check to make sure the caller has permission to make a privileged
|
|
|
|
// worker if they called the ChromeWorker/ChromeSharedWorker constructor.
|
2011-07-17 23:09:13 +04:00
|
|
|
if (aIsChromeWorker && !isChrome) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2015-06-10 00:34:00 +03:00
|
|
|
// Chrome callers (whether creating a ChromeWorker or Worker) always get the
|
|
|
|
// system principal here as they're allowed to load anything. The script
|
|
|
|
// loader will refuse to run any script that does not also have the system
|
|
|
|
// principal.
|
2013-06-05 18:04:23 +04:00
|
|
|
if (isChrome) {
|
|
|
|
rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2015-06-10 00:34:00 +03:00
|
|
|
|
|
|
|
loadInfo.mPrincipalIsSystem = true;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
// See if we're being called from a window.
|
2016-01-30 20:05:36 +03:00
|
|
|
nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow;
|
2013-06-05 18:04:23 +04:00
|
|
|
if (!globalWindow) {
|
2013-10-23 17:16:49 +04:00
|
|
|
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
|
|
|
|
nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
|
|
|
|
if (scriptGlobal) {
|
2013-06-05 18:04:23 +04:00
|
|
|
globalWindow = do_QueryInterface(scriptGlobal);
|
|
|
|
MOZ_ASSERT(globalWindow);
|
|
|
|
}
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
nsCOMPtr<nsIDocument> document;
|
|
|
|
|
|
|
|
if (globalWindow) {
|
2011-07-17 23:09:13 +04:00
|
|
|
// Only use the current inner window, and only use it if the caller can
|
|
|
|
// access it.
|
2016-01-30 20:05:36 +03:00
|
|
|
if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
|
2015-06-28 06:19:24 +03:00
|
|
|
// TODO: fix this for SharedWorkers with multiple documents (bug 1177935)
|
|
|
|
loadInfo.mServiceWorkersTestingInWindow =
|
|
|
|
outerWindow->GetServiceWorkersTestingEnabled();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
if (!loadInfo.mWindow ||
|
|
|
|
(globalWindow != loadInfo.mWindow &&
|
|
|
|
!nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
|
|
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
|
|
|
|
MOZ_ASSERT(sgo);
|
|
|
|
|
|
|
|
loadInfo.mScriptContext = sgo->GetContext();
|
|
|
|
NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
// If we're called from a window then we can dig out the principal and URI
|
|
|
|
// from the document.
|
2013-06-05 18:04:23 +04:00
|
|
|
document = loadInfo.mWindow->GetExtantDoc();
|
|
|
|
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
loadInfo.mBaseURI = document->GetDocBaseURI();
|
2014-12-12 19:06:00 +03:00
|
|
|
loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
|
2017-05-15 16:52:00 +03:00
|
|
|
NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
// Use the document's NodePrincipal as our principal if we're not being
|
|
|
|
// called from chrome.
|
2013-06-05 18:04:23 +04:00
|
|
|
if (!loadInfo.mPrincipal) {
|
|
|
|
loadInfo.mPrincipal = document->NodePrincipal();
|
|
|
|
NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-01-09 01:53:32 +04:00
|
|
|
// We use the document's base domain to limit the number of workers
|
|
|
|
// each domain can create. For sandboxed documents, we use the domain
|
|
|
|
// of their first non-sandboxed document, walking up until we find
|
|
|
|
// one. If we can't find one, we fall back to using the GUID of the
|
|
|
|
// null principal as the base domain.
|
|
|
|
if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
|
|
|
|
nsCOMPtr<nsIDocument> tmpDoc = document;
|
|
|
|
do {
|
|
|
|
tmpDoc = tmpDoc->GetParentDocument();
|
|
|
|
} while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
|
|
|
|
|
|
|
|
if (tmpDoc) {
|
|
|
|
// There was an unsandboxed ancestor, yay!
|
|
|
|
nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
|
2013-06-05 18:04:23 +04:00
|
|
|
rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2013-01-09 01:53:32 +04:00
|
|
|
} else {
|
|
|
|
// No unsandboxed ancestor, use our GUID.
|
2013-06-05 18:04:23 +04:00
|
|
|
rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2012-08-20 22:34:32 +04:00
|
|
|
} else {
|
2013-01-09 01:53:32 +04:00
|
|
|
// Document creating the worker is not sandboxed.
|
2013-06-05 18:04:23 +04:00
|
|
|
rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2012-09-17 04:20:16 +04:00
|
|
|
|
2017-05-15 16:52:00 +03:00
|
|
|
NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
|
|
|
|
loadInfo.mPrincipal),
|
|
|
|
NS_ERROR_FAILURE);
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
nsCOMPtr<nsIPermissionManager> permMgr =
|
|
|
|
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
uint32_t perm;
|
|
|
|
rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR",
|
|
|
|
&perm);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
|
2014-08-20 21:34:14 +04:00
|
|
|
|
2014-12-17 09:26:15 +03:00
|
|
|
loadInfo.mFromWindow = true;
|
|
|
|
loadInfo.mWindowID = globalWindow->WindowID();
|
2015-07-16 00:01:02 +03:00
|
|
|
nsContentUtils::StorageAccess access =
|
|
|
|
nsContentUtils::StorageAllowedForWindow(globalWindow);
|
|
|
|
loadInfo.mStorageAllowed = access > nsContentUtils::StorageAccess::eDeny;
|
2016-09-20 04:13:00 +03:00
|
|
|
loadInfo.mOriginAttributes = nsContentUtils::GetOriginAttributes(document);
|
2013-06-05 18:04:23 +04:00
|
|
|
} else {
|
|
|
|
// Not a window
|
|
|
|
MOZ_ASSERT(isChrome);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
// We're being created outside of a window. Need to figure out the script
|
|
|
|
// that is creating us in order for us to use relative URIs later on.
|
2016-03-09 13:20:11 +03:00
|
|
|
JS::AutoFilename fileName;
|
2014-02-25 19:43:14 +04:00
|
|
|
if (JS::DescribeScriptedCaller(aCx, &fileName)) {
|
2013-11-06 18:05:17 +04:00
|
|
|
// In most cases, fileName is URI. In a few other cases
|
|
|
|
// (e.g. xpcshell), fileName is a file path. Ideally, we would
|
|
|
|
// prefer testing whether fileName parses as an URI and fallback
|
|
|
|
// to file path in case of error, but Windows file paths have
|
|
|
|
// the interesting property that they can be parsed as bogus
|
|
|
|
// URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
|
|
|
|
// hostname "Windows", path "Tmp"), which defeats this algorithm.
|
|
|
|
// Therefore, we adopt the opposite convention.
|
|
|
|
nsCOMPtr<nsIFile> scriptFile =
|
|
|
|
do_CreateInstance("@mozilla.org/file/local;1", &rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2012-09-17 04:20:16 +04:00
|
|
|
|
2014-02-25 19:43:14 +04:00
|
|
|
rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
|
2013-11-06 18:05:17 +04:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI),
|
|
|
|
scriptFile);
|
|
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
// As expected, fileName is not a path, so proceed with
|
|
|
|
// a uri.
|
|
|
|
rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
|
2014-02-25 19:43:14 +04:00
|
|
|
fileName.get());
|
2013-11-06 18:05:17 +04:00
|
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
loadInfo.mXHRParamsAllowed = true;
|
2014-12-17 09:26:15 +03:00
|
|
|
loadInfo.mFromWindow = false;
|
|
|
|
loadInfo.mWindowID = UINT64_MAX;
|
2015-07-16 00:01:02 +03:00
|
|
|
loadInfo.mStorageAllowed = true;
|
2017-01-12 19:38:48 +03:00
|
|
|
loadInfo.mOriginAttributes = OriginAttributes();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
MOZ_ASSERT(loadInfo.mPrincipal);
|
|
|
|
MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
|
2012-09-15 22:51:55 +04:00
|
|
|
|
2015-02-21 18:09:17 +03:00
|
|
|
if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
|
|
|
|
OverrideLoadInfoLoadGroup(loadInfo);
|
2014-12-12 19:06:00 +03:00
|
|
|
}
|
|
|
|
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
|
|
|
|
loadInfo.mPrincipal));
|
|
|
|
|
2016-04-28 10:44:08 +03:00
|
|
|
// Top level workers' main script use the document charset for the script
|
|
|
|
// uri encoding.
|
|
|
|
bool useDefaultEncoding = false;
|
2013-06-05 18:04:23 +04:00
|
|
|
rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI,
|
2014-12-12 19:06:00 +03:00
|
|
|
document, loadInfo.mLoadGroup,
|
|
|
|
aScriptURL,
|
2015-06-17 04:21:08 +03:00
|
|
|
ContentPolicyType(aWorkerType),
|
2016-04-28 10:44:08 +03:00
|
|
|
useDefaultEncoding,
|
2013-06-05 18:04:23 +04:00
|
|
|
getter_AddRefs(loadInfo.mChannel));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2013-03-13 00:33:40 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
rv = NS_GetFinalChannelURI(loadInfo.mChannel,
|
|
|
|
getter_AddRefs(loadInfo.mResolvedScriptURI));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2017-02-07 18:28:39 +03:00
|
|
|
|
|
|
|
rv = loadInfo.SetPrincipalFromChannel(loadInfo.mChannel);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-02-14 18:06:39 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
aLoadInfo->StealFrom(loadInfo);
|
|
|
|
return NS_OK;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2015-02-21 18:09:17 +03:00
|
|
|
// static
|
|
|
|
void
|
2015-02-12 12:50:05 +03:00
|
|
|
WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo)
|
2015-02-21 18:09:17 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
|
|
|
|
|
2015-02-12 12:50:05 +03:00
|
|
|
aLoadInfo.mInterfaceRequestor =
|
|
|
|
new WorkerLoadInfo::InterfaceRequestor(aLoadInfo.mPrincipal,
|
|
|
|
aLoadInfo.mLoadGroup);
|
2015-02-21 18:09:17 +03:00
|
|
|
aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
|
|
|
|
|
2015-10-01 02:11:03 +03:00
|
|
|
// NOTE: this defaults the load context to:
|
|
|
|
// - private browsing = false
|
|
|
|
// - content = true
|
|
|
|
// - use remote tabs = false
|
2015-02-21 18:09:17 +03:00
|
|
|
nsCOMPtr<nsILoadGroup> loadGroup =
|
|
|
|
do_CreateInstance(NS_LOADGROUP_CONTRACTID);
|
|
|
|
|
|
|
|
nsresult rv =
|
|
|
|
loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(rv);
|
2015-02-21 18:09:17 +03:00
|
|
|
|
|
|
|
aLoadInfo.mLoadGroup = loadGroup.forget();
|
2017-05-15 16:52:00 +03:00
|
|
|
|
|
|
|
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup,
|
|
|
|
aLoadInfo.mPrincipal));
|
2015-02-21 18:09:17 +03:00
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
void
|
|
|
|
WorkerPrivate::DoRunLoop(JSContext* aCx)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(mThread);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
mJSContext = aCx;
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(mStatus == Pending);
|
2011-07-17 23:09:13 +04:00
|
|
|
mStatus = Running;
|
|
|
|
}
|
|
|
|
|
2016-02-26 23:23:13 +03:00
|
|
|
// Now that we've done that, we can go ahead and set up our AutoJSAPI. We
|
|
|
|
// can't before this point, because it can't find the right JSContext before
|
|
|
|
// then, since it gets it from our mJSContext.
|
|
|
|
AutoJSAPI jsapi;
|
|
|
|
jsapi.Init();
|
|
|
|
MOZ_ASSERT(jsapi.cx() == aCx);
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
EnableMemoryReporter();
|
2012-01-18 00:05:25 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
InitializeGCTimers();
|
2012-01-18 00:05:25 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
Maybe<JSAutoCompartment> workerCompartment;
|
2011-09-09 04:03:03 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
for (;;) {
|
2016-09-01 07:33:05 +03:00
|
|
|
Status currentStatus, previousStatus;
|
2015-03-04 17:11:32 +03:00
|
|
|
bool debuggerRunnablesPending = false;
|
2013-10-23 17:16:49 +04:00
|
|
|
bool normalRunnablesPending = false;
|
2012-01-18 00:05:25 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
2016-09-01 07:33:05 +03:00
|
|
|
previousStatus = mStatus;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
while (mControlQueue.IsEmpty() &&
|
2015-03-04 17:11:32 +03:00
|
|
|
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
|
2013-10-23 17:16:49 +04:00
|
|
|
!(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
|
2012-12-30 22:21:52 +04:00
|
|
|
WaitForWorkerEvents();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-05-11 21:45:58 +03:00
|
|
|
auto result = ProcessAllControlRunnablesLocked();
|
|
|
|
if (result != ProcessAllControlRunnablesResult::Nothing) {
|
|
|
|
// NB: There's no JS on the stack here, so Abort vs MayContinue is
|
|
|
|
// irrelevant
|
|
|
|
|
|
|
|
// The state of the world may have changed, recheck it.
|
|
|
|
normalRunnablesPending = NS_HasPendingEvents(mThread);
|
|
|
|
// The debugger queue doesn't get cleared, so we can ignore that.
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
currentStatus = mStatus;
|
|
|
|
}
|
|
|
|
|
2016-09-01 07:33:05 +03:00
|
|
|
// if all holders are done then we can kill this thread.
|
2016-06-23 11:53:14 +03:00
|
|
|
if (currentStatus != Running && !HasActiveHolders()) {
|
2016-09-01 07:33:05 +03:00
|
|
|
|
|
|
|
// If we just changed status, we must schedule the current runnables.
|
|
|
|
if (previousStatus != Running && currentStatus != Killing) {
|
2016-02-27 05:15:56 +03:00
|
|
|
NotifyInternal(aCx, Killing);
|
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
currentStatus = mStatus;
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(currentStatus == Killing);
|
2011-07-17 23:09:13 +04:00
|
|
|
#else
|
|
|
|
currentStatus = Killing;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're supposed to die then we should exit the loop.
|
|
|
|
if (currentStatus == Killing) {
|
2015-04-10 18:27:57 +03:00
|
|
|
// Flush uncaught rejections immediately, without
|
|
|
|
// waiting for a next tick.
|
|
|
|
PromiseDebugging::FlushUncaughtRejections();
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
ShutdownGCTimers();
|
2012-01-18 00:05:25 +04:00
|
|
|
|
2011-09-09 04:03:03 +04:00
|
|
|
DisableMemoryReporter();
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
mStatus = Dead;
|
|
|
|
mJSContext = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// After mStatus is set to Dead there can be no more
|
|
|
|
// WorkerControlRunnables so no need to lock here.
|
|
|
|
if (!mControlQueue.IsEmpty()) {
|
|
|
|
WorkerControlRunnable* runnable;
|
|
|
|
while (mControlQueue.Pop(runnable)) {
|
|
|
|
runnable->Cancel();
|
|
|
|
runnable->Release();
|
|
|
|
}
|
|
|
|
}
|
2013-11-05 18:16:26 +04:00
|
|
|
|
2015-03-04 02:51:53 +03:00
|
|
|
// Unroot the globals
|
2013-11-05 18:16:26 +04:00
|
|
|
mScope = nullptr;
|
2015-03-04 02:51:53 +03:00
|
|
|
mDebuggerScope = nullptr;
|
2013-11-05 18:16:26 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-03-04 17:11:32 +03:00
|
|
|
if (debuggerRunnablesPending || normalRunnablesPending) {
|
|
|
|
// Start the periodic GC timer if it is not already running.
|
|
|
|
SetGCTimerMode(PeriodicTimer);
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
2015-03-04 17:11:32 +03:00
|
|
|
if (debuggerRunnablesPending) {
|
|
|
|
WorkerRunnable* runnable;
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-03-04 17:11:32 +03:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-03-04 17:11:32 +03:00
|
|
|
mDebuggerQueue.Pop(runnable);
|
|
|
|
debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(runnable);
|
|
|
|
static_cast<nsIRunnable*>(runnable)->Run();
|
|
|
|
runnable->Release();
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2016-03-24 18:12:00 +03:00
|
|
|
// Flush the promise queue.
|
|
|
|
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
|
|
|
|
2015-03-04 17:11:32 +03:00
|
|
|
if (debuggerRunnablesPending) {
|
|
|
|
WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
|
|
|
|
MOZ_ASSERT(globalScope);
|
2014-10-28 15:08:19 +03:00
|
|
|
|
2015-03-04 17:11:32 +03:00
|
|
|
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
|
|
|
JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
|
|
|
|
JS_MaybeGC(aCx);
|
|
|
|
}
|
|
|
|
} else if (normalRunnablesPending) {
|
|
|
|
// Process a single runnable from the main queue.
|
2016-09-01 07:33:05 +03:00
|
|
|
NS_ProcessNextEvent(mThread, false);
|
2015-03-04 17:11:32 +03:00
|
|
|
|
|
|
|
normalRunnablesPending = NS_HasPendingEvents(mThread);
|
|
|
|
if (normalRunnablesPending && GlobalScope()) {
|
|
|
|
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
|
|
|
JSAutoCompartment ac(aCx, GlobalScope()->GetGlobalJSObject());
|
2013-10-23 17:16:49 +04:00
|
|
|
JS_MaybeGC(aCx);
|
|
|
|
}
|
|
|
|
}
|
2015-03-04 17:11:32 +03:00
|
|
|
|
2015-11-18 06:45:43 +03:00
|
|
|
if (!debuggerRunnablesPending && !normalRunnablesPending) {
|
2015-03-04 17:11:32 +03:00
|
|
|
// Both the debugger event queue and the normal event queue has been
|
|
|
|
// exhausted, cancel the periodic GC timer and schedule the idle GC timer.
|
2013-10-23 17:16:49 +04:00
|
|
|
SetGCTimerMode(IdleTimer);
|
|
|
|
}
|
2016-09-14 06:14:02 +03:00
|
|
|
|
|
|
|
// If the worker thread is spamming the main thread faster than it can
|
|
|
|
// process the work, then pause the worker thread until the MT catches
|
|
|
|
// up.
|
2016-11-07 23:30:17 +03:00
|
|
|
if (mMainThreadThrottledEventQueue &&
|
|
|
|
mMainThreadThrottledEventQueue->Length() > 5000) {
|
|
|
|
mMainThreadThrottledEventQueue->AwaitIdle();
|
2016-09-14 06:14:02 +03:00
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
2014-04-20 11:36:40 +04:00
|
|
|
MOZ_CRASH("Shouldn't get here!");
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
WorkerPrivate::OnProcessNextEvent()
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
|
2016-09-14 16:47:32 +03:00
|
|
|
uint32_t recursionDepth = CycleCollectedJSContext::Get()->RecursionDepth();
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
MOZ_ASSERT(recursionDepth);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
// Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
|
|
|
|
// However, it's possible that non-worker C++ could spin its own nested event
|
|
|
|
// loop, and in that case we must ensure that we continue to process control
|
|
|
|
// runnables here.
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
if (recursionDepth > 1 &&
|
|
|
|
mSyncLoopStack.Length() < recursionDepth - 1) {
|
2016-05-11 21:45:58 +03:00
|
|
|
Unused << ProcessAllControlRunnables();
|
|
|
|
// There's no running JS, and no state to revalidate, so we can ignore the
|
|
|
|
// return value.
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
WorkerPrivate::AfterProcessNextEvent()
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2016-09-14 16:47:32 +03:00
|
|
|
MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());
|
2014-12-17 09:26:15 +03:00
|
|
|
}
|
|
|
|
|
2015-07-15 22:21:40 +03:00
|
|
|
void
|
|
|
|
WorkerPrivate::MaybeDispatchLoadFailedRunnable()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable();
|
|
|
|
if (!runnable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-14 06:14:02 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(DispatchToMainThread(runnable.forget()));
|
2016-09-12 22:32:21 +03:00
|
|
|
}
|
|
|
|
|
2016-09-14 06:14:02 +03:00
|
|
|
nsIEventTarget*
|
|
|
|
WorkerPrivate::MainThreadEventTarget()
|
|
|
|
{
|
|
|
|
return mMainThreadEventTarget;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable, uint32_t aFlags)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> r = aRunnable;
|
|
|
|
return DispatchToMainThread(r.forget(), aFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
WorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
|
|
|
|
uint32_t aFlags)
|
|
|
|
{
|
2017-01-19 01:01:05 +03:00
|
|
|
nsCOMPtr<nsIRunnable> runnable = aRunnable;
|
|
|
|
if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
|
|
|
|
named->SetName("WorkerRunnable");
|
|
|
|
}
|
|
|
|
return mMainThreadEventTarget->Dispatch(runnable.forget(), aFlags);
|
2016-09-14 06:14:02 +03:00
|
|
|
}
|
|
|
|
|
2017-01-26 19:01:33 +03:00
|
|
|
nsIEventTarget*
|
|
|
|
WorkerPrivate::ControlEventTarget()
|
|
|
|
{
|
|
|
|
return mWorkerControlEventTarget;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
void
|
|
|
|
WorkerPrivate::InitializeGCTimers()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-11-18 06:45:43 +03:00
|
|
|
// We need a timer for GC. The basic plan is to run a non-shrinking GC
|
2013-10-23 17:16:49 +04:00
|
|
|
// periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
|
|
|
|
// Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
|
2015-11-18 06:45:43 +03:00
|
|
|
// run a shrinking GC. If the worker receives more messages then the short
|
|
|
|
// timer is canceled and the periodic timer resumes.
|
|
|
|
mGCTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
|
|
|
MOZ_ASSERT(mGCTimer);
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
mPeriodicGCTimerRunning = false;
|
2013-12-20 23:03:19 +04:00
|
|
|
mIdleGCTimerRunning = false;
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerPrivate::SetGCTimerMode(GCTimerMode aMode)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2015-11-18 06:45:43 +03:00
|
|
|
MOZ_ASSERT(mGCTimer);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-11-18 06:45:43 +03:00
|
|
|
if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) ||
|
|
|
|
(aMode == IdleTimer && mIdleGCTimerRunning)) {
|
2013-10-23 17:16:49 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-11-18 06:45:43 +03:00
|
|
|
mPeriodicGCTimerRunning = false;
|
|
|
|
mIdleGCTimerRunning = false;
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(WorkerLog(),
|
|
|
|
("Worker %p canceled GC timer because %s\n", this,
|
2015-11-18 06:45:43 +03:00
|
|
|
aMode == PeriodicTimer ?
|
|
|
|
"periodic" :
|
|
|
|
aMode == IdleTimer ? "idle" : "none"));
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
if (aMode == NoTimer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
|
|
|
|
|
2017-01-26 19:01:33 +03:00
|
|
|
uint32_t delay = 0;
|
|
|
|
int16_t type = nsITimer::TYPE_ONE_SHOT;
|
|
|
|
nsTimerCallbackFunc callback = nullptr;
|
|
|
|
const char* name = nullptr;
|
2015-11-18 06:45:43 +03:00
|
|
|
|
|
|
|
if (aMode == PeriodicTimer) {
|
|
|
|
delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
|
|
|
|
type = nsITimer::TYPE_REPEATING_SLACK;
|
2017-01-26 19:01:33 +03:00
|
|
|
callback = PeriodicGCTimerCallback;
|
|
|
|
name = "dom::workers::PeriodicGCTimerCallback";
|
2015-11-18 06:45:43 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
|
|
|
|
type = nsITimer::TYPE_ONE_SHOT;
|
2017-01-26 19:01:33 +03:00
|
|
|
callback = IdleGCTimerCallback;
|
|
|
|
name = "dom::workers::IdleGCTimerCallback";
|
2015-11-18 06:45:43 +03:00
|
|
|
}
|
|
|
|
|
2017-01-26 19:01:33 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(mGCTimer->SetTarget(mWorkerControlEventTarget));
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(
|
2017-01-26 19:01:33 +03:00
|
|
|
mGCTimer->InitWithNamedFuncCallback(callback, this, delay, type, name));
|
2015-11-18 06:45:43 +03:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
if (aMode == PeriodicTimer) {
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
|
2013-10-23 17:16:49 +04:00
|
|
|
mPeriodicGCTimerRunning = true;
|
2015-11-18 06:45:43 +03:00
|
|
|
}
|
|
|
|
else {
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
|
2013-12-20 23:03:19 +04:00
|
|
|
mIdleGCTimerRunning = true;
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerPrivate::ShutdownGCTimers()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-11-18 06:45:43 +03:00
|
|
|
MOZ_ASSERT(mGCTimer);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-11-18 06:45:43 +03:00
|
|
|
// Always make sure the timer is canceled.
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
|
2013-10-23 17:16:49 +04:00
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this));
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-11-18 06:45:43 +03:00
|
|
|
mGCTimer = nullptr;
|
2013-10-23 17:16:49 +04:00
|
|
|
mPeriodicGCTimerRunning = false;
|
2013-12-20 23:03:19 +04:00
|
|
|
mIdleGCTimerRunning = false;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2014-03-11 01:28:43 +04:00
|
|
|
WorkerPrivate::InterruptCallback(JSContext* aCx)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2016-03-02 00:52:27 +03:00
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
bool mayContinue = true;
|
2013-10-23 17:16:49 +04:00
|
|
|
bool scheduledIdleGC = false;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
// Run all control events now.
|
2016-05-11 21:45:58 +03:00
|
|
|
auto result = ProcessAllControlRunnables();
|
|
|
|
if (result == ProcessAllControlRunnablesResult::Abort) {
|
|
|
|
mayContinue = false;
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
bool mayFreeze = mFrozen;
|
|
|
|
if (mayFreeze) {
|
2013-06-05 18:04:23 +04:00
|
|
|
MutexAutoLock lock(mMutex);
|
2015-04-01 12:00:19 +03:00
|
|
|
mayFreeze = mStatus <= Running;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
if (!mayContinue || !mayFreeze) {
|
2011-07-17 23:09:13 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
// Cancel the periodic GC timer here before freezing. The idle GC timer
|
2013-10-23 17:16:49 +04:00
|
|
|
// will clean everything up once it runs.
|
|
|
|
if (!scheduledIdleGC) {
|
|
|
|
SetGCTimerMode(IdleTimer);
|
|
|
|
scheduledIdleGC = true;
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
while ((mayContinue = MayContinueRunning())) {
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (!mControlQueue.IsEmpty()) {
|
|
|
|
break;
|
|
|
|
}
|
2011-08-02 08:06:17 +04:00
|
|
|
|
2016-09-01 07:33:05 +03:00
|
|
|
WaitForWorkerEvents(PR_MillisecondsToInterval(UINT32_MAX));
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mayContinue) {
|
|
|
|
// We want only uncatchable exceptions here.
|
|
|
|
NS_ASSERTION(!JS_IsExceptionPending(aCx),
|
|
|
|
"Should not have an exception set here!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Make sure the periodic timer gets turned back on here.
|
|
|
|
SetGCTimerMode(PeriodicTimer);
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
nsresult
|
|
|
|
WorkerPrivate::IsOnCurrentThread(bool* aIsOnCurrentThread)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
// May be called on any thread!
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(aIsOnCurrentThread);
|
2014-11-17 22:55:37 +03:00
|
|
|
MOZ_ASSERT(mPRThread);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2014-11-17 22:55:37 +03:00
|
|
|
*aIsOnCurrentThread = PR_GetCurrentThread() == mPRThread;
|
2013-10-23 17:16:49 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-03-05 03:09:23 +04:00
|
|
|
WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
MOZ_ASSERT(mChildWorkers.IsEmpty());
|
|
|
|
MOZ_ASSERT(mSyncLoopStack.IsEmpty());
|
2015-09-29 00:34:28 +03:00
|
|
|
MOZ_ASSERT(!mPendingEventQueueClearing);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2014-03-05 03:09:23 +04:00
|
|
|
ClearMainEventQueue(aRanOrNot);
|
2014-07-29 17:25:29 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (WorkerRan == aRanOrNot) {
|
|
|
|
nsIThread* currentThread = NS_GetCurrentThread();
|
|
|
|
MOZ_ASSERT(currentThread);
|
|
|
|
MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
|
|
|
|
}
|
|
|
|
#endif
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
if (WorkerPrivate* parent = GetParent()) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerFinishedRunnable> runnable =
|
2013-10-23 17:16:49 +04:00
|
|
|
new WorkerFinishedRunnable(parent, this);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_WARNING("Failed to dispatch runnable!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TopLevelWorkerFinishedRunnable> runnable =
|
2013-10-23 17:16:49 +04:00
|
|
|
new TopLevelWorkerFinishedRunnable(this);
|
2016-09-14 06:14:02 +03:00
|
|
|
if (NS_FAILED(DispatchToMainThread(runnable.forget()))) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_WARNING("Failed to dispatch runnable!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-07 05:03:46 +04:00
|
|
|
bool
|
2016-10-28 12:50:16 +03:00
|
|
|
WorkerPrivate::CollectRuntimeStats(JS::RuntimeStats* aRtStats,
|
|
|
|
bool aAnonymize)
|
2011-08-07 05:03:46 +04:00
|
|
|
{
|
2016-10-28 12:50:16 +03:00
|
|
|
AssertIsOnWorkerThread();
|
2013-04-12 07:52:32 +04:00
|
|
|
NS_ASSERTION(aRtStats, "Null RuntimeStats!");
|
2013-02-08 15:50:00 +04:00
|
|
|
NS_ASSERTION(mJSContext, "This must never be null!");
|
2012-12-30 22:21:52 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize);
|
2011-09-09 04:03:03 +04:00
|
|
|
}
|
|
|
|
|
2012-12-30 22:21:52 +04:00
|
|
|
void
|
|
|
|
WorkerPrivate::EnableMemoryReporter()
|
2011-09-09 04:03:03 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(!mMemoryReporter);
|
2011-09-09 04:03:03 +04:00
|
|
|
|
2012-12-30 22:21:52 +04:00
|
|
|
// No need to lock here since the main thread can't race until we've
|
|
|
|
// successfully registered the reporter.
|
|
|
|
mMemoryReporter = new MemoryReporter(this);
|
2011-09-09 04:03:03 +04:00
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
if (NS_FAILED(RegisterWeakAsyncMemoryReporter(mMemoryReporter))) {
|
2012-12-30 22:21:52 +04:00
|
|
|
NS_WARNING("Failed to register memory reporter!");
|
|
|
|
// No need to lock here since a failed registration means our memory
|
|
|
|
// reporter can't start running. Just clean up.
|
|
|
|
mMemoryReporter = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerPrivate::DisableMemoryReporter()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<MemoryReporter> memoryReporter;
|
2011-09-09 04:03:03 +04:00
|
|
|
{
|
2016-10-28 12:50:16 +03:00
|
|
|
// Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by
|
|
|
|
// MemoryReporter::Disable() below.
|
2011-09-09 04:03:03 +04:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
2013-02-08 15:50:00 +04:00
|
|
|
// There is nothing to do here if the memory reporter was never successfully
|
|
|
|
// registered.
|
|
|
|
if (!mMemoryReporter) {
|
|
|
|
return;
|
|
|
|
}
|
2012-05-30 06:39:38 +04:00
|
|
|
|
2013-02-08 15:50:00 +04:00
|
|
|
// We don't need this set any longer. Swap it out so that we can unregister
|
|
|
|
// below.
|
|
|
|
mMemoryReporter.swap(memoryReporter);
|
2011-09-09 04:03:03 +04:00
|
|
|
|
2013-02-08 15:50:00 +04:00
|
|
|
// Next disable the memory reporter so that the main thread stops trying to
|
|
|
|
// signal us.
|
|
|
|
memoryReporter->Disable();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally unregister the memory reporter.
|
2013-11-07 09:35:30 +04:00
|
|
|
if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
|
2013-02-08 15:50:00 +04:00
|
|
|
NS_WARNING("Failed to unregister memory reporter!");
|
2012-12-30 22:21:52 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
|
|
|
|
2016-10-28 12:50:16 +03:00
|
|
|
// Wait for a worker event.
|
2012-12-30 22:21:52 +04:00
|
|
|
mCondVar.Wait(aInterval);
|
2011-09-09 04:03:03 +04:00
|
|
|
}
|
|
|
|
|
2016-05-11 21:45:58 +03:00
|
|
|
WorkerPrivate::ProcessAllControlRunnablesResult
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate::ProcessAllControlRunnablesLocked()
|
2011-09-09 04:03:03 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2013-10-23 17:16:49 +04:00
|
|
|
mMutex.AssertCurrentThreadOwns();
|
2011-09-09 04:03:03 +04:00
|
|
|
|
2016-05-11 21:45:58 +03:00
|
|
|
auto result = ProcessAllControlRunnablesResult::Nothing;
|
2011-09-09 04:03:03 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
for (;;) {
|
|
|
|
WorkerControlRunnable* event;
|
|
|
|
if (!mControlQueue.Pop(event)) {
|
|
|
|
break;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MutexAutoUnlock unlock(mMutex);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(event);
|
|
|
|
if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
|
2016-05-11 21:45:58 +03:00
|
|
|
result = ProcessAllControlRunnablesResult::Abort;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-05-11 21:45:58 +03:00
|
|
|
if (result == ProcessAllControlRunnablesResult::Nothing) {
|
|
|
|
// We ran at least one thing.
|
|
|
|
result = ProcessAllControlRunnablesResult::MayContinue;
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
event->Release();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
return result;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-03-05 03:09:23 +04:00
|
|
|
WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2016-07-04 09:18:53 +03:00
|
|
|
MOZ_ASSERT(mSyncLoopStack.IsEmpty());
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(!mCancelAllPendingRunnables);
|
|
|
|
mCancelAllPendingRunnables = true;
|
|
|
|
|
2014-03-05 03:09:23 +04:00
|
|
|
if (WorkerNeverRan == aRanOrNot) {
|
|
|
|
for (uint32_t count = mPreStartRunnables.Length(), index = 0;
|
|
|
|
index < count;
|
|
|
|
index++) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
|
2014-03-05 03:09:23 +04:00
|
|
|
static_cast<nsIRunnable*>(runnable.get())->Run();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nsIThread* currentThread = NS_GetCurrentThread();
|
|
|
|
MOZ_ASSERT(currentThread);
|
|
|
|
|
|
|
|
NS_ProcessPendingEvents(currentThread);
|
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
MOZ_ASSERT(mCancelAllPendingRunnables);
|
|
|
|
mCancelAllPendingRunnables = false;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-02-26 19:32:28 +03:00
|
|
|
void
|
|
|
|
WorkerPrivate::ClearDebuggerEventQueue()
|
|
|
|
{
|
|
|
|
while (!mDebuggerQueue.IsEmpty()) {
|
|
|
|
WorkerRunnable* runnable;
|
|
|
|
mDebuggerQueue.Pop(runnable);
|
|
|
|
// It should be ok to simply release the runnable, without running it.
|
|
|
|
runnable->Release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
bool
|
2016-02-29 22:52:43 +03:00
|
|
|
WorkerPrivate::FreezeInternal()
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
NS_ASSERTION(!mFrozen, "Already frozen!");
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
mFrozen = true;
|
2016-10-04 13:13:33 +03:00
|
|
|
|
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
|
|
|
mChildWorkers[index]->Freeze(nullptr);
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2016-02-29 22:52:43 +03:00
|
|
|
WorkerPrivate::ThawInternal()
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
NS_ASSERTION(mFrozen, "Not yet frozen!");
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-10-04 13:13:33 +03:00
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
|
|
|
mChildWorkers[index]->Thaw(nullptr);
|
|
|
|
}
|
|
|
|
|
2015-04-01 12:00:19 +03:00
|
|
|
mFrozen = false;
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-08-16 09:10:30 +03:00
|
|
|
WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2016-08-16 09:10:30 +03:00
|
|
|
for (uint32_t i = 0; i < mTimeouts.Length(); ++i) {
|
|
|
|
TimeoutInfo* tmp = mTimeouts[i];
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHandler)
|
2016-08-16 18:11:22 +03:00
|
|
|
}
|
2016-08-16 09:10:30 +03:00
|
|
|
}
|
|
|
|
|
2016-08-16 09:10:30 +03:00
|
|
|
void
|
|
|
|
WorkerPrivate::UnlinkTimeouts()
|
|
|
|
{
|
|
|
|
mTimeouts.Clear();
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
bool
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivate::ModifyBusyCountFromWorker(bool aIncrease)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
// If we're in shutdown then the busy count is no longer being considered so
|
|
|
|
// just return now.
|
|
|
|
if (mStatus >= Killing) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ModifyBusyCountRunnable> runnable =
|
2011-07-17 23:09:13 +04:00
|
|
|
new ModifyBusyCountRunnable(this, aIncrease);
|
2016-02-26 23:23:11 +03:00
|
|
|
return runnable->Dispatch();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2016-03-28 20:28:14 +03:00
|
|
|
WorkerPrivate::AddChildWorker(ParentType* aChildWorker)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2013-03-13 00:33:40 +04:00
|
|
|
#ifdef DEBUG
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2013-03-13 00:33:40 +04:00
|
|
|
Status currentStatus;
|
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
currentStatus = mStatus;
|
2013-03-13 00:33:40 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-03-13 00:33:40 +04:00
|
|
|
MOZ_ASSERT(currentStatus == Running);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2013-03-13 00:33:40 +04:00
|
|
|
#endif
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
|
|
|
|
"Already know about this one!");
|
|
|
|
mChildWorkers.AppendElement(aChildWorker);
|
|
|
|
|
|
|
|
return mChildWorkers.Length() == 1 ?
|
2016-02-26 23:23:12 +03:00
|
|
|
ModifyBusyCountFromWorker(true) :
|
2011-07-17 23:09:13 +04:00
|
|
|
true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-03-28 20:28:14 +03:00
|
|
|
WorkerPrivate::RemoveChildWorker(ParentType* aChildWorker)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
|
|
|
|
"Didn't know about this one!");
|
|
|
|
mChildWorkers.RemoveElement(aChildWorker);
|
|
|
|
|
2016-02-26 23:23:12 +03:00
|
|
|
if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_WARNING("Failed to modify busy count!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2016-08-18 17:11:04 +03:00
|
|
|
WorkerPrivate::AddHolder(WorkerHolder* aHolder, Status aFailStatus)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
2016-08-18 17:11:04 +03:00
|
|
|
if (mStatus >= aFailStatus) {
|
2011-07-17 23:09:13 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-23 11:53:14 +03:00
|
|
|
MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!");
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-05-10 19:27:10 +03:00
|
|
|
if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
|
|
|
|
if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(true)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
mNumHoldersPreventingShutdownStart += 1;
|
2015-06-22 22:13:38 +03:00
|
|
|
}
|
|
|
|
|
2016-06-23 11:53:14 +03:00
|
|
|
mHolders.AppendElement(aHolder);
|
2015-06-22 22:13:38 +03:00
|
|
|
return true;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-06-23 11:53:14 +03:00
|
|
|
WorkerPrivate::RemoveHolder(WorkerHolder* aHolder)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2016-06-23 11:53:14 +03:00
|
|
|
MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!");
|
|
|
|
mHolders.RemoveElement(aHolder);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-05-10 19:27:10 +03:00
|
|
|
if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
|
|
|
|
mNumHoldersPreventingShutdownStart -= 1;
|
|
|
|
if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(false)) {
|
|
|
|
NS_WARNING("Failed to modify busy count!");
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-06-23 11:53:14 +03:00
|
|
|
WorkerPrivate::NotifyHolders(JSContext* aCx, Status aStatus)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2016-02-27 05:15:56 +03:00
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
NS_ASSERTION(aStatus > Running, "Bad status!");
|
|
|
|
|
|
|
|
if (aStatus >= Closing) {
|
2016-02-27 05:15:56 +03:00
|
|
|
CancelAllTimeouts();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-06-23 11:53:14 +03:00
|
|
|
nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
|
2015-04-07 20:31:51 +03:00
|
|
|
while (iter.HasMore()) {
|
2016-08-21 09:41:34 +03:00
|
|
|
WorkerHolder* holder = iter.GetNext();
|
|
|
|
if (!holder->Notify(aStatus)) {
|
|
|
|
NS_WARNING("Failed to notify holder!");
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2016-02-27 05:15:56 +03:00
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<ParentType*, 10> children;
|
2011-07-17 23:09:13 +04:00
|
|
|
children.AppendElements(mChildWorkers);
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < children.Length(); index++) {
|
2016-02-26 00:05:39 +03:00
|
|
|
if (!children[index]->Notify(aStatus)) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_WARNING("Failed to notify child worker!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-02-27 05:15:56 +03:00
|
|
|
WorkerPrivate::CancelAllTimeouts()
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
2012-03-27 08:05:09 +04:00
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
if (mTimerRunning) {
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
NS_ASSERTION(mTimer && mTimerRunnable, "Huh?!");
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
|
|
|
|
|
|
|
|
if (NS_FAILED(mTimer->Cancel())) {
|
|
|
|
NS_WARNING("Failed to cancel timer!");
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
|
2011-07-17 23:09:13 +04:00
|
|
|
mTimeouts[index]->mCanceled = true;
|
|
|
|
}
|
|
|
|
|
2016-02-27 05:15:56 +03:00
|
|
|
// If mRunningExpiredTimeouts, then the fact that they are all canceled now
|
|
|
|
// means that the currently executing RunExpiredTimeouts will deal with
|
|
|
|
// them. Otherwise, we need to clean them up ourselves.
|
|
|
|
if (!mRunningExpiredTimeouts) {
|
|
|
|
mTimeouts.Clear();
|
|
|
|
ModifyBusyCountFromWorker(false);
|
2012-03-27 08:05:09 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-02-27 05:15:56 +03:00
|
|
|
// Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that
|
|
|
|
// if we get reentered under this same RunExpiredTimeouts call we don't
|
|
|
|
// assert above that !mTimeouts().IsEmpty(), because that's clearly false
|
|
|
|
// now.
|
2012-03-27 08:05:09 +04:00
|
|
|
mTimerRunning = false;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2012-03-27 08:05:09 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
else if (!mRunningExpiredTimeouts) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
|
|
|
|
}
|
2012-03-27 08:05:09 +04:00
|
|
|
#endif
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
mTimer = nullptr;
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
mTimerRunnable = nullptr;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
already_AddRefed<nsIEventTarget>
|
2017-01-05 12:05:32 +03:00
|
|
|
WorkerPrivate::CreateNewSyncLoop(Status aFailStatus)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2017-01-05 12:05:32 +03:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mStatus >= aFailStatus) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread());
|
|
|
|
MOZ_ASSERT(thread);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
nsCOMPtr<nsIEventTarget> realEventTarget;
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(thread->PushEventQueue(getter_AddRefs(realEventTarget)));
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<EventTarget> workerEventTarget =
|
2013-10-23 17:16:49 +04:00
|
|
|
new EventTarget(this, realEventTarget);
|
|
|
|
|
|
|
|
{
|
|
|
|
// Modifications must be protected by mMutex in DEBUG builds, see comment
|
|
|
|
// about mSyncLoopStack in WorkerPrivate.h.
|
|
|
|
#ifdef DEBUG
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
|
|
|
|
}
|
|
|
|
|
|
|
|
return workerEventTarget.forget();
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate::RunCurrentSyncLoop()
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
JSContext* cx = GetJSContext();
|
|
|
|
MOZ_ASSERT(cx);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// This should not change between now and the time we finish running this sync
|
|
|
|
// loop.
|
|
|
|
uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex];
|
|
|
|
|
|
|
|
MOZ_ASSERT(loopInfo);
|
|
|
|
MOZ_ASSERT(!loopInfo->mHasRun);
|
|
|
|
MOZ_ASSERT(!loopInfo->mCompleted);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
loopInfo->mHasRun = true;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (!loopInfo->mCompleted) {
|
|
|
|
bool normalRunnablesPending = false;
|
|
|
|
|
|
|
|
// Don't block with the periodic GC timer running.
|
2014-11-17 22:55:37 +03:00
|
|
|
if (!NS_HasPendingEvents(mThread)) {
|
2013-10-23 17:16:49 +04:00
|
|
|
SetGCTimerMode(IdleTimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for something to do.
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
for (;;) {
|
|
|
|
while (mControlQueue.IsEmpty() &&
|
|
|
|
!normalRunnablesPending &&
|
2014-11-17 22:55:37 +03:00
|
|
|
!(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
|
2013-10-23 17:16:49 +04:00
|
|
|
WaitForWorkerEvents();
|
|
|
|
}
|
|
|
|
|
2016-05-11 21:45:58 +03:00
|
|
|
auto result = ProcessAllControlRunnablesLocked();
|
|
|
|
if (result != ProcessAllControlRunnablesResult::Nothing) {
|
|
|
|
// XXXkhuey how should we handle Abort here? See Bug 1003730.
|
|
|
|
|
|
|
|
// The state of the world may have changed. Recheck it.
|
|
|
|
normalRunnablesPending = NS_HasPendingEvents(mThread);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2016-05-11 21:45:58 +03:00
|
|
|
// NB: If we processed a NotifyRunnable, we might have run
|
|
|
|
// non-control runnables, one of which may have shut down the
|
|
|
|
// sync loop.
|
|
|
|
if (loopInfo->mCompleted) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we *didn't* run any control runnables, this should be unchanged.
|
|
|
|
MOZ_ASSERT(!loopInfo->mCompleted);
|
|
|
|
|
|
|
|
if (normalRunnablesPending) {
|
2013-10-23 17:16:49 +04:00
|
|
|
break;
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-14 03:22:53 +04:00
|
|
|
if (normalRunnablesPending) {
|
|
|
|
// Make sure the periodic timer is running before we continue.
|
|
|
|
SetGCTimerMode(PeriodicTimer);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2014-11-17 22:55:37 +03:00
|
|
|
MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2014-01-14 03:22:53 +04:00
|
|
|
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
2015-06-11 00:12:55 +03:00
|
|
|
if (JS::CurrentGlobalOrNull(cx)) {
|
|
|
|
JS_MaybeGC(cx);
|
|
|
|
}
|
2014-01-14 03:22:53 +04:00
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that the stack didn't change underneath us.
|
|
|
|
MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2014-01-11 04:37:47 +04:00
|
|
|
return DestroySyncLoop(currentLoopIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
|
|
|
|
MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
|
|
|
|
|
|
|
|
if (!aThread) {
|
2014-11-17 22:55:37 +03:00
|
|
|
aThread = mThread;
|
2014-01-11 04:37:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// We're about to delete the loop, stash its event target and result.
|
|
|
|
SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex];
|
2013-10-23 17:16:49 +04:00
|
|
|
nsIEventTarget* nestedEventTarget =
|
|
|
|
loopInfo->mEventTarget->GetWeakNestedEventTarget();
|
|
|
|
MOZ_ASSERT(nestedEventTarget);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
bool result = loopInfo->mResult;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
{
|
|
|
|
// Modifications must be protected by mMutex in DEBUG builds, see comment
|
|
|
|
// about mSyncLoopStack in WorkerPrivate.h.
|
2011-07-17 23:09:13 +04:00
|
|
|
#ifdef DEBUG
|
2013-10-23 17:16:49 +04:00
|
|
|
MutexAutoLock lock(mMutex);
|
2011-07-17 23:09:13 +04:00
|
|
|
#endif
|
|
|
|
|
2014-01-11 04:37:47 +04:00
|
|
|
// This will delete |loopInfo|!
|
|
|
|
mSyncLoopStack.RemoveElementAt(aLoopIndex);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(aThread->PopEventQueue(nestedEventTarget));
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2016-07-04 09:18:53 +03:00
|
|
|
if (mSyncLoopStack.IsEmpty() && mPendingEventQueueClearing) {
|
2015-09-29 00:34:28 +03:00
|
|
|
mPendingEventQueueClearing = false;
|
2016-07-06 09:36:54 +03:00
|
|
|
ClearMainEventQueue(WorkerRan);
|
2015-09-29 00:34:28 +03:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
return result;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2013-10-23 17:16:49 +04:00
|
|
|
AssertValidSyncLoop(aSyncLoopTarget);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
|
|
|
|
|
|
|
|
for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
|
|
|
|
nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1];
|
|
|
|
MOZ_ASSERT(loopInfo);
|
|
|
|
MOZ_ASSERT(loopInfo->mEventTarget);
|
|
|
|
|
|
|
|
if (loopInfo->mEventTarget == aSyncLoopTarget) {
|
|
|
|
// Can't assert |loop->mHasRun| here because dispatch failures can cause
|
|
|
|
// us to bail out early.
|
|
|
|
MOZ_ASSERT(!loopInfo->mCompleted);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
loopInfo->mResult = aResult;
|
|
|
|
loopInfo->mCompleted = true;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
loopInfo->mEventTarget->Disable();
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_CRASH("Unknown sync loop!");
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
#ifdef DEBUG
|
2012-12-22 00:14:47 +04:00
|
|
|
void
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
|
2012-12-22 00:14:47 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(aSyncLoopTarget);
|
|
|
|
|
|
|
|
EventTarget* workerTarget;
|
|
|
|
nsresult rv =
|
|
|
|
aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID,
|
|
|
|
reinterpret_cast<void**>(&workerTarget));
|
|
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
MOZ_ASSERT(workerTarget);
|
|
|
|
|
|
|
|
bool valid = false;
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
|
|
|
|
nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index];
|
|
|
|
MOZ_ASSERT(loopInfo);
|
|
|
|
MOZ_ASSERT(loopInfo->mEventTarget);
|
|
|
|
|
|
|
|
if (loopInfo->mEventTarget == aSyncLoopTarget) {
|
|
|
|
valid = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
|
|
|
|
}
|
|
|
|
}
|
2012-12-22 00:14:47 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(valid);
|
2012-12-22 00:14:47 +04:00
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
#endif
|
2012-12-22 00:14:47 +04:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
void
|
|
|
|
WorkerPrivate::PostMessageToParentInternal(
|
|
|
|
JSContext* aCx,
|
|
|
|
JS::Handle<JS::Value> aMessage,
|
2017-02-03 13:00:38 +03:00
|
|
|
const Sequence<JSObject*>& aTransferable,
|
2013-11-05 18:16:26 +04:00
|
|
|
ErrorResult& aRv)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
2017-02-03 13:00:38 +03:00
|
|
|
|
|
|
|
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
|
|
|
|
&transferable);
|
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return;
|
2013-11-05 18:16:26 +04:00
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<MessageEventRunnable> runnable =
|
2015-06-17 13:44:27 +03:00
|
|
|
new MessageEventRunnable(this,
|
2015-09-16 06:27:56 +03:00
|
|
|
WorkerRunnable::ParentThreadUnchangedBusyCount);
|
2011-08-16 07:40:38 +04:00
|
|
|
|
2015-10-22 00:10:05 +03:00
|
|
|
UniquePtr<AbstractTimelineMarker> start;
|
|
|
|
UniquePtr<AbstractTimelineMarker> end;
|
|
|
|
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
|
|
|
bool isTimelineRecording = timelines && !timelines->IsEmpty();
|
|
|
|
|
|
|
|
if (isTimelineRecording) {
|
|
|
|
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
|
|
|
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
|
|
|
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
|
|
|
MarkerTracingType::START);
|
|
|
|
}
|
|
|
|
|
2016-10-24 16:14:45 +03:00
|
|
|
runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
|
2015-10-22 00:10:05 +03:00
|
|
|
|
|
|
|
if (isTimelineRecording) {
|
|
|
|
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
|
|
|
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
|
|
|
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
|
|
|
MarkerTracingType::END);
|
|
|
|
timelines->AddMarkerForAllObservedDocShells(start);
|
|
|
|
timelines->AddMarkerForAllObservedDocShells(end);
|
|
|
|
}
|
|
|
|
|
2015-08-27 19:19:13 +03:00
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
2013-11-05 18:16:26 +04:00
|
|
|
return;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2013-11-05 18:16:26 +04:00
|
|
|
aRv = NS_ERROR_FAILURE;
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2015-03-27 09:17:16 +03:00
|
|
|
void
|
|
|
|
WorkerPrivate::EnterDebuggerEventLoop()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
JSContext* cx = GetJSContext();
|
|
|
|
MOZ_ASSERT(cx);
|
|
|
|
|
|
|
|
uint32_t currentEventLoopLevel = ++mDebuggerEventLoopLevel;
|
|
|
|
|
|
|
|
while (currentEventLoopLevel <= mDebuggerEventLoopLevel) {
|
|
|
|
bool debuggerRunnablesPending = false;
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't block with the periodic GC timer running.
|
|
|
|
if (!debuggerRunnablesPending) {
|
|
|
|
SetGCTimerMode(IdleTimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for something to do
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
while (mControlQueue.IsEmpty() &&
|
|
|
|
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty())) {
|
|
|
|
WaitForWorkerEvents();
|
|
|
|
}
|
|
|
|
|
|
|
|
ProcessAllControlRunnablesLocked();
|
2016-05-11 21:45:58 +03:00
|
|
|
|
|
|
|
// XXXkhuey should we abort JS on the stack here if we got Abort above?
|
2015-03-27 09:17:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (debuggerRunnablesPending) {
|
|
|
|
// Start the periodic GC timer if it is not already running.
|
|
|
|
SetGCTimerMode(PeriodicTimer);
|
|
|
|
|
|
|
|
WorkerRunnable* runnable;
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
mDebuggerQueue.Pop(runnable);
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(runnable);
|
|
|
|
static_cast<nsIRunnable*>(runnable)->Run();
|
|
|
|
runnable->Release();
|
|
|
|
|
2016-03-24 18:12:00 +03:00
|
|
|
// Flush the promise queue.
|
|
|
|
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
|
|
|
|
2015-03-27 09:17:16 +03:00
|
|
|
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
2015-06-11 00:12:55 +03:00
|
|
|
if (JS::CurrentGlobalOrNull(cx)) {
|
|
|
|
JS_MaybeGC(cx);
|
|
|
|
}
|
2015-03-27 09:17:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerPrivate::LeaveDebuggerEventLoop()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mDebuggerEventLoopLevel > 0) {
|
|
|
|
--mDebuggerEventLoopLevel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-20 14:15:59 +03:00
|
|
|
void
|
|
|
|
WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage)
|
|
|
|
{
|
|
|
|
mDebugger->PostMessageToDebugger(aMessage);
|
|
|
|
}
|
|
|
|
|
2015-03-30 14:54:38 +03:00
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler, ErrorResult& aRv)
|
2015-03-30 14:54:38 +03:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<DebuggerImmediateRunnable> runnable =
|
2015-03-30 14:54:38 +03:00
|
|
|
new DebuggerImmediateRunnable(this, aHandler);
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!runnable->Dispatch()) {
|
2015-03-30 14:54:38 +03:00
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-26 22:09:45 +03:00
|
|
|
void
|
|
|
|
WorkerPrivate::ReportErrorToDebugger(const nsAString& aFilename,
|
|
|
|
uint32_t aLineno,
|
|
|
|
const nsAString& aMessage)
|
|
|
|
{
|
|
|
|
mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
bool
|
|
|
|
WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<EventTarget> eventTarget;
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
// Save the old status and set the new status.
|
|
|
|
Status previousStatus;
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mStatus >= aStatus) {
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(!mEventTarget);
|
2011-07-17 23:09:13 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
previousStatus = mStatus;
|
|
|
|
mStatus = aStatus;
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2017-05-03 17:42:43 +03:00
|
|
|
// Mark parent status as closing immediately to avoid new events being
|
|
|
|
// dispatched after we clear the queue below.
|
|
|
|
if (aStatus == Closing) {
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
mEventTarget.swap(eventTarget);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that mStatus > Running, no-one can create a new WorkerEventTarget or
|
|
|
|
// WorkerCrossThreadDispatcher if we don't already have one.
|
|
|
|
if (eventTarget) {
|
|
|
|
// Since we'll no longer process events, make sure we no longer allow anyone
|
|
|
|
// to post them. We have to do this without mMutex held, since our mutex
|
|
|
|
// must be acquired *after* the WorkerEventTarget's mutex when they're both
|
|
|
|
// held.
|
|
|
|
eventTarget->Disable();
|
|
|
|
eventTarget = nullptr;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2011-12-05 11:58:27 +04:00
|
|
|
if (mCrossThreadDispatcher) {
|
|
|
|
// Since we'll no longer process events, make sure we no longer allow
|
2013-10-23 17:16:49 +04:00
|
|
|
// anyone to post them. We have to do this without mMutex held, since our
|
|
|
|
// mutex must be acquired *after* mCrossThreadDispatcher's mutex when
|
|
|
|
// they're both held.
|
2011-12-05 11:58:27 +04:00
|
|
|
mCrossThreadDispatcher->Forget();
|
2013-10-23 17:16:49 +04:00
|
|
|
mCrossThreadDispatcher = nullptr;
|
2011-12-05 11:58:27 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(previousStatus != Pending);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-08-21 09:41:34 +03:00
|
|
|
// Let all our holders know the new status.
|
2016-06-23 11:53:14 +03:00
|
|
|
NotifyHolders(aCx, aStatus);
|
2016-02-27 05:15:56 +03:00
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2012-10-03 06:03:28 +04:00
|
|
|
// If this is the first time our status has changed then we need to clear the
|
|
|
|
// main event queue.
|
|
|
|
if (previousStatus == Running) {
|
2015-09-29 00:34:28 +03:00
|
|
|
// NB: If we're in a sync loop, we can't clear the queue immediately,
|
|
|
|
// because this is the wrong queue. So we have to defer it until later.
|
2016-07-04 09:18:53 +03:00
|
|
|
if (!mSyncLoopStack.IsEmpty()) {
|
2015-09-29 00:34:28 +03:00
|
|
|
mPendingEventQueueClearing = true;
|
|
|
|
} else {
|
|
|
|
ClearMainEventQueue(WorkerRan);
|
|
|
|
}
|
2012-10-03 06:03:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the worker script never ran, or failed to compile, we don't need to do
|
2016-09-01 07:33:05 +03:00
|
|
|
// anything else.
|
2016-04-06 06:12:56 +03:00
|
|
|
if (!GlobalScope()) {
|
2012-10-03 06:03:28 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-03 17:42:43 +03:00
|
|
|
// Don't abort the script.
|
2011-07-17 23:09:13 +04:00
|
|
|
if (aStatus == Closing) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-01 07:33:05 +03:00
|
|
|
MOZ_ASSERT(aStatus == Terminating ||
|
|
|
|
aStatus == Canceling ||
|
|
|
|
aStatus == Killing);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Always abort the script.
|
2011-07-17 23:09:13 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
void
|
|
|
|
WorkerErrorBase::AssignErrorBase(JSErrorBase* aReport)
|
|
|
|
{
|
|
|
|
mFilename = NS_ConvertUTF8toUTF16(aReport->filename);
|
|
|
|
mLineNumber = aReport->lineno;
|
|
|
|
mColumnNumber = aReport->column;
|
|
|
|
mErrorNumber = aReport->errorNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerErrorNote::AssignErrorNote(JSErrorNotes::Note* aNote)
|
|
|
|
{
|
|
|
|
WorkerErrorBase::AssignErrorBase(aNote);
|
|
|
|
xpc::ErrorNote::ErrorNoteToMessageString(aNote, mMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerErrorReport::AssignErrorReport(JSErrorReport* aReport)
|
|
|
|
{
|
|
|
|
WorkerErrorBase::AssignErrorBase(aReport);
|
|
|
|
xpc::ErrorReport::ErrorReportToMessageString(aReport, mMessage);
|
|
|
|
|
|
|
|
mLine.Assign(aReport->linebuf(), aReport->linebufLength());
|
|
|
|
mFlags = aReport->flags;
|
|
|
|
MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT);
|
|
|
|
mExnType = JSExnType(aReport->exnType);
|
|
|
|
mMutedError = aReport->isMuted;
|
|
|
|
|
|
|
|
if (aReport->notes) {
|
|
|
|
if (!mNotes.SetLength(aReport->notes->length(), fallible)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
for (auto&& note : *aReport->notes) {
|
|
|
|
mNotes.ElementAt(i).AssignErrorNote(note.get());
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
void
|
2016-08-14 14:39:31 +03:00
|
|
|
WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
|
2011-07-17 23:09:13 +04:00
|
|
|
JSErrorReport* aReport)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
|
|
|
|
mErrorHandlerRecursionCount == 1,
|
|
|
|
"Bad recursion logic!");
|
|
|
|
|
2016-08-29 19:30:51 +03:00
|
|
|
JS::Rooted<JS::Value> exn(aCx);
|
|
|
|
if (!JS_GetPendingException(aCx, &exn)) {
|
|
|
|
// Probably shouldn't actually happen? But let's go ahead and just use null
|
|
|
|
// for lack of anything better.
|
|
|
|
exn.setNull();
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
JS_ClearPendingException(aCx);
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
WorkerErrorReport report;
|
2011-07-17 23:09:13 +04:00
|
|
|
if (aReport) {
|
2017-02-15 17:53:07 +03:00
|
|
|
report.AssignErrorReport(aReport);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
else {
|
2017-02-15 17:53:07 +03:00
|
|
|
report.mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
if (report.mMessage.IsEmpty() && aToStringResult) {
|
2016-08-14 14:39:31 +03:00
|
|
|
nsDependentCString toStringResult(aToStringResult.c_str());
|
2017-02-15 17:53:07 +03:00
|
|
|
if (!AppendUTF8toUTF16(toStringResult, report.mMessage, mozilla::fallible)) {
|
2016-03-10 12:50:56 +03:00
|
|
|
// Try again, with only a 1 KB string. Do this infallibly this time.
|
|
|
|
// If the user doesn't have 1 KB to spare we're done anyways.
|
2016-08-14 14:39:31 +03:00
|
|
|
uint32_t index = std::min(uint32_t(1024), toStringResult.Length());
|
|
|
|
|
|
|
|
// Drop the last code point that may be cropped.
|
|
|
|
index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
|
|
|
|
|
|
|
|
nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
|
|
|
|
index);
|
2017-02-15 17:53:07 +03:00
|
|
|
AppendUTF8toUTF16(truncatedToStringResult, report.mMessage);
|
2016-03-10 12:50:56 +03:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
mErrorHandlerRecursionCount++;
|
|
|
|
|
|
|
|
// Don't want to run the scope's error handler if this is a recursive error or
|
2016-09-01 07:33:05 +03:00
|
|
|
// if we ran out of memory.
|
2011-07-17 23:09:13 +04:00
|
|
|
bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
|
2017-02-15 17:53:07 +03:00
|
|
|
report.mErrorNumber != JSMSG_OUT_OF_MEMORY &&
|
2015-06-11 00:12:55 +03:00
|
|
|
JS::CurrentGlobalOrNull(aCx);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2017-02-15 17:53:07 +03:00
|
|
|
ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, report, 0,
|
|
|
|
exn);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
mErrorHandlerRecursionCount--;
|
|
|
|
}
|
|
|
|
|
2016-04-12 02:41:00 +03:00
|
|
|
// static
|
|
|
|
void
|
|
|
|
WorkerPrivate::ReportErrorToConsole(const char* aMessage)
|
|
|
|
{
|
|
|
|
WorkerPrivate* wp = nullptr;
|
|
|
|
if (!NS_IsMainThread()) {
|
|
|
|
wp = GetCurrentThreadWorkerPrivate();
|
|
|
|
}
|
|
|
|
|
|
|
|
ReportErrorToConsoleRunnable::Report(wp, aMessage);
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
int32_t
|
|
|
|
WorkerPrivate::SetTimeout(JSContext* aCx,
|
2016-08-16 09:10:30 +03:00
|
|
|
nsIScriptTimeoutHandler* aHandler,
|
|
|
|
int32_t aTimeout, bool aIsInterval,
|
2013-11-05 18:16:26 +04:00
|
|
|
ErrorResult& aRv)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2016-08-16 09:10:30 +03:00
|
|
|
MOZ_ASSERT(aHandler);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
const int32_t timerId = mNextTimeoutId++;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
Status currentStatus;
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
currentStatus = mStatus;
|
|
|
|
}
|
|
|
|
|
2012-03-27 08:05:09 +04:00
|
|
|
// If the worker is trying to call setTimeout/setInterval and the parent
|
|
|
|
// thread has initiated the close process then just silently fail.
|
|
|
|
if (currentStatus >= Closing) {
|
2013-11-05 18:16:26 +04:00
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return 0;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
|
|
|
|
newInfo->mIsInterval = aIsInterval;
|
|
|
|
newInfo->mId = timerId;
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_WARNING("Timeout ids overflowed!");
|
|
|
|
mNextTimeoutId = 1;
|
|
|
|
}
|
|
|
|
|
2016-08-16 09:10:30 +03:00
|
|
|
newInfo->mHandler = aHandler;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
// See if any of the optional arguments were passed.
|
2013-11-05 18:16:26 +04:00
|
|
|
aTimeout = std::max(0, aTimeout);
|
|
|
|
newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
nsAutoPtr<TimeoutInfo>* insertedInfo =
|
|
|
|
mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts));
|
2011-07-17 23:09:13 +04:00
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n",
|
|
|
|
this, aTimeout, aIsInterval ? "yes" : "no"));
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
// If the timeout we just made is set to fire next then we need to update the
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
// timer, unless we're currently running timeouts.
|
|
|
|
if (insertedInfo == mTimeouts.Elements() && !mRunningExpiredTimeouts) {
|
2011-07-17 23:09:13 +04:00
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
if (!mTimer) {
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
|
2011-07-17 23:09:13 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
2013-11-05 18:16:26 +04:00
|
|
|
aRv.Throw(rv);
|
|
|
|
return 0;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2013-10-23 17:16:49 +04:00
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
mTimerRunnable = new TimerRunnable(this);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!mTimerRunning) {
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!ModifyBusyCountFromWorker(true)) {
|
2013-11-05 18:16:26 +04:00
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return 0;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
mTimerRunning = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!RescheduleTimeoutTimer(aCx)) {
|
2013-11-05 18:16:26 +04:00
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return 0;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
return timerId;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
void
|
|
|
|
WorkerPrivate::ClearTimeout(int32_t aId)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
if (!mTimeouts.IsEmpty()) {
|
|
|
|
NS_ASSERTION(mTimerRunning, "Huh?!");
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
|
2011-07-17 23:09:13 +04:00
|
|
|
nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
|
|
|
|
if (info->mId == aId) {
|
|
|
|
info->mCanceled = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
// We may be called recursively (e.g. close() inside a timeout) or we could
|
|
|
|
// have been canceled while this event was pending, bail out if there is
|
|
|
|
// nothing to do.
|
|
|
|
if (mRunningExpiredTimeouts || !mTimerRunning) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
NS_ASSERTION(mTimer && mTimerRunnable, "Must have a timer!");
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
|
|
|
|
|
|
|
|
bool retval = true;
|
|
|
|
|
2011-08-26 11:34:10 +04:00
|
|
|
AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
|
2013-11-11 12:04:41 +04:00
|
|
|
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
// We want to make sure to run *something*, even if the timer fired a little
|
|
|
|
// early. Fudge the value of now to at least include the first timeout.
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
const TimeStamp actual_now = TimeStamp::Now();
|
|
|
|
const TimeStamp now = std::max(actual_now, mTimeouts[0]->mTargetTime);
|
|
|
|
|
|
|
|
if (now != actual_now) {
|
|
|
|
LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this,
|
|
|
|
(now - actual_now).ToMilliseconds()));
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<TimeoutInfo*, 10> expiredTimeouts;
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
|
2011-07-17 23:09:13 +04:00
|
|
|
nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
|
2011-08-26 11:34:10 +04:00
|
|
|
if (info->mTargetTime > now) {
|
|
|
|
break;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2011-08-26 11:34:10 +04:00
|
|
|
expiredTimeouts.AppendElement(info);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Guard against recursion.
|
|
|
|
mRunningExpiredTimeouts = true;
|
|
|
|
|
|
|
|
// Run expired timeouts.
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
|
2011-07-17 23:09:13 +04:00
|
|
|
TimeoutInfo*& info = expiredTimeouts[index];
|
|
|
|
|
|
|
|
if (info->mCanceled) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n",
|
|
|
|
this, info->mInterval.ToMilliseconds()));
|
|
|
|
|
2016-02-26 23:23:13 +03:00
|
|
|
// Always check JS_IsExceptionPending if something fails, and if
|
|
|
|
// JS_IsExceptionPending returns false (i.e. uncatchable exception) then
|
2011-08-26 11:34:10 +04:00
|
|
|
// break out of the loop.
|
2016-02-26 23:23:13 +03:00
|
|
|
const char *reason;
|
|
|
|
if (info->mIsInterval) {
|
|
|
|
reason = "setInterval handler";
|
|
|
|
} else {
|
|
|
|
reason = "setTimeout handler";
|
|
|
|
}
|
|
|
|
|
2016-08-16 09:10:30 +03:00
|
|
|
RefPtr<Function> callback = info->mHandler->GetCallback();
|
|
|
|
if (!callback) {
|
|
|
|
// scope for the AutoEntryScript, so it comes off the stack before we do
|
2016-02-26 23:23:13 +03:00
|
|
|
// Promise::PerformMicroTaskCheckpoint.
|
2016-07-08 03:08:25 +03:00
|
|
|
AutoEntryScript aes(global, reason, false);
|
2016-08-16 09:10:30 +03:00
|
|
|
|
|
|
|
// Evaluate the timeout expression.
|
2016-09-05 22:25:13 +03:00
|
|
|
const nsAString& script = info->mHandler->GetHandlerText();
|
2016-08-16 09:10:30 +03:00
|
|
|
|
|
|
|
const char* filename = nullptr;
|
|
|
|
uint32_t lineNo = 0, dummyColumn = 0;
|
|
|
|
info->mHandler->GetLocation(&filename, &lineNo, &dummyColumn);
|
|
|
|
|
|
|
|
JS::CompileOptions options(aes.cx());
|
|
|
|
options.setFileAndLine(filename, lineNo).setNoScriptRval(true);
|
|
|
|
|
|
|
|
JS::Rooted<JS::Value> unused(aes.cx());
|
|
|
|
|
2016-09-05 22:25:13 +03:00
|
|
|
if (!JS::Evaluate(aes.cx(), options, script.BeginReading(),
|
2016-08-16 09:10:30 +03:00
|
|
|
script.Length(), &unused) &&
|
|
|
|
!JS_IsExceptionPending(aCx)) {
|
|
|
|
retval = false;
|
|
|
|
break;
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2016-08-16 09:10:30 +03:00
|
|
|
} else {
|
|
|
|
ErrorResult rv;
|
|
|
|
JS::Rooted<JS::Value> ignoredVal(aCx);
|
|
|
|
callback->Call(GlobalScope(), info->mHandler->GetArgs(), &ignoredVal, rv,
|
|
|
|
reason);
|
|
|
|
if (rv.IsUncatchableException()) {
|
|
|
|
rv.SuppressException();
|
|
|
|
retval = false;
|
|
|
|
break;
|
2013-11-05 18:16:26 +04:00
|
|
|
}
|
2016-08-16 09:10:30 +03:00
|
|
|
|
|
|
|
rv.SuppressException();
|
2013-11-05 18:16:26 +04:00
|
|
|
}
|
2011-08-26 11:34:10 +04:00
|
|
|
|
2015-05-07 21:49:31 +03:00
|
|
|
// Since we might be processing more timeouts, go ahead and flush
|
|
|
|
// the promise queue now before we do that.
|
2016-03-24 18:12:00 +03:00
|
|
|
Promise::PerformWorkerMicroTaskCheckpoint();
|
2015-05-07 21:49:31 +03:00
|
|
|
|
2012-03-27 08:05:09 +04:00
|
|
|
NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// No longer possible to be called recursively.
|
|
|
|
mRunningExpiredTimeouts = false;
|
|
|
|
|
2011-08-26 11:34:10 +04:00
|
|
|
// Now remove canceled and expired timeouts from the main list.
|
2012-08-10 20:10:22 +04:00
|
|
|
// NB: The timeouts present in expiredTimeouts must have the same order
|
|
|
|
// with respect to each other in mTimeouts. That is, mTimeouts is just
|
|
|
|
// expiredTimeouts with extra elements inserted. There may be unexpired
|
|
|
|
// timeouts that have been inserted between the expired timeouts if the
|
|
|
|
// timeout event handler called setTimeout/setInterval.
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0, expiredTimeoutIndex = 0,
|
2012-08-10 20:10:22 +04:00
|
|
|
expiredTimeoutLength = expiredTimeouts.Length();
|
|
|
|
index < mTimeouts.Length(); ) {
|
2011-08-26 11:34:10 +04:00
|
|
|
nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
|
2012-08-10 20:10:22 +04:00
|
|
|
if ((expiredTimeoutIndex < expiredTimeoutLength &&
|
|
|
|
info == expiredTimeouts[expiredTimeoutIndex] &&
|
|
|
|
++expiredTimeoutIndex) ||
|
|
|
|
info->mCanceled) {
|
|
|
|
if (info->mIsInterval && !info->mCanceled) {
|
|
|
|
// Reschedule intervals.
|
|
|
|
info->mTargetTime = info->mTargetTime + info->mInterval;
|
|
|
|
// Don't resort the list here, we'll do that at the end.
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mTimeouts.RemoveElement(info);
|
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
2011-08-26 11:34:10 +04:00
|
|
|
else {
|
2012-08-10 20:10:22 +04:00
|
|
|
// If info did not match the current entry in expiredTimeouts, it
|
|
|
|
// shouldn't be there at all.
|
|
|
|
NS_ASSERTION(!expiredTimeouts.Contains(info),
|
|
|
|
"Our timeouts are out of order!");
|
|
|
|
++index;
|
2011-08-26 11:34:10 +04:00
|
|
|
}
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
|
2012-08-10 20:10:22 +04:00
|
|
|
mTimeouts.Sort(comparator);
|
|
|
|
|
2012-03-27 08:05:09 +04:00
|
|
|
// Either signal the parent that we're no longer using timeouts or reschedule
|
|
|
|
// the timer.
|
2011-07-17 23:09:13 +04:00
|
|
|
if (mTimeouts.IsEmpty()) {
|
2016-02-26 23:23:12 +03:00
|
|
|
if (!ModifyBusyCountFromWorker(false)) {
|
2011-07-17 23:09:13 +04:00
|
|
|
retval = false;
|
|
|
|
}
|
|
|
|
mTimerRunning = false;
|
|
|
|
}
|
|
|
|
else if (retval && !RescheduleTimeoutTimer(aCx)) {
|
|
|
|
retval = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
MOZ_ASSERT(!mRunningExpiredTimeouts);
|
2011-07-17 23:09:13 +04:00
|
|
|
NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
NS_ASSERTION(mTimer && mTimerRunnable, "Should have a timer!");
|
|
|
|
|
|
|
|
// NB: This is important! The timer may have already fired, e.g. if a timeout
|
|
|
|
// callback itself calls setTimeout for a short duration and then takes longer
|
|
|
|
// than that to finish executing. If that has happened, it's very important
|
|
|
|
// that we don't execute the event that is now pending in our event queue, or
|
|
|
|
// our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an
|
|
|
|
// early timeout when we execute the event we're about to queue.
|
|
|
|
mTimer->Cancel();
|
2011-07-17 23:09:13 +04:00
|
|
|
|
|
|
|
double delta =
|
|
|
|
(mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
|
2013-01-15 16:22:03 +04:00
|
|
|
uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2016-12-16 06:16:31 +03:00
|
|
|
LOG(TimeoutsLog(), ("Worker %p scheduled timer for %d ms, %" PRIuSIZE " pending timeouts\n",
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
this, delay, mTimeouts.Length()));
|
|
|
|
|
|
|
|
nsresult rv = mTimer->InitWithCallback(mTimerRunnable, delay, nsITimer::TYPE_ONE_SHOT);
|
2011-07-17 23:09:13 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
2016-08-14 14:39:28 +03:00
|
|
|
JS_ReportErrorASCII(aCx, "Failed to start timer!");
|
2011-07-17 23:09:13 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-07-07 09:15:15 +03:00
|
|
|
WorkerPrivate::UpdateContextOptionsInternal(
|
2014-02-26 13:25:36 +04:00
|
|
|
JSContext* aCx,
|
2016-07-07 09:15:15 +03:00
|
|
|
const JS::ContextOptions& aContextOptions)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2016-07-07 09:15:15 +03:00
|
|
|
JS::ContextOptionsRef(aCx) = aContextOptions;
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
2016-07-07 09:15:15 +03:00
|
|
|
mChildWorkers[index]->UpdateContextOptions(aContextOptions);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-05 18:26:34 +04:00
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivate::UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages)
|
2014-09-05 18:26:34 +04:00
|
|
|
{
|
|
|
|
WorkerGlobalScope* globalScope = GlobalScope();
|
|
|
|
if (globalScope) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
|
2014-09-05 18:26:34 +04:00
|
|
|
if (nav) {
|
|
|
|
nav->SetLanguages(aLanguages);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
2016-02-26 23:23:12 +03:00
|
|
|
mChildWorkers[index]->UpdateLanguages(aLanguages);
|
2014-09-05 18:26:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-24 23:27:15 +04:00
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivate::UpdatePreferenceInternal(WorkerPreference aPref, bool aValue)
|
2013-11-24 23:27:15 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
|
|
|
|
|
|
|
|
mPreferences[aPref] = aValue;
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
2016-02-26 23:23:12 +03:00
|
|
|
mChildWorkers[index]->UpdatePreference(aPref, aValue);
|
2013-11-24 23:27:15 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-04 23:11:32 +04:00
|
|
|
void
|
2013-01-11 02:50:40 +04:00
|
|
|
WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
|
|
|
|
JSGCParamKey aKey,
|
|
|
|
uint32_t aValue)
|
2012-01-04 23:11:32 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
2013-05-17 02:49:43 +04:00
|
|
|
|
|
|
|
// XXX aValue might be 0 here (telling us to unset a previous value for child
|
|
|
|
// workers). Calling JS_SetGCParameter with a value of 0 isn't actually
|
|
|
|
// supported though. We really need some way to revert to a default value
|
|
|
|
// here.
|
|
|
|
if (aValue) {
|
2016-07-05 15:35:21 +03:00
|
|
|
JS_SetGCParameter(aCx, aKey, aValue);
|
2013-05-17 02:49:43 +04:00
|
|
|
}
|
2012-01-04 23:11:32 +04:00
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
2016-02-26 23:23:12 +03:00
|
|
|
mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue);
|
2012-01-04 23:11:32 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
#ifdef JS_GC_ZEAL
|
|
|
|
void
|
2013-05-17 02:49:43 +04:00
|
|
|
WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
|
|
|
|
uint32_t aFrequency)
|
2011-07-17 23:09:13 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2016-07-05 12:06:05 +03:00
|
|
|
JS_SetGCZeal(aCx, aGCZeal, aFrequency);
|
2011-07-17 23:09:13 +04:00
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
2016-02-26 23:23:12 +03:00
|
|
|
mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
|
2011-07-17 23:09:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-01-18 00:05:25 +04:00
|
|
|
void
|
|
|
|
WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
|
|
|
|
bool aCollectChildren)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-06-11 00:12:55 +03:00
|
|
|
if (!GlobalScope()) {
|
2013-10-23 17:16:49 +04:00
|
|
|
// We haven't compiled anything yet. Just bail out.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-25 23:03:27 +04:00
|
|
|
if (aShrinking || aCollectChildren) {
|
2016-07-07 10:55:41 +03:00
|
|
|
JS::PrepareForFullGC(aCx);
|
2013-07-25 23:03:27 +04:00
|
|
|
|
2013-05-17 02:49:43 +04:00
|
|
|
if (aShrinking) {
|
2016-07-07 10:55:41 +03:00
|
|
|
JS::GCForReason(aCx, GC_SHRINK, JS::gcreason::DOM_WORKER);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
if (!aCollectChildren) {
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
2013-05-17 02:49:43 +04:00
|
|
|
}
|
|
|
|
else {
|
2016-07-07 10:55:41 +03:00
|
|
|
JS::GCForReason(aCx, GC_NORMAL, JS::gcreason::DOM_WORKER);
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
|
2013-05-17 02:49:43 +04:00
|
|
|
}
|
2012-01-18 00:05:25 +04:00
|
|
|
}
|
|
|
|
else {
|
2013-05-17 02:49:43 +04:00
|
|
|
JS_MaybeGC(aCx);
|
Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent
RunExpiredTimeouts has "fudging" code to always ensure that we execute at least one timeout. This is intended to cover cases where an nsITimer fires slightly early, but it means we must be careful not to fire a timer more times than we intend to or we'll execute a timeout prematurely.
Consider a sequences of setTimeout calls alternating in delay between 0ms and 1000ms. When the 1000ms timeout fires, it schedules a 0ms timeout. The setTimeout call itself calls RescheduleTimeoutTimer, which schedules the timer for a 0 ms delay. And once we unwind the 1000ms timeout RunExpiredTimeouts will also schedule the timer for a 0 ms delay. If the timer has fired (remember, it's processed on a completely different thread) in the meantime, we ultimately will get two callbacks from nsITimer for our 0 ms timeout. The first will run the 0 ms timeout and schedule a 1000 ms timeout, and the second will run the 1000 ms timeout (remember, RunExpiredTimeouts always runs at least one timeout!) ~999 ms ahead of schedule.
The solution is to cancel the timer in RescheduleTimeoutTimer, so that when we call it the second time it will cause any pending events from the first scheduling to be canceled. But this actually doesn't work at all, because of how we use nsITimer. Before worker threads were capable of accepting arbitrary runnables we created TimerThreadEventTarget, which translates the timer firing to the special worker event queue when the timer thread attempts to *dispatch* a runnable to the worker. We still need this for some of the other types of timers (which use control runnables that interrupt JS, and not the regular event queue). But setTimeout can simply run like a normal nsITimer callback now. We need that here, or calling nsITimer::Cancel won't actually do anything, because the timer's event was ignored and TimerThreadEventTarget created its own event.
2016-01-07 00:18:29 +03:00
|
|
|
LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
|
2012-01-18 00:05:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aCollectChildren) {
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
2016-02-26 23:23:12 +03:00
|
|
|
mChildWorkers[index]->GarbageCollect(aShrinking);
|
2012-01-18 00:05:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-03 08:07:02 +04:00
|
|
|
void
|
2016-02-26 23:23:12 +03:00
|
|
|
WorkerPrivate::CycleCollectInternal(bool aCollectChildren)
|
2013-12-03 08:07:02 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
nsCycleCollector_collect(nullptr);
|
|
|
|
|
|
|
|
if (aCollectChildren) {
|
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
2016-02-26 23:23:12 +03:00
|
|
|
mChildWorkers[index]->CycleCollect(/* dummy = */ false);
|
2013-12-03 08:07:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-24 00:55:07 +03:00
|
|
|
void
|
|
|
|
WorkerPrivate::MemoryPressureInternal()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
RefPtr<Console> console = mScope ? mScope->GetConsoleIfExists() : nullptr;
|
|
|
|
if (console) {
|
|
|
|
console->ClearStorage();
|
|
|
|
}
|
|
|
|
|
|
|
|
console = mDebuggerScope ? mDebuggerScope->GetConsoleIfExists() : nullptr;
|
|
|
|
if (console) {
|
|
|
|
console->ClearStorage();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
|
|
|
|
mChildWorkers[index]->MemoryPressure(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-23 01:48:00 +04:00
|
|
|
void
|
2014-11-17 22:55:37 +03:00
|
|
|
WorkerPrivate::SetThread(WorkerThread* aThread)
|
2013-06-23 01:48:00 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
if (aThread) {
|
2014-11-17 22:55:37 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
bool isOnCurrentThread;
|
|
|
|
MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
|
|
|
|
MOZ_ASSERT(isOnCurrentThread);
|
|
|
|
}
|
|
|
|
#endif
|
2013-06-23 01:48:00 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(!mPRThread);
|
|
|
|
mPRThread = PRThreadFromThread(aThread);
|
|
|
|
MOZ_ASSERT(mPRThread);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MOZ_ASSERT(mPRThread);
|
|
|
|
}
|
2013-06-23 01:48:00 +04:00
|
|
|
|
2014-11-17 22:55:37 +03:00
|
|
|
const WorkerThreadFriendKey friendKey;
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerThread> doomedThread;
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
{ // Scope so that |doomedThread| is released without holding the lock.
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (aThread) {
|
|
|
|
MOZ_ASSERT(!mThread);
|
|
|
|
MOZ_ASSERT(mStatus == Pending);
|
|
|
|
|
|
|
|
mThread = aThread;
|
2014-11-17 22:55:37 +03:00
|
|
|
mThread->SetWorker(friendKey, this);
|
2013-10-23 17:16:49 +04:00
|
|
|
|
|
|
|
if (!mPreStartRunnables.IsEmpty()) {
|
|
|
|
for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
|
2016-03-28 20:28:15 +03:00
|
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
|
|
mThread->DispatchAnyThread(friendKey, mPreStartRunnables[index].forget()));
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
mPreStartRunnables.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
MOZ_ASSERT(mThread);
|
2014-11-17 22:55:37 +03:00
|
|
|
|
|
|
|
mThread->SetWorker(friendKey, nullptr);
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
mThread.swap(doomedThread);
|
|
|
|
}
|
|
|
|
}
|
2013-06-23 01:48:00 +04:00
|
|
|
}
|
|
|
|
|
2011-12-05 11:58:27 +04:00
|
|
|
WorkerCrossThreadDispatcher*
|
|
|
|
WorkerPrivate::GetCrossThreadDispatcher()
|
|
|
|
{
|
2013-06-05 18:04:23 +04:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
2011-12-05 11:58:27 +04:00
|
|
|
if (!mCrossThreadDispatcher && mStatus <= Running) {
|
|
|
|
mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2011-12-05 11:58:27 +04:00
|
|
|
return mCrossThreadDispatcher;
|
|
|
|
}
|
|
|
|
|
2012-12-30 22:21:52 +04:00
|
|
|
void
|
|
|
|
WorkerPrivate::BeginCTypesCall()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Don't try to GC while we're blocked in a ctypes call.
|
|
|
|
SetGCTimerMode(NoTimer);
|
2012-12-30 22:21:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WorkerPrivate::EndCTypesCall()
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Make sure the periodic timer is running before we start running JS again.
|
|
|
|
SetGCTimerMode(PeriodicTimer);
|
2012-12-30 22:21:52 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
bool
|
2015-09-16 06:27:56 +03:00
|
|
|
WorkerPrivate::ConnectMessagePort(JSContext* aCx,
|
|
|
|
MessagePortIdentifier& aIdentifier)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
WorkerGlobalScope* globalScope = GlobalScope();
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
|
|
|
|
MOZ_ASSERT(jsGlobal);
|
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
// This MessagePortIdentifier is used to create a new port, still connected
|
|
|
|
// with the other one, but in the worker thread.
|
|
|
|
ErrorResult rv;
|
2016-03-01 17:21:11 +03:00
|
|
|
RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
|
2015-09-16 06:27:56 +03:00
|
|
|
if (NS_WARN_IF(rv.Failed())) {
|
2016-07-22 17:50:10 +03:00
|
|
|
rv.SuppressException();
|
2015-09-16 06:27:56 +03:00
|
|
|
return false;
|
|
|
|
}
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
GlobalObject globalObject(aCx, jsGlobal);
|
|
|
|
if (globalObject.Failed()) {
|
2013-06-05 18:04:23 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-22 00:42:50 +04:00
|
|
|
RootedDictionary<MessageEventInit> init(aCx);
|
2013-11-22 00:39:43 +04:00
|
|
|
init.mBubbles = false;
|
|
|
|
init.mCancelable = false;
|
2013-12-20 12:51:03 +04:00
|
|
|
init.mSource.SetValue().SetAsMessagePort() = port;
|
2016-10-26 23:00:17 +03:00
|
|
|
if (!init.mPorts.AppendElement(port.forget(), fallible)) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-11-22 00:39:43 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<MessageEvent> event =
|
2014-06-16 20:52:00 +04:00
|
|
|
MessageEvent::Constructor(globalObject,
|
2014-02-27 14:51:14 +04:00
|
|
|
NS_LITERAL_STRING("connect"), init, rv);
|
2013-11-05 18:16:26 +04:00
|
|
|
|
|
|
|
event->SetTrusted(true);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2013-11-05 18:16:26 +04:00
|
|
|
nsEventStatus dummy = nsEventStatus_eIgnore;
|
|
|
|
globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
|
2015-09-15 21:08:09 +03:00
|
|
|
|
2015-09-16 06:27:56 +03:00
|
|
|
return true;
|
2015-09-15 21:08:09 +03:00
|
|
|
}
|
|
|
|
|
2015-03-04 02:51:53 +03:00
|
|
|
WorkerGlobalScope*
|
|
|
|
WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
|
2013-11-05 18:16:26 +04:00
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
2015-03-04 02:51:53 +03:00
|
|
|
if (!mScope) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerGlobalScope> globalScope;
|
2015-03-04 02:51:53 +03:00
|
|
|
if (IsSharedWorker()) {
|
2015-10-01 02:11:03 +03:00
|
|
|
globalScope = new SharedWorkerGlobalScope(this, WorkerName());
|
2015-03-04 02:51:53 +03:00
|
|
|
} else if (IsServiceWorker()) {
|
2017-03-25 02:56:48 +03:00
|
|
|
globalScope = new ServiceWorkerGlobalScope(this, ServiceWorkerScope());
|
2015-03-04 02:51:53 +03:00
|
|
|
} else {
|
2017-05-17 17:48:54 +03:00
|
|
|
globalScope = new DedicatedWorkerGlobalScope(this, WorkerName());
|
2015-03-04 02:51:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> global(aCx);
|
|
|
|
NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
|
|
|
|
|
|
|
|
JSAutoCompartment ac(aCx, global);
|
|
|
|
|
2015-06-11 00:12:55 +03:00
|
|
|
// RegisterBindings() can spin a nested event loop so we have to set mScope
|
|
|
|
// before calling it, and we have to make sure to unset mScope if it fails.
|
|
|
|
mScope = Move(globalScope);
|
|
|
|
|
2015-03-04 02:51:53 +03:00
|
|
|
if (!RegisterBindings(aCx, global)) {
|
2015-06-11 00:12:55 +03:00
|
|
|
mScope = nullptr;
|
2015-03-04 02:51:53 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_FireOnNewGlobalObject(aCx, global);
|
2013-11-05 18:16:26 +04:00
|
|
|
}
|
|
|
|
|
2015-03-04 02:51:53 +03:00
|
|
|
return mScope;
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkerDebuggerGlobalScope*
|
|
|
|
WorkerPrivate::CreateDebuggerGlobalScope(JSContext* aCx)
|
|
|
|
{
|
|
|
|
AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mDebuggerScope);
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerDebuggerGlobalScope> globalScope =
|
2015-03-04 02:51:53 +03:00
|
|
|
new WorkerDebuggerGlobalScope(this);
|
|
|
|
|
2015-01-09 00:56:42 +03:00
|
|
|
JS::Rooted<JSObject*> global(aCx);
|
2015-03-04 02:51:53 +03:00
|
|
|
NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
|
2013-11-05 18:16:26 +04:00
|
|
|
|
|
|
|
JSAutoCompartment ac(aCx, global);
|
|
|
|
|
2016-02-22 12:41:09 +03:00
|
|
|
// RegisterDebuggerBindings() can spin a nested event loop so we have to set
|
|
|
|
// mDebuggerScope before calling it, and we have to make sure to unset
|
|
|
|
// mDebuggerScope if it fails.
|
|
|
|
mDebuggerScope = Move(globalScope);
|
|
|
|
|
|
|
|
if (!RegisterDebuggerBindings(aCx, global)) {
|
|
|
|
mDebuggerScope = nullptr;
|
2013-11-05 18:16:26 +04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_FireOnNewGlobalObject(aCx, global);
|
|
|
|
|
2015-03-04 02:51:53 +03:00
|
|
|
return mDebuggerScope;
|
2013-11-05 18:16:26 +04:00
|
|
|
}
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
#ifdef DEBUG
|
2013-10-23 17:16:49 +04:00
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
void
|
2013-10-23 17:16:49 +04:00
|
|
|
WorkerPrivate::AssertIsOnWorkerThread() const
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
// This is much more complicated than it needs to be but we can't use mThread
|
|
|
|
// because it must be protected by mMutex and sometimes this method is called
|
|
|
|
// when mMutex is already locked. This method should always work.
|
|
|
|
MOZ_ASSERT(mPRThread,
|
|
|
|
"AssertIsOnWorkerThread() called before a thread was assigned!");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIThread> thread;
|
|
|
|
nsresult rv =
|
2016-06-10 09:04:49 +03:00
|
|
|
nsThreadManager::get().GetThreadFromPRThread(mPRThread,
|
|
|
|
getter_AddRefs(thread));
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
MOZ_ASSERT(thread);
|
|
|
|
|
|
|
|
bool current;
|
|
|
|
rv = thread->IsOnCurrentThread(¤t);
|
|
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
MOZ_ASSERT(current, "Wrong thread!");
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable)
|
|
|
|
|
2013-06-05 18:04:23 +04:00
|
|
|
template <class Derived>
|
2013-10-23 17:16:49 +04:00
|
|
|
NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget)
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget)
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
#ifdef DEBUG
|
|
|
|
// kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
|
|
|
|
// result.
|
|
|
|
if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
|
|
|
|
*aInstancePtr = this;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerPrivateParent<Derived>::
|
2015-07-10 06:21:46 +03:00
|
|
|
EventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> event(aRunnable);
|
|
|
|
return Dispatch(event.forget(), aFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Derived>
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerPrivateParent<Derived>::
|
2016-06-01 03:04:54 +03:00
|
|
|
EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
// May be called on any thread!
|
2015-07-10 06:21:46 +03:00
|
|
|
nsCOMPtr<nsIRunnable> event(aRunnable);
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Workers only support asynchronous dispatch for now.
|
|
|
|
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<WorkerRunnable> workerRunnable;
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
NS_WARNING("A runnable was posted to a worker that is already shutting "
|
|
|
|
"down!");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:21:46 +03:00
|
|
|
if (event) {
|
|
|
|
workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());
|
2013-10-23 17:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv =
|
2015-07-10 06:21:46 +03:00
|
|
|
mWorkerPrivate->DispatchPrivate(workerRunnable.forget(), mNestedEventTarget);
|
2013-10-23 17:16:49 +04:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2016-05-13 01:15:43 +03:00
|
|
|
template <class Derived>
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerPrivateParent<Derived>::
|
2016-06-01 03:04:54 +03:00
|
|
|
EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
|
2016-05-13 01:15:43 +03:00
|
|
|
{
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
template <class Derived>
|
|
|
|
NS_IMETHODIMP
|
|
|
|
WorkerPrivateParent<Derived>::
|
|
|
|
EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
|
2013-06-05 18:04:23 +04:00
|
|
|
{
|
2013-10-23 17:16:49 +04:00
|
|
|
// May be called on any thread!
|
2013-06-05 18:04:23 +04:00
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
MOZ_ASSERT(aIsOnCurrentThread);
|
|
|
|
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (!mWorkerPrivate) {
|
|
|
|
NS_WARNING("A worker's event target was used after the worker has !");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
2013-06-05 18:04:23 +04:00
|
|
|
}
|
|
|
|
|
2011-08-05 02:01:45 +04:00
|
|
|
BEGIN_WORKERS_NAMESPACE
|
|
|
|
|
2013-11-05 18:16:24 +04:00
|
|
|
WorkerCrossThreadDispatcher*
|
2016-09-11 12:15:24 +03:00
|
|
|
GetWorkerCrossThreadDispatcher(JSContext* aCx, const JS::Value& aWorker)
|
2013-11-05 18:16:24 +04:00
|
|
|
{
|
|
|
|
if (!aWorker.isObject()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkerPrivate* w = nullptr;
|
2013-11-21 16:51:16 +04:00
|
|
|
UNWRAP_OBJECT(Worker, &aWorker.toObject(), w);
|
2013-11-05 18:16:24 +04:00
|
|
|
MOZ_ASSERT(w);
|
|
|
|
return w->GetCrossThreadDispatcher();
|
|
|
|
}
|
|
|
|
|
2013-10-23 17:16:49 +04:00
|
|
|
// Force instantiation.
|
|
|
|
template class WorkerPrivateParent<WorkerPrivate>;
|
|
|
|
|
2011-07-17 23:09:13 +04:00
|
|
|
END_WORKERS_NAMESPACE
|