diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp index 9f88ca046a9e..1b55b6f974d3 100644 --- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -23,8 +23,6 @@ #include "nsProxyRelease.h" #include "nsIScriptError.h" #include "nsISupportsPrimitives.h" -#include "nsGlobalWindowInner.h" -#include "js/friend/ErrorMessages.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/BrowserParent.h" @@ -396,53 +394,6 @@ nsresult nsConsoleService::LogMessageWithMode( return NS_OK; } -// See nsIConsoleService.idl for more info about this method -NS_IMETHODIMP -nsConsoleService::CallFunctionAndLogException( - JS::Handle targetGlobal, JS::HandleValue function, JSContext* cx, - JS::MutableHandleValue retval) { - if (!targetGlobal.isObject() || !function.isObject()) { - return NS_ERROR_INVALID_ARG; - } - - JS::Rooted contextRealm(cx, JS::GetCurrentRealmOrNull(cx)); - if (!contextRealm) { - return NS_ERROR_INVALID_ARG; - } - - JS::Rooted global( - cx, js::CheckedUnwrapDynamic(&targetGlobal.toObject(), cx)); - if (!global) { - return NS_ERROR_INVALID_ARG; - } - - // Use AutoJSAPI in order to trigger AutoJSAPI::ReportException - // which will do most of the work required for this function. - // - // We only have to pick the right global for which we want to flag - // the exception against. - AutoJSAPI jsapi; - if (!jsapi.Init(global)) { - return NS_ERROR_UNEXPECTED; - } - JSContext* ccx = jsapi.cx(); - - // AutoJSAPI picks `targetGlobal` as execution compartment - // whereas we expect to run `function` from the callsites compartment. - JSAutoRealm ar(ccx, JS::GetRealmGlobalOrNull(contextRealm)); - - JS::RootedValue funVal(ccx, function); - if (!JS_WrapValue(ccx, &funVal)) { - return NS_ERROR_FAILURE; - } - if (!JS_CallFunctionValue(ccx, nullptr, funVal, JS::HandleValueArray::empty(), - retval)) { - return NS_ERROR_XPC_JAVASCRIPT_ERROR; - } - - return NS_OK; -} - void nsConsoleService::CollectCurrentListeners( nsCOMArray& aListeners) { MutexAutoLock lock(mLock); diff --git a/xpcom/base/nsIConsoleService.idl b/xpcom/base/nsIConsoleService.idl index 1122875aa5cd..68315d57fd6d 100644 --- a/xpcom/base/nsIConsoleService.idl +++ b/xpcom/base/nsIConsoleService.idl @@ -13,18 +13,6 @@ interface nsIConsoleService : nsISupports { void logMessage(in nsIConsoleMessage message); - // This helper function executes `function` and redirects any exception - // that may be thrown while running it to the DevTools Console currently - // debugging `targetGlobal`. - // - // This helps flag the nsIScriptError with a particular innerWindowID - // which is especially useful for WebExtension content scripts - // where script are running in a Sandbox whose prototype is the content window. - // We expect content script exception to be flaged with the content window - // innerWindowID in order to appear in the tab's DevTools. - [implicit_jscontext] - jsval callFunctionAndLogException(in jsval targetGlobal, in jsval function); - // This is a variant of LogMessage which allows the caller to determine // if the message should be output to an OS-specific log. This is used on // B2G to control whether the message is logged to the android log or not. diff --git a/xpcom/tests/unit/test_console_service_callFunctionAndLogException.js b/xpcom/tests/unit/test_console_service_callFunctionAndLogException.js deleted file mode 100644 index eeb9ceb42504..000000000000 --- a/xpcom/tests/unit/test_console_service_callFunctionAndLogException.js +++ /dev/null @@ -1,265 +0,0 @@ -let lastMessage; -const consoleListener = { - observe(message) { - dump(" >> new message: " + message.errorMessage + "\n"); - lastMessage = message; - }, -}; -Services.console.registerListener(consoleListener); - -// The Console Service notifies its listener after one event loop cycle. -// So wait for one tick after each action dispatching a message/error to the service. -function waitForATick() { - return new Promise(resolve => Services.tm.dispatchToMainThread(resolve)); -} - -add_task(async function customScriptError() { - const scriptError = Cc["@mozilla.org/scripterror;1"].createInstance( - Ci.nsIScriptError - ); - scriptError.init( - "foo", - "file.js", - null, - 1, - 2, - Ci.nsIScriptError.warningFlag, - "some javascript" - ); - Services.console.logMessage(scriptError); - - await waitForATick(); - - Assert.equal( - lastMessage, - scriptError, - "We receive the exact same nsIScriptError object" - ); - - Assert.equal(lastMessage.errorMessage, "foo"); - Assert.equal(lastMessage.sourceName, "file.js"); - Assert.equal(lastMessage.lineNumber, 1); - Assert.equal(lastMessage.columnNumber, 2); - Assert.equal(lastMessage.flags, Ci.nsIScriptError.warningFlag); - Assert.equal(lastMessage.category, "some javascript"); - - Assert.equal( - lastMessage.stack, - undefined, - "Custom nsIScriptError object created from JS can't convey any stack" - ); -}); - -add_task(async function callFunctionAndLogExceptionWithChromeGlobal() { - try { - Services.console.callFunctionAndLogException(globalThis, function () { - throw new Error("custom exception"); - }); - Assert.fail("callFunctionAndLogException should throw"); - } catch (e) { - Assert.equal( - e.name, - "NS_ERROR_XPC_JAVASCRIPT_ERROR", - "callFunctionAndLogException thrown" - ); - } - - await waitForATick(); - - Assert.ok(!!lastMessage, "Got the message"); - Assert.ok( - lastMessage instanceof Ci.nsIScriptError, - "This is a nsIScriptError" - ); - - Assert.equal(lastMessage.errorMessage, "Error: custom exception"); - Assert.equal(lastMessage.sourceName, _TEST_FILE); - Assert.equal(lastMessage.lineNumber, 56); - Assert.equal(lastMessage.columnNumber, 13); - Assert.equal(lastMessage.flags, Ci.nsIScriptError.errorFlag); - Assert.equal(lastMessage.category, "chrome javascript"); - Assert.ok(lastMessage.stack, "It has a stack"); - Assert.equal(lastMessage.stack.source, _TEST_FILE); - Assert.equal(lastMessage.stack.line, 56); - Assert.equal(lastMessage.stack.column, 13); - Assert.ok(!!lastMessage.stack.parent, "stack has a parent frame"); - Assert.equal( - lastMessage.innerWindowID, - 0, - "The message isn't bound to any WindowGlobal" - ); -}); - -add_task(async function callFunctionAndLogExceptionWithContentGlobal() { - const window = createContentWindow(); - try { - Services.console.callFunctionAndLogException(window, function () { - throw new Error("another custom exception"); - }); - Assert.fail("callFunctionAndLogException should throw"); - } catch (e) { - Assert.equal( - e.name, - "NS_ERROR_XPC_JAVASCRIPT_ERROR", - "callFunctionAndLogException thrown" - ); - } - - await waitForATick(); - - Assert.ok(!!lastMessage, "Got the message"); - Assert.ok( - lastMessage instanceof Ci.nsIScriptError, - "This is a nsIScriptError" - ); - - Assert.equal(lastMessage.errorMessage, "Error: another custom exception"); - Assert.equal(lastMessage.sourceName, _TEST_FILE); - Assert.equal(lastMessage.lineNumber, 97); - Assert.equal(lastMessage.columnNumber, 13); - Assert.equal(lastMessage.flags, Ci.nsIScriptError.errorFlag); - Assert.equal(lastMessage.category, "content javascript"); - Assert.ok(lastMessage.stack, "It has a stack"); - Assert.equal(lastMessage.stack.source, _TEST_FILE); - Assert.equal(lastMessage.stack.line, 97); - Assert.equal(lastMessage.stack.column, 13); - Assert.ok(!!lastMessage.stack.parent, "stack has a parent frame"); - Assert.ok( - !!window.windowGlobalChild.innerWindowId, - "The window has a innerWindowId" - ); - Assert.equal( - lastMessage.innerWindowID, - window.windowGlobalChild.innerWindowId, - "The message is bound to the content window" - ); -}); - -add_task(async function callFunctionAndLogExceptionForContentScriptSandboxes() { - const { sandbox, window } = createContentScriptSandbox(); - Cu.evalInSandbox( - `function foo() { throw new Error("sandbox exception"); }`, - sandbox, - null, - "sandbox-file.js", - 1, - 0 - ); - try { - Services.console.callFunctionAndLogException(window, sandbox.foo); - Assert.fail("callFunctionAndLogException should throw"); - } catch (e) { - Assert.equal( - e.name, - "NS_ERROR_XPC_JAVASCRIPT_ERROR", - "callFunctionAndLogException thrown" - ); - } - - await waitForATick(); - - Assert.ok(!!lastMessage, "Got the message"); - // Note that it is important to "instanceof" in order to expose the nsIScriptError attributes. - Assert.ok( - lastMessage instanceof Ci.nsIScriptError, - "This is a nsIScriptError" - ); - - Assert.equal(lastMessage.errorMessage, "Error: sandbox exception"); - Assert.equal(lastMessage.sourceName, "sandbox-file.js"); - Assert.equal(lastMessage.lineNumber, 1); - Assert.equal(lastMessage.columnNumber, 24); - Assert.equal(lastMessage.flags, Ci.nsIScriptError.errorFlag); - Assert.equal(lastMessage.category, "content javascript"); - Assert.ok(lastMessage.stack, "It has a stack"); - Assert.equal(lastMessage.stack.source, "sandbox-file.js"); - Assert.equal(lastMessage.stack.line, 1); - Assert.equal(lastMessage.stack.column, 24); - Assert.ok(!!lastMessage.stack.parent, "stack has a parent frame"); - Assert.ok( - !!window.windowGlobalChild.innerWindowId, - "The sandbox's prototype is a window and has a innerWindowId" - ); - Assert.equal( - lastMessage.innerWindowID, - window.windowGlobalChild.innerWindowId, - "The message is bound to the sandbox's prototype WindowGlobal" - ); -}); - -add_task( - async function callFunctionAndLogExceptionForContentScriptSandboxesWrappedInChrome() { - const { sandbox, window } = createContentScriptSandbox(); - Cu.evalInSandbox( - `function foo() { throw new Error("sandbox exception"); }`, - sandbox, - null, - "sandbox-file.js", - 1, - 0 - ); - try { - Services.console.callFunctionAndLogException(window, function () { - sandbox.foo(); - }); - Assert.fail("callFunctionAndLogException should throw"); - } catch (e) { - Assert.equal( - e.name, - "NS_ERROR_XPC_JAVASCRIPT_ERROR", - "callFunctionAndLogException thrown" - ); - } - - await waitForATick(); - - Assert.ok(!!lastMessage, "Got the message"); - // Note that it is important to "instanceof" in order to expose the nsIScriptError attributes. - Assert.ok( - lastMessage instanceof Ci.nsIScriptError, - "This is a nsIScriptError" - ); - - Assert.ok( - !!window.windowGlobalChild.innerWindowId, - "The sandbox's prototype is a window and has a innerWindowId" - ); - Assert.equal( - lastMessage.innerWindowID, - window.windowGlobalChild.innerWindowId, - "The message is bound to the sandbox's prototype WindowGlobal" - ); - } -); - -add_task(function teardown() { - Services.console.unregisterListener(consoleListener); -}); - -// We are in xpcshell, so we can't have a real DOM Window as in Firefox -// but let's try to have a fake one. -function createContentWindow() { - const principal = - Services.scriptSecurityManager.createContentPrincipalFromOrigin( - "http://example.com/" - ); - - const webnav = Services.appShell.createWindowlessBrowser(false); - - webnav.docShell.createAboutBlankContentViewer(principal, principal); - - return webnav.document.defaultView; -} - -// Create a Sandbox as in WebExtension content scripts -function createContentScriptSandbox() { - const window = createContentWindow(); - // The sandboxPrototype is the key here in order to - // make xpc::SandboxWindowOrNull ignore the sandbox - // and instead retrieve its prototype and link the error message - // to the window instead of the sandbox. - return { - sandbox: Cu.Sandbox(window, { sandboxPrototype: window }), - window, - }; -} diff --git a/xpcom/tests/unit/xpcshell.ini b/xpcom/tests/unit/xpcshell.ini index 137ab2df4562..1c933251db1a 100644 --- a/xpcom/tests/unit/xpcshell.ini +++ b/xpcom/tests/unit/xpcshell.ini @@ -19,7 +19,6 @@ skip-if = os == "win" fail-if = os == "android" [test_bug478086.js] [test_bug1434856.js] -[test_console_service_callFunctionAndLogException.js] [test_debugger_malloc_size_of.js] [test_file_createUnique.js] [test_file_equality.js]