Skip to content

[webview_flutter] Add document-start JavaScript API - #12442

Draft
lyllyl-bp wants to merge 2 commits into
flutter:mainfrom
lyllyl-bp:feature/webview-initial-user-script
Draft

[webview_flutter] Add document-start JavaScript API#12442
lyllyl-bp wants to merge 2 commits into
flutter:mainfrom
lyllyl-bp:feature/webview-initial-user-script

Conversation

@lyllyl-bp

Copy link
Copy Markdown

Adds addDocumentStartJavaScript to WebViewController and the platform interface, returning a DocumentStartJavaScript handle that can be used to stop injecting the script into future document loads.

Implements support on Android and WKWebView, including Android feature gating and WKWebView re-registration when user scripts are reset.

Throws UnsupportedError on unsupported platforms (including web), and updates examples, documentation, changelogs, generated bindings, and tests

Example usage:

final WebViewController controller = WebViewController();

final DocumentStartJavaScriptRegistration registration =
    await controller.addDocumentStartJavaScript(
  'window.exampleValue = "Hello from a document start script!";',
);

await controller.loadRequest(Uri.parse('https://flutter.dev'));

// Later, if the script should no longer be injected into future document loads:
await registration.remove();

The registered script runs:

  • at the start of each document loaded after addDocumentStartJavaScript is called;
  • before the loaded page's own scripts run;
  • in the order that document-start scripts were added;
  • in every frame of the loaded document, including cross-origin <iframe>s.

The script does not run in the currently loaded document, so apps should call addDocumentStartJavaScript before loading the page where the script is needed. Because the script is injected into every frame, including cross-origin frames, it should not contain sensitive data.

The returned DocumentStartJavaScriptRegistration can be kept and used to call remove(), which stops injecting that specific script into future document loads. If the script should remain active for the lifetime of the controller, the registration can be discarded.

This PR includes:

  • Adds addDocumentStartJavaScript to WebViewController.
  • Adds PlatformWebViewController.addDocumentStartJavaScript and PlatformDocumentStartJavaScriptRegistration to the platform interface.
  • Exposes the public DocumentStartJavaScriptRegistration handle from webview_flutter.
  • Implements document-start JavaScript support for Android.
  • Adds Android feature gating for the WebView DOCUMENT_START_SCRIPT feature.
  • Implements document-start JavaScript support for WKWebView.
  • Ensures WKWebView document-start scripts are re-registered when user scripts are reset.
  • Throws UnsupportedError on unsupported platforms, including web and Android devices whose installed WebView does not support DOCUMENT_START_SCRIPT.
  • Updates README documentation and example usage.
  • Updates generated Android Pigeon bindings.
  • Updates changelogs and package versions.
  • Adds and updates tests for the platform interface, Android implementation, WKWebView implementation, web unsupported behavior, examples, and generated mocks.

Fixes flutter/flutter#36752, flutter/flutter#82682

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

Adds addDocumentStartJavaScript to WebViewController and the platform
interface, returning a DocumentStartJavaScript handle that can be used
to stop injecting the script into future document loads.

Implements support on Android and WKWebView, including Android feature
gating and WKWebView re-registration when user scripts are reset.

Throws UnsupportedError on unsupported platforms (including web), and
updates examples, documentation, changelogs, generated bindings, and
tests

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds the addDocumentStartJavaScript feature to WebViewController across the webview_flutter packages, allowing users to inject JavaScript at the start of future document loads. The feature is implemented for Android and iOS, while throwing an UnsupportedError on the web and unsupported Android devices. The review feedback highlights potential race conditions in the iOS implementation (webview_flutter_wkwebview) when resetting user scripts concurrently or when adding a document start script concurrently with a reset.

Comment on lines +827 to +829
// This is awaited so that it cannot remove the scripts that are re-added
// below.
await controller.removeAllUserScripts();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Awaiting removeAllUserScripts introduces a race condition when _resetUserScripts is called concurrently (for example, if multiple scripts or channels are removed in the same event loop cycle without awaiting each sequentially). Concurrent overlapping executions of _resetUserScripts can result in duplicate scripts being registered or some scripts/channels being wiped out. Consider serializing executions of _resetUserScripts using a task queue or chaining futures to ensure thread-safe/reentrant-safe sequential execution.

Comment on lines +461 to +465
) async {
final registration = WebKitDocumentStartJavaScriptRegistration._(this, javaScript);
await _addDocumentStartJavaScript(javaScript);
_documentStartJavaScriptRegistrations.add(registration);
return registration;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a race condition between addDocumentStartJavaScript and _resetUserScripts. Because _addDocumentStartJavaScript is awaited before the registration is added to _documentStartJavaScriptRegistrations, a concurrent or pending _resetUserScripts execution can call removeAllUserScripts() and copy _documentStartJavaScriptRegistrations after the script is added to the webview but before the registration is added to the Dart list. This results in the script being removed from the webview and not re-added, leaving the Dart registration active but the script missing from the webview. To prevent this, consider serializing all user script operations (adding, removing, and resetting) using a sequential task queue or a Future chain.

@stuartmorgan-g

stuartmorgan-g commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the contribution! There is already a PR open for this feature, and we strongly prefer not to have everyone investing time in competing PRs that can't both land. I'm going to mark this as a Draft for now, pending resolution of that PR; if it doesn't proceed successfully, please feel free to mark this as ready for review again.

In the meantime, please feel free to leave review feedback on that PR if there are places where you feel it could be improved based on your experience preparing this PR.

@stuartmorgan-g
stuartmorgan-g marked this pull request as draft August 12, 2026 14:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[webview_flutter] Support injecting js script atDocumentStart instead of onload

2 participants