Skip to content

Conversation

sadym-chromium
Copy link
Contributor

@sadym-chromium sadym-chromium commented Jul 8, 2025

  • Enable scripting disabled emulation via WebDriver BiDi. Required for WebDriver BiDi command emulation.setScriptingEnabled.
  • Allow bypassDisabledScripting to force-run scripts. Required for some automation scenarios like WebDriver BiDi command script.evaluate.

(See WHATWG Working Mode: Changes for more details.)


/infrastructure.html ( diff )
/webappapis.html ( diff )

@sadym-chromium sadym-chromium force-pushed the sadym/emulation.setDisableScriptingOverride branch from 61b293b to dbd6338 Compare July 23, 2025 08:23
@sadym-chromium sadym-chromium changed the title [DRAFT][WebDriver BiDi] add a way to force run script when "scripting is disabled" [DRAFT][WebDriver BiDi] enable / disable scripting via BiDi Jul 23, 2025
@sadym-chromium sadym-chromium changed the title [DRAFT][WebDriver BiDi] enable / disable scripting via BiDi [DRAFT][WebDriver BiDi] disable scripting via BiDi Jul 23, 2025
@sadym-chromium sadym-chromium changed the title [DRAFT][WebDriver BiDi] disable scripting via BiDi [WebDriver BiDi] enable and disable scripting via BiDi Jul 23, 2025
sadym-chromium added a commit to w3c/webdriver-bidi that referenced this pull request Aug 11, 2025
`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.
@sadym-chromium sadym-chromium force-pushed the sadym/emulation.setDisableScriptingOverride branch from 263cdc2 to dc2ae87 Compare August 13, 2025 14:07
@sadym-chromium sadym-chromium marked this pull request as ready for review August 13, 2025 14:12
@sadym-chromium
Copy link
Contributor Author

@domenic could you please take a look?

Copy link
Member

@domenic domenic left a 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 in script.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 by script.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 domenic added topic: script integration Better coordination across standards needed labels Aug 21, 2025
@sadym-chromium
Copy link
Contributor Author

@domenic thanks a lot for the detailed analyses!

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 in script.evaluate() will not actually run.

WebDriver BiDi spec invokes EcmaScript's ScriptEvaluation ( scriptRecord ) to run the script. This means the BiDi's scripts should work.

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 by script.evaluate(), element.onxyz === null.

I think this is acceptable.

  • HostEnqueueFinalizationRegistryCleanupJob's dependency means that weak references created by scripts from script.evaluate() will leak forever.

It looks like we can safely remove the "check if we can run script" step from HostEnqueueFinalizationRegistryCleanupJob, as there is no reason for not cleaning things up.

  • HostEnqueuePromiseJob's dependency means that promise handlers / async functions spawned from script.evaluate() will never run.

I assume the "check if we can run script" HostEnqueuePromiseJob is required to stop processing existing scripts if the script was disabled during the realm's lifetime. In Chromium, we don't perform this step.

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???

I don't think the blocklist addresses the issue with the JavaScript disabling during some script execution.

But of course, a blocklist approach is fragile in its own way.

I wonder what implementations do?

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:

  1. Remove "check if we can run script" from HostEnqueueFinalizationRegistryCleanupJob.
  2. Breaking change Remove "check if we can run script" from HostEnqueuePromiseJob

WDYT?

@sadym-chromium sadym-chromium force-pushed the sadym/emulation.setDisableScriptingOverride branch from dc2ae87 to 88b10e4 Compare August 21, 2025 16:16
@domenic
Copy link
Member

domenic commented Aug 22, 2025

WebDriver BiDi spec invokes EcmaScript's ScriptEvaluation ( scriptRecord ) to run the script. This means the BiDi's scripts should work.

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?

I think this is acceptable.

Really? Is that what implementations do?

It looks like we can safely remove the "check if we can run script" step from HostEnqueueFinalizationRegistryCleanupJob, as there is no reason for not cleaning things up.

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 someObject. At that point, "foo" will be logged, even though scripting is disabled. (Of course, it could be much worse than logging; it could execute arbitrary script.)

I doubt that is what you want?

I assume the "check if we can run script" HostEnqueuePromiseJob is required to stop processing existing scripts if the script was disabled during the realm's lifetime. In Chromium, we don't perform this step.

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".

…stEnqueuePromiseJob` when the scripting is disabled"

This reverts commit 88b10e4.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration Better coordination across standards needed topic: script
Development

Successfully merging this pull request may close these issues.

2 participants