[WebDriver] Properly report error: "No such window" (#37385)

For WebDriver, return "No Such Window" properly according to spec.

Testing: `./mach test-wpt -r --log-raw "D:\servo test log\all.txt"
.\tests\wpt\tests\webdriver\tests\classic\ --product servodriver`

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
Euclid Ye 2025-06-11 03:43:03 +08:00 committed by GitHub
parent 63e27bcab9
commit 56bbc49f21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 108 additions and 43 deletions

View file

@ -667,6 +667,7 @@ impl Handler {
cmd_msg: WebDriverScriptCommand,
) -> WebDriverResult<()> {
let browsing_context_id = self.session()?.browsing_context_id;
self.verify_browsing_context_is_open(browsing_context_id)?;
let msg = EmbedderToConstellationMessage::WebDriverCommand(
WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd_msg),
);
@ -675,7 +676,9 @@ impl Handler {
}
fn top_level_script_command(&self, cmd_msg: WebDriverScriptCommand) -> WebDriverResult<()> {
let browsing_context_id = BrowsingContextId::from(self.session()?.webview_id);
let webview_id = self.session()?.webview_id;
self.verify_top_level_browsing_context_is_open(webview_id)?;
let browsing_context_id = BrowsingContextId::from(webview_id);
let msg = EmbedderToConstellationMessage::WebDriverCommand(
WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd_msg),
);
@ -683,7 +686,14 @@ impl Handler {
Ok(())
}
/// <https://w3c.github.io/webdriver/#navigate-to>
fn handle_get(&self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
let webview_id = self.session()?.webview_id;
// Step 2. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(webview_id)?;
// Step 3. If URL is not an absolute URL or is not an absolute URL with fragment
// or not a local scheme, return error with error code invalid argument.
let url = match ServoUrl::parse(&parameters.url[..]) {
Ok(url) => url,
Err(_) => {
@ -694,8 +704,6 @@ impl Handler {
},
};
let webview_id = self.session()?.webview_id;
let cmd_msg =
WebDriverCommandMsg::LoadUrl(webview_id, url, self.load_status_sender.clone());
self.constellation_chan
@ -732,6 +740,9 @@ impl Handler {
fn handle_window_size(&self) -> WebDriverResult<WebDriverResponse> {
let (sender, receiver) = ipc::channel().unwrap();
let webview_id = self.session()?.webview_id;
self.verify_top_level_browsing_context_is_open(webview_id)?;
let cmd_msg = WebDriverCommandMsg::GetWindowSize(webview_id, sender);
self.constellation_chan
@ -764,6 +775,11 @@ impl Handler {
let height = params.height.unwrap_or(0);
let size = Size2D::new(width as u32, height as u32);
let webview_id = self.session()?.webview_id;
// TODO: Return some other error earlier if the size is invalid.
// Step 12. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(webview_id)?;
let cmd_msg = WebDriverCommandMsg::SetWindowSize(webview_id, size.to_i32(), sender.clone());
self.constellation_chan
@ -793,6 +809,10 @@ impl Handler {
}
fn handle_is_enabled(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
// Step 1. If session's current browsing context is no longer open,
// return error with error code no such window.
self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?;
let (sender, receiver) = ipc::channel().unwrap();
self.top_level_script_command(WebDriverScriptCommand::IsEnabled(
@ -809,6 +829,10 @@ impl Handler {
}
fn handle_is_selected(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
// Step 1. If session's current browsing context is no longer open,
// return error with error code no such window.
self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?;
let (sender, receiver) = ipc::channel().unwrap();
self.top_level_script_command(WebDriverScriptCommand::IsSelected(
@ -826,6 +850,9 @@ impl Handler {
fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
let webview_id = self.session()?.webview_id;
// Step 1. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(webview_id)?;
let direction = TraversalDirection::Back(1);
let msg = EmbedderToConstellationMessage::TraverseHistory(webview_id, direction);
self.constellation_chan.send(msg).unwrap();
@ -834,6 +861,9 @@ impl Handler {
fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
let webview_id = self.session()?.webview_id;
// Step 1. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(webview_id)?;
let direction = TraversalDirection::Forward(1);
let msg = EmbedderToConstellationMessage::TraverseHistory(webview_id, direction);
self.constellation_chan.send(msg).unwrap();
@ -842,6 +872,9 @@ impl Handler {
fn handle_refresh(&self) -> WebDriverResult<WebDriverResponse> {
let webview_id = self.session()?.webview_id;
// Step 1. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(webview_id)?;
let cmd_msg = WebDriverCommandMsg::Refresh(webview_id, self.load_status_sender.clone());
self.constellation_chan
@ -864,6 +897,9 @@ impl Handler {
fn handle_window_handle(&self) -> WebDriverResult<WebDriverResponse> {
let session = self.session.as_ref().unwrap();
// Step 1. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(session.webview_id)?;
match session.window_handles.get(&session.webview_id) {
Some(handle) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(handle)?,
@ -929,10 +965,13 @@ impl Handler {
}
}
/// <https://w3c.github.io/webdriver/#close-window>
fn handle_close_window(&mut self) -> WebDriverResult<WebDriverResponse> {
{
let webview_id = self.session()?.webview_id;
self.verify_top_level_browsing_context_is_open(webview_id)?;
let session = self.session_mut().unwrap();
session.window_handles.remove(&session.webview_id);
session.window_handles.remove(&webview_id);
let cmd_msg = WebDriverCommandMsg::CloseWebView(session.webview_id);
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
@ -957,8 +996,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let session = self.session().unwrap();
// Step 2. (TODO) If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(session.webview_id)?;
let cmd_msg = WebDriverCommandMsg::NewWebView(
session.webview_id,
@ -1505,6 +1543,9 @@ impl Handler {
&mut self,
parameters: ActionsParameters,
) -> WebDriverResult<WebDriverResponse> {
// Step 1. If session's current browsing context is no longer open,
// return error with error code no such window.
self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?;
// Step 5. Let actions by tick be the result of trying to extract an action sequence
let actions_by_tick = self.extract_an_action_sequence(parameters);
@ -1520,6 +1561,9 @@ impl Handler {
// TODO: The previous implementation of this function was different from the spec.
// Need to re-implement this to match the spec.
let session = self.session()?;
// Step 1. If session's current browsing context is no longer open,
// return error with error code no such window.
self.verify_browsing_context_is_open(session.browsing_context_id)?;
session.input_state_table.borrow_mut().clear();
Ok(WebDriverResponse::Void)
@ -1639,7 +1683,9 @@ impl Handler {
keys: &SendKeysParameters,
) -> WebDriverResult<WebDriverResponse> {
let browsing_context_id = self.session()?.browsing_context_id;
// Step 3. If session's current browsing context is no longer open,
// return error with error code no such window.
self.verify_browsing_context_is_open(browsing_context_id)?;
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::WillSendKeys(
@ -1759,6 +1805,9 @@ impl Handler {
}
fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
// Step 1. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(self.session()?.webview_id)?;
let mut img = None;
let interval = 1000;
@ -1906,6 +1955,46 @@ impl Handler {
serde_json::to_value(map)?,
)))
}
fn verify_top_level_browsing_context_is_open(
&self,
webview_id: WebViewId,
) -> Result<(), WebDriverError> {
let (sender, receiver) = ipc::channel().unwrap();
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(
WebDriverCommandMsg::IsWebViewOpen(webview_id, sender),
))
.unwrap();
if !receiver.recv().unwrap_or(false) {
Err(WebDriverError::new(
ErrorStatus::NoSuchWindow,
"No such window",
))
} else {
Ok(())
}
}
fn verify_browsing_context_is_open(
&self,
browsing_context_id: BrowsingContextId,
) -> Result<(), WebDriverError> {
let (sender, receiver) = ipc::channel().unwrap();
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(
WebDriverCommandMsg::IsBrowsingContextOpen(browsing_context_id, sender),
))
.unwrap();
if !receiver.recv().unwrap_or(false) {
Err(WebDriverError::new(
ErrorStatus::NoSuchWindow,
"No such window",
))
} else {
Ok(())
}
}
}
impl WebDriverHandler<ServoExtensionRoute> for Handler {