https://bugs.kde.org/show_bug.cgi?id=527056

--- Comment #3 from Con Hennessy <[email protected]> ---
This is what claude's deeper explanation is (and for my simple world it makes
sense).

"
Here's the mechanics behind the crash, walking through the backtrace:

What InterceptNavigationThrottle is for

components/navigation_interception is a piece of Chromium's content layer
specifically built for embedders (Android WebView, and QtWebEngine) that want
veto power over navigations before they commit. QtWebEngine attaches one of
these throttles to every main-frame navigation. When the throttle's
WillStartRequest() runs, it synchronously calls into
QWebEnginePage::acceptNavigationRequest() — that's the only reason your C++
override of acceptNavigationRequest() exists; it's a Qt-level callback invoked
from inside Chromium's navigation pipeline, not from some safe, decoupled
place.

Where the reentrancy happens

Look at the ownership chain from the bottom of your trace upward:

- NavigationControllerImpl::LoadURLWithParams (#22) starts a new navigation.

- Navigator::Navigate (#20) asks the FrameTreeNode for the slot to put it in.

- FrameTreeNode::TakeNavigationRequest → ResetNavigationRequestButKeepState
(#18-19): this is Chromium's rule that a FrameTreeNode can only have one
NavigationRequest in flight at a time. Starting a new one means the old one for
that frame is destroyed right here, synchronously.

- That destruction cascades: NavigationRequest::~NavigationRequest (#16) →
NavigationThrottleRegistryImpl::~NavigationThrottleRegistryImpl (#12) → the
throttle vector is torn down →
InterceptNavigationThrottle::~InterceptNavigationThrottle (#2-4) →
logging::CheckFailure() (#1) → abort.

The question is: whose old NavigationRequest is being destroyed? It's the one
for the link-click navigation that's still executing. Frame #26,
WebEngineFrame::openUrl, is the thing that called load() to start the "new"
navigation at #23-25. And WebEngineFrame::openUrl was reached via a plain,
direct (non-queued) Qt signal chain — Frame::signalOpenUrlRequest (#30) →
FrameManager::openUrl (#27) — that chain originates one level further up (above
where your paste cuts off) from
ArticleViewerWebEnginePage::acceptNavigationRequest(), i.e. from inside the
InterceptNavigationThrottle callback for the very navigation that's about to
get destroyed.

So the sequence on a single call stack, never returning to the event loop in
between, is:

1. Chromium starts navigating to the clicked link's URL.

2. InterceptNavigationThrottle::WillStartRequest() calls
acceptNavigationRequest().

3. Your override emits urlClicked — a direct Qt connection, so the slot runs
immediately, same stack.

4. That slot chain (slotLinkClicked → openSafeUrl → signalOpenUrlRequest →
FrameManager::openUrl → WebEngineFrame::openUrl → loadUrl) calls
QWebEnginePage::load() on the same page/FrameTreeNode, asking Chromium to start
a second navigation right now.

5. Chromium's TakeNavigationRequest() enforces "one NavigationRequest per
frame" by deleting the current one — but "the current one" is the
NavigationRequest whose throttle is still on the call stack, three frames
below, waiting for step 2's call to return.

6. InterceptNavigationThrottle's destructor has a CHECK guarding against
exactly this — being torn down while it's still mid-callback / hasn't been
resumed or cancelled through its own proper lifecycle — and it fires, which
under Chromium's DCHECK/CHECK machinery calls ImmediateCrash().

In short: step 2 never got to return false and let Chromium cancel the nav told
Chromium to replace it with a new one. You're deleting an object (the
throttle/NavigationRequest) out from under a stack frame that belongs to that
same object. That's a reentrancy bug, not a logic bug in the URL handling — the
decision (open this
link in the current tab) was correct, only the timing was wrong.

Why Qt::QueuedConnection fixes it

By queuing the urlClicked → slotLinkClicked connection, step 3 no longer  runs
the rest of the chain  synchronously. Instead, acceptNavigationRequest()
returns false immediately, Chromium's
InterceptNavigationThrottle::WillStartRequest() returns control cleanly, the
throttle and its NavigationRequest finish being cancelled/destroyed through
their normal lifecycle (no CHECK violation, since nothing else is trying to
replace it s back to the Qt event loop, and only then does the queued
slotLinkClickedrun and call load() — at which point there's no competing
in-flight NavigationRequest on that FrameTreeNode to destroy reentrantly.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to