tools: Support tab navigation via navigateTo, goBack, and goForward (#43026)

Add support for navigation requests ("navigateTo", "goBack", and
"goForward") over the Remote Debugging Protocol. These may be sent by a
UI client in response to user input (for example the address bar in the
Firefox inspector), or they can be used to automate navigation during
unit tests.

This currently only supports navigation within the URL domain at which
servoshell is initially launched, due to a bug in servo's
`BrowsingContextActor` implementation. (Unit tests covering a fix for
that issue will depend on this change.)

Testing: The behavior of all 3 new message types is covered by a new
test case—`test_navigation`—in the devtools unit test suite.
Fixes: #38668

---------

Signed-off-by: Brent Schroeter <contact@brentsch.com>
Co-authored-by: eri <eri@igalia.com>
This commit is contained in:
Brent Schroeter
2026-03-06 01:13:39 -08:00
committed by GitHub
parent 9a915ece22
commit 4c69d856f6
7 changed files with 136 additions and 11 deletions

View File

@@ -43,7 +43,7 @@ use chrono::{DateTime, Local};
use constellation_traits::{
JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior, ScreenshotReadinessResponse,
ScriptToConstellationChan, ScriptToConstellationMessage, ScrollStateUpdate,
StructuredSerializedData, WindowSizeType,
StructuredSerializedData, TraversalDirection, WindowSizeType,
};
use crossbeam_channel::unbounded;
use data_url::mime::Mime;
@@ -2232,6 +2232,15 @@ impl ScriptThread {
DevtoolScriptControlMsg::RequestAnimationFrame(id, name) => {
devtools::handle_request_animation_frame(&documents, id, name)
},
DevtoolScriptControlMsg::NavigateTo(pipeline_id, url) => {
self.handle_navigate_to(pipeline_id, url)
},
DevtoolScriptControlMsg::GoBack(pipeline_id) => {
self.handle_traverse_history(pipeline_id, TraversalDirection::Back(1))
},
DevtoolScriptControlMsg::GoForward(pipeline_id) => {
self.handle_traverse_history(pipeline_id, TraversalDirection::Forward(1))
},
DevtoolScriptControlMsg::Reload(id) => self.handle_reload(id, CanGc::from_cx(cx)),
DevtoolScriptControlMsg::GetCssDatabase(reply) => {
devtools::handle_get_css_database(reply)
@@ -4109,6 +4118,39 @@ impl ScriptThread {
}
}
fn handle_navigate_to(&self, pipeline_id: PipelineId, url: ServoUrl) {
// The constellation only needs to know the WebView ID for navigation,
// but actors don't keep track of it. Infer WebView ID from pipeline ID instead.
if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
self.senders
.pipeline_to_constellation_sender
.send((
document.webview_id(),
pipeline_id,
ScriptToConstellationMessage::LoadUrl(
LoadData::new_for_new_unrelated_webview(url),
NavigationHistoryBehavior::Push,
),
))
.unwrap();
}
}
fn handle_traverse_history(&self, pipeline_id: PipelineId, direction: TraversalDirection) {
// The constellation only needs to know the WebView ID for navigation,
// but actors don't keep track of it. Infer WebView ID from pipeline ID instead.
if let Some(document) = self.documents.borrow().find_document(pipeline_id) {
self.senders
.pipeline_to_constellation_sender
.send((
document.webview_id(),
pipeline_id,
ScriptToConstellationMessage::TraverseHistory(direction),
))
.unwrap();
}
}
fn handle_reload(&self, pipeline_id: PipelineId, can_gc: CanGc) {
let window = self.documents.borrow().find_window(pipeline_id);
if let Some(window) = window {