diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index ed401a18c007..3d43ae72304a 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -623,6 +623,13 @@ bool mozJSComponentLoader::ReuseGlobal(nsIURI* aURI) { return false; } + // BrowserTestUtils.jsm calls Cu.permitCPOWsInScope(this) which sets a + // per-compartment flag to permit CPOWs. We don't want to set this flag for + // all other JSMs. + if (spec.EqualsASCII("resource://testing-common/BrowserTestUtils.jsm")) { + return false; + } + // Some SpecialPowers jsms call Cu.forcePermissiveCOWs(), // which sets a per-compartment flag that disables certain // security wrappers, so don't use the shared global for them diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 68ae298355ca..83766352adc0 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -1625,6 +1625,7 @@ bool SandboxOptions::Parse() { &isWebExtensionContentScript) && ParseString("sandboxName", sandboxName) && ParseObject("sameZoneAs", &sameZoneAs) && + ParseBoolean("freshCompartment", &freshCompartment) && ParseBoolean("freshZone", &freshZone) && ParseBoolean("invisibleToDebugger", &invisibleToDebugger) && ParseBoolean("discardSource", &discardSource) && diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index eeb116e674f9..aa6217877c47 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -1943,10 +1943,13 @@ nsXPCComponents_Utils::PermitCPOWsInScope(HandleValue obj) { } JSObject* scopeObj = js::UncheckedUnwrap(&obj.toObject()); - MOZ_DIAGNOSTIC_ASSERT( - !mozJSComponentLoader::Get()->IsLoaderGlobal(scopeObj), - "Don't call Cu.PermitCPOWsInScope() in a JSM that shares its global"); - CompartmentPrivate::Get(scopeObj)->allowCPOWs = true; + JS::Compartment* scopeComp = js::GetObjectCompartment(scopeObj); + JS::Compartment* systemComp = + js::GetObjectCompartment(xpc::PrivilegedJunkScope()); + MOZ_RELEASE_ASSERT(scopeComp != systemComp, + "Don't call Cu.PermitCPOWsInScope() on scopes in the " + "shared system compartment"); + CompartmentPrivate::Get(scopeComp)->allowCPOWs = true; return NS_OK; } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index a14e440d519d..1453f5d06434 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2475,6 +2475,7 @@ class MOZ_STACK_CLASS SandboxOptions : public OptionsBase { isWebExtensionContentScript(false), proto(cx), sameZoneAs(cx), + freshCompartment(false), freshZone(false), isContentXBLScope(false), isUAWidgetScope(false), @@ -2494,6 +2495,7 @@ class MOZ_STACK_CLASS SandboxOptions : public OptionsBase { JS::RootedObject proto; nsCString sandboxName; JS::RootedObject sameZoneAs; + bool freshCompartment; bool freshZone; bool isContentXBLScope; bool isUAWidgetScope; diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index ccb7d2af5b7f..78a621cd06a7 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -382,8 +382,12 @@ function Tester(aTests, structuredLogger, aCallback) { // In order to allow existing tests to continue using unsafe CPOWs // with EventUtils, we need to load a separate copy into a sandbox - // which has unsafe CPOW usage whitelisted. - this.cpowSandbox = Cu.Sandbox(window, {sandboxPrototype: window}); + // which has unsafe CPOW usage whitelisted. We need to create a new + // compartment for Cu.permitCPOWsInScope. + this.cpowSandbox = Cu.Sandbox(window, { + freshCompartment: true, + sandboxPrototype: window, + }); Cu.permitCPOWsInScope(this.cpowSandbox); this.cpowEventUtils = new this.cpowSandbox.Object(); @@ -1454,7 +1458,13 @@ testScope.prototype = { Assert: null, _createSandbox() { - let sandbox = Cu.Sandbox(window, {sandboxPrototype: window}); + // Force this sandbox to be in its own compartment because we call + // Cu.permitCPOWsInScope on it and we can't call that on objects in the + // shared system compartment. + let sandbox = Cu.Sandbox(window, { + freshCompartment: true, + sandboxPrototype: window, + }); for (let prop in this) { if (typeof this[prop] == "function") {