diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index ec956fe7fcb..37c25e9ff20 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2181,8 +2181,6 @@ WorkerPrivateParent::DisableDebugger() if (NS_FAILED(UnregisterWorkerDebugger(self->mDebugger))) { NS_WARNING("Failed to unregister worker debugger!"); } - - self->mDebugger = nullptr; } template @@ -3504,6 +3502,22 @@ WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate) WorkerDebugger::~WorkerDebugger() { MOZ_ASSERT(!mWorkerPrivate); + MOZ_ASSERT(!mIsEnabled); + + if (!NS_IsMainThread()) { + nsCOMPtr 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) @@ -3519,6 +3533,60 @@ WorkerDebugger::GetIsClosed(bool* aResult) 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 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) { @@ -3534,6 +3602,53 @@ WorkerDebugger::GetUrl(nsAString& aResult) 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 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) { @@ -3576,6 +3691,15 @@ WorkerDebugger::Disable() MOZ_ASSERT(mWorkerPrivate); mWorkerPrivate = nullptr; + { + MutexAutoUnlock unlock(mMutex); + + nsTArray> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnClose(); + } + } + NotifyIsEnabled(false); } diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 5e1f07bc514..c3b18465683 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -64,6 +64,8 @@ class WorkerPrivate; class WorkerRunnable; class WorkerDebugger; +// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to +// be updated too. enum WorkerType { WorkerTypeDedicated, @@ -663,6 +665,12 @@ public: return mIsChromeWorker; } + WorkerType + Type() const + { + return mWorkerType; + } + bool IsDedicatedWorker() const { @@ -737,6 +745,9 @@ class WorkerDebugger : public nsIWorkerDebugger { WorkerPrivate* mWorkerPrivate; bool mIsEnabled; + // Only touched on the main thread. + nsTArray> mListeners; + public: explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate); @@ -868,6 +879,14 @@ public: const nsAString& aScriptURL, bool aIsChromeWorker, LoadInfo* aLoadInfo); + WorkerDebugger* + Debugger() const + { + AssertIsOnMainThread(); + MOZ_ASSERT(mDebugger); + return mDebugger; + } + void DoRunLoop(JSContext* aCx); diff --git a/dom/workers/nsIWorkerDebugger.idl b/dom/workers/nsIWorkerDebugger.idl index 83213f2e140..deef971f754 100644 --- 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); }; diff --git a/dom/workers/test/WorkerDebugger_childWorker.js b/dom/workers/test/WorkerDebugger_childWorker.js new file mode 100644 index 00000000000..18f086ce67f --- /dev/null +++ b/dom/workers/test/WorkerDebugger_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebugger_parentWorker.js b/dom/workers/test/WorkerDebugger_parentWorker.js new file mode 100644 index 00000000000..229e9bc446e --- /dev/null +++ b/dom/workers/test/WorkerDebugger_parentWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger_childWorker.js"); diff --git a/dom/workers/test/WorkerDebugger_sharedWorker.js b/dom/workers/test/WorkerDebugger_sharedWorker.js new file mode 100644 index 00000000000..d4f95eb20f2 --- /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; + } + }; +}; diff --git a/dom/workers/test/chrome.ini b/dom/workers/test/chrome.ini index 339d821ef2a..0aedd05e376 100644 --- a/dom/workers/test/chrome.ini +++ b/dom/workers/test/chrome.ini @@ -2,6 +2,9 @@ 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 @@ -24,6 +27,7 @@ support-files = file_url.jsm bug1062920_worker.js +[test_WorkerDebugger.xul] [test_WorkerDebuggerManager.xul] [test_bug883784.jsm] [test_bug883784.xul] diff --git a/dom/workers/test/dom_worker_helper.js b/dom/workers/test/dom_worker_helper.js index 706ac46a631..9cbfa589169 100644 --- a/dom/workers/test/dom_worker_helper.js +++ b/dom/workers/test/dom_worker_helper.js @@ -84,6 +84,20 @@ function waitForUnregister(predicate = () => true) { }); } +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 = []; diff --git a/dom/workers/test/test_WorkerDebugger.xul b/dom/workers/test/test_WorkerDebugger.xul new file mode 100644 index 00000000000..77c5e71a02b --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.xul @@ -0,0 +1,98 @@ + + + + + + + +

+ +

+  
+