-
Notifications
You must be signed in to change notification settings - Fork 3k
[WebDriver BiDi] enable and disable scripting via BiDi #11441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[WebDriver BiDi] enable and disable scripting via BiDi #11441
Conversation
61b293b to
dbd6338
Compare
`emulation. setScriptingEnabled` command. Related [HTML PR](whatwg/html#11441) should be landed after this one. * Store the state in `scripting enabled overrides map`. * Provide a hook "WebDriver BiDi scripting is enabled" to be called by HTML's ["Scripting is enabled"](https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-script) method. * Force scripting enabled for scripts initiated by BiDi protocol user via "bypassDisabledScripting" argument.
263cdc2 to
dc2ae87
Compare
|
@domenic could you please take a look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The edit to "scripting is disabled" makes sense, but I suspect the bypass is not thorough enough.
If you look at all the call sites for "scripting is disabled" or "scripting is enabled", the ones that worry me are:
#check-if-we-can-run-script's check means that the script created inscript.evaluate()will not actually run. If we ignore that, then the following are still broken:#getting-the-current-value-of-the-event-handler's dependency means that inside any script run byscript.evaluate(),element.onxyz === null.- HostEnqueueFinalizationRegistryCleanupJob's dependency means that weak references created by scripts from
script.evaluate()will leak forever. - HostEnqueuePromiseJob's dependency means that promise handlers / async functions spawned from
script.evaluate()will never run.
I'm not sure what a good approach that avoids these problems might be.
Maybe, it'd be best to flip this and try to blocklist all the "bad" sources of script, while leaving scripting enabled???? You could try to censor "create a classic script", "create a JavaScript module script", and "create a WebAssembly module script" by having them use the empty string / empty wasm byte sequence. That might catch them all???
But of course, a blocklist approach is fragile in its own way.
I wonder what implementations do?
|
@domenic thanks a lot for the detailed analyses!
WebDriver BiDi spec invokes EcmaScript's
I think this is acceptable.
It looks like we can safely remove the "check if we can run script" step from
I assume the "check if we can run script"
I don't think the blocklist addresses the issue with the JavaScript disabling during some script execution.
From the implementation perspective, in Chromium we don't stop processing microtasks queue when the scripting was disabled. In order to address the mentioned concerns I would propose to:
WDYT? |
dc2ae87 to
88b10e4
Compare
It is a little bit scary that you are not using the encapsulation HTML provides. For example, it looks like you are not handling parse errors (stored in "error to rethrow"). Do you really need the full power of going directly to the ECMAScript spec layer?
Really? Is that what implementations do?
The issue is that this might run the web developer-registered handlers for WeakRefs. I don't know if this is a concrete problem, but it is a conceptual one. For example, let's suppose WebDriver BiDi lets you mutably toggle on and off scripting disabled vs. enabled. (I can't tell quickly whether that is true or not, but maybe it is?) Then, JS code could do const someObject = {};
const registry = new FinalizationRegistry(() => {
console.log("foo");
});
registry.register(someObject);while scripting is enabled. Then, WebDriver BiDi makes scripting disabled. Then, GC runs and collects I doubt that is what you want?
Or maybe, that is what you want? If you disable script during a realm's lifetime, you are OK with still running script that was registered in promise handlers and weakref finalizers? Why? Why make exceptions for those constructs, in particular? What about, e.g., normal event listeners? In particular, it seems bad that web APIs that use promises vs. web APIs that use events have completely different behavior when WebDriver BiDi "disables script". |
annevk
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unfortunate that both Domenic and bz left the browser space as they would be the best reviewers for this. On the upside it's not web-exposed I suppose.
@annevk correct, it is not intended to be web exposed |
|
This is how currently Chrome behaves when emulating disabled JS (web-platform-tests/wpt#55479):
The
The promise chains ( |
7baaedb to
2b3b71e
Compare
To align spec with this behavior, I can move the check "if scripting is disabled" from "get the current value of the event handler" to "the event handler processing algorithm", so the "get the current value of the event handler" and "event handler IDL attribute" will always return the actual handler, but they will not be processed.
To align with the implementation, I can remove the step "Check if we can run script" from "8.1.6.6.2 HostEnqueueFinalizationRegistryCleanupJob(finalizationRegistry)". I don't think this approach exposes new risks, as in order to create "FinalizationRegistry", the script have to be executed. And the arbitrary script can be injected right there.
To align with the current implementation, the check "check if we can run script" should be removed from "8.1.6.6.4 HostEnqueuePromiseJob(job, realm)". The promise jobs are scheduled immediately one after another, so I don't think there is a way to force-stop JS execution between microtasks. |
|
That sounds sensible to me, but where in the event handler processing algorithm will you check if scripting is disabled? Browsers have internal event handlers for lots of things, so those still need to run, it's just event handlers that would run JS callbacks that we should skip. |
Current spec:
If scripting is disabled, getting the current value of the event handler returns null.
Proposed spec:
|
|
Hmm, scripting is disabled for nodes, and not all event targets are nodes. Is there a document you can check on instead? |
According to "get the current value of the event handler", the |
|
If we get the document from window that should work, yes. (Window isn't a Node) |
|
@foolip done |
foolip
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that the assert will hold.
While searching for terms in the spec I also found https://html.spec.whatwg.org/#killing-scripts which claims that "If scripting is disabled while a script is executing, the script should be terminated immediately." I'm not sure if browsers really do that, can you test it? If we should maintain that idea in the spec, then after all there might be extra work needed to prevent microtasks from running.
source
Outdated
| <code>Event</code> object <var>event</var> is as follows:</p> | ||
|
|
||
| <ol> | ||
| <li><p>If <var>eventTarget</var> is an element, then let <var>document</var> be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is similar to https://html.spec.whatwg.org/#getting-the-current-value-of-the-event-handler where it is assumed that eventTarget is an element or a document. I guess that assumption is true but didn't check carefully.
This ("the event handler processing algorithm") code seems to reachable for other types, however, via "activate an event handler" which in turn is referenced by by the https://html.spec.whatwg.org/#event-handler-idl-attributes setter. And there are other kinds of objects than nodes which have event handler IDL attribute, for example OffscreenCanvas and CloseWatcher.
Given that there is already a "scripting is disabled" check inside of https://html.spec.whatwg.org/#getting-the-current-value-of-the-event-handler, should we have checks in both places? What additional cases are covered by checking here?
Sorry, I'm not deeply familiar with this, learning as I go...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this PR I moved the "scripting is disabled" check from "get the current value of the event handler" to "the event handler processing algorithm", so that the IDL getters and setters are working even if the scripting is disabled. My assumption is that the all the on... events are invoked only by the "The event handler processing algorithm", while IDL setters and getters are used for assigning the handlers, but not invoking them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you removed the "If scripting is disabled for document, then return null" step, I missed that. You're indeed moving the check, not adding one.
You're right that the IDL setter is just for adding a handler, not invoking it, but it does create a callback that "executes the steps of the event handler processing algorithm" and the this object can be an OffscreenCanvas, it seems to me.
I think the reason it previously worked is because of the "If eventHandler's value is an internal raw uncompiled handler" condition, which would only be for Element and Window objects, from on... HTML attributes.
What is the expected behavior for normal event handlers registered via addEventListener()? I don't see it tested in web-platform-tests/wpt#54289 or web-platform-tests/wpt#55479 and I'm not sure what the behavior would be in the spec as it is. If only the "internal raw uncompiled handler" case has a scripting is disabled check, then do other event handlers just fire? This should be possible to test with a click event handler and clicking the element with WebDriver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I extended the "Scripting is enabled" / "Scripting is disabled" algorithms to accept platform objects. Does it address your concerns about OffscreenCanvas?
|
|
||
| <li><p>If <span data-x="concept-n-noscript">scripting is disabled</span> for | ||
| <var>document</var>, then return null.</p></li> | ||
| <li><p>If <var>document</var>'s <span>active sandboxing flag set</span> has its |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "sandboxed scripts browsing context flag" check used to be a part of the "Scripting is enabled". Now as the "Scripting is enabled" is removed, we still don't want to allow for sandboxed scripts to have access to the event handlers, so check "sandboxed scripts browsing context flag" here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect that the sandbox flag check isn't needed for the same reason it could be removed from FinalizationRegistry and microtasks above. For <iframe sandbox> without the "allow-scripts" flag, no scripts can run in the first place.
It's possible I don't understand sandboxing, but we should probably do the same thing in all three of these cases.
71f16b0 to
c160f0c
Compare
|
@zcorpan can you take a look at this? I've reviewed it and I think it all makes sense and matches the desired behavior now, but I'm worried there are details here that I don't understand all the ways that scripts can run, sandboxing, etc. |
Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review)
…rm-tests#55479) Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review)
…ripting emulation, a=testonly Automatic update from web-platform-tests [wdspec] tentative tests for disabled scripting emulation (#55479) Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review) -- wpt-commits: 80cc31004c3109482251ce68ca8a31845dad78ec wpt-pr: 55479
…ripting emulation, a=testonly Automatic update from web-platform-tests [wdspec] tentative tests for disabled scripting emulation (#55479) Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review) -- wpt-commits: 80cc31004c3109482251ce68ca8a31845dad78ec wpt-pr: 55479
…ripting emulation, a=testonly Automatic update from web-platform-tests [wdspec] tentative tests for disabled scripting emulation (#55479) Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review) -- wpt-commits: 80cc31004c3109482251ce68ca8a31845dad78ec wpt-pr: 55479 UltraBlame original commit: 387b26784c3943e7d4bbc5efcd31cf58a6c43db8
…ripting emulation, a=testonly Automatic update from web-platform-tests [wdspec] tentative tests for disabled scripting emulation (#55479) Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review) -- wpt-commits: 80cc31004c3109482251ce68ca8a31845dad78ec wpt-pr: 55479 UltraBlame original commit: 387b26784c3943e7d4bbc5efcd31cf58a6c43db8
…ripting emulation, a=testonly Automatic update from web-platform-tests [wdspec] tentative tests for disabled scripting emulation (#55479) Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review) -- wpt-commits: 80cc31004c3109482251ce68ca8a31845dad78ec wpt-pr: 55479 UltraBlame original commit: 387b26784c3943e7d4bbc5efcd31cf58a6c43db8
|
Domenic's questions about "why" in #11441 (comment) are not well addressed, as far as I can tell. I've discussed with @juliandescottes , he will file an issue that details the use cases and rationale for the exceptions to disabling script. |
I believe this relates to:
Which in turn relates to:
Basically the question is what should happen if there is an infinite loop with promises, and the user disables the page. In case of Chromium, we don't stop already running JS, but prevent from running those scheduled by |
Based on whatwg#11441. The bare minimal required changes.
…ePromiseJob` when the scripting is disabled
…stEnqueuePromiseJob` when the scripting is disabled" This reverts commit 88b10e4.
… of the event handler" to "the event handler processing algorithm", so the "get the current value of the event handler" and "event handler IDL attribute" will always return the actual handler, but they will not be processed.
…eueFinalizationRegistryCleanupJob(finalizationRegistry)"
c00ca69 to
5174d2e
Compare
|
I created another PR #11884 with a bare minimal hooks required for BiDi emulation of disabled JS. I propose to land it first, and to continue discussion on the controversial nuances here. After we have a consensus, we land this PR as a follow-up. |
…ripting emulation, a=testonly Automatic update from web-platform-tests [wdspec] tentative tests for disabled scripting emulation (#55479) Checks the behavior of disabled scripting for the corner cases mentioned in whatwg/html#11441 (review) -- wpt-commits: 80cc31004c3109482251ce68ca8a31845dad78ec wpt-pr: 55479
emulation.setScriptingEnabled.bypassDisabledScriptingto force-run scripts. Required for some automation scenarios like WebDriver BiDi commandscript.evaluate.Observable effects:
on...IDL properties return functions instead of null even if the scripting is off.FinalizationRegistryis triggered even when the JS is turned off..thenworks even if the scripting is off.Tests for effects: web-platform-tests/wpt#55479
emulation.setScriptingEnabledweb-platform-tests/wpt#54289emulation.setScriptingEnabledcommand GoogleChromeLabs/chromium-bidi#3566(See WHATWG Working Mode: Changes for more details.)
/infrastructure.html ( diff )
/webappapis.html ( diff )