script::webdriver_handlers: Replace find_node_by_unique_id with get_known_element (#37502)

Implement previously missing step 3 of [get a known
element](https://w3c.github.io/webdriver/#dfn-get-a-known-element): "If
node is not null and node does not implement Element, return error with
error code no such element"

Testing: All webdriver conformance test

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
Euclid Ye 2025-06-17 23:23:59 +08:00 committed by GitHub
parent 6159a11905
commit f22c273ea9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -72,17 +72,24 @@ use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
use crate::script_thread::ScriptThread;
/// <https://w3c.github.io/webdriver/#dfn-get-a-known-element>
fn find_node_by_unique_id(
fn get_known_element(
documents: &DocumentCollection,
pipeline: PipelineId,
node_id: String,
) -> Result<DomRoot<Node>, ErrorStatus> {
) -> Result<DomRoot<Element>, ErrorStatus> {
let doc = documents
.find_document(pipeline)
.expect("webdriver_handlers::Document should exists");
find_node_by_unique_id_in_document(&doc, node_id)
// Step 3. If node is not null and node does not implement Element
// return error with error code no such element.
find_node_by_unique_id_in_document(&doc, node_id).and_then(|node| {
node.downcast::<Element>()
.map(DomRoot::from_ref)
.ok_or(ErrorStatus::NoSuchElement)
})
}
// This is also used by `dom/window.rs`
pub(crate) fn find_node_by_unique_id_in_document(
document: &Document,
node_id: String,
@ -526,8 +533,9 @@ pub(crate) fn handle_get_browsing_context_id(
Err(ErrorStatus::UnsupportedOperation)
},
WebDriverFrameId::Element(element_id) => {
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
node.downcast::<HTMLIFrameElement>()
get_known_element(documents, pipeline, element_id).and_then(|element| {
element
.downcast::<HTMLIFrameElement>()
.and_then(|element| element.browsing_context_id())
.ok_or(ErrorStatus::NoSuchFrame)
})
@ -588,9 +596,8 @@ pub(crate) fn handle_get_element_in_view_center_point(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).map(|node| {
get_element_in_view_center_point(node.downcast::<Element>().unwrap(), can_gc)
.map(|point| (point.x, point.y))
get_known_element(documents, pipeline, element_id).map(|element| {
get_element_in_view_center_point(&element, can_gc).map(|point| (point.x, point.y))
}),
)
.unwrap();
@ -627,7 +634,7 @@ pub(crate) fn handle_find_element_css(
document
.QuerySelector(DOMString::from(selector))
.map_err(|_| ErrorStatus::InvalidSelector)
.map(|node| node.map(|x| x.upcast::<Node>().unique_id(pipeline))),
.map(|element| element.map(|x| x.upcast::<Node>().unique_id(pipeline))),
)
.unwrap(),
Err(error) => reply.send(Err(error)).unwrap(),
@ -668,7 +675,7 @@ pub(crate) fn handle_find_element_tag_name(
.GetElementsByTagName(DOMString::from(selector), can_gc)
.elements_iter()
.next()
.map(|node| node.upcast::<Node>().unique_id(pipeline))))
.map(|element| element.upcast::<Node>().unique_id(pipeline))))
.unwrap(),
Err(error) => reply.send(Err(error)).unwrap(),
}
@ -747,10 +754,12 @@ pub(crate) fn handle_find_element_element_css(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
node.query_selector(DOMString::from(selector))
get_known_element(documents, pipeline, element_id).and_then(|element| {
element
.upcast::<Node>()
.query_selector(DOMString::from(selector))
.map_err(|_| ErrorStatus::InvalidSelector)
.map(|node| node.map(|x| x.upcast::<Node>().unique_id(pipeline)))
.map(|element| element.map(|x| x.upcast::<Node>().unique_id(pipeline)))
}),
)
.unwrap();
@ -767,8 +776,9 @@ pub(crate) fn handle_find_element_element_link_text(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id)
.and_then(|node| first_matching_link(&node, selector.clone(), partial, can_gc)),
get_known_element(documents, pipeline, element_id).and_then(|element| {
first_matching_link(element.upcast::<Node>(), selector.clone(), partial, can_gc)
}),
)
.unwrap();
}
@ -783,15 +793,12 @@ pub(crate) fn handle_find_element_element_tag_name(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| match node
.downcast::<Element>(
) {
Some(element) => Ok(element
get_known_element(documents, pipeline, element_id).map(|element| {
element
.GetElementsByTagName(DOMString::from(selector), can_gc)
.elements_iter()
.next()
.map(|x| x.upcast::<Node>().unique_id(pipeline))),
None => Err(ErrorStatus::UnknownError),
.map(|x| x.upcast::<Node>().unique_id(pipeline))
}),
)
.unwrap();
@ -806,8 +813,10 @@ pub(crate) fn handle_find_element_elements_css(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
node.query_selector_all(DOMString::from(selector))
get_known_element(documents, pipeline, element_id).and_then(|element| {
element
.upcast::<Node>()
.query_selector_all(DOMString::from(selector))
.map_err(|_| ErrorStatus::InvalidSelector)
.map(|nodes| {
nodes
@ -831,8 +840,9 @@ pub(crate) fn handle_find_element_elements_link_text(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id)
.and_then(|node| all_matching_links(&node, selector.clone(), partial, can_gc)),
get_known_element(documents, pipeline, element_id).and_then(|element| {
all_matching_links(element.upcast::<Node>(), selector.clone(), partial, can_gc)
}),
)
.unwrap();
}
@ -847,15 +857,12 @@ pub(crate) fn handle_find_element_elements_tag_name(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| match node
.downcast::<Element>(
) {
Some(element) => Ok(element
get_known_element(documents, pipeline, element_id).map(|element| {
element
.GetElementsByTagName(DOMString::from(selector), can_gc)
.elements_iter()
.map(|x| x.upcast::<Node>().unique_id(pipeline))
.collect::<Vec<String>>()),
None => Err(ErrorStatus::UnknownError),
.collect::<Vec<String>>()
}),
)
.unwrap();
@ -870,13 +877,10 @@ pub(crate) fn handle_get_element_shadow_root(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| match node
.downcast::<Element>(
) {
Some(element) => Ok(element
get_known_element(documents, pipeline, element_id).map(|element| {
element
.GetShadowRoot()
.map(|x| x.upcast::<Node>().unique_id(pipeline))),
None => Err(ErrorStatus::NoSuchElement),
.map(|x| x.upcast::<Node>().unique_id(pipeline))
}),
)
.unwrap();
@ -893,16 +897,16 @@ pub(crate) fn handle_will_send_keys(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
get_known_element(documents, pipeline, element_id).and_then(|element| {
// Step 6: Let file be true if element is input element
// in the file upload state, or false otherwise
let file_input = node
let file_input = element
.downcast::<HTMLInputElement>()
.filter(|&input_element| input_element.input_type() == InputType::File);
// Step 7: If file is false or the session's strict file interactability
if file_input.is_none() || strict_file_interactability {
match node.downcast::<HTMLElement>() {
match element.downcast::<HTMLElement>() {
Some(element) => {
// Need a way to find if this actually succeeded
element.Focus(can_gc);
@ -970,12 +974,8 @@ pub(crate) fn handle_get_computed_role(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, node_id).and_then(|node| {
match node.downcast::<Element>() {
Some(element) => Ok(element.GetRole().map(String::from)),
None => Err(ErrorStatus::UnknownError),
}
}),
get_known_element(documents, pipeline, node_id)
.map(|element| element.GetRole().map(String::from)),
)
.unwrap();
}
@ -1181,9 +1181,9 @@ pub(crate) fn handle_get_rect(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
get_known_element(documents, pipeline, element_id).and_then(|element| {
// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-calculate-the-absolute-position
match node.downcast::<HTMLElement>() {
match element.downcast::<HTMLElement>() {
Some(html_element) => {
// Step 1
let mut x = 0;
@ -1227,17 +1227,12 @@ pub(crate) fn handle_get_bounding_client_rect(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| match node
.downcast::<Element>(
) {
Some(element) => {
let rect = element.GetBoundingClientRect(can_gc);
Ok(Rect::new(
Point2D::new(rect.X() as f32, rect.Y() as f32),
Size2D::new(rect.Width() as f32, rect.Height() as f32),
))
},
None => Err(ErrorStatus::UnknownError),
get_known_element(documents, pipeline, element_id).map(|element| {
let rect = element.GetBoundingClientRect(can_gc);
Rect::new(
Point2D::new(rect.X() as f32, rect.Y() as f32),
Size2D::new(rect.Width() as f32, rect.Height() as f32),
)
}),
)
.unwrap();
@ -1253,10 +1248,16 @@ pub(crate) fn handle_get_text(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, node_id).map(|node| {
node.downcast::<HTMLElement>()
.map(|element| element.InnerText(can_gc).to_string())
.unwrap_or_else(|| node.GetTextContent().map_or("".to_owned(), String::from))
get_known_element(documents, pipeline, node_id).map(|element| {
element
.downcast::<HTMLElement>()
.map(|htmlelement| htmlelement.InnerText(can_gc).to_string())
.unwrap_or_else(|| {
element
.upcast::<Node>()
.GetTextContent()
.map_or("".to_owned(), String::from)
})
}),
)
.unwrap();
@ -1270,8 +1271,8 @@ pub(crate) fn handle_get_name(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, node_id)
.map(|node| String::from(node.downcast::<Element>().unwrap().TagName())),
get_known_element(documents, pipeline, node_id)
.map(|element| String::from(element.TagName())),
)
.unwrap();
}
@ -1285,9 +1286,8 @@ pub(crate) fn handle_get_attribute(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, node_id).map(|node| {
node.downcast::<Element>()
.unwrap()
get_known_element(documents, pipeline, node_id).map(|element| {
element
.GetAttribute(DOMString::from(name))
.map(String::from)
}),
@ -1306,7 +1306,7 @@ pub(crate) fn handle_get_property(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, node_id).map(|node| {
get_known_element(documents, pipeline, node_id).map(|element| {
let document = documents.find_document(pipeline).unwrap();
let realm = enter_realm(&*document);
let cx = document.window().get_cx();
@ -1315,7 +1315,7 @@ pub(crate) fn handle_get_property(
match unsafe {
get_property_jsval(
*cx,
node.reflector().get_jsobject(),
element.reflector().get_jsobject(),
&name,
property.handle_mut(),
)
@ -1323,7 +1323,7 @@ pub(crate) fn handle_get_property(
Ok(_) => {
match jsval_to_webdriver(
cx,
&node.global(),
&element.global(),
property.handle(),
InRealm::entered(&realm),
can_gc,
@ -1333,7 +1333,7 @@ pub(crate) fn handle_get_property(
}
},
Err(error) => {
throw_dom_exception(cx, &node.global(), error, can_gc);
throw_dom_exception(cx, &element.global(), error, can_gc);
WebDriverJSValue::Undefined
},
}
@ -1352,12 +1352,11 @@ pub(crate) fn handle_get_css(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, node_id).map(|node| {
let window = node.owner_window();
let element = node.downcast::<Element>().unwrap();
get_known_element(documents, pipeline, node_id).map(|element| {
let window = element.owner_window();
String::from(
window
.GetComputedStyle(element, None)
.GetComputedStyle(&element, None)
.GetPropertyValue(DOMString::from(name), can_gc),
)
}),
@ -1422,15 +1421,15 @@ pub(crate) fn handle_element_click(
reply
.send(
// Step 3
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
get_known_element(documents, pipeline, element_id).and_then(|element| {
// Step 4
if let Some(input_element) = node.downcast::<HTMLInputElement>() {
if let Some(input_element) = element.downcast::<HTMLInputElement>() {
if input_element.input_type() == InputType::File {
return Err(ErrorStatus::InvalidArgument);
}
}
let Some(container) = get_container(&node) else {
let Some(container) = get_container(element.upcast::<Node>()) else {
return Err(ErrorStatus::UnknownError);
};
@ -1444,7 +1443,7 @@ pub(crate) fn handle_element_click(
// TODO: return error if obscured
// Step 8
match node.downcast::<HTMLOptionElement>() {
match element.downcast::<HTMLOptionElement>() {
Some(option_element) => {
// Steps 8.2 - 8.4
let event_target = container.upcast::<EventTarget>();
@ -1488,7 +1487,7 @@ pub(crate) fn handle_element_click(
Ok(None)
},
None => Ok(Some(node.unique_id(pipeline))),
None => Ok(Some(element.upcast::<Node>().unique_id(pipeline))),
}
}),
)
@ -1503,12 +1502,8 @@ pub(crate) fn handle_is_enabled(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| match node
.downcast::<Element>(
) {
Some(element) => Ok(element.enabled_state()),
None => Err(ErrorStatus::UnknownError),
}),
get_known_element(documents, pipeline, element_id)
.map(|element| element.enabled_state()),
)
.unwrap();
}
@ -1521,12 +1516,12 @@ pub(crate) fn handle_is_selected(
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
if let Some(input_element) = node.downcast::<HTMLInputElement>() {
get_known_element(documents, pipeline, element_id).and_then(|element| {
if let Some(input_element) = element.downcast::<HTMLInputElement>() {
Ok(input_element.Checked())
} else if let Some(option_element) = node.downcast::<HTMLOptionElement>() {
} else if let Some(option_element) = element.downcast::<HTMLOptionElement>() {
Ok(option_element.Selected())
} else if node.is::<HTMLElement>() {
} else if element.is::<HTMLElement>() {
Ok(false) // regular elements are not selectable
} else {
Err(ErrorStatus::UnknownError)