author | Eddy Bruël <ejpbruel@gmail.com> |
Fri, 27 Mar 2015 07:17:16 +0100 | |
changeset 264927 | 3b30137b0f6aef6f8b17bae314a175f727a66e66 |
parent 264926 | cc3988bc43b6a6d86a4a02df13eb4f06bb54dcee |
child 264928 | 1c4030c686d6cf5d143be795c6228368729581c5 |
push id | 4718 |
push user | raliiev@mozilla.com |
push date | Mon, 11 May 2015 18:39:53 +0000 |
treeherder | mozilla-beta@c20c4ef55f08 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | khuey |
bugs | 1092102 |
milestone | 39.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/webidl/WorkerDebuggerGlobalScope.webidl +++ b/dom/webidl/WorkerDebuggerGlobalScope.webidl @@ -2,16 +2,20 @@ /* 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/. */ [Global=(WorkerDebugger), Exposed=WorkerDebugger] interface WorkerDebuggerGlobalScope : EventTarget { readonly attribute object global; + void enterEventLoop(); + + void leaveEventLoop(); + void postMessage(DOMString message); attribute EventHandler onmessage; void reportError(DOMString message); }; // So you can debug while you debug
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -4578,16 +4578,17 @@ WorkerPrivate::WorkerPrivate(JSContext* bool aIsChromeWorker, WorkerType aWorkerType, const nsACString& aSharedWorkerName, WorkerLoadInfo& aLoadInfo) : WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL, aIsChromeWorker, aWorkerType, aSharedWorkerName, aLoadInfo) , mJSContext(nullptr) , mPRThread(nullptr) + , mDebuggerEventLoopLevel(0) , mErrorHandlerRecursionCount(0) , mNextTimeoutId(1) , mStatus(Pending) , mSuspended(false) , mTimerRunning(false) , mRunningExpiredTimeouts(false) , mCloseHandlerStarted(false) , mCloseHandlerFinished(false) @@ -6182,16 +6183,86 @@ WorkerPrivate::PostMessageToParentMessag return; } PostMessageToParentInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial, aRv); } 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(); + } + + 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(); + + // Now *might* be a good time to GC. Let the JS engine make the decision. + JS_MaybeGC(cx); + } + } +} + +void +WorkerPrivate::LeaveDebuggerEventLoop() +{ + AssertIsOnWorkerThread(); + + MutexAutoLock lock(mMutex); + + if (mDebuggerEventLoopLevel > 0) { + --mDebuggerEventLoopLevel; + } +} + +void WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage) { mDebugger->PostMessageToDebugger(aMessage); } void WorkerPrivate::ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -810,16 +810,17 @@ class WorkerPrivate : public WorkerPriva PRThread* mPRThread; // Things touched on worker thread only. nsRefPtr<WorkerGlobalScope> mScope; nsRefPtr<WorkerDebuggerGlobalScope> mDebuggerScope; nsTArray<ParentType*> mChildWorkers; nsTArray<WorkerFeature*> mFeatures; nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts; + uint32_t mDebuggerEventLoopLevel; struct SyncLoopInfo { explicit SyncLoopInfo(EventTarget* aEventTarget); nsRefPtr<EventTarget> mEventTarget; bool mCompleted; bool mResult; @@ -971,16 +972,22 @@ public: PostMessageToParentMessagePort( JSContext* aCx, uint64_t aMessagePortSerial, JS::Handle<JS::Value> aMessage, const Optional<Sequence<JS::Value>>& aTransferable, ErrorResult& aRv); void + EnterDebuggerEventLoop(); + + void + LeaveDebuggerEventLoop(); + + void PostMessageToDebugger(const nsAString& aMessage); void ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno, const nsAString& aMessage); bool NotifyInternal(JSContext* aCx, Status aStatus);
--- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -509,16 +509,28 @@ WorkerDebuggerGlobalScope::WrapGlobalObj void WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aGlobal) { aGlobal.set(mWorkerPrivate->GetOrCreateGlobalScope(aCx)->GetWrapper()); } void +WorkerDebuggerGlobalScope::EnterEventLoop() +{ + mWorkerPrivate->EnterDebuggerEventLoop(); +} + +void +WorkerDebuggerGlobalScope::LeaveEventLoop() +{ + mWorkerPrivate->LeaveDebuggerEventLoop(); +} + +void WorkerDebuggerGlobalScope::PostMessage(const nsAString& aMessage) { mWorkerPrivate->PostMessageToDebugger(aMessage); } void WorkerDebuggerGlobalScope::ReportError(JSContext* aCx, const nsAString& aMessage)
--- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -260,16 +260,22 @@ public: { return GetWrapper(); } void GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aGlobal); void + EnterEventLoop(); + + void + LeaveEventLoop(); + + void PostMessage(const nsAString& aMessage); IMPL_EVENT_HANDLER(message) void ReportError(JSContext* aCx, const nsAString& aMessage); void
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js @@ -0,0 +1,14 @@ +"use strict"; + +function f() { + debugger; +} + +self.onmessage = function (event) { + switch (event.data) { + case "ping": + debugger; + postMessage("pong"); + break; + }; +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js @@ -0,0 +1,29 @@ +"use strict"; + +let frames = []; + +var dbg = new Debugger(global); +dbg.onDebuggerStatement = function (frame) { + frames.push(frame); + postMessage("paused"); + enterEventLoop(); + frames.pop(); + postMessage("resumed"); +}; + +this.onmessage = function (event) { + switch (event.data) { + case "eval": + frames[frames.length - 1].eval("f()"); + postMessage("evalled"); + break; + + case "ping": + postMessage("pong"); + break; + + case "resume": + leaveEventLoop(); + break; + }; +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js @@ -0,0 +1,25 @@ +"use strict"; + +function f() { + debugger; +} + +var worker = new Worker("WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js"); + +worker.onmessage = function (event) { + postMessage("child:" + event.data); +}; + +self.onmessage = function (event) { + var message = event.data; + if (message.indexOf(":") >= 0) { + worker.postMessage(message.split(":")[1]); + return; + } + switch (message) { + case "ping": + debugger; + postMessage("pong"); + break; + }; +};
--- a/dom/workers/test/chrome.ini +++ b/dom/workers/test/chrome.ini @@ -2,16 +2,19 @@ skip-if = buildapp == 'b2g' support-files = WorkerDebugger.initialize_childWorker.js WorkerDebugger.initialize_debugger.js WorkerDebugger.initialize_worker.js WorkerDebugger.postMessage_childWorker.js WorkerDebugger.postMessage_debugger.js WorkerDebugger.postMessage_worker.js + WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js + WorkerDebuggerGlobalScope.enterEventLoop_debugger.js + WorkerDebuggerGlobalScope.enterEventLoop_worker.js WorkerDebuggerGlobalScope.reportError_childWorker.js WorkerDebuggerGlobalScope.reportError_debugger.js WorkerDebuggerGlobalScope.reportError_worker.js WorkerDebuggerManager_childWorker.js WorkerDebuggerManager_worker.js WorkerDebugger_childWorker.js WorkerDebugger_worker.js WorkerDebugger_sharedWorker.js @@ -35,16 +38,17 @@ support-files = jsm_url_worker.js workersDisabled_worker.js file_url.jsm bug1062920_worker.js [test_WorkerDebugger.xul] [test_WorkerDebugger.initialize.xul] [test_WorkerDebugger.postMessage.xul] +[test_WorkerDebuggerGlobalScope.enterEventLoop.xul] [test_WorkerDebuggerGlobalScope.reportError.xul] [test_WorkerDebuggerManager.xul] [test_bug883784.jsm] [test_bug883784.xul] [test_chromeWorker.xul] [test_chromeWorkerJSM.xul] [test_extension.xul] [test_extensionBootstrap.xul]
--- a/dom/workers/test/test_WorkerDebugger.postMessage.xul +++ b/dom/workers/test/test_WorkerDebugger.postMessage.xul @@ -28,24 +28,24 @@ "debuggers to be registered, and initialize them."); let promise = waitForMultiple([ waitForRegister(WORKER_URL, DEBUGGER_URL), waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL) ]); let worker = new Worker(WORKER_URL); let [dbg, childDbg] = yield promise; - info("Check that posting a ping message to the worker debugger " + - "results in a pong message being received."); + info("Send a request to the worker debugger. This should cause a " + + "response to be received from the worker debugger."); promise = waitForDebuggerMessage(dbg, "pong"); dbg.postMessage("ping"); yield promise; - info("Check that posting a ping message to the child worker " + - "debugger results in a pong message being received."); + info("Send a request to the child worker debugger. This should cause " + + "a response to be received from the child worker debugger."); promise = waitForDebuggerMessage(childDbg, "pong"); childDbg.postMessage("ping"); yield promise; SimpleTest.finish(); }); }
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul @@ -0,0 +1,124 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebuggerGlobalScope.enterEventLoop" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + <script type="application/javascript" src="dom_worker_helper.js"/> + + <script type="application/javascript"> + <![CDATA[ + + const WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_worker.js"; + const CHILD_WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js"; + const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.enterEventLoop_debugger.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + info("Create a worker that creates a child worker, wait for their " + + "debuggers to be registered, and initialize them."); + let promise = waitForMultiple([ + waitForRegister(WORKER_URL, DEBUGGER_URL), + waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL) + ]); + let worker = new Worker(WORKER_URL); + let [dbg, childDbg] = yield promise; + + info("Send a request to the child worker. This should cause a nested " + + "event loop to be entered."); + promise = waitForDebuggerMessage(childDbg, "paused"); + worker.postMessage("child:ping"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "a second nested event loop to be entered."); + promise = waitForDebuggerMessage(childDbg, "paused"); + childDbg.postMessage("eval"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "the second nested event loop to be left. Check that a response " + + "for the previous request is not received from the child worker " + + "debugger until after the event loop is left."); + promise = waitForMultiple([ + waitForDebuggerMessage(childDbg, "resumed"), + waitForDebuggerMessage(childDbg, "evalled") + ]); + childDbg.postMessage("resume"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "the first nested event loop to be left. Check that a response " + + "for the previous request is not received from the child worker " + + "until after the event loop is left."); + promise = waitForMultiple([ + waitForDebuggerMessage(childDbg, "resumed"), + waitForWorkerMessage(worker, "child:pong") + ]); + childDbg.postMessage("resume"); + yield promise; + + info("Send a request to the worker. This should cause a nested event " + + "loop to be entered."); + promise = waitForDebuggerMessage(dbg, "paused"); + worker.postMessage("ping"); + yield promise; + + info("Terminate the worker. This should not cause the worker " + + "debugger to stop running."); + worker.terminate(); + + worker.onmessage = function () { + ok(false, "Worker should have been terminated."); + }; + + info("Send a request to the worker debugger. This should cause a " + + "second nested event loop to be entered."); + promise = waitForDebuggerMessage(dbg, "paused"); + dbg.postMessage("eval"); + yield promise; + + info("Send a request to the worker debugger. This should cause the " + + "second nested event loop to be left. Check that a response for " + + "the previous request is not received from the worker " + + "debugger until after the event loop is left."); + promise = waitForMultiple([ + waitForDebuggerMessage(dbg, "resumed"), + waitForDebuggerMessage(dbg, "evalled") + ]); + dbg.postMessage("resume"); + yield promise; + + info("Send a request to the child worker debugger. This should cause " + + "the first nested event loop to be left. No response for the " + + "previous request should be received from the worker debugger, " + + "since it has been terminated."); + promise = waitForMultiple([ + waitForDebuggerMessage(dbg, "resumed"), + ]); + dbg.postMessage("resume"); + yield promise; + + SimpleTest.finish(); + }); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + </body> + <label id="test-result"/> +</window>