[WebDriver] Fully implement "switch to (parent) frame" (#37685)

1. Separate the handling of ["switch to parent
frame"](https://w3c.github.io/webdriver/#switch-to-parent-frame) from
the rest as the processing is a bit different
2. Implement "Select frame by 16-bits numbered ID" for ["switch to
frame"](https://w3c.github.io/webdriver/#switch-to-frame)
3. Implement other missing steps

Testing: All WebDriver Conformance test with new passing cases

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
Euclid Ye 2025-06-26 15:44:16 +08:00 committed by GitHub
parent 253fb247f5
commit f9880637e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 79 additions and 61 deletions

View file

@ -2494,6 +2494,9 @@ impl ScriptThread {
can_gc, can_gc,
) )
}, },
WebDriverScriptCommand::GetParentFrameId(reply) => {
webdriver_handlers::handle_get_parent_frame_id(&documents, pipeline_id, reply)
},
WebDriverScriptCommand::GetBrowsingContextId(webdriver_frame_id, reply) => { WebDriverScriptCommand::GetBrowsingContextId(webdriver_frame_id, reply) => {
webdriver_handlers::handle_get_browsing_context_id( webdriver_handlers::handle_get_browsing_context_id(
&documents, &documents,

View file

@ -586,6 +586,30 @@ pub(crate) fn handle_execute_async_script(
} }
} }
/// Get BrowsingContextId for <https://w3c.github.io/webdriver/#switch-to-parent-frame>
pub(crate) fn handle_get_parent_frame_id(
documents: &DocumentCollection,
pipeline: PipelineId,
reply: IpcSender<Result<BrowsingContextId, ErrorStatus>>,
) {
// Step 2. If session's current parent browsing context is no longer open,
// return error with error code no such window.
reply
.send(
documents
.find_window(pipeline)
.and_then(|window| {
window
.window_proxy()
.parent()
.map(|parent| parent.browsing_context_id())
})
.ok_or(ErrorStatus::NoSuchWindow),
)
.unwrap();
}
/// Get the BrowsingContextId for <https://w3c.github.io/webdriver/#dfn-switch-to-frame>
pub(crate) fn handle_get_browsing_context_id( pub(crate) fn handle_get_browsing_context_id(
documents: &DocumentCollection, documents: &DocumentCollection,
pipeline: PipelineId, pipeline: PipelineId,
@ -594,9 +618,20 @@ pub(crate) fn handle_get_browsing_context_id(
) { ) {
reply reply
.send(match webdriver_frame_id { .send(match webdriver_frame_id {
WebDriverFrameId::Short(_) => { WebDriverFrameId::Short(id) => {
// This isn't supported yet // Step 5. If id is not a supported property index of window,
Err(ErrorStatus::UnsupportedOperation) // return error with error code no such frame.
documents
.find_document(pipeline)
.ok_or(ErrorStatus::NoSuchWindow)
.and_then(|document| {
document
.iframes()
.iter()
.nth(id as usize)
.and_then(|iframe| iframe.browsing_context_id())
.ok_or(ErrorStatus::NoSuchFrame)
})
}, },
WebDriverFrameId::Element(element_id) => { WebDriverFrameId::Element(element_id) => {
get_known_element(documents, pipeline, element_id).and_then(|element| { get_known_element(documents, pipeline, element_id).and_then(|element| {
@ -606,15 +641,6 @@ pub(crate) fn handle_get_browsing_context_id(
.ok_or(ErrorStatus::NoSuchFrame) .ok_or(ErrorStatus::NoSuchFrame)
}) })
}, },
WebDriverFrameId::Parent => documents
.find_window(pipeline)
.and_then(|window| {
window
.window_proxy()
.parent()
.map(|parent| parent.browsing_context_id())
})
.ok_or(ErrorStatus::NoSuchFrame),
}) })
.unwrap(); .unwrap();
} }

View file

@ -198,6 +198,7 @@ pub enum WebDriverScriptCommand {
WebDriverFrameId, WebDriverFrameId,
IpcSender<Result<BrowsingContextId, ErrorStatus>>, IpcSender<Result<BrowsingContextId, ErrorStatus>>,
), ),
GetParentFrameId(IpcSender<Result<BrowsingContextId, ErrorStatus>>),
GetUrl(IpcSender<ServoUrl>), GetUrl(IpcSender<ServoUrl>),
GetPageSource(IpcSender<Result<String, ErrorStatus>>), GetPageSource(IpcSender<Result<String, ErrorStatus>>),
IsEnabled(String, IpcSender<Result<bool, ErrorStatus>>), IsEnabled(String, IpcSender<Result<bool, ErrorStatus>>),
@ -240,7 +241,6 @@ pub type WebDriverJSResult = Result<WebDriverJSValue, WebDriverJSError>;
pub enum WebDriverFrameId { pub enum WebDriverFrameId {
Short(u16), Short(u16),
Element(String), Element(String),
Parent,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]

View file

@ -1079,33 +1079,62 @@ impl Handler {
})) }))
} }
/// <https://w3c.github.io/webdriver/#dfn-switch-to-frame>
fn handle_switch_to_frame( fn handle_switch_to_frame(
&mut self, &mut self,
parameters: &SwitchToFrameParameters, parameters: &SwitchToFrameParameters,
) -> WebDriverResult<WebDriverResponse> { ) -> WebDriverResult<WebDriverResponse> {
self.verify_top_level_browsing_context_is_open(self.session()?.webview_id)?;
use webdriver::common::FrameId; use webdriver::common::FrameId;
let frame_id = match parameters.id { let frame_id = match parameters.id {
// id is null
FrameId::Top => { FrameId::Top => {
let session = self.session_mut()?; let webview_id = self.session()?.webview_id;
session.browsing_context_id = BrowsingContextId::from(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)?;
// (SKIP) Step 2. Try to handle any user prompts with session.
// Step 3. Set the current browsing context with session and
// session's current top-level browsing context.
self.session_mut()?.browsing_context_id = BrowsingContextId::from(webview_id);
return Ok(WebDriverResponse::Void); return Ok(WebDriverResponse::Void);
}, },
FrameId::Short(ref x) => WebDriverFrameId::Short(*x), // id is a Number object
FrameId::Short(ref x) => {
// (Already handled when deserializing in webdriver-crate)
// Step 1. If id is less than 0 or greater than 2^16 1,
// return error with error code invalid argument.
WebDriverFrameId::Short(*x)
},
FrameId::Element(ref x) => WebDriverFrameId::Element(x.to_string()), FrameId::Element(ref x) => WebDriverFrameId::Element(x.to_string()),
}; };
self.switch_to_frame(frame_id) self.switch_to_frame(frame_id)
} }
/// <https://w3c.github.io/webdriver/#switch-to-parent-frame>
fn handle_switch_to_parent_frame(&mut self) -> WebDriverResult<WebDriverResponse> { fn handle_switch_to_parent_frame(&mut self) -> WebDriverResult<WebDriverResponse> {
let webview_id = self.session()?.webview_id; let webview_id = self.session()?.webview_id;
self.verify_top_level_browsing_context_is_open(webview_id)?; let browsing_context = self.session()?.browsing_context_id;
if self.session()?.browsing_context_id == webview_id { // Step 1. If session's current browsing context is already the top-level browsing context:
if browsing_context == webview_id {
// Step 1.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(browsing_context)?;
// Step 1.2. Return success with data null.
return Ok(WebDriverResponse::Void); return Ok(WebDriverResponse::Void);
} }
self.switch_to_frame(WebDriverFrameId::Parent) let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetParentFrameId(sender);
// TODO: Track Parent Browsing Context directly in the session, as expected by Spec.
self.browsing_context_script_command::<true>(cmd)?;
match wait_for_script_response(receiver)? {
Ok(browsing_context_id) => {
self.session_mut()?.browsing_context_id = browsing_context_id;
Ok(WebDriverResponse::Void)
},
Err(error) => Err(WebDriverError::new(error, "")),
}
} }
// https://w3c.github.io/webdriver/#switch-to-window // https://w3c.github.io/webdriver/#switch-to-window
@ -1141,13 +1170,6 @@ impl Handler {
&mut self, &mut self,
frame_id: WebDriverFrameId, frame_id: WebDriverFrameId,
) -> WebDriverResult<WebDriverResponse> { ) -> WebDriverResult<WebDriverResponse> {
if let WebDriverFrameId::Short(_) = frame_id {
return Err(WebDriverError::new(
ErrorStatus::UnsupportedOperation,
"Selecting frame by id not supported",
));
}
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender); let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender);
self.browsing_context_script_command::<true>(cmd)?; self.browsing_context_script_command::<true>(cmd)?;

View file

@ -4,6 +4,3 @@
[test_textarea_append] [test_textarea_append]
expected: FAIL expected: FAIL
[test_date]
expected: FAIL

View file

@ -20,8 +20,5 @@
[test_disabled] [test_disabled]
expected: FAIL expected: FAIL
[test_transparent_element]
expected: FAIL
[test_readonly_element] [test_readonly_element]
expected: FAIL expected: FAIL

View file

@ -2,9 +2,6 @@
[test_no_browsing_context] [test_no_browsing_context]
expected: FAIL expected: FAIL
[test_no_such_element_with_startnode_from_other_frame]
expected: FAIL
[test_find_element[xpath-//a\]] [test_find_element[xpath-//a\]]
expected: FAIL expected: FAIL

View file

@ -2,9 +2,6 @@
[test_no_browsing_context] [test_no_browsing_context]
expected: FAIL expected: FAIL
[test_no_such_shadow_root_with_shadow_root_from_other_frame]
expected: FAIL
[test_find_element[open-xpath-//a\]] [test_find_element[open-xpath-//a\]]
expected: FAIL expected: FAIL

View file

@ -2,9 +2,6 @@
[test_no_browsing_context] [test_no_browsing_context]
expected: FAIL expected: FAIL
[test_no_such_element_with_startnode_from_other_frame]
expected: FAIL
[test_find_elements[xpath-//a\]] [test_find_elements[xpath-//a\]]
expected: FAIL expected: FAIL

View file

@ -2,9 +2,6 @@
[test_no_browsing_context] [test_no_browsing_context]
expected: FAIL expected: FAIL
[test_no_such_shadow_root_with_shadow_root_from_other_frame]
expected: FAIL
[test_find_elements[open-xpath-//a\]] [test_find_elements[open-xpath-//a\]]
expected: FAIL expected: FAIL

View file

@ -1,15 +0,0 @@
[switch_number.py]
[test_frame_id_number_index_out_of_bounds[1\]]
expected: FAIL
[test_frame_id_number_index_out_of_bounds[65535\]]
expected: FAIL
[test_frame_id_number_index[0-foo\]]
expected: FAIL
[test_frame_id_number_index[1-bar\]]
expected: FAIL
[test_frame_id_number_index_nested]
expected: FAIL