[webview_flutter] Add document-start JavaScript API - #12442
Conversation
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
There was a problem hiding this comment.
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.
| // This is awaited so that it cannot remove the scripts that are re-added | ||
| // below. | ||
| await controller.removeAllUserScripts(); |
There was a problem hiding this comment.
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.
| ) async { | ||
| final registration = WebKitDocumentStartJavaScriptRegistration._(this, javaScript); | ||
| await _addDocumentStartJavaScript(javaScript); | ||
| _documentStartJavaScriptRegistrations.add(registration); | ||
| return registration; |
There was a problem hiding this comment.
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.
|
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. |
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:
The registered script runs:
addDocumentStartJavaScriptis called;<iframe>s.The script does not run in the currently loaded document, so apps should call
addDocumentStartJavaScriptbefore 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
DocumentStartJavaScriptRegistrationcan be kept and used to callremove(), 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:
addDocumentStartJavaScripttoWebViewController.PlatformWebViewController.addDocumentStartJavaScriptandPlatformDocumentStartJavaScriptRegistrationto the platform interface.DocumentStartJavaScriptRegistrationhandle fromwebview_flutter.DOCUMENT_START_SCRIPTfeature.UnsupportedErroron unsupported platforms, including web and Android devices whose installed WebView does not support DOCUMENT_START_SCRIPT.Fixes flutter/flutter#36752, flutter/flutter#82682
Pre-Review Checklist
[shared_preferences]///).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-assistbot 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
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