# HG changeset patch # User Andrea Marchesini # Date 1517303546 -3600 # Node ID a21f55ba5530649e029b615c835eaffed7799e28 # Parent 0894869ef55942ce139b898992e972493fb5f39b Bug 1413112 - Separate files for WorkerDebugger, r=bkelly diff --git a/dom/serviceworkers/ServiceWorkerPrivate.cpp b/dom/serviceworkers/ServiceWorkerPrivate.cpp --- a/dom/serviceworkers/ServiceWorkerPrivate.cpp +++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp @@ -17,16 +17,17 @@ #include "nsISupportsImpl.h" #include "nsITimedChannel.h" #include "nsIUploadChannel2.h" #include "nsNetUtil.h" #include "nsProxyRelease.h" #include "nsQueryObject.h" #include "nsStreamUtils.h" #include "nsStringStream.h" +#include "WorkerDebugger.h" #include "WorkerRunnable.h" #include "WorkerScope.h" #include "mozilla/Assertions.h" #include "mozilla/dom/Client.h" #include "mozilla/dom/ClientIPCTypes.h" #include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/FetchUtil.h" #include "mozilla/dom/IndexedDatabaseManager.h" diff --git a/dom/workers/ScriptLoader.h b/dom/workers/ScriptLoader.h --- a/dom/workers/ScriptLoader.h +++ b/dom/workers/ScriptLoader.h @@ -20,16 +20,18 @@ class nsIChannel; namespace mozilla { class ErrorResult; } // namespace mozilla BEGIN_WORKERS_NAMESPACE +struct WorkerLoadInfo; + enum WorkerScriptType { WorkerScript, DebuggerScript }; namespace scriptloader { nsresult diff --git a/dom/workers/WorkerDebugger.cpp b/dom/workers/WorkerDebugger.cpp new file mode 100644 --- /dev/null +++ b/dom/workers/WorkerDebugger.cpp @@ -0,0 +1,477 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "WorkerDebugger.h" + +#include "mozilla/dom/MessageEvent.h" +#include "mozilla/dom/MessageEventBinding.h" +#include "nsProxyRelease.h" +#include "nsQueryObject.h" +#include "nsThreadUtils.h" +#include "ScriptLoader.h" +#include "WorkerCommon.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" +#include "WorkerScope.h" + +namespace mozilla { +namespace dom { +namespace workers { + +namespace { + +class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable +{ + nsString mMessage; + +public: + DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate, + const nsAString& aMessage) + : WorkerDebuggerRunnable(aWorkerPrivate), + mMessage(aMessage) + { + } + +private: + virtual bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + WorkerDebuggerGlobalScope* globalScope = aWorkerPrivate->DebuggerGlobalScope(); + MOZ_ASSERT(globalScope); + + JS::Rooted message(aCx, JS_NewUCStringCopyN(aCx, mMessage.get(), + mMessage.Length())); + if (!message) { + return false; + } + JS::Rooted data(aCx, JS::StringValue(message)); + + RefPtr event = new MessageEvent(globalScope, nullptr, + nullptr); + event->InitMessageEvent(nullptr, + NS_LITERAL_STRING("message"), + false, // canBubble + true, // cancelable + data, + EmptyString(), + EmptyString(), + nullptr, + Sequence>()); + event->SetTrusted(true); + + nsCOMPtr domEvent = do_QueryObject(event); + bool dummy; + globalScope->DispatchEvent(domEvent, &dummy); + return true; + } +}; + +class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable +{ + nsString mScriptURL; + +public: + CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate, + const nsAString& aScriptURL) + : WorkerDebuggerRunnable(aWorkerPrivate), + mScriptURL(aScriptURL) + { } + +private: + virtual bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + aWorkerPrivate->AssertIsOnWorkerThread(); + + WorkerDebuggerGlobalScope* globalScope = + aWorkerPrivate->CreateDebuggerGlobalScope(aCx); + if (!globalScope) { + NS_WARNING("Failed to make global!"); + return false; + } + + if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) { + return false; + } + + JS::Rooted global(aCx, globalScope->GetWrapper()); + + ErrorResult rv; + JSAutoCompartment ac(aCx, global); + scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL, + DebuggerScript, rv); + rv.WouldReportJSException(); + // 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; + } + // 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. + if (rv.MaybeSetPendingException(aCx)) { + return false; + } + + return true; + } +}; + +} // anonymous + +class WorkerDebugger::PostDebuggerMessageRunnable final : public Runnable +{ + WorkerDebugger *mDebugger; + nsString mMessage; + +public: + PostDebuggerMessageRunnable(WorkerDebugger* aDebugger, + const nsAString& aMessage) + : mozilla::Runnable("PostDebuggerMessageRunnable") + , mDebugger(aDebugger) + , mMessage(aMessage) + { + } + +private: + ~PostDebuggerMessageRunnable() + { } + + NS_IMETHOD + Run() override + { + mDebugger->PostMessageToDebuggerOnMainThread(mMessage); + + return NS_OK; + } +}; + +class WorkerDebugger::ReportDebuggerErrorRunnable final : public Runnable +{ + WorkerDebugger *mDebugger; + nsString mFilename; + uint32_t mLineno; + nsString mMessage; + +public: + ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger, + const nsAString& aFilename, + uint32_t aLineno, + const nsAString& aMessage) + : Runnable("ReportDebuggerErrorRunnable") + , mDebugger(aDebugger) + , mFilename(aFilename) + , mLineno(aLineno) + , mMessage(aMessage) + { + } + +private: + ~ReportDebuggerErrorRunnable() + { } + + NS_IMETHOD + Run() override + { + mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage); + + return NS_OK; + } +}; + +WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate) +: mWorkerPrivate(aWorkerPrivate), + mIsInitialized(false) +{ + AssertIsOnMainThread(); +} + +WorkerDebugger::~WorkerDebugger() +{ + MOZ_ASSERT(!mWorkerPrivate); + + if (!NS_IsMainThread()) { + for (size_t index = 0; index < mListeners.Length(); ++index) { + NS_ReleaseOnMainThreadSystemGroup( + "WorkerDebugger::mListeners", mListeners[index].forget()); + } + } +} + +NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger) + +NS_IMETHODIMP +WorkerDebugger::GetIsClosed(bool* aResult) +{ + AssertIsOnMainThread(); + + *aResult = !mWorkerPrivate; + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::GetIsChrome(bool* aResult) +{ + AssertIsOnMainThread(); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + *aResult = mWorkerPrivate->IsChromeWorker(); + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::GetIsInitialized(bool* aResult) +{ + AssertIsOnMainThread(); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + *aResult = mIsInitialized; + return NS_OK; +} + +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 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; +} + +NS_IMETHODIMP +WorkerDebugger::GetUrl(nsAString& aResult) +{ + AssertIsOnMainThread(); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + aResult = mWorkerPrivate->ScriptURL(); + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::GetWindow(mozIDOMWindow** aResult) +{ + AssertIsOnMainThread(); + + 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::GetPrincipal(nsIPrincipal** aResult) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aResult); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr 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; +} + +NS_IMETHODIMP +WorkerDebugger::Initialize(const nsAString& aURL) +{ + AssertIsOnMainThread(); + + if (!mWorkerPrivate) { + return NS_ERROR_UNEXPECTED; + } + + if (!mIsInitialized) { + RefPtr runnable = + new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL); + if (!runnable->Dispatch()) { + return NS_ERROR_FAILURE; + } + + mIsInitialized = true; + } + + return NS_OK; +} + +NS_IMETHODIMP +WorkerDebugger::PostMessageMoz(const nsAString& aMessage) +{ + AssertIsOnMainThread(); + + if (!mWorkerPrivate || !mIsInitialized) { + return NS_ERROR_UNEXPECTED; + } + + RefPtr runnable = + new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage); + if (!runnable->Dispatch()) { + return NS_ERROR_FAILURE; + } + + 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::Close() +{ + MOZ_ASSERT(mWorkerPrivate); + mWorkerPrivate = nullptr; + + nsTArray> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnClose(); + } +} + +void +WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage) +{ + mWorkerPrivate->AssertIsOnWorkerThread(); + + RefPtr runnable = + new PostDebuggerMessageRunnable(this, aMessage); + if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) { + NS_WARNING("Failed to post message to debugger on main thread!"); + } +} + +void +WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage) +{ + AssertIsOnMainThread(); + + nsTArray> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnMessage(aMessage); + } +} + +void +WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename, + uint32_t aLineno, + const nsAString& aMessage) +{ + mWorkerPrivate->AssertIsOnWorkerThread(); + + RefPtr runnable = + new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage); + if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) { + NS_WARNING("Failed to report error to debugger on main thread!"); + } +} + +void +WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename, + uint32_t aLineno, + const nsAString& aMessage) +{ + AssertIsOnMainThread(); + + nsTArray> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnError(aFilename, aLineno, aMessage); + } + + WorkerErrorReport report; + report.mMessage = aMessage; + report.mFilename = aFilename; + LogErrorToConsole(report, 0); +} + + +} // worker namespace +} // dom namespace +} // mozilla namespace diff --git a/dom/workers/WorkerDebugger.h b/dom/workers/WorkerDebugger.h new file mode 100644 --- /dev/null +++ b/dom/workers/WorkerDebugger.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_workers_WorkerDebugger_h +#define mozilla_dom_workers_WorkerDebugger_h + +#include "mozilla/dom/workers/WorkerCommon.h" +#include "nsIWorkerDebugger.h" + +BEGIN_WORKERS_NAMESPACE + +class WorkerDebugger : public nsIWorkerDebugger +{ + class ReportDebuggerErrorRunnable; + class PostDebuggerMessageRunnable; + + WorkerPrivate* mWorkerPrivate; + bool mIsInitialized; + nsTArray> mListeners; + +public: + explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate); + + NS_DECL_ISUPPORTS + NS_DECL_NSIWORKERDEBUGGER + + void + AssertIsOnParentThread(); + + void + Close(); + + void + PostMessageToDebugger(const nsAString& aMessage); + + void + ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno, + const nsAString& aMessage); + +private: + virtual + ~WorkerDebugger(); + + void + PostMessageToDebuggerOnMainThread(const nsAString& aMessage); + + void + ReportErrorToDebuggerOnMainThread(const nsAString& aFilename, + uint32_t aLineno, + const nsAString& aMessage); +}; + +END_WORKERS_NAMESPACE + +#endif // mozilla_dom_workers_WorkerDebugger_h diff --git a/dom/workers/WorkerDebuggerManager.cpp b/dom/workers/WorkerDebuggerManager.cpp --- a/dom/workers/WorkerDebuggerManager.cpp +++ b/dom/workers/WorkerDebuggerManager.cpp @@ -6,16 +6,17 @@ #include "WorkerDebuggerManager.h" #include "nsISimpleEnumerator.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPtr.h" +#include "WorkerDebugger.h" #include "WorkerPrivate.h" USING_WORKERS_NAMESPACE namespace { class RegisterDebuggerMainThreadRunnable final : public mozilla::Runnable { diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -23,17 +23,16 @@ #include "nsIScriptSecurityManager.h" #include "nsIScriptTimeoutHandler.h" #include "nsITextToSubURI.h" #include "nsIThreadInternal.h" #include "nsITimer.h" #include "nsIURI.h" #include "nsIURL.h" #include "nsIWeakReferenceUtils.h" -#include "nsIWorkerDebugger.h" #include "nsIXPConnect.h" #include "nsPIDOMWindow.h" #include "nsGlobalWindow.h" #include #include "ImageContainer.h" #include "jsfriendapi.h" #include "js/MemoryMetrics.h" @@ -106,16 +105,17 @@ #include "Navigator.h" #include "Principal.h" #include "RuntimeService.h" #include "ScriptLoader.h" #include "mozilla/dom/ServiceWorkerEvents.h" #include "mozilla/dom/ServiceWorkerManager.h" #include "SharedWorker.h" +#include "WorkerDebugger.h" #include "WorkerDebuggerManager.h" #include "WorkerHolder.h" #include "WorkerNavigator.h" #include "WorkerRunnable.h" #include "WorkerScope.h" #include "WorkerThread.h" // JS_MaybeGC will run once every second during normal execution. @@ -270,79 +270,16 @@ struct WindowAction bool operator==(const WindowAction& aOther) const { return mWindow == aOther.mWindow; } }; -void -LogErrorToConsole(const WorkerErrorReport& aReport, uint64_t aInnerWindowId) -{ - AssertIsOnMainThread(); - - RefPtr scriptError = new nsScriptError(); - NS_WARNING_ASSERTION(scriptError, "Failed to create script error!"); - - if (scriptError) { - nsAutoCString category("Web Worker"); - if (NS_FAILED(scriptError->InitWithWindowID(aReport.mMessage, - aReport.mFilename, - aReport.mLine, - aReport.mLineNumber, - aReport.mColumnNumber, - aReport.mFlags, - category, - aInnerWindowId))) { - NS_WARNING("Failed to init script error!"); - scriptError = nullptr; - } - - 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); - } - } - - nsCOMPtr consoleService = - do_GetService(NS_CONSOLESERVICE_CONTRACTID); - NS_WARNING_ASSERTION(consoleService, "Failed to get console service!"); - - if (consoleService) { - if (scriptError) { - if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) { - return; - } - NS_WARNING("LogMessage failed!"); - } else if (NS_SUCCEEDED(consoleService->LogStringMessage( - aReport.mMessage.BeginReading()))) { - return; - } - NS_WARNING("LogStringMessage failed!"); - } - - NS_ConvertUTF16toUTF8 msg(aReport.mMessage); - NS_ConvertUTF16toUTF8 filename(aReport.mFilename); - - static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]"; - -#ifdef ANDROID - __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(), - filename.get(), aReport.mLineNumber); -#endif - - fprintf(stderr, kErrorString, msg.get(), filename.get(), aReport.mLineNumber); - fflush(stderr); -} - class WorkerFinishedRunnable final : public WorkerControlRunnable { WorkerPrivate* mFinishedWorker; public: WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate, WorkerPrivate* aFinishedWorker) : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), @@ -589,115 +526,16 @@ private: return false; } aWorkerPrivate->SetWorkerScriptExecutedSuccessfully(); return true; } }; -class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable -{ - nsString mScriptURL; - -public: - CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate, - const nsAString& aScriptURL) - : WorkerDebuggerRunnable(aWorkerPrivate), - mScriptURL(aScriptURL) - { } - -private: - virtual bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override - { - aWorkerPrivate->AssertIsOnWorkerThread(); - - WorkerDebuggerGlobalScope* globalScope = - aWorkerPrivate->CreateDebuggerGlobalScope(aCx); - if (!globalScope) { - NS_WARNING("Failed to make global!"); - return false; - } - - if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) { - return false; - } - - JS::Rooted global(aCx, globalScope->GetWrapper()); - - ErrorResult rv; - JSAutoCompartment ac(aCx, global); - scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL, - DebuggerScript, rv); - rv.WouldReportJSException(); - // 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; - } - // 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. - if (rv.MaybeSetPendingException(aCx)) { - return false; - } - - return true; - } -}; - -class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable { - nsString mMessage; - -public: - DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate, - const nsAString& aMessage) - : WorkerDebuggerRunnable(aWorkerPrivate), - mMessage(aMessage) - { - } - -private: - virtual bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override - { - WorkerDebuggerGlobalScope* globalScope = aWorkerPrivate->DebuggerGlobalScope(); - MOZ_ASSERT(globalScope); - - JS::Rooted message(aCx, JS_NewUCStringCopyN(aCx, mMessage.get(), - mMessage.Length())); - if (!message) { - return false; - } - JS::Rooted data(aCx, JS::StringValue(message)); - - RefPtr event = new MessageEvent(globalScope, nullptr, - nullptr); - event->InitMessageEvent(nullptr, - NS_LITERAL_STRING("message"), - false, // canBubble - true, // cancelable - data, - EmptyString(), - EmptyString(), - nullptr, - Sequence>()); - event->SetTrusted(true); - - nsCOMPtr domEvent = do_QueryObject(event); - bool dummy; - globalScope->DispatchEvent(domEvent, &dummy); - return true; - } -}; - class NotifyRunnable final : public WorkerControlRunnable { Status mStatus; public: NotifyRunnable(WorkerPrivate* aWorkerPrivate, Status aStatus) : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), mStatus(aStatus) @@ -3481,362 +3319,16 @@ WorkerPrivateParent::AssertInne template bool WorkerPrivateParent::PrincipalIsValid() const { return mLoadInfo.PrincipalIsValid(); } #endif -class PostDebuggerMessageRunnable final : public Runnable -{ - WorkerDebugger *mDebugger; - nsString mMessage; - -public: - PostDebuggerMessageRunnable(WorkerDebugger* aDebugger, - const nsAString& aMessage) - : mozilla::Runnable("PostDebuggerMessageRunnable") - , mDebugger(aDebugger) - , mMessage(aMessage) - { - } - -private: - ~PostDebuggerMessageRunnable() - { } - - NS_IMETHOD - Run() override - { - mDebugger->PostMessageToDebuggerOnMainThread(mMessage); - - return NS_OK; - } -}; - -class ReportDebuggerErrorRunnable final : public Runnable -{ - WorkerDebugger *mDebugger; - nsString mFilename; - uint32_t mLineno; - nsString mMessage; - -public: - ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger, - const nsAString& aFilename, - uint32_t aLineno, - const nsAString& aMessage) - : mozilla::Runnable("ReportDebuggerErrorRunnable") - , mDebugger(aDebugger) - , mFilename(aFilename) - , mLineno(aLineno) - , mMessage(aMessage) - { - } - -private: - ~ReportDebuggerErrorRunnable() - { } - - NS_IMETHOD - Run() override - { - mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage); - - return NS_OK; - } -}; - -WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate) -: mWorkerPrivate(aWorkerPrivate), - mIsInitialized(false) -{ - AssertIsOnMainThread(); -} - -WorkerDebugger::~WorkerDebugger() -{ - MOZ_ASSERT(!mWorkerPrivate); - - if (!NS_IsMainThread()) { - for (size_t index = 0; index < mListeners.Length(); ++index) { - NS_ReleaseOnMainThreadSystemGroup( - "WorkerDebugger::mListeners", mListeners[index].forget()); - } - } -} - -NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger) - -NS_IMETHODIMP -WorkerDebugger::GetIsClosed(bool* aResult) -{ - AssertIsOnMainThread(); - - *aResult = !mWorkerPrivate; - return NS_OK; -} - -NS_IMETHODIMP -WorkerDebugger::GetIsChrome(bool* aResult) -{ - AssertIsOnMainThread(); - - if (!mWorkerPrivate) { - return NS_ERROR_UNEXPECTED; - } - - *aResult = mWorkerPrivate->IsChromeWorker(); - return NS_OK; -} - -NS_IMETHODIMP -WorkerDebugger::GetIsInitialized(bool* aResult) -{ - AssertIsOnMainThread(); - - if (!mWorkerPrivate) { - return NS_ERROR_UNEXPECTED; - } - - *aResult = mIsInitialized; - return NS_OK; -} - -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 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; -} - -NS_IMETHODIMP -WorkerDebugger::GetUrl(nsAString& aResult) -{ - AssertIsOnMainThread(); - - if (!mWorkerPrivate) { - return NS_ERROR_UNEXPECTED; - } - - aResult = mWorkerPrivate->ScriptURL(); - return NS_OK; -} - -NS_IMETHODIMP -WorkerDebugger::GetWindow(mozIDOMWindow** aResult) -{ - AssertIsOnMainThread(); - - 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::GetPrincipal(nsIPrincipal** aResult) -{ - AssertIsOnMainThread(); - MOZ_ASSERT(aResult); - - if (!mWorkerPrivate) { - return NS_ERROR_UNEXPECTED; - } - - nsCOMPtr 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; -} - -NS_IMETHODIMP -WorkerDebugger::Initialize(const nsAString& aURL) -{ - AssertIsOnMainThread(); - - if (!mWorkerPrivate) { - return NS_ERROR_UNEXPECTED; - } - - if (!mIsInitialized) { - RefPtr runnable = - new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL); - if (!runnable->Dispatch()) { - return NS_ERROR_FAILURE; - } - - mIsInitialized = true; - } - - return NS_OK; -} - -NS_IMETHODIMP -WorkerDebugger::PostMessageMoz(const nsAString& aMessage) -{ - AssertIsOnMainThread(); - - if (!mWorkerPrivate || !mIsInitialized) { - return NS_ERROR_UNEXPECTED; - } - - RefPtr runnable = - new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage); - if (!runnable->Dispatch()) { - return NS_ERROR_FAILURE; - } - - 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::Close() -{ - MOZ_ASSERT(mWorkerPrivate); - mWorkerPrivate = nullptr; - - nsTArray> listeners(mListeners); - for (size_t index = 0; index < listeners.Length(); ++index) { - listeners[index]->OnClose(); - } -} - -void -WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage) -{ - mWorkerPrivate->AssertIsOnWorkerThread(); - - RefPtr runnable = - new PostDebuggerMessageRunnable(this, aMessage); - if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) { - NS_WARNING("Failed to post message to debugger on main thread!"); - } -} - -void -WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage) -{ - AssertIsOnMainThread(); - - nsTArray> listeners(mListeners); - for (size_t index = 0; index < listeners.Length(); ++index) { - listeners[index]->OnMessage(aMessage); - } -} - -void -WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename, - uint32_t aLineno, - const nsAString& aMessage) -{ - mWorkerPrivate->AssertIsOnWorkerThread(); - - RefPtr runnable = - new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage); - if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) { - NS_WARNING("Failed to report error to debugger on main thread!"); - } -} - -void -WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename, - uint32_t aLineno, - const nsAString& aMessage) -{ - AssertIsOnMainThread(); - - nsTArray> listeners(mListeners); - for (size_t index = 0; index < listeners.Length(); ++index) { - listeners[index]->OnError(aFilename, aLineno, aMessage); - } - - WorkerErrorReport report; - report.mMessage = aMessage; - report.mFilename = aFilename; - LogErrorToConsole(report, 0); -} - WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, WorkerType aWorkerType, const nsAString& aWorkerName, const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo) : WorkerPrivateParent(aParent, aScriptURL, aIsChromeWorker, aWorkerType, @@ -6683,14 +6175,77 @@ EventTarget::IsOnCurrentThreadInfallible if (!mWorkerPrivate) { NS_WARNING("A worker's event target was used after the worker has !"); return false; } return mWorkerPrivate->IsOnCurrentThread(); } +void +LogErrorToConsole(const WorkerErrorReport& aReport, uint64_t aInnerWindowId) +{ + AssertIsOnMainThread(); + + RefPtr scriptError = new nsScriptError(); + NS_WARNING_ASSERTION(scriptError, "Failed to create script error!"); + + if (scriptError) { + nsAutoCString category("Web Worker"); + if (NS_FAILED(scriptError->InitWithWindowID(aReport.mMessage, + aReport.mFilename, + aReport.mLine, + aReport.mLineNumber, + aReport.mColumnNumber, + aReport.mFlags, + category, + aInnerWindowId))) { + NS_WARNING("Failed to init script error!"); + scriptError = nullptr; + } + + 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); + } + } + + nsCOMPtr consoleService = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + NS_WARNING_ASSERTION(consoleService, "Failed to get console service!"); + + if (consoleService) { + if (scriptError) { + if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) { + return; + } + NS_WARNING("LogMessage failed!"); + } else if (NS_SUCCEEDED(consoleService->LogStringMessage( + aReport.mMessage.BeginReading()))) { + return; + } + NS_WARNING("LogStringMessage failed!"); + } + + NS_ConvertUTF16toUTF8 msg(aReport.mMessage); + NS_ConvertUTF16toUTF8 filename(aReport.mFilename); + + static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]"; + +#ifdef ANDROID + __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(), + filename.get(), aReport.mLineNumber); +#endif + + fprintf(stderr, kErrorString, msg.get(), filename.get(), aReport.mLineNumber); + fflush(stderr); +} + BEGIN_WORKERS_NAMESPACE // Force instantiation. template class WorkerPrivateParent; END_WORKERS_NAMESPACE diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -9,17 +9,16 @@ #include "WorkerCommon.h" #include "WorkerLoadInfo.h" #include "js/CharacterEncoding.h" #include "nsIContentPolicy.h" #include "nsIContentSecurityPolicy.h" #include "nsILoadGroup.h" -#include "nsIWorkerDebugger.h" #include "nsPIDOMWindow.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/CondVar.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/Move.h" @@ -78,19 +77,16 @@ struct WorkerOptions; } // namespace dom namespace ipc { class PrincipalInfo; } // namespace ipc } // namespace mozilla struct PRThread; -class ReportDebuggerErrorRunnable; -class PostDebuggerMessageRunnable; - BEGIN_WORKERS_NAMESPACE class AutoSyncLoopHolder; class SharedWorker; class ServiceWorkerClientInfo; class WorkerEventTarget; class WorkerControlRunnable; class WorkerDebugger; @@ -950,56 +946,16 @@ public: // shutting down. uint32_t BusyCount() { return mBusyCount; } }; -class WorkerDebugger : public nsIWorkerDebugger { - friend class ::ReportDebuggerErrorRunnable; - friend class ::PostDebuggerMessageRunnable; - - WorkerPrivate* mWorkerPrivate; - bool mIsInitialized; - nsTArray> mListeners; - -public: - explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate); - - NS_DECL_ISUPPORTS - NS_DECL_NSIWORKERDEBUGGER - - void - AssertIsOnParentThread(); - - void - Close(); - - void - PostMessageToDebugger(const nsAString& aMessage); - - void - ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno, - const nsAString& aMessage); - -private: - virtual - ~WorkerDebugger(); - - void - PostMessageToDebuggerOnMainThread(const nsAString& aMessage); - - void - ReportErrorToDebuggerOnMainThread(const nsAString& aFilename, - uint32_t aLineno, - const nsAString& aMessage); -}; - class WorkerPrivate : public WorkerPrivateParent { friend class WorkerHolder; friend class WorkerPrivateParent; typedef WorkerPrivateParent ParentType; friend class AutoSyncLoopHolder; struct TimeoutInfo; @@ -1695,11 +1651,15 @@ public: nsIEventTarget* GetEventTarget() const { // This can be null if CreateNewSyncLoop() fails. return mTarget; } }; +// TODO: this will be removed in the next patch +void +LogErrorToConsole(const WorkerErrorReport& aReport, uint64_t aInnerWindowId); + END_WORKERS_NAMESPACE #endif /* mozilla_dom_workers_workerprivate_h__ */ diff --git a/dom/workers/WorkerRunnable.h b/dom/workers/WorkerRunnable.h --- a/dom/workers/WorkerRunnable.h +++ b/dom/workers/WorkerRunnable.h @@ -3,16 +3,17 @@ /* 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/. */ #ifndef mozilla_dom_workers_workerrunnable_h__ #define mozilla_dom_workers_workerrunnable_h__ #include "WorkerCommon.h" +#include "WorkerHolder.h" #include "nsICancelableRunnable.h" #include "mozilla/Atomics.h" #include "nsISupportsImpl.h" #include "nsThreadUtils.h" /* nsRunnable */ struct JSContext; diff --git a/dom/workers/moz.build b/dom/workers/moz.build --- a/dom/workers/moz.build +++ b/dom/workers/moz.build @@ -15,16 +15,17 @@ EXPORTS.mozilla.dom += [ 'WorkerPrivate.h', 'WorkerRunnable.h', 'WorkerScope.h', ] EXPORTS.mozilla.dom.workers += [ 'RuntimeService.h', 'WorkerCommon.h', + 'WorkerDebugger.h', 'WorkerDebuggerManager.h', 'WorkerLoadInfo.h', ] # Stuff needed for the bindings, not really public though. EXPORTS.mozilla.dom.workers.bindings += [ 'SharedWorker.h', 'WorkerHolder.h', @@ -42,16 +43,17 @@ UNIFIED_SOURCES += [ 'ChromeWorkerScope.cpp', 'FileReaderSync.cpp', 'MessageEventRunnable.cpp', 'Principal.cpp', 'RegisterBindings.cpp', 'RuntimeService.cpp', 'ScriptLoader.cpp', 'SharedWorker.cpp', + 'WorkerDebugger.cpp', 'WorkerDebuggerManager.cpp', 'WorkerHolder.cpp', 'WorkerHolderToken.cpp', 'WorkerLoadInfo.cpp', 'WorkerLocation.cpp', 'WorkerNavigator.cpp', 'WorkerPrivate.cpp', 'WorkerRunnable.cpp',