From 2e8fac939567c61ac7172309442df648bb814878 Mon Sep 17 00:00:00 2001 From: Euclid Ye Date: Sat, 27 Sep 2025 10:20:35 +0800 Subject: [PATCH] webdriver: Elegantly handle "element screenshot" when bounding box has area zero (#39499) It is possible that the bounding rectangle of an element has area 0. This PR avoids panic in this case. It is worth to mention that the panic itself won't kill the entire program for interaction, but only the webdriver thread. Testing: Manually tested on the case of #39495 Signed-off-by: Euclid Ye --- components/servo/lib.rs | 11 ++++++++++- components/shared/embedder/webdriver.rs | 2 +- components/webdriver_server/lib.rs | 25 +++++++++++++++++-------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 7c504f08b29..dd2b3965ac5 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -1059,6 +1059,15 @@ impl Servo { pub fn execute_webdriver_command(&self, command: WebDriverCommandMsg) { if let WebDriverCommandMsg::TakeScreenshot(webview_id, page_rect, response_sender) = command { + if let Some(ref rect) = page_rect { + if rect.height() == 0.0 || rect.width() == 0.0 { + error!("Taking screenshot of bounding box with zero area"); + if let Err(e) = response_sender.send(Err(())) { + error!("Sending reply to create png failed {e:?}"); + } + } + } + let res = self .compositor .borrow_mut() @@ -1067,7 +1076,7 @@ impl Servo { error!("Error retrieving PNG: {:?}", e); } let img = res.unwrap_or(None); - if let Err(e) = response_sender.send(img) { + if let Err(e) = response_sender.send(Ok(img)) { error!("Sending reply to create png failed ({:?}).", e); } } else { diff --git a/components/shared/embedder/webdriver.rs b/components/shared/embedder/webdriver.rs index 2b27615ab26..45c888b032f 100644 --- a/components/shared/embedder/webdriver.rs +++ b/components/shared/embedder/webdriver.rs @@ -144,7 +144,7 @@ pub enum WebDriverCommandMsg { TakeScreenshot( WebViewId, Option>, - IpcSender>, + IpcSender, ()>>, ), /// Create a new webview that loads about:blank. The embedder will use /// the provided channels to return the top level browsing context id diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 4b5f10a52ba..4dbfe5bd325 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -2320,14 +2320,23 @@ impl Handler { for _ in 0..iterations { let (sender, receiver) = ipc::channel().unwrap(); - self.send_message_to_embedder(WebDriverCommandMsg::TakeScreenshot( webview_id, rect, sender, ))?; - if let Some(x) = wait_for_ipc_response(receiver)? { - img = Some(x); - break; + match wait_for_ipc_response(receiver)? { + Ok(output_img) => { + if let Some(x) = output_img { + img = Some(x); + break; + } + }, + Err(()) => { + return Err(WebDriverError::new( + ErrorStatus::UnknownError, + "The bounding box of element has either 0 width or 0 height", + )); + }, }; thread::sleep(Duration::from_millis(interval)); @@ -2384,8 +2393,6 @@ impl Handler { &self, element: &WebElement, ) -> WebDriverResult { - let (sender, receiver) = ipc::channel().unwrap(); - // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. let webview_id = self.webview_id()?; @@ -2394,7 +2401,9 @@ impl Handler { // Step 2. Try to handle any user prompts with session. self.handle_any_user_prompts(webview_id)?; - // Step 3 - 4 + // Step 3. Trying to get element. + // Step 4. Scroll into view into element. + let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::ScrollAndGetBoundingClientRect(element.to_string(), sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?; @@ -2409,7 +2418,7 @@ impl Handler { serde_json::to_value(encoded)?, ))) }, - Err(error) => Err(WebDriverError::new(error, "Element not found")), + Err(error) => Err(WebDriverError::new(error, "")), } }