author | Eddy Bruël <ejpbruel@gmail.com> |
Wed, 29 Oct 2014 21:11:33 +0100 | |
changeset 213012 | e30de6729affd6aa103bbccb40be280af22cad93 |
parent 213011 | 0f69fbc0d65d60b169b4d1d3f87359c753344a5e |
child 213013 | a009be3f978a5493b4451ed51caf640d6c5bb520 |
push id | 27738 |
push user | cbook@mozilla.com |
push date | Thu, 30 Oct 2014 13:46:07 +0000 |
treeherder | mozilla-central@1aa1b23d799e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | khuey |
bugs | 757133 |
milestone | 36.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/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2176,18 +2176,16 @@ WorkerPrivateParent<Derived>::DisableDeb if (!self->mDebugger) { return; } if (NS_FAILED(UnregisterWorkerDebugger(self->mDebugger))) { NS_WARNING("Failed to unregister worker debugger!"); } - - self->mDebugger = nullptr; } template <class Derived> nsresult WorkerPrivateParent<Derived>::DispatchControlRunnable( WorkerControlRunnable* aWorkerControlRunnable) { // May be called on any thread! @@ -3499,46 +3497,163 @@ WorkerDebugger::WorkerDebugger(WorkerPri mIsEnabled(false) { mWorkerPrivate->AssertIsOnParentThread(); } WorkerDebugger::~WorkerDebugger() { MOZ_ASSERT(!mWorkerPrivate); + MOZ_ASSERT(!mIsEnabled); + + if (!NS_IsMainThread()) { + nsCOMPtr<nsIThread> mainThread; + if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) { + NS_WARNING("Failed to proxy release of listeners, leaking instead!"); + } + + for (size_t index = 0; index < mListeners.Length(); ++index) { + nsIWorkerDebuggerListener* listener = nullptr; + mListeners[index].forget(&listener); + if (NS_FAILED(NS_ProxyRelease(mainThread, listener))) { + NS_WARNING("Failed to proxy release of listener, leaking instead!"); + } + } + } } NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger) NS_IMETHODIMP WorkerDebugger::GetIsClosed(bool* aResult) { AssertIsOnMainThread(); MutexAutoLock lock(mMutex); *aResult = !mWorkerPrivate; return NS_OK; } NS_IMETHODIMP +WorkerDebugger::GetIsChrome(bool* aResult) +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + *aResult = mWorkerPrivate->IsChromeWorker(); + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::GetParent(nsIWorkerDebugger** aResult) +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + 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(); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + *aResult = mWorkerPrivate->Type(); + return NS_OK; +} + +NS_IMETHODIMP WorkerDebugger::GetUrl(nsAString& aResult) { AssertIsOnMainThread(); MutexAutoLock lock(mMutex); if (!mWorkerPrivate) { return NS_ERROR_UNEXPECTED; } aResult = mWorkerPrivate->ScriptURL(); return NS_OK; } +NS_IMETHODIMP +WorkerDebugger::GetWindow(nsIDOMWindow** aResult) +{ + AssertIsOnMainThread(); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + if (mWorkerPrivate->GetParent() || !mWorkerPrivate->IsDedicatedWorker()) { + *aResult = nullptr; + return NS_OK; + } + + nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow(); + window.forget(aResult); + return NS_OK; +} + +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; +} + void WorkerDebugger::WaitIsEnabled(bool aIsEnabled) { MutexAutoLock lock(mMutex); while (mIsEnabled != aIsEnabled) { mCondVar.Wait(); } @@ -3571,16 +3686,25 @@ WorkerDebugger::Disable() { AssertIsOnMainThread(); MutexAutoLock lock(mMutex); MOZ_ASSERT(mWorkerPrivate); mWorkerPrivate = nullptr; + { + MutexAutoUnlock unlock(mMutex); + + nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnClose(); + } + } + NotifyIsEnabled(false); } WorkerPrivate::WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, WorkerType aWorkerType, const nsACString& aSharedWorkerName,
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -59,16 +59,18 @@ class AutoSyncLoopHolder; class MessagePort; class SharedWorker; class WorkerControlRunnable; class WorkerGlobalScope; class WorkerPrivate; class WorkerRunnable; class WorkerDebugger; +// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to +// be updated too. enum WorkerType { WorkerTypeDedicated, WorkerTypeShared, WorkerTypeService }; // SharedMutex is a small wrapper around an (internal) reference-counted Mutex @@ -658,16 +660,22 @@ public: // The ability to be a chrome worker is orthogonal to the type of // worker [Dedicated|Shared|Service]. bool IsChromeWorker() const { return mIsChromeWorker; } + WorkerType + Type() const + { + return mWorkerType; + } + bool IsDedicatedWorker() const { return mWorkerType == WorkerTypeDedicated; } bool IsSharedWorker() const @@ -732,16 +740,19 @@ public: class WorkerDebugger : public nsIWorkerDebugger { mozilla::Mutex mMutex; mozilla::CondVar mCondVar; // Protected by mMutex WorkerPrivate* mWorkerPrivate; bool mIsEnabled; + // Only touched on the main thread. + nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners; + public: explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIWORKERDEBUGGER void AssertIsOnParentThread(); @@ -863,16 +874,24 @@ public: static bool WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */); static nsresult GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, LoadInfo* aLoadInfo); + WorkerDebugger* + Debugger() const + { + AssertIsOnMainThread(); + MOZ_ASSERT(mDebugger); + return mDebugger; + } + void DoRunLoop(JSContext* aCx); bool InterruptCallback(JSContext* aCx); nsresult IsOnCurrentThread(bool* aIsOnCurrentThread);
--- a/dom/workers/nsIWorkerDebugger.idl +++ b/dom/workers/nsIWorkerDebugger.idl @@ -1,9 +1,33 @@ #include "nsISupports.idl" +interface nsIDOMWindow; + +[scriptable, uuid(54fd2dd3-c01b-4f71-888f-462f37a54f57)] +interface nsIWorkerDebuggerListener : nsISupports +{ + void onClose(); +}; + [scriptable, builtinclass, uuid(0833b363-bffe-4cdb-ad50-1c4563e0C8ff)] interface nsIWorkerDebugger : nsISupports { + const unsigned long TYPE_DEDICATED = 0; + const unsigned long TYPE_SHARED = 1; + const unsigned long TYPE_SERVICE = 2; + readonly attribute bool isClosed; + readonly attribute bool isChrome; + + readonly attribute nsIWorkerDebugger parent; + + readonly attribute unsigned long type; + readonly attribute DOMString url; + + readonly attribute nsIDOMWindow window; + + void addListener(in nsIWorkerDebuggerListener listener); + + void removeListener(in nsIWorkerDebuggerListener listener); };
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +onmessage = function () {};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_parentWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger_childWorker.js");
new file mode 100644 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_sharedWorker.js @@ -0,0 +1,11 @@ +"use strict"; + +onconnect = function (event) { + event.ports[0].onmessage = function (event) { + switch (event.data) { + case "close": + close(); + break; + } + }; +};
--- a/dom/workers/test/chrome.ini +++ b/dom/workers/test/chrome.ini @@ -1,12 +1,15 @@ [DEFAULT] support-files = WorkerDebuggerManager_childWorker.js WorkerDebuggerManager_parentWorker.js + WorkerDebugger_childWorker.js + WorkerDebugger_parentWorker.js + WorkerDebugger_sharedWorker.js WorkerTest.jsm WorkerTest_subworker.js WorkerTest_worker.js chromeWorker_subworker.js chromeWorker_worker.js dom_worker_helper.js file_url.jsm file_worker.js @@ -19,16 +22,17 @@ support-files = fileSlice_worker.js fileSubWorker_worker.js file_worker.js jsm_url_worker.js workersDisabled_worker.js file_url.jsm bug1062920_worker.js +[test_WorkerDebugger.xul] [test_WorkerDebuggerManager.xul] [test_bug883784.jsm] [test_bug883784.xul] [test_chromeWorker.xul] [test_chromeWorkerJSM.xul] [test_extension.xul] [test_extensionBootstrap.xul] [test_file.xul]
--- a/dom/workers/test/dom_worker_helper.js +++ b/dom/workers/test/dom_worker_helper.js @@ -79,16 +79,30 @@ function waitForUnregister(predicate = ( } wdm.removeListener(this); resolve(dbg); } }); }); } +function waitForDebuggerClose(dbg, predicate = () => true) { + return new Promise(function (resolve) { + dbg.addListener({ + onClose: function () { + if (!predicate()) { + return; + } + dbg.removeListener(this); + resolve(); + } + }); + }); +} + function waitForMultiple(promises) { return new Promise(function (resolve) { let results = []; for (let i = 0; i < promises.length; ++i) { let promise = promises[i]; let index = i; promise.then(function (result) { is(results.length, index, "events should occur in the specified order");
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.xul @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for WorkerDebugger" + 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 PARENT_WORKER_URL = "WorkerDebugger_parentWorker.js"; + const CHILD_WORKER_URL = "WorkerDebugger_childWorker.js"; + const SHARED_WORKER_URL = "WorkerDebugger_sharedWorker.js"; + + function test() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + let promise = waitForMultiple([ + waitForRegister((dbg) => dbg.url === PARENT_WORKER_URL), + waitForRegister((dbg) => dbg.url === CHILD_WORKER_URL), + ]); + worker = new ChromeWorker(PARENT_WORKER_URL); + let dbgs = yield promise; + is(dbgs[0].isChrome, true, "debugger should be for chrome worker"); + is(dbgs[0].parent, null, + "debugger for a top-level worker should not have parent"); + is(dbgs[0].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED, + "debugger should be for dedicated worker"); + is(dbgs[0].window, window, + "debugger for top-level dedicated worker should have window"); + is(dbgs[1].isChrome, false, "debugger should be for content worker"); + is(dbgs[1].parent, dbgs[0], + "debugger for child worker should have parent"); + is(dbgs[1].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED); + is(dbgs[1].window, null, + "debugger for non-top-level worker should not have window"); + + promise = waitForMultiple([ + waitForUnregister((dbg) => dbg.url === CHILD_WORKER_URL), + waitForDebuggerClose(dbgs[1]), + waitForUnregister((dbg) => dbg.url === PARENT_WORKER_URL), + waitForDebuggerClose(dbgs[0]), + ]); + worker.terminate(); + yield promise; + + promise = waitForRegister(); + worker = new SharedWorker(SHARED_WORKER_URL); + let dbg = yield promise; + is(dbg.isChrome, false, "debugger should be for content worker"); + is(dbg.parent, null, + "debugger for top-level worker should not have parent"); + is(dbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED, + "debugger should be for shared worker"); + is(dbg.window, null, + "debugger for non-dedicated worker should not have window"); + + let listener = { + onRegistered: function () { + ok(false, + "debugger for shared worker should not be registered twice"); + }, + }; + wdm.addListener(listener); + worker = new SharedWorker(SHARED_WORKER_URL); + + dbg.addListener({ + onClose: function () { + is(dbg.isClosed, true, "debugger should be closed"); + wdm.removeListener(listener); + dbg.removeListener(this); + SimpleTest.finish(); + } + }); + worker.port.start(); + worker.port.postMessage("close"); + }); + } + + ]]> + </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>