Bug 1864128 - Allow loading JSProcess Actors in the DevTools specific module loader. r=arai

Passing `loadInDevToolsLoader` to JSProcess actor options will force loading
both parent and content modules in the distinct module loader used by DevTools.

This distinct module loader is maintained by mozJSModuleLoader and is used
by the DevTools Browser Console/Toolbox to debug privileged JS code
typically using the "shared JSM Global" from DevTools modules
loaded in a distinct and specific DevTools module loader.

Differential Revision: https://phabricator.services.mozilla.com/D193280
This commit is contained in:
Alexandre Poirot 2023-11-21 10:02:47 +00:00
Родитель d59b225e76
Коммит b3006699b6
10 изменённых файлов: 102 добавлений и 9 удалений

Просмотреть файл

@ -54,6 +54,13 @@ dictionary ProcessActorOptions {
*/
boolean includeParent = false;
/**
* If true, the actor will be loaded in the loader dedicated to DevTools.
*
* This ultimately prevents DevTools to debug itself.
*/
boolean loadInDevToolsLoader = false;
/** This fields are used for configuring individual sides of the actor. */
ProcessActorSidedOptions parent;
ProcessActorChildOptions child;

Просмотреть файл

@ -301,6 +301,10 @@ struct JSWindowActorInfo
// False if `url` is for JSM or nothing.
bool isESModule;
// This is to align with JSProcessActorInfo.
// This attribute isn't used for JSWindow Actors.
bool loadInDevToolsLoader;
// The module of the url.
nsCString? url;
@ -322,6 +326,9 @@ struct JSProcessActorInfo
// False if `url` is for JSM or nothing.
bool isESModule;
// True if the actor should be loaded in the distinct loader dedicated to DevTools.
bool loadInDevToolsLoader;
// The module of the url.
nsCString? url;

Просмотреть файл

@ -59,14 +59,18 @@ already_AddRefed<JSActor> JSActorManager::GetActor(JSContext* aCx,
? protocol->Parent()
: protocol->Child();
// We're about to construct the actor, so make sure we're in the JSM realm
// while importing etc.
JSAutoRealm ar(aCx, xpc::PrivilegedJunkScope());
// Load the module using mozJSModuleLoader.
RefPtr loader = mozJSModuleLoader::Get();
// If the JSActor uses `loadInDevToolsLoader`, force loading in the DevTools
// specific's loader.
RefPtr loader = protocol->mLoadInDevToolsLoader
? mozJSModuleLoader::GetOrCreateDevToolsLoader()
: mozJSModuleLoader::Get();
MOZ_ASSERT(loader);
// We're about to construct the actor, so make sure we're in the loader realm
// while importing etc.
JSAutoRealm ar(aCx, loader->GetSharedGlobal(aCx));
// If a module URI was provided, use it to construct an instance of the actor.
JS::Rooted<JSObject*> actorObj(aCx);
if (side.mModuleURI || side.mESModuleURI) {

Просмотреть файл

@ -26,6 +26,8 @@ class JSActorProtocolUtils {
aProto->mChild.mModuleURI = aInfo.url();
}
aProto->mLoadInDevToolsLoader = aInfo.loadInDevToolsLoader();
aProto->mChild.mObservers = aInfo.observers().Clone();
}
@ -43,6 +45,8 @@ class JSActorProtocolUtils {
aInfo.isESModule() = true;
}
aInfo.loadInDevToolsLoader() = aProto->mLoadInDevToolsLoader;
aInfo.observers() = aProto->mChild.mObservers.Clone();
}

Просмотреть файл

@ -92,7 +92,7 @@ class JSActorService final {
};
/**
* Base clsas for both `JSWindowActorProtocol` and `JSProcessActorProtocol`
* Base class for both `JSWindowActorProtocol` and `JSProcessActorProtocol`
* which can be used by generic code.
*/
class JSActorProtocol : public nsISupports {
@ -104,6 +104,7 @@ class JSActorProtocol : public nsISupports {
virtual const Sided& Parent() const = 0;
virtual const Sided& Child() const = 0;
bool mLoadInDevToolsLoader = false;
};
} // namespace dom

Просмотреть файл

@ -64,6 +64,8 @@ JSProcessActorProtocol::FromWebIDLOptions(const nsACString& aName,
proto->mIncludeParent = aOptions.mIncludeParent;
proto->mLoadInDevToolsLoader = aOptions.mLoadInDevToolsLoader;
return proto.forget();
}

Просмотреть файл

@ -1,6 +1,8 @@
[DEFAULT]
support-files = ["head.js"]
["browser_devtools_loader.js"]
["browser_getActor.js"]
["browser_getActor_filter.js"]

Просмотреть файл

@ -0,0 +1,57 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
declTest("getActor in the regular shared loader", {
loadInDevToolsLoader: false,
async test(browser) {
let parent = browser.browsingContext.currentWindowGlobal.domProcess;
let parentActor = parent.getActor("TestProcessActor");
ok(parentActor, "JSProcessActorParent should have value.");
is(
Cu.getRealmLocation(Cu.getGlobalForObject(parentActor)),
"shared JSM global",
"The JSActor module in the parent process should be loaded in the shared global"
);
await SpecialPowers.spawn(browser, [], async function () {
let child = ChromeUtils.domProcessChild;
ok(child, "DOMProcessChild should have value.");
let childActor = child.getActor("TestProcessActor");
ok(childActor, "JSProcessActorChild should have value.");
is(
Cu.getRealmLocation(Cu.getGlobalForObject(childActor)),
"shared JSM global",
"The JSActor module in the child process should be loaded in the shared global"
);
});
},
});
declTest("getActor in the distinct DevTools loader", {
loadInDevToolsLoader: true,
async test(browser) {
let parent = browser.browsingContext.currentWindowGlobal.domProcess;
let parentActor = parent.getActor("TestProcessActor");
ok(parentActor, "JSProcessActorParent should have value.");
is(
Cu.getRealmLocation(Cu.getGlobalForObject(parentActor)),
"DevTools global",
"The JSActor module in the parent process should be loaded in the distinct DevTools global"
);
await SpecialPowers.spawn(browser, [], async function () {
let child = ChromeUtils.domProcessChild;
ok(child, "DOMProcessChild should have value.");
let childActor = child.getActor("TestProcessActor");
ok(childActor, "JSProcessActorChild should have value.");
is(
Cu.getRealmLocation(Cu.getGlobalForObject(childActor)),
"DevTools global",
"The JSActor module in the child process should be loaded in the distinct DevTools global"
);
});
},
});

Просмотреть файл

@ -46,7 +46,13 @@ function declTest(name, cfg) {
}
function declTestWithOptions(name, cfg, fileExt) {
let { url = "about:blank", includeParent = false, remoteTypes, test } = cfg;
let {
url = "about:blank",
includeParent = false,
remoteTypes,
loadInDevToolsLoader = false,
test,
} = cfg;
// Build the actor options object which will be used to register & unregister
// our process actor.
@ -58,6 +64,9 @@ function declTestWithOptions(name, cfg, fileExt) {
if (remoteTypes !== undefined) {
actorOptions.remoteTypes = remoteTypes;
}
if (loadInDevToolsLoader) {
actorOptions.loadInDevToolsLoader = true;
}
// Add a new task for the actor test declared here.
add_task(async function () {

Просмотреть файл

@ -65,6 +65,8 @@ class mozJSModuleLoader final : public nsIMemoryReporter {
return sSelf;
}
JSObject* GetSharedGlobal(JSContext* aCx);
static mozJSModuleLoader* GetDevToolsLoader() { return sDevToolsLoader; }
static mozJSModuleLoader* GetOrCreateDevToolsLoader();
@ -146,8 +148,6 @@ class mozJSModuleLoader final : public nsIMemoryReporter {
bool CreateJSServices(JSContext* aCx);
JSObject* GetSharedGlobal(JSContext* aCx);
static nsresult GetSourceFile(nsIURI* aResolvedURI, nsIFile** aSourceFileOut);
static bool LocationIsRealFile(nsIURI* aURI);