fix(servo): skip WebView::load when Servo already at requested URL
Servo's \`WebView::set_history\` fires \`notify_url_changed\` on **every** history mutation — full navigations, redirects, in-page link clicks, and JS-driven \`history.pushState\` / \`history.replaceState\`. Our pipeline fans that delegate signal back through \`WebSurfaceUrlChange\` into \`tab.url\`. The next \`ensure_surface\` observes the new tab URL, hashes a fresh ensure key, and calls \`ServoLiveClient::ensure\` → \`apply_navigation\`. Until this commit \`apply_navigation\` compared the request against the local \`session.requested_url\` cache and, on mismatch, sent Servo a \`webview.load(url)\` — even when Servo was the one who *just* told us about that URL. \`WebView::load\` is a hard navigation: it tells the constellation to abort the current document, clear the surface, and refetch. google.com's homepage \`replaceState\`s a fresh \`?zx=<timestamp>\` roughly once a second to bust caches; with this round-trip we were turning each of those into a full load-clear-refetch and producing one visible white flash per second. Use \`host.snapshot(webview_id).url()\` (which Servo keeps in lock-step with \`set_history\`) as the source of truth. If Servo's WebView is already at the requested URL, just sync our \`session.requested_url\` bookkeeping and return — no \`load\` message, no surface clear, no flash. Genuine embedder-initiated navigations (chrome URL bar typed, in-app link click) still take the \`should_navigate\` path because Servo's URL hasn't caught up to the requested target yet.
This commit is contained in:
@@ -234,6 +234,33 @@ impl ServoLiveClient {
|
||||
tab_id: TabId,
|
||||
requested_url: UrlText,
|
||||
) -> Result<(), ServoLiveError> {
|
||||
// Servo's `set_history` fires `notify_url_changed` on *every*
|
||||
// history mutation — full navigations, redirects, in-page
|
||||
// links, and JS-driven `history.pushState` / `replaceState`.
|
||||
// Our embedder fans that back through `WebSurfaceUrlChange`
|
||||
// into `tab.url`, which makes the next `ensure_surface` see a
|
||||
// "different" URL and call back into this method. Without a
|
||||
// guard we then send Servo `WebView::load(new_url)` for a URL
|
||||
// Servo just informed us it is *already* at — and `load` is a
|
||||
// hard navigation that aborts the live document, clears the
|
||||
// surface, and refetches.
|
||||
//
|
||||
// The google.com homepage `replaceState`s a `?zx=<timestamp>`
|
||||
// every second; under the unguarded path that turned into a
|
||||
// load-clear-refetch cycle per second, i.e. the continuous
|
||||
// white flash. Servo's own `webview.url()` (driven by
|
||||
// `set_history`) is the source of truth — if it already
|
||||
// matches the requested URL, this URL change came *from*
|
||||
// Servo and only needs an embedder-side bookkeeping sync.
|
||||
let servo_current_url =
|
||||
self.host.snapshot(webview_id)?.url().map(str::to_string);
|
||||
if servo_current_url.as_deref() == Some(requested_url.as_str()) {
|
||||
if let Some(session) = self.sessions.get_mut(&request.tab_id) {
|
||||
session.requested_url = Some(requested_url.as_str().to_string());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let should_navigate = self
|
||||
.sessions
|
||||
.get(&request.tab_id)
|
||||
|
||||
Reference in New Issue
Block a user