Skip to content

feat: expose pending JavaScript invocations without draining the queue - #26658

Draft
totally-not-ai[bot] wants to merge 4 commits into
mainfrom
refactor/public-pending-js-invocations
Draft

totally-not-ai[bot] wants to merge 4 commits into
mainfrom
refactor/public-pending-js-invocations

Conversation

@totally-not-ai

@totally-not-ai totally-not-ai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Review risk: two-way door — widens one method of the internal-only UIInternals from package-private to public; no change for existing callers.

missing API · flow-server · tools that drive the client side without a browser, such as server-side UI test frameworks

Code that simulates the browser had no way to look at the JavaScript queued for the client without taking all of it. The only public reader marks every invocation as sent, so picking out one call, for example a page reload, dropped all other queued JavaScript. The queue can now be read without changing it.

Risks:

  • ⚠️ Public API: UIInternals.getPendingJavaScriptInvocations() is now public; the class is documented as internal-only
  • ✅ No behaviour, serialization, threading, security, memory or performance impact; no migration needed

Context. JsCall is documented as the way a driver without a browser recognises calls in the pending queue (definitionType(), methodName(), invokeOn(…)), and containsPendingJsCall can already tell whether such a call is queued, but nothing public could hand the invocation over so it can be acted on and canceled.

  • Made UIInternals.getPendingJavaScriptInvocations() public and changed its return type from Stream to List
    • It returns an unmodifiable snapshot of the queue without marking anything as sent and skips canceled invocations, so a caller can handle the calls it simulates, cancel them with PendingJavaScriptInvocation.cancelExecution() (which reports a failure to the call's error handler), and leave the rest for the client; invocations of invisible owners are included
    • Internal callers keep a private stream over the live queue, so isDirty() and the containsPending… checks do not copy it
    • The snapshot means acting on a call, such as canceling it when its error handler queues more JavaScript, no longer fails with ConcurrentModificationException
  • Rewrote its Javadoc to describe that contract and dropped the "non-private for testing" comment
  • Added a test that cancels one invocation while reading the queue and checks the others, including one queued by the error handler, are still sent

Working on behalf of Artur

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

 1 670 files  ±0   1 756 suites  ±0   2h 14m 11s ⏱️ - 4m 4s
13 026 tests +1  12 957 ✅ +1  69 💤 ±0  0 ❌ ±0 
13 660 runs  +1  13 591 ✅ +1  69 💤 ±0  0 ❌ ±0 

Results for commit ef966c0. ± Comparison against base commit f04c592.

♻️ This comment has been updated with latest results.

@totally-not-ai

Copy link
Copy Markdown
Contributor Author

Replaced both collect(Collectors.toList()) calls in the production dependency warning with Stream.toList(). The lists are only passed to addAll, so returning an unmodifiable list doesn't change anything.

session.checkHasLock();
return pendingJsInvocations.stream()
.filter(invocation -> !invocation.isCanceled());
.filter(invocation -> !invocation.isCanceled()).toList()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we anyway make a copy - wouldn't it be smarter to change the return type to List?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@Artur- Yes, that's better. getPendingJavaScriptInvocations() now returns an unmodifiable List snapshot. The internal callers (dumpPendingJavaScriptInvocations(), isDirty(), containsPendingJavascript and containsPendingJsCall) use a private stream over the live queue instead, so they don't copy it on every call. The tests now use size() and stream().

@github-actions github-actions Bot added +1.0.0 and removed +0.0.1 labels Oct 24, 2026
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant