Can't interact with chromeonly methods on content documents in multiprocess browser toolbox
Categories
(DevTools :: Console, defect, P2)
Tracking
(Fission Milestone:Future)
Fission Milestone | Future |
People
(Reporter: Gijs, Unassigned)
References
(Blocks 1 open bug)
Details
(Whiteboard: dt-fission-future)
Attachments
(1 file)
(deleted),
image/jpeg
|
Details |
As far as I can tell, the new multiprocess browser toolbox implementation uses the principal of the document to decide how to inspect it / how to interact with it.
This is generally a sensible strategy, but in the browser toolbox it makes it impossible to work with things like our builtin video controls or to check from the console what the result of calling ChromeOnly APIs would be. Some examples:
STR:
- open https://codepen.io/ksy36/pen/dyPaopz
- open multiprocess browser toolbox
- try to inspect the video controls on the video in the bottom pane
ER:
you can inspect them.
AR:
you cannot; they don't show up in the inspector's DOM tree
STR:
- open any webpage
- open multiprocess browser toolbox
- inspect any element on the page
- press [esc] to open the console
- evaluate
$0.ownerDocument.defaultView.windowUtils
ER:
you get a reference to the window utils object
AR:
undefined
, or, if the node is in a cross-origin subframe like in the first set of STR, Error: Permission denied to access property "ownerDocument"
Reporter | ||
Updated•5 years ago
|
Comment 2•5 years ago
|
||
Hi Gijs,
For the first part of your report, to inspect the video controls which are under the shadow root, I believe you need to flip this pref: devtools.inspector.showUserAgentShadowRoots
.
If you want to inspect with the browser toolbox you need to flip that in the browser toolbox's profile (it has a different profile than the browser).
One way to do so:
- Open the multiprocess browser toolbox
- Click the DevTools settings meatball menu (...) and pick Documentation
- In the window that opens, navigate to
about:config
and flip the pref:devtools.inspector.showUserAgentShadowRoots
(Aside, we should make a direct option in the DevTools settings)
You may have to restart the browser toolbox. Afterwards, you should be able to inspect/navigate into the shadow DOM of the <video>
element using the browser toolbox Inspector.
Please let me know if this solves your issue.
With regards to the second part of your report regarding the console, I will ping Nicolas to confirm whether this functionality is expected to work yet or not. It's part of the Fission work, but I don't know if we got to the stage of supporting commands referencing $0
in the right context.
Comment 3•5 years ago
|
||
Setting pref devtools.inspector.showUserAgentShadowRoots
to true
Reporter | ||
Comment 4•5 years ago
|
||
(In reply to Razvan Caliman [:rcaliman] from comment #2)
For the first part of your report, to inspect the video controls which are under the shadow root, I believe you need to flip this pref:
devtools.inspector.showUserAgentShadowRoots
.
Thanks. this wasn't obvious and if there was ever a memo I must have missed it. :-)
Why is flipping showAllAnonymousContent
not good enough here? Do we really need more than one pref?
Comment 5•5 years ago
|
||
(In reply to :Gijs (he/him) from comment #4)
(In reply to Razvan Caliman [:rcaliman] from comment #2)
For the first part of your report, to inspect the video controls which are under the shadow root, I believe you need to flip this pref:
devtools.inspector.showUserAgentShadowRoots
.Thanks. this wasn't obvious and if there was ever a memo I must have missed it. :-)
Why is flippingshowAllAnonymousContent
not good enough here? Do we really need more than one pref?
It was added in https://bugzilla.mozilla.org/show_bug.cgi?id=1483660 and there's some discussion in the thread about if we should reuse showAllAnonymousContent, but I don't see any strong opinions one way or another there. IMO we should fold them together at this point to make things simpler for Firefox devs, unless if there's something I'm not considering (which Julian may be aware of).
Comment 6•5 years ago
|
||
No strong reason to keep the preferences separated.
Filed Bug 1613773 since this Bug also described a second unrelated STR. (maybe we should rename the bug and move to console?)
Updated•5 years ago
|
Comment 7•5 years ago
|
||
Yes, let's move this bug to the console module since there's seem to be an issue with $0
here.
Comment 8•5 years ago
|
||
Tracking dt-fission-m2-mvp bugs for Fission Nightly (M6) milestone
Comment 9•5 years ago
|
||
This reminds me bug 1027310.
Being able to execute from system compartment when debugging content document is an old request.
It was easier a few years ago when we we just using eval or evalInSubScript.
Now we always go through the Debugger API and it isn't clear if existing Debugger API allow such behavior.
I imagine such feature would require a few tweak to this code:
- Where we execute the console input:
https://searchfox.org/mozilla-central/rev/567b68b8ff4b6d607ba34a6f1926873d21a7b4d7/devtools/server/actors/webconsole/eval-with-debugger.js#223-232
let result;
if (frame) {
result = frame.evalWithBindings(string, bindings, evalOptions);
} else {
result = dbgWindow.executeInGlobalWithBindings(
string,
bindings,
evalOptions
);
}
- Where define the
dbgWindow
object used in previous code:
https://searchfox.org/mozilla-central/rev/567b68b8ff4b6d607ba34a6f1926873d21a7b4d7/devtools/server/actors/webconsole/eval-with-debugger.js#499-525
const dbgWindow = dbg.makeGlobalObjectReference(webConsole.evalWindow);
// If we have an object to bind to |_self|, create a Debugger.Object
// referring to that object, belonging to dbg.
if (!options.selectedObjectActor) {
return { bindSelf: null, dbgWindow };
}
const actor = webConsole.actor(options.selectedObjectActor);
if (!actor) {
return { bindSelf: null, dbgWindow };
}
const jsVal = actor instanceof LongStringActor ? actor.str : actor.rawValue();
if (!isObject(jsVal)) {
return { bindSelf: jsVal, dbgWindow };
}
// If we use the makeDebuggeeValue method of jsVal's own global, then
// we'll get a D.O that sees jsVal as viewed from its own compartment -
// that is, without wrappers. The evalWithBindings call will then wrap
// jsVal appropriately for the evaluation compartment.
const bindSelf = dbgWindow.makeDebuggeeValue(jsVal);
return { bindSelf, dbgWindow };
Logan, Do you have any idea how we could possibly implement this?
Could executeInGlobalWithBindings
be forced to execute via the system compartment?
Or should we provide a special dbgWindow
which would be the content window, but somehow wrapped from the system compartment?
Or something else...?
Otherwise, while this feature would be a really nice addition for the browser toolbox, it feels out of scope for M2.
We are now rather trying to focus on delivering Fission support for DevTools against tab rather than improving the Browser Toolbox.
Comment 10•5 years ago
|
||
Could
executeInGlobalWithBindings
be forced to execute via the system compartment?
My impression was that [ChromeOnly]
was something that took affect based on the itself, not the code accessing the object, so when you do $0.ownerDocument.defaultView.windowUtils
it is undefined
as expected because the document is not a Chrome document. Am I right about that? I wouldn't generally expect that the properties exposed from an object would change based on the permissions of who is doing the viewing, and I'd be surprised if there were a way to do that.
Or should we provide a special dbgWindow which would be the content window, but somehow wrapped from the system compartment?
I guess technically the console could wrap every value and provide a membrane that provided extra functionality like somehow injecting additional properties into an object, but to me that seems exceedingly complex, error-prone, and like a generally strange mental model to have to teach people.
Or something else...?
If the objective is to run Chrome code from the console but interact with a content window, what I'd expect devtools to do would be to have a selector that allows the developer to choose to execute console commands in a specific realm in the target like we allow users to select iframes now. I think for a fully-functional console that's probably necessary anyway since any number of realms could exist within a specific debugging session. Then instead of $0.ownerDocument.defaultView.windowUtils
you could explicitly select "JSM Realm" for the console realm and run ChromeUtils.import("WindowUtils.jsm").utilsForWindow($0.ownerDocument.defaultView)
to get the utilities by running Chrome-permissioned code while passing in a cross-compartment wrapper for the content window object.
That would require:
- Users can choose a realm for the console command to run in
$0
and such need to handle wrapping and unwrapping objects across compartments
Comment 11•5 years ago
|
||
(In reply to Logan Smyth [:loganfsmyth] from comment #10)
Could
executeInGlobalWithBindings
be forced to execute via the system compartment?My impression was that
[ChromeOnly]
was something that took affect based on the itself, not the code accessing the object, so when you do$0.ownerDocument.defaultView.windowUtils
it isundefined
as expected because the document is not a Chrome document. Am I right about that? I wouldn't generally expect that the properties exposed from an object would change based on the permissions of who is doing the viewing, and I'd be surprised if there were a way to do that.
Here is the definition of ChromeOnly.
will automatically check whether the caller script has the system principal (is chrome or a worker started from a chrome page)
So, it is a little bit of both. Depending on script principal, the object will change to either be:
- the "real"/"not proxified in any way" Window object from the content page, if the script principal is the content one,
or, - a Xray, if the script principal is the system principal.
Or something else...?
If the objective is to run Chrome code from the console but interact with a content window, what I'd expect devtools to do would be to have a selector that allows the developer to choose to execute console commands in a specific realm in the target like we allow users to select iframes now.
Yes, I was having something similar to this in mind. But instead of it being yet another element(s) in the selector, I was rather seeing a checkbox, only available when you are on a content principal. This checkbox would allow to run against the same content global, but via system principal/realm.
I think for a fully-functional console that's probably necessary anyway since any number of realms could exist within a specific debugging session. Then instead of
$0.ownerDocument.defaultView.windowUtils
you could explicitly select "JSM Realm" for the console realm and runChromeUtils.import("WindowUtils.jsm").utilsForWindow($0.ownerDocument.defaultView)
to get the utilities by running Chrome-permissioned code while passing in a cross-compartment wrapper for the content window object.
I think you are confused here. ChromeOnly will automatically appear on xrays, so you shouldn't need utilsForWindow
helper. Or if you need such helper, it means that are are somewhat trying to expose xrays to content principal scope, which kind of defeat/duplicate the role of xrays.
Getting back to my original question:
Could
executeInGlobalWithBindings
be forced to execute via the system compartment?
Looking at your response, it sounds like, in the current state, we can't do that?
Comment 12•5 years ago
|
||
a Xray, if the script principal is the system principal.
Right! Since we were talking about executing in the context of the content global itself, I hadn't really thought about cross-compartment wrappers coming into play.
I think you are confused here. ChromeOnly will automatically appear on xrays, so you shouldn't need utilsForWindow helper. Or if you need such helper, it means that are are somewhat trying to expose xrays to content principal scope, which kind of defeat/duplicate the role of xrays.
You're right I was confused, I forgot that xray wrappers were a thing because honestly when I said "exceedingly complex, error-prone, and like a generally strange mental model" about membranes above, that's what Xray wrappers are, and I continue stand by calling them confusing.
Looking at your response, it sounds like, in the current state, we can't do that?
I don't think there's an easy way that I can see to do that and I'm not sure it would really make sense in the context of how wrappers work. If you want to execute something in a chrome context, you should have that chrome context as a debuggee and be evaling in that global. So given what we've now said about wrappers, you could for instance do:
dbg.makeGlobalObjectReference(jsmChromeGlobal).executeInGlobalWithBindings("$0.ownerDocument.defaultView.windowUtils", { "$0": nodeDO })
I think it would work the way you're expecting it to work because it will end up wrapped.
But instead of it being yet another element(s) in the selector, I was rather seeing a checkbox, only available when you are on a content principal. This checkbox would allow to run against the same content global, but via system principal/realm.
I think the difficulty here is that you don't run code with a given principal, you run code in a realm that has a principal, if I understand right. The $0
would always represent an object in the content currently viewed in the inspector, potentially wrapped with an xray wrapper, but the code snippet in the console would need to be executed in the JSM realm, which has the system principal. Given that, it's not the principal you are choosing, it's the realm. If it is a checkbox, it seems like it would be extremely easy for users to misunderstand and then not know why their console isn't behaving like they expect.
Comment 13•5 years ago
|
||
(In reply to Logan Smyth [:loganfsmyth] from comment #12)
Looking at your response, it sounds like, in the current state, we can't do that?
I don't think there's an easy way that I can see to do that and I'm not sure it would really make sense in the context of how wrappers work. If you want to execute something in a chrome context, you should have that chrome context as a debuggee and be evaling in that global. So given what we've now said about wrappers, you could for instance do:
dbg.makeGlobalObjectReference(jsmChromeGlobal).executeInGlobalWithBindings("$0.ownerDocument.defaultView.windowUtils", { "$0": nodeDO })
I think it would work the way you're expecting it to work because it will end up wrapped.
I tried a bit this path, but hit the issue of "debugee can't be in the same compartment as debugger".
Doing that would require to load DevTools in another distinct system compartment. But may be that's something we have to live with until we possibly revisit this restriction.
But instead of it being yet another element(s) in the selector, I was rather seeing a checkbox, only available when you are on a content principal. This checkbox would allow to run against the same content global, but via system principal/realm.
I think the difficulty here is that you don't run code with a given principal, you run code in a realm that has a principal, if I understand right. The
$0
would always represent an object in the content currently viewed in the inspector, potentially wrapped with an xray wrapper, but the code snippet in the console would need to be executed in the JSM realm, which has the system principal. Given that, it's not the principal you are choosing, it's the realm. If it is a checkbox, it seems like it would be extremely easy for users to misunderstand and then not know why their console isn't behaving like they expect.
Realm... Principals... The concept of the two changed recently for me via bug 1357862. I probably mention compartment more than realm because I used to work more with compartments rather than realms. Feel free to replace any mention of compartment by realm.
My point is that there is mostly one realm/compartment we care about, the system one.
The checkbox may actually not even exist and we may execute via system realm/compartment by default from the Browser Toolbox. See comment 0 second STR. Gijs would assume this behavior to be the default one.
But for me, this is clearly a boolean flag, we either:
- keep things as it works today
- in the browser toolbox, optionaly or by default, execute through the system compartment/realm, so that we can have xrays, see the same expandos, and have access to ChromeOnly methods.
Now, yes, in theory, we might want to use any arbitrary realm, but I'm not sure there is any user request for it, and it would make the UI really complex with a matrix of all the globals versus all the realms.
Comment 14•5 years ago
|
||
I tried a bit this path, but hit the issue of "debugee can't be in the same compartment as debugger".
Doing that would require to load DevTools in another distinct system compartment. But may be that's something we have to live with until we possibly revisit this restriction.
Hard to say the specific cause without more of an example, but you're right that this would be a concern. I was under the impression that in the context of the browser toolbox, the JSM global realm would already be a debuggee, but I guess that varies depending on the target type and which type of server we're connected to.
My point is that there is mostly one realm/compartment we care about, the system one.
For the browser toolbox, there'd be one JSM realm Chrome-permissioned realm that we care about most per-process, but for instance you might want to select the JSM scope of a specific child process to run a command in the context of. Right now we rely on switching to the debugger to use its thread list to select this, but really it should be the console itself that chooses the global of console-executed strings.
The checkbox may actually not even exist and we may execute via system realm/compartment by default from the Browser Toolbox.
I think the parent-process JSM global is a reasonable default global to choose, but once you have to start interacting with content-process objects in the console, that becomes a lot less obvious, and by not having realm selector, the console itself has no way to reflect the realm that you'll be executing in if you run a console command at a particular point.
Now, yes, in theory, we might want to use any arbitrary realm, but I'm not sure there is any user request for it, and it would make the UI really complex with a matrix of all the globals versus all the realms.
Did you mean something other that "all the globals", since a global is a realm?
But for me, this is clearly a boolean flag, we either:
I think I misunderstood what you had in mind when you mentioned a boolean flag. If this a flag within the server itself, enabled for the browser-toolbox server, that says "use the jsm global for console eval by default" then I think that's fine. When you mentioned a flag I was thinking specifically about like checkbox in the devtools UI to enable "chrome-permission eval" or something.
so that we can have xrays, see the same expandos, and have access to ChromeOnly methods.
Don't xray wrappers hide expandos, so by opting into chrome-only access, you'd be opting out of the ability to see expando properties?
I think in general with this issue, I'm struggling to differentiate between what would be in my opinion ideal long-term, and what is actually achievable short-term. I'm also almost certainly talking about things that I simply don't have the full context on, so I hope you'll forgive me for that. I'm also very much a power user, so I'd love to be able to just choose what realm I can run a console command in.
Reporter | ||
Comment 15•5 years ago
|
||
(In reply to Logan Smyth [:loganfsmyth] from comment #14)
But for me, this is clearly a boolean flag, we either:
I think I misunderstood what you had in mind when you mentioned a boolean flag. If this a flag within the server itself, enabled for the browser-toolbox server, that says "use the jsm global for console eval by default" then I think that's fine. When you mentioned a flag I was thinking specifically about like checkbox in the devtools UI to enable "chrome-permission eval" or something.
From a user perspective, I would see the checkbox as implicit in the sense that if I opt to use the browser toolbox (rather than "normal" devtools), I'm opting into system principal evaluation.
so that we can have xrays, see the same expandos, and have access to ChromeOnly methods.
Don't xray wrappers hide expandos, so by opting into chrome-only access, you'd be opting out of the ability to see expando properties?
Yes, as a user I'd have to manually unwrap to see the expandos. But that is possible with Cu.waiveXrays
in this case; it's not possible to do the inverse, ie if I'm evaluating with a non-system-principal compartment/realm, I (of course!) cannot escalate from inside that scope to see the chrome-only things.
I think in general with this issue, I'm struggling to differentiate between what would be in my opinion ideal long-term, and what is actually achievable short-term. I'm also almost certainly talking about things that I simply don't have the full context on, so I hope you'll forgive me for that. I'm also very much a power user, so I'd love to be able to just choose what realm I can run a console command in.
There's a picker now, which chooses processes + worker scopes, in the browser toolbox's console. I don't know how easy it'd be to update that to be some kind of nested menu with realms, but yes, that'd help for (if you forgive the expression) "people like us". We would probably want to hide some of the complexity if we show that picker in the web console with fission enabled though...
Comment 16•5 years ago
|
||
(In reply to :Gijs (he/him) from comment #15)
There's a picker now, which chooses processes + worker scopes, in the browser toolbox's console. I don't know how easy it'd be to update that to be some kind of nested menu with realms, but yes, that'd help for (if you forgive the expression) "people like us". We would probably want to hide some of the complexity if we show that picker in the web console with fission enabled though...
Would you benefit from using a realm other than the shared system principal one? Supporting more than just that sounds like a quite rare edge cage to me, and would require some decent amount of work to make it right.
Otherwise, the following hack demonstrates comment 12 suggestion dbg.makeGlobalObjectReference(jsmChromeGlobal).executeInGlobalWithBindings("$0.ownerDocument.defaultView.windowUtils", { "$0": nodeDO })
:
diff --git a/devtools/server/actors/webconsole/eval-with-debugger.js b/devtools/server/actors/webconsole/eval-with-debugger.js
index 9d24ec2be8ef..3f6f17bc0eab 100644
--- a/devtools/server/actors/webconsole/eval-with-debugger.js
+++ b/devtools/server/actors/webconsole/eval-with-debugger.js
@@ -224,6 +224,11 @@ function getEvalResult(
if (frame) {
result = frame.evalWithBindings(string, bindings, evalOptions);
} else {
+ const ChromeUtils = require("ChromeUtils");
+ const { Cu } = require("chrome");
+ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ bindings.window = dbgWindow;
+ dbgWindow = dbg.makeGlobalObjectReference(Cu.getGlobalForObject(Services));
result = dbgWindow.executeInGlobalWithBindings(
string,
bindings,
It is interesting to see that:
- we do not hit the "debuggee must be in another compartment than its debugger" exception. Mostly because we don't try to add the JSM global as a new debugee. We only use
makeGlobalObjectReference
and notaddDebuggee
. - it demonstrates that it doesn't expose document globals. So you would have to use
bindings
argument to pass all document globals. I did it here only for thewindow
global. But we miss "document" and many others.
(3) many things are broken, like autocompletion and it throws a lot. But simple evaluation likewindow.docShell
works)
Comment 17•5 years ago
|
||
We only use makeGlobalObjectReference and not addDebuggee.
Good, I was hoping that addDebuggee
wouldn't be necessary, but I was not 100% sure if that case was supported.
bindings.window = dbgWindow;
This is the part that made me want a realm selector instead of an executing in the JSM realm but trying to quietly make it behave kind of like you're inside the content document. For instance in the console, something like a = 4
would create a new global called a
, and in this case that global will be created on the JSM global, not on the window global. In the general case, injecting window
seems super confusing because if you're executing code in the JSM realm, there is no one window, there are many. If we want to expose something like the selected window, I'd expect that to be a special binding like $window
so it's more clearly a special-case of the inspector's selected element's window. I think it makes sense for $0
because those already have behaviors that are relatively well-understood for users of the devtools.
Comment 18•5 years ago
|
||
Honestly the more I think about this the more I lean back to really wanting a realm selector that, for each process, shows the JSM realm, and any window frames. I don't think trying to quietly use the JSM realm is ever going to be transparent enough to not confuse people in all kinds of ways.
We only use makeGlobalObjectReference and not addDebuggee.
Thinking on this more, I really don't know how to feel about this actually. I really think if we're executing in the JSM realm, it should be a debuggee, and if it's not, we're gonna end up with really confusing behavior because if you run code in the console, you'll potentially be calling code that isn't visible in the debugger's source tree, and things like debugger;
and throw new Error()
and such won't cause the debugger to pause.
I appreciate that there's a desire to simplify the user experience by making it seem like you're executing code inside some specific window, but if we want to execute code with Chrome permissions, I really think
- That realm needs to be a debuggee
- If accessing a specific window is wanted, it should be done explicitly via a binding with a clear devtools-associated name like
$window
or by using the existing$0
-like bindings - We should not attempt to hide the fact that your console code runs in the JSM realm
So if you select a specific frame in the browser toolbox, that would mean that you're explicitly choosing to run code in the context of that realm with that realm's normal non-Chrome permissions, and if you want Chrome permissions, you need to access that by selecting the JSM realm of that process, and accessing the specific window realm by something like $0
or else we expose some explicit devtools functions to make it easier to save references to objects and reference them later once you've switched to the JSM realm with the selector.
Reporter | ||
Comment 19•5 years ago
|
||
Right now, could we use the frame message manager global/realm/whatever for a given window? It'll go away when message managers get completely replaced with actors and then we'll need something else, but we can cross that bridge when we get there. It comes with window
, and docShell
getters and such, and it's more or less what you got when basing things off one of the tabs
variable entries in the browser content toolbox. And yes, it should already be a debuggee as far as the debugger is concerned.
Comment 20•4 years ago
|
||
dt-fission-m2-reserve bugs do not need to block Fission Nightly (M6). For now, let's track them for Fission riding the trains to Beta (M7) so we revisit these bugs before we ship Fission.
Comment 21•4 years ago
|
||
Bulk move of all dt-fission-m2-reserve bugs to Fission MVP milestone.
Updated•4 years ago
|
Comment 22•4 years ago
|
||
Moving "dt-fission-m3-reserve" bugs to "dt-fission-future" because they don't block Fission MVP.
Updated•2 years ago
|
Description
•