diff --git a/devtools/client/webconsole/test/browser/_browser_console.ini b/devtools/client/webconsole/test/browser/_browser_console.ini index 3cedc0c608a0..d2999c52ab32 100644 --- a/devtools/client/webconsole/test/browser/_browser_console.ini +++ b/devtools/client/webconsole/test/browser/_browser_console.ini @@ -52,6 +52,7 @@ skip-if = os == "mac" && webrender # Bug 1689000 os == "linux" && debug # Bug 1689000 [browser_console_webextension.js] +[browser_console_window_object_inheritance.js] [browser_toolbox_console_new_process.js] skip-if = asan || debug || ccov # Bug 1591590 diff --git a/devtools/client/webconsole/test/browser/browser_console_window_object_inheritance.js b/devtools/client/webconsole/test/browser/browser_console_window_object_inheritance.js new file mode 100644 index 000000000000..1e9dc0bf416d --- /dev/null +++ b/devtools/client/webconsole/test/browser/browser_console_window_object_inheritance.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function() { + await addTab("about:blank"); + + info(`Open browser console`); + const hud = await BrowserConsoleManager.openBrowserConsoleOrFocus(); + + info(`Clear existing messages`); + const onMessagesCleared = hud.ui.once("messages-cleared"); + await clearOutput(hud); + await onMessagesCleared; + + info(`Create a DOM window object`); + await hud.commands.scriptCommand.execute(` + globalThis.myBrowser = Services.appShell.createWindowlessBrowser(); + globalThis.myWindow = myBrowser.document.defaultView; + `); + + info(`Check objects inheriting from a DOM window`); + async function check(input, expected, name) { + const msg = await executeAndWaitForMessage( + hud, + input, + "", + ".message.result" + ); + is(msg.node.querySelector(".message-body").textContent, expected, name); + } + await check("Object.create(myWindow)", "Object { }", "Empty object"); + await check( + "Object.create(myWindow, { location: { value: 1, enumerable: true } })", + "Object { location: 1 }", + "Object with 'location' property" + ); + await check( + `Object.create(myWindow, { + location: { + get() { + console.error("pwned!"); + return { href: "Oops" }; + }, + enumerable: true, + }, + })`, + "Object { location: Getter }", + "Object with 'location' unsafe getter" + ); + + info(`Check that no error was logged`); + // wait a bit so potential errors can be printed + await wait(1000); + const error = findMessage(hud, "", ".message.error:not(.network)"); + if (error) { + ok(false, `Got error ${JSON.stringify(error.textContent)}`); + } else { + ok(true, "No error was logged"); + } + + info(`Cleanup`); + await hud.commands.scriptCommand.execute(` + myBrowser.close(); + delete globalThis.myBrowser; + delete globalThis.myWindow; + `); +}); diff --git a/devtools/server/actors/object/previewers.js b/devtools/server/actors/object/previewers.js index ab2566e17061..24f392b1f99e 100644 --- a/devtools/server/actors/object/previewers.js +++ b/devtools/server/actors/object/previewers.js @@ -692,21 +692,22 @@ previewers.Object = [ }, function ObjectWithURL({ obj, hooks }, grip, rawObj) { + if (isWorker || !rawObj) { + return false; + } + + const isWindow = Window.isInstance(rawObj); if ( - isWorker || - !rawObj || - !( - obj.class == "CSSImportRule" || - obj.class == "CSSStyleSheet" || - obj.class == "Location" || - rawObj instanceof Ci.nsIDOMWindow - ) + obj.class != "CSSImportRule" && + obj.class != "CSSStyleSheet" && + obj.class != "Location" && + !isWindow ) { return false; } let url; - if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) { + if (isWindow && rawObj.location) { url = rawObj.location.href; } else if (rawObj.href) { url = rawObj.href; diff --git a/devtools/shared/builtin-modules.js b/devtools/shared/builtin-modules.js index b8bc3e7b0d46..05d11516b7fd 100644 --- a/devtools/shared/builtin-modules.js +++ b/devtools/shared/builtin-modules.js @@ -63,6 +63,7 @@ const debuggerSandbox = (exports.internalSandbox = Cu.Sandbox(systemPrincipal, { "TextDecoder", "TextEncoder", "URL", + "Window", "XMLHttpRequest", ], })); @@ -87,6 +88,7 @@ const { TextDecoder, TextEncoder, URL, + Window, XMLHttpRequest, } = debuggerSandbox; @@ -280,6 +282,7 @@ exports.globals = { TextDecoder, TextEncoder, URL, + Window, XMLHttpRequest, }; // DevTools loader copy globals property descriptors on each module global diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index bf357a8e0072..990cb1da2623 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -79,6 +79,7 @@ #include "mozilla/dom/URLSearchParamsBinding.h" #include "mozilla/dom/XMLHttpRequest.h" #include "mozilla/dom/WebSocketBinding.h" +#include "mozilla/dom/WindowBinding.h" #include "mozilla/dom/XMLSerializerBinding.h" #include "mozilla/dom/FormDataBinding.h" #include "mozilla/dom/nsCSPContext.h" @@ -946,6 +947,8 @@ bool xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj) { XMLHttpRequest = true; } else if (JS_LinearStringEqualsLiteral(nameStr, "WebSocket")) { WebSocket = true; + } else if (JS_LinearStringEqualsLiteral(nameStr, "Window")) { + Window = true; } else if (JS_LinearStringEqualsLiteral(nameStr, "XMLSerializer")) { XMLSerializer = true; } else if (JS_LinearStringEqualsLiteral(nameStr, "atob")) { @@ -1099,6 +1102,8 @@ bool xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj) { if (WebSocket && !dom::WebSocket_Binding::GetConstructorObject(cx)) return false; + if (Window && !dom::Window_Binding::GetConstructorObject(cx)) return false; + if (XMLSerializer && !dom::XMLSerializer_Binding::GetConstructorObject(cx)) return false; diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 4c6e3ebb4c68..ea2d492430ca 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2245,6 +2245,7 @@ struct GlobalProperties { bool URLSearchParams : 1; bool XMLHttpRequest : 1; bool WebSocket : 1; + bool Window : 1; bool XMLSerializer : 1; // Ad-hoc property names we implement.