Files
servo/components/shared/embedder/webdriver.rs
Euclid Ye 16f9f7500e webdriver: Retrieve viewport size in CSS pixel (#42628)
According to
[spec](https://w3c.github.io/webdriver/#ref-for-dfn-move-target-out-of-bounds-2:~:text=the%20viewport%20in%20CSS%20pixels),
we are supposed to retrieve viewport size in CSS pixel.

This is a critical step when performing scroll/pointermove, to stop
invalid actions that happen outside viewport.

Testing: Tested with tests in headed mode manually, narrowing
discrepancy between headed/headless mode. Should not have effect on
automated tests in CI.

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
2026-02-15 07:55:08 +00:00

302 lines
11 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#![allow(missing_docs)]
use std::collections::HashMap;
use base::generic_channel::{GenericOneshotSender, GenericSender};
use base::id::{BrowsingContextId, WebViewId};
use cookie::Cookie;
use crossbeam_channel::Sender;
use euclid::default::Rect as UntypedRect;
use euclid::{Rect, Size2D};
use hyper_serde::Serde;
use image::RgbaImage;
use malloc_size_of_derive::MallocSizeOf;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentPixel};
use style_traits::CSSPixel;
use url::Url;
use webdriver::error::ErrorStatus;
use crate::{InputEvent, JSValue, JavaScriptEvaluationError, ScreenshotCaptureError, TraversalId};
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum WebDriverUserPrompt {
Alert,
BeforeUnload,
Confirm,
Default,
File,
Prompt,
FallbackDefault,
}
impl WebDriverUserPrompt {
pub fn new_from_str(s: &str) -> Option<Self> {
match s {
"alert" => Some(WebDriverUserPrompt::Alert),
"beforeUnload" => Some(WebDriverUserPrompt::BeforeUnload),
"confirm" => Some(WebDriverUserPrompt::Confirm),
"default" => Some(WebDriverUserPrompt::Default),
"file" => Some(WebDriverUserPrompt::File),
"prompt" => Some(WebDriverUserPrompt::Prompt),
"fallbackDefault" => Some(WebDriverUserPrompt::FallbackDefault),
_ => None,
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum WebDriverUserPromptAction {
Accept,
Dismiss,
Ignore,
}
impl WebDriverUserPromptAction {
pub fn new_from_str(s: &str) -> Option<Self> {
match s {
"accept" => Some(WebDriverUserPromptAction::Accept),
"dismiss" => Some(WebDriverUserPromptAction::Dismiss),
"ignore" => Some(WebDriverUserPromptAction::Ignore),
_ => None,
}
}
}
/// <https://html.spec.whatwg.org/multipage/#registerprotocolhandler()-automation-mode>
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
pub enum CustomHandlersAutomationMode {
AutoAccept,
AutoReject,
#[default]
None,
}
/// <https://w3c.github.io/webdriver/#new-window>
#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
pub enum NewWindowTypeHint {
Auto,
Tab,
Window,
}
/// Messages to the constellation originating from the WebDriver server.
#[derive(Debug)]
pub enum WebDriverCommandMsg {
/// Get the window rectangle.
GetWindowRect(WebViewId, GenericOneshotSender<DeviceIndependentIntRect>),
/// Get the viewport size.
GetViewportSize(
WebViewId,
GenericOneshotSender<Size2D<f32, DeviceIndependentPixel>>,
),
/// Load a URL in the top-level browsing context with the given ID.
LoadUrl(WebViewId, Url, GenericSender<WebDriverLoadStatus>),
/// Refresh the top-level browsing context with the given ID.
Refresh(WebViewId, GenericSender<WebDriverLoadStatus>),
/// Navigate the webview with the given ID to the previous page in the browsing context's history.
GoBack(WebViewId, GenericSender<WebDriverLoadStatus>),
/// Navigate the webview with the given ID to the next page in the browsing context's history.
GoForward(WebViewId, GenericSender<WebDriverLoadStatus>),
/// Pass a webdriver command to the script thread of the current pipeline
/// of a browsing context.
ScriptCommand(BrowsingContextId, WebDriverScriptCommand),
/// Dispatch an input event to the given [`WebView`]. Once the event has been handled in the
/// page DOM a single message should be sent through the [`Sender`], if provided, informing the
/// WebDriver server that the inpute event has been handled.
InputEvent(WebViewId, InputEvent, Option<Sender<()>>),
/// Set the outer window rectangle.
SetWindowRect(
WebViewId,
DeviceIndependentIntRect,
GenericOneshotSender<DeviceIndependentIntRect>,
),
/// Maximize the window. Send back result window rectangle.
MaximizeWebView(WebViewId, GenericOneshotSender<DeviceIndependentIntRect>),
/// Take a screenshot of the viewport.
TakeScreenshot(
WebViewId,
Option<Rect<f32, CSSPixel>>,
Sender<Result<RgbaImage, ScreenshotCaptureError>>,
),
/// Create a new webview that loads about:blank. The embedder will use
/// the provided channels to return the top level browsing context id
/// associated with the new webview, and sets a "load status sender" if provided.
NewWindow(
NewWindowTypeHint,
GenericOneshotSender<WebViewId>,
Option<GenericSender<WebDriverLoadStatus>>,
),
/// Close the webview associated with the provided id.
CloseWebView(WebViewId, GenericOneshotSender<()>),
/// Focus the webview associated with the provided id.
FocusWebView(WebViewId),
/// Get focused webview. For now, this is only used when start new session.
GetFocusedWebView(GenericOneshotSender<Option<WebViewId>>),
/// Get webviews state
GetAllWebViews(GenericOneshotSender<Vec<WebViewId>>),
/// Check whether top-level browsing context is open.
IsWebViewOpen(WebViewId, GenericOneshotSender<bool>),
/// Check whether browsing context is open.
IsBrowsingContextOpen(BrowsingContextId, GenericOneshotSender<bool>),
CurrentUserPrompt(WebViewId, GenericOneshotSender<Option<WebDriverUserPrompt>>),
HandleUserPrompt(
WebViewId,
WebDriverUserPromptAction,
GenericOneshotSender<Result<String, ()>>,
),
GetAlertText(WebViewId, GenericOneshotSender<Result<String, ()>>),
SendAlertText(WebViewId, String),
FocusBrowsingContext(BrowsingContextId),
Shutdown,
ResetAllCookies(Sender<()>),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverScriptCommand {
AddCookie(
#[serde(
deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize"
)]
Cookie<'static>,
GenericSender<Result<(), ErrorStatus>>,
),
DeleteCookies(GenericSender<Result<(), ErrorStatus>>),
DeleteCookie(String, GenericSender<Result<(), ErrorStatus>>),
ElementClear(String, GenericSender<Result<(), ErrorStatus>>),
ExecuteScriptWithCallback(String, GenericSender<WebDriverJSResult>),
FindElementsCSSSelector(String, GenericSender<Result<Vec<String>, ErrorStatus>>),
FindElementsLinkText(
String,
bool,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindElementsTagName(String, GenericSender<Result<Vec<String>, ErrorStatus>>),
FindElementsXpathSelector(String, GenericSender<Result<Vec<String>, ErrorStatus>>),
FindElementElementsCSSSelector(
String,
String,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindElementElementsLinkText(
String,
String,
bool,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindElementElementsTagName(
String,
String,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindElementElementsXPathSelector(
String,
String,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindShadowElementsCSSSelector(
String,
String,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindShadowElementsLinkText(
String,
String,
bool,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindShadowElementsTagName(
String,
String,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
FindShadowElementsXPathSelector(
String,
String,
GenericSender<Result<Vec<String>, ErrorStatus>>,
),
GetElementShadowRoot(String, GenericSender<Result<Option<String>, ErrorStatus>>),
ElementClick(String, GenericSender<Result<Option<String>, ErrorStatus>>),
GetKnownElement(String, GenericSender<Result<(), ErrorStatus>>),
GetKnownShadowRoot(String, GenericSender<Result<(), ErrorStatus>>),
GetKnownWindow(String, GenericSender<Result<(), ErrorStatus>>),
GetActiveElement(GenericSender<Option<String>>),
GetComputedRole(String, GenericSender<Result<Option<String>, ErrorStatus>>),
GetCookie(
String,
GenericSender<Result<Vec<Serde<Cookie<'static>>>, ErrorStatus>>,
),
GetCookies(GenericSender<Result<Vec<Serde<Cookie<'static>>>, ErrorStatus>>),
GetElementAttribute(
String,
String,
GenericSender<Result<Option<String>, ErrorStatus>>,
),
GetElementProperty(String, String, GenericSender<Result<JSValue, ErrorStatus>>),
GetElementCSS(String, String, GenericSender<Result<String, ErrorStatus>>),
GetElementRect(String, GenericSender<Result<UntypedRect<f64>, ErrorStatus>>),
GetElementTagName(String, GenericSender<Result<String, ErrorStatus>>),
GetElementText(String, GenericSender<Result<String, ErrorStatus>>),
GetElementInViewCenterPoint(
String,
GenericOneshotSender<Result<Option<(i64, i64)>, ErrorStatus>>,
),
ScrollAndGetBoundingClientRect(String, GenericSender<Result<UntypedRect<f32>, ErrorStatus>>),
GetBrowsingContextId(
WebDriverFrameId,
GenericSender<Result<BrowsingContextId, ErrorStatus>>,
),
GetParentFrameId(GenericSender<Result<BrowsingContextId, ErrorStatus>>),
GetUrl(GenericSender<String>),
GetPageSource(GenericSender<Result<String, ErrorStatus>>),
IsEnabled(String, GenericSender<Result<bool, ErrorStatus>>),
IsSelected(String, GenericSender<Result<bool, ErrorStatus>>),
GetTitle(GenericSender<String>),
/// Deal with the case of input element for Element Send Keys, which does not send keys.
WillSendKeys(
String,
String,
bool,
GenericSender<Result<bool, ErrorStatus>>,
),
AddLoadStatusSender(WebViewId, GenericSender<WebDriverLoadStatus>),
RemoveLoadStatusSender(WebViewId),
SetProtocolHandlerAutomationMode(CustomHandlersAutomationMode),
}
pub type WebDriverJSResult = Result<JSValue, JavaScriptEvaluationError>;
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverFrameId {
Short(u16),
Element(String),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverLoadStatus {
NavigationStart,
// Navigation stops for any reason
NavigationStop,
// Document ready state is complete
Complete,
// Load timeout
Timeout,
// Navigation is blocked by a user prompt
Blocked,
}
/// A collection of [`GenericSender`]s that are used to asynchronously communicate
/// to a WebDriver server with information about application state.
#[derive(Clone, Default)]
pub struct WebDriverSenders {
pub load_status_senders: FxHashMap<WebViewId, GenericSender<WebDriverLoadStatus>>,
pub script_evaluation_interrupt_sender: Option<GenericSender<WebDriverJSResult>>,
pub pending_traversals: HashMap<TraversalId, GenericSender<WebDriverLoadStatus>>,
}