diff --git a/dom/serviceworkers/ServiceWorkerPrivate.cpp b/dom/serviceworkers/ServiceWorkerPrivate.cpp index 542a67a18780..05b3df95c6eb 100644 --- a/dom/serviceworkers/ServiceWorkerPrivate.cpp +++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp @@ -22,6 +22,7 @@ #include "nsQueryObject.h" #include "nsStreamUtils.h" #include "nsStringStream.h" +#include "WorkerDebugger.h" #include "WorkerRunnable.h" #include "WorkerScope.h" #include "mozilla/Assertions.h" diff --git a/dom/workers/ScriptLoader.h b/dom/workers/ScriptLoader.h index 46a9da30295e..c5bf3fda02bb 100644 --- a/dom/workers/ScriptLoader.h +++ b/dom/workers/ScriptLoader.h @@ -25,6 +25,8 @@ class ErrorResult; BEGIN_WORKERS_NAMESPACE +struct WorkerLoadInfo; + enum WorkerScriptType { WorkerScript, DebuggerScript diff --git a/dom/workers/WorkerDebugger.cpp b/dom/workers/WorkerDebugger.cpp new file mode 100644 index 000000000000..9a36883a559d --- /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 index 000000000000..0fec61588aa2 --- /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 index dfb907ac20c3..d6e9fc72cb7f 100644 --- a/dom/workers/WorkerDebuggerManager.cpp +++ b/dom/workers/WorkerDebuggerManager.cpp @@ -11,6 +11,7 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPtr.h" +#include "WorkerDebugger.h" #include "WorkerPrivate.h" USING_WORKERS_NAMESPACE diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 4aa4471fe5b4..dd1c062979ce 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -28,7 +28,6 @@ #include "nsIURI.h" #include "nsIURL.h" #include "nsIWeakReferenceUtils.h" -#include "nsIWorkerDebugger.h" #include "nsIXPConnect.h" #include "nsPIDOMWindow.h" #include "nsGlobalWindow.h" @@ -111,6 +110,7 @@ #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" @@ -275,69 +275,6 @@ struct WindowAction } }; -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; @@ -594,105 +531,6 @@ private: } }; -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; @@ -3484,352 +3322,6 @@ WorkerPrivateParent::PrincipalIsValid() const } #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, @@ -6686,6 +6178,69 @@ EventTarget::IsOnCurrentThreadInfallible() 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. diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 0c879d4e918f..8707598599e9 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -14,7 +14,6 @@ #include "nsIContentPolicy.h" #include "nsIContentSecurityPolicy.h" #include "nsILoadGroup.h" -#include "nsIWorkerDebugger.h" #include "nsPIDOMWindow.h" #include "mozilla/Assertions.h" @@ -83,9 +82,6 @@ class PrincipalInfo; struct PRThread; -class ReportDebuggerErrorRunnable; -class PostDebuggerMessageRunnable; - BEGIN_WORKERS_NAMESPACE class AutoSyncLoopHolder; @@ -955,46 +951,6 @@ public: } }; -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; @@ -1700,6 +1656,10 @@ public: } }; +// 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 index 11132e8fd4c6..f3c4678b5ac6 100644 --- a/dom/workers/WorkerRunnable.h +++ b/dom/workers/WorkerRunnable.h @@ -8,6 +8,7 @@ #define mozilla_dom_workers_workerrunnable_h__ #include "WorkerCommon.h" +#include "WorkerHolder.h" #include "nsICancelableRunnable.h" diff --git a/dom/workers/moz.build b/dom/workers/moz.build index 5951cbc8cf79..ba3213206c8d 100644 --- a/dom/workers/moz.build +++ b/dom/workers/moz.build @@ -20,6 +20,7 @@ EXPORTS.mozilla.dom += [ EXPORTS.mozilla.dom.workers += [ 'RuntimeService.h', 'WorkerCommon.h', + 'WorkerDebugger.h', 'WorkerDebuggerManager.h', 'WorkerLoadInfo.h', ] @@ -47,6 +48,7 @@ UNIFIED_SOURCES += [ 'RuntimeService.cpp', 'ScriptLoader.cpp', 'SharedWorker.cpp', + 'WorkerDebugger.cpp', 'WorkerDebuggerManager.cpp', 'WorkerHolder.cpp', 'WorkerHolderToken.cpp',