Improve some webdriver conformance tests results (#36673)

These changes allow test_dom_token_list from
/execute_script/collections.py to pass, and various tests in
/execute_script/arguments.py to expose new failures.

Testing: Not run in CI yet, but verified results from
tests/wpt/tests/webdriver/tests/classic/{execute_script,execute_async_script}
locally.
Fixes: #35738

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2025-05-07 03:22:29 -04:00 committed by GitHub
parent a18c6e2c78
commit f47e69c112
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 128 additions and 165 deletions

View file

@ -65,6 +65,7 @@ use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
use script_bindings::interfaces::WindowHelpers;
use script_bindings::root::Root;
use script_layout_interface::{
FragmentType, Layout, PendingImageState, QueryMsg, Reflow, ReflowGoal, ReflowRequest,
TrustedNodeAddress, combine_id_with_fragment_type,
@ -146,6 +147,7 @@ use crate::dom::performance::Performance;
use crate::dom::promise::Promise;
use crate::dom::screen::Screen;
use crate::dom::selection::Selection;
use crate::dom::shadowroot::ShadowRoot;
use crate::dom::storage::Storage;
#[cfg(feature = "bluetooth")]
use crate::dom::testrunner::TestRunner;
@ -165,7 +167,7 @@ use crate::script_runtime::{CanGc, JSContext, Runtime};
use crate::script_thread::ScriptThread;
use crate::timers::{IsInterval, TimerCallback};
use crate::unminify::unminified_path;
use crate::webdriver_handlers::jsval_to_webdriver;
use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver};
use crate::{fetch, window_named_properties};
/// A callback to call when a response comes back from the `ImageCache`.
@ -1394,6 +1396,30 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
}
}
fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
find_node_by_unique_id_in_document(&self.Document(), id.into())
.ok()
.and_then(Root::downcast)
}
fn WebdriverFrame(&self, id: DOMString) -> Option<DomRoot<Element>> {
find_node_by_unique_id_in_document(&self.Document(), id.into())
.ok()
.and_then(Root::downcast::<HTMLIFrameElement>)
.map(Root::upcast::<Element>)
}
fn WebdriverWindow(&self, _id: DOMString) -> Option<DomRoot<Window>> {
warn!("Window references are not supported in webdriver yet");
None
}
fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
find_node_by_unique_id_in_document(&self.Document(), id.into())
.ok()
.and_then(Root::downcast)
}
// https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle
fn GetComputedStyle(
&self,

View file

@ -53,6 +53,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::element::Element;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@ -77,12 +78,27 @@ fn find_node_by_unique_id(
pipeline: PipelineId,
node_id: String,
) -> Result<DomRoot<Node>, ErrorStatus> {
match documents.find_document(pipeline).and_then(|document| {
document
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::Yes)
.find(|node| node.unique_id() == node_id)
}) {
match documents.find_document(pipeline) {
Some(doc) => find_node_by_unique_id_in_document(&doc, node_id),
None => {
if ScriptThread::has_node_id(&node_id) {
Err(ErrorStatus::StaleElementReference)
} else {
Err(ErrorStatus::NoSuchElement)
}
},
}
}
pub(crate) fn find_node_by_unique_id_in_document(
document: &Document,
node_id: String,
) -> Result<DomRoot<Node>, ErrorStatus> {
match document
.upcast::<Node>()
.traverse_preorder(ShadowIncluding::Yes)
.find(|node| node.unique_id() == node_id)
{
Some(node) => Ok(node),
None => {
if ScriptThread::has_node_id(&node_id) {

View file

@ -532,7 +532,7 @@ where
/// Returns whether `value` is an array-like object (Array, FileList,
/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection,
/// NodeList).
/// NodeList, DOMTokenList).
///
/// # Safety
/// `cx` must point to a valid, non-null JSContext.
@ -548,6 +548,10 @@ pub unsafe fn is_array_like<D: crate::DomTypes>(cx: *mut JSContext, value: Handl
_ => return false,
};
// TODO: HTMLAllCollection
if root_from_object::<D::DOMTokenList>(object, cx).is_ok() {
return true;
}
if root_from_object::<D::FileList>(object, cx).is_ok() {
return true;
}

View file

@ -150,6 +150,10 @@ partial interface Window {
undefined webdriverCallback(optional any result);
undefined webdriverException(optional any result);
undefined webdriverTimeout();
Element? webdriverElement(DOMString id);
Element? webdriverFrame(DOMString id);
Window? webdriverWindow(DOMString id);
ShadowRoot? webdriverShadowRoot(DOMString id);
};
// https://html.spec.whatwg.org/multipage/#dom-sessionstorage

View file

@ -1915,6 +1915,15 @@ impl WebDriverHandler<ServoExtensionRoute> for Handler {
}
}
/// <https://w3c.github.io/webdriver/#dfn-web-element-identifier>
const ELEMENT_IDENTIFIER: &str = "element-6066-11e4-a52e-4f735466cecf";
/// <https://w3c.github.io/webdriver/#dfn-web-frame-identifier>
const FRAME_IDENTIFIER: &str = "frame-075b-4da1-b6ba-e579c2d3230a";
/// <https://w3c.github.io/webdriver/#dfn-web-window-identifier>
const WINDOW_IDENTIFIER: &str = "window-fcc6-11e5-b4f8-330a88ab9d7f";
/// <https://w3c.github.io/webdriver/#dfn-shadow-root-identifier>
const SHADOW_ROOT_IDENTIFIER: &str = "shadow-6066-11e4-a52e-4f735466cecf";
fn webdriver_value_to_js_argument(v: &Value) -> String {
match v {
Value::String(s) => format!("\"{}\"", s),
@ -1929,6 +1938,22 @@ fn webdriver_value_to_js_argument(v: &Value) -> String {
format!("[{}]", elems.join(", "))
},
Value::Object(map) => {
let key = map.keys().next().map(String::as_str);
match (key, map.values().next()) {
(Some(ELEMENT_IDENTIFIER), Some(id)) => {
return format!("window.webdriverElement({})", id);
},
(Some(FRAME_IDENTIFIER), Some(id)) => {
return format!("window.webdriverFrame({})", id);
},
(Some(WINDOW_IDENTIFIER), Some(id)) => {
return format!("window.webdriverWindow({})", id);
},
(Some(SHADOW_ROOT_IDENTIFIER), Some(id)) => {
return format!("window.webdriverShadowRoot({})", id);
},
_ => {},
}
let elems = map
.iter()
.map(|(k, v)| format!("{}: {}", k, webdriver_value_to_js_argument(v)))

View file

@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL

View file

@ -1,30 +1,30 @@
[scroll_into_view.py]
[test_scroll_into_view]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[9\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[8\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[7\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[6\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[5\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[4\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[3\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[2\]]
expected: ERROR
expected: FAIL
[test_partially_visible_does_not_scroll[1\]]
expected: ERROR
expected: FAIL

View file

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

View file

@ -1,7 +1,4 @@
[scroll_into_view.py]
[test_element_outside_of_not_scrollable_viewport]
expected: FAIL
[test_element_outside_of_scrollable_viewport]
expected: FAIL

View file

@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL

View file

@ -2,9 +2,6 @@
[test_array_in_array]
expected: FAIL
[test_dom_token_list]
expected: FAIL
[test_file_list]
expected: FAIL

View file

@ -1,7 +1,4 @@
[collections.py]
[test_dom_token_list]
expected: FAIL
[test_file_list]
expected: FAIL

View file

@ -14,11 +14,5 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_computed_roles[<article>foo</article>-article-article\]]
expected: FAIL

View file

@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL

View file

@ -14,11 +14,5 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_basic]
expected: FAIL

View file

@ -14,11 +14,5 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_get_element_tag_name]
expected: FAIL

View file

@ -14,12 +14,6 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_transform_capitalize[space\]]
expected: FAIL

View file

@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL

View file

@ -1,15 +1,9 @@
[new_tab.py]
[test_keeps_current_window_handle]
expected: ERROR
expected: FAIL
[test_opens_about_blank_in_new_tab]
expected: ERROR
expected: FAIL
[test_initial_selection_for_contenteditable]
expected: ERROR
[test_sets_no_window_name]
expected: ERROR
[test_sets_no_opener]
expected: ERROR
expected: FAIL

View file

@ -1,20 +1,13 @@
[pointer_mouse.py]
expected: TIMEOUT
[test_no_top_browsing_context]
expected: FAIL
[test_no_browsing_context]
expected: ERROR
expected: FAIL
[test_pointer_down_closes_browsing_context]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_click_at_coordinates]
expected: FAIL
@ -39,9 +32,6 @@
[test_click_element_in_shadow_tree[inner-closed\]]
expected: FAIL
[test_click_navigation]
expected: FAIL
[test_move_to_position_in_viewport[x\]]
expected: FAIL
@ -60,5 +50,5 @@
[test_move_to_origin_position_within_frame[element\]]
expected: FAIL
[test_invalid_element_origin]
[test_params_actions_origin_outside_viewport[element\]]
expected: FAIL

View file

@ -1,63 +1,63 @@
[pointer_mouse_drag.py]
[test_drag_and_drop[20-0-0\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[20-0-300\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[20-0-800\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[0-15-0\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[0-15-300\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[0-15-800\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[10-15-0\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[10-15-300\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[10-15-800\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[-20-0-0\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[-20-0-300\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[-20-0-800\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[10--15-0\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[10--15-300\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[10--15-800\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[-10--15-0\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[-10--15-300\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop[-10--15-800\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop_with_draggable_element[0\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop_with_draggable_element[300\]]
expected: ERROR
expected: FAIL
[test_drag_and_drop_with_draggable_element[800\]]
expected: ERROR
expected: FAIL

View file

@ -1,27 +1,18 @@
[pointer_origin.py]
[test_viewport_inside]
expected: ERROR
expected: FAIL
[test_pointer_inside]
expected: ERROR
expected: FAIL
[test_element_center_point]
expected: ERROR
expected: FAIL
[test_element_center_point_with_offset]
expected: ERROR
expected: FAIL
[test_element_in_view_center_point_partly_visible]
expected: ERROR
expected: FAIL
[test_element_larger_than_viewport]
expected: ERROR
[test_element_outside_of_view_port]
expected: ERROR
[test_viewport_outside]
expected: ERROR
[test_pointer_outside]
expected: ERROR
expected: FAIL

View file

@ -8,12 +8,6 @@
[test_pointer_down_closes_browsing_context]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_pen_pointer_in_shadow_tree[outer-open\]]
expected: FAIL

View file

@ -8,12 +8,6 @@
[test_pointer_down_closes_browsing_context]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_touch_pointer_in_shadow_tree[outer-open\]]
expected: FAIL

View file

@ -1,3 +0,0 @@
[sequence.py]
[test_perform_no_actions_send_no_events]
expected: FAIL

View file

@ -1,10 +1,4 @@
[switch_webelement.py]
[test_frame_id_webelement_stale_element_reference[top_context\]]
expected: FAIL
[test_frame_id_webelement_stale_element_reference[child_context\]]
expected: FAIL
[test_frame_id_webelement_frame[0-foo\]]
expected: FAIL

View file

@ -19,12 +19,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
[test_stale_element_reference[top_context\]]
expected: FAIL
[test_stale_element_reference[child_context\]]
expected: FAIL
[test_format_and_dimensions]
expected: FAIL

View file

@ -1,3 +0,0 @@
[iframe.py]
[test_always_captures_top_browsing_context]
expected: FAIL