Skip to content

WebContent: Flush pending WebDriver messages before the process exits - #11028

Merged
AtkinsSJ merged 2 commits into
LadybirdBrowser:masterfrom
sideshowbarker:webcontent-flush-webdriver-on-exit
Aug 7, 2026
Merged

WebContent: Flush pending WebDriver messages before the process exits#11028
AtkinsSJ merged 2 commits into
LadybirdBrowser:masterfrom
sideshowbarker:webcontent-flush-webdriver-on-exit

Conversation

@sideshowbarker

Copy link
Copy Markdown
Member

Problem: TestWebDriverSessionHistory timeouts on the x86_64 Release GNU CI job in the cross-process no-content navigation-restore subtest. The harness waits 30s for the restored /a document — which never loads.

https://github.com/LadybirdBrowser/ladybird/actions/runs/30988508142
https://github.com/LadybirdBrowser/ladybird/actions/runs/30999552155
https://github.com/LadybirdBrowser/ladybird/actions/runs/31042505962
https://github.com/LadybirdBrowser/ladybird/actions/runs/31082213133

Cause: When a cross-site navigation replaces a WebContent process, the UI tells the outgoing process to notify WebDriver of the window replacement, then immediately tells it to close. Both are async IPC messages: the notify only enqueues on the send queue for the IO thread to write, while close_server() exits the process via terminate_immediately(). Under load, the process exits before the IO thread flushes, so WebDriver never receives the replacement notice. It sees only the socket EOF, treats the window as closed, and tears the session down — SIGTERM-ing the UI mid-restore, so the restored /a document never loads.

Fix: Before WebContent exits in close_server(), flush each page’s WebDriver transport with close_after_sending_all_pending_messages(), so the pending-replacement notice reaches WebDriver ahead of the EOF. The flush runs in close_server() rather than die() because die() can run during construction, before the page host is initialized.

The first commit in the branch here is a separate-but-related ordering bug found incidentally on the way: A sync IPC wait could report Failed to receive message_id: N even though the peer had sent the response — when the peer replied and then exited so that the response and the socket EOF were read in the same drain. wait_for_specific_endpoint_message_impl() broke out of its loop on EOF without rescanning the unprocessed messages the drain had just appended. It now rescans once — before reporting failure.

Problem: A sync IPC wait could fail with “Failed to receive message_id:
N” even though the peer had sent the response, when the peer replied and
then exited so the response and socket EOF were read in the same drain.

Cause: wait_for_specific_endpoint_message_impl() scans the unprocessed
messages, then blocks. On EOF it breaks out of the loop and reported
failure without rescanning — but the drain that reported the EOF has
just appended the awaited response to the queue, where it sits unread.

Fix: After the loop breaks with the peer-disconnected flag set, scan the
unprocessed messages once more and return the awaited response if it’s
there. The drain scheduled its deferred handle_messages before its
deferred shutdown, so any other messages from that final batch are still
dispatched before the connection close is acted on.
Problem: TestWebDriverSessionHistory timeouts on the x86_64 Release GNU
CI job in the cross-process no-content navigation-restore subtest. The
harness waits 30s for the restored /a document — which never loads.

https://github.com/LadybirdBrowser/ladybird/actions/runs/30988508142
https://github.com/LadybirdBrowser/ladybird/actions/runs/30999552155
https://github.com/LadybirdBrowser/ladybird/actions/runs/31042505962
https://github.com/LadybirdBrowser/ladybird/actions/runs/31082213133

Cause: When a cross-site navigation replaces a WebContent process, the
UI tells the outgoing process to notify WebDriver of the window replace-
ment, then immediately tells it to close. Both are async IPC messages:
the notify only enqueues on the send queue for the IO thread to write,
while close_server() exits the process via terminate_immediately().
Under load, the process exits before the IO thread flushes, so WebDriver
never receives the replacement notice. It sees only the socket EOF,
treats the window as closed, and tears the session down — SIGTERM-ing
the UI mid-restore, so the restored /a document never loads.

Fix: Before WebContent exits in close_server(), flush each page’s
WebDriver transport with close_after_sending_all_pending_messages(), so
the pending-replacement notice reaches WebDriver ahead of the EOF. The
flush runs in close_server() rather than die() because die() can run
during construction, before the page host is initialized.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 40b3fd9d-1f7e-42e0-870e-90cf6f246480

📥 Commits

Reviewing files that changed from the base of the PR and between bf6c4c7 and 4759a00.

📒 Files selected for processing (6)
  • Libraries/LibIPC/Connection.cpp
  • Services/WebContent/ConnectionFromClient.cpp
  • Services/WebContent/PageClient.cpp
  • Services/WebContent/PageClient.h
  • Services/WebContent/PageHost.cpp
  • Services/WebContent/PageHost.h

📝 Walkthrough

Walkthrough

The change preserves responses delivered during IPC peer disconnect handling. It also defers WebDriver connection closure until pending messages are sent during WebContent server shutdown.

Changes

Connection shutdown handling

Layer / File(s) Summary
Retrieve queued responses after EOF
Libraries/LibIPC/Connection.cpp
Queued-message matching is reused for the initial lookup and repeated after an EOF-triggering drain.
Defer WebDriver closure during server shutdown
Services/WebContent/ConnectionFromClient.cpp, Services/WebContent/PageHost.*, Services/WebContent/PageClient.*
Server shutdown requests each page to close its WebDriver connection after pending messages are transmitted.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ConnectionFromClient
  participant PageHost
  participant PageClient
  participant WebDriverTransport
  ConnectionFromClient->>PageHost: close WebDriver connections after pending messages
  PageHost->>PageClient: request deferred closure
  PageClient->>WebDriverTransport: close after flushing pending messages
  ConnectionFromClient->>ConnectionFromClient: shut down IPC
Loading

Possibly related PRs

Suggested reviewers: kalenikaliaksandr, trflynn89

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description directly explains the WebDriver timeout, its IPC causes, and the corresponding fixes in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@AtkinsSJ AtkinsSJ left a comment

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.

Thanks for tracking this down!

@AtkinsSJ
AtkinsSJ merged commit e14fd3f into LadybirdBrowser:master Aug 7, 2026
21 of 22 checks passed
@sideshowbarker
sideshowbarker deleted the webcontent-flush-webdriver-on-exit branch August 7, 2026 19:50
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.

2 participants