Skip to content

[Win32][Edge] Fix BrowserFunction race condition using AddScriptToExecuteOnDocumentCreated#3166

Open
HeikoKlare wants to merge 1 commit intoeclipse-platform:masterfrom
HeikoKlare:issue-20
Open

[Win32][Edge] Fix BrowserFunction race condition using AddScriptToExecuteOnDocumentCreated#3166
HeikoKlare wants to merge 1 commit intoeclipse-platform:masterfrom
HeikoKlare:issue-20

Conversation

@HeikoKlare
Copy link
Copy Markdown
Contributor

@HeikoKlare HeikoKlare commented Apr 1, 2026

Summary

Fixes #20 (see also #2449 for a reproduction snippet).

In Edge/WebView2, execute() is asynchronous. When new BrowserFunction() is called, the injection script is queued via ExecuteScript(), but if a page navigation completes before WebView2 processes that queued script, the function is unavailable in the new document. This race is most easily triggered when two Browser instances are created in quick succession, as the second browser's initialization forces event-loop processing that can advance the first browser's navigation past the injection window.

Fix: Override createFunction() in Edge to additionally register the function script via AddScriptToExecuteOnDocumentCreated. This WebView2 API guarantees the script runs on every future document creation, before any page scripts — eliminating the race condition. The script ID returned by the async registration is stored so it can be cleaned up via RemoveScriptToExecuteOnDocumentCreated when the BrowserFunction is disposed.

When createFunction() is called from within a WebView2 callback (inCallback > 0), blocking on the registration would deadlock, so the persistent registration is skipped and the existing NavigationStarting re-injection remains as a fallback for that case.

Changes

  • ICoreWebView2.java: Add missing RemoveScriptToExecuteOnDocumentCreated vtbl binding (index 28, between AddScriptToExecuteOnDocumentCreated at 27 and ExecuteScript at 29)
  • Edge.java: Override createFunction() to persistently register via AddScriptToExecuteOnDocumentCreated; override deregisterFunction() to call RemoveScriptToExecuteOnDocumentCreated on disposal
  • Test_org_eclipse_swt_browser_Browser.java: Two regression tests (isEdge-only):
    • test_BrowserFunction_availableBeforePageScripts_issue20: registers a function, navigates to a page whose inline <script> immediately calls it, asserts the function was invoked (tests the AddScriptToExecuteOnDocumentCreated guarantee directly)
    • test_BrowserFunction_availableOnLoad_concurrentInstances_issue20: creates two browsers concurrently without waiting for initialization, verifies both functions are callable when their pages complete loading (replicates the original bug report timing)

Test plan

  • Run test_BrowserFunction_availableBeforePageScripts_issue20 on Windows with Edge
  • Run test_BrowserFunction_availableOnLoad_concurrentInstances_issue20 on Windows with Edge
  • Run existing test_BrowserFunction_* tests to verify no regressions
  • Manually verify with the reproduction snippet from #2449 comment

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Test Results

  141 files   -    29    141 suites   - 29   25m 11s ⏱️ - 1m 51s
4 683 tests +    4  4 660 ✅ +    2   23 💤 +2  0 ❌ ±0 
5 519 runs   - 1 073  5 371 ✅  - 1 066  148 💤  - 7  0 ❌ ±0 

Results for commit a7f51b1. ± Comparison against base commit e476728.

♻️ This comment has been updated with latest results.

AddScriptToExecuteOnDocumentCreated

In Edge/WebView2, execute() is asynchronous. When new BrowserFunction()
is
called, the injection script is queued via ExecuteScript(), but if a
page
navigation completes before WebView2 processes that queued script, the
function
is unavailable in the new document. This race is most easily triggered
when two
Browser instances are created in quick succession.

Fix: override createFunction() to also register the function script via
AddScriptToExecuteOnDocumentCreated. This API guarantees that the script
runs
on every future document creation, before any page scripts — eliminating
the
race condition. The script ID returned by the async registration is
stored so
it can be cleaned up via RemoveScriptToExecuteOnDocumentCreated when the
BrowserFunction is disposed.

When createFunction() is called from within a WebView2 callback
(inCallback>0),
blocking on the registration callback would deadlock, so the persistent
registration is skipped and the existing NavigationStarting re-injection
remains as a fallback.

Fixes eclipse-platform#20
@HeikoKlare
Copy link
Copy Markdown
Contributor Author

Test plan is done. The automated tests are run via CI anyway. I also successfully executed the reproduction snippet: #2449 (comment)

@sratz can you please review this Edge enhancement?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Edge: not properly loaded browser functions [Edge][BrowserFunction] BrowserFunctions added to window undefined when URL loaded

1 participant