From 5e8754bb1d20208da35730720f35944b5f375c52 Mon Sep 17 00:00:00 2001 From: Kingsley Yung Date: Fri, 1 Aug 2025 16:44:26 +0800 Subject: [PATCH] webdriver: consider boolean attribute when get element attribute (#38401) The function handle_get_attribute should act differently when the attribute is a boolean attribute. The full list of attributes can be found in [1]. All attributes marked as "Boolean attribute" in the "Value" column are boolean attributes. Note that "hidden" is effectively treated as a boolean attribute, according to WPT test "test_global_boolean_attributes" in webdriver/tests/classic/get_element_attribute/get.py [1] https://html.spec.whatwg.org/multipage/#attributes-3 Testing: Updated WPT test expectation Fixes: #38353 --------- Signed-off-by: Kingsley Yung --- components/script/dom/attr.rs | 44 ++++++++++++++ components/script/webdriver_handlers.rs | 16 +++++- .../classic/get_element_attribute/get.py.ini | 57 ------------------- 3 files changed, 57 insertions(+), 60 deletions(-) diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index 9f1520bd085..fe41fee6250 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -289,3 +289,47 @@ pub(crate) fn is_relevant_attribute(namespace: &Namespace, local_name: &LocalNam // namespace == &ns!() || (namespace == &ns!(xlink) && local_name == &local_name!("href")) } + +/// A help function to check if an attribute is a boolean attribute. +pub(crate) fn is_boolean_attribute(name: &str) -> bool { + // The full list of attributes can be found in [1]. All attributes marked as "Boolean + // attribute" in the "Value" column are boolean attributes. Note that "hidden" is effectively + // treated as a boolean attribute, according to WPT test "test_global_boolean_attributes" in + // webdriver/tests/classic/get_element_attribute/get.py + // + // [1] + [ + "allowfullscreen", + "alpha", + "async", + "autofocus", + "autoplay", + "checked", + "controls", + "default", + "defer", + "disabled", + "formnovalidate", + "hidden", + "inert", + "ismap", + "itemscope", + "loop", + "multiple", + "muted", + "nomodule", + "novalidate", + "open", + "playsinline", + "readonly", + "required", + "reversed", + "selected", + "shadowrootclonable", + "shadowrootcustomelementregistry", + "shadowrootdelegatesfocus", + "shadowrootserializable", + ] + .iter() + .any(|&boolean_attr| boolean_attr.eq_ignore_ascii_case(name)) +} diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index b9476371dc4..6f7286c1c49 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -34,6 +34,7 @@ use webdriver::common::{WebElement, WebFrame, WebWindow}; use webdriver::error::ErrorStatus; use crate::document_collection::DocumentCollection; +use crate::dom::attr::is_boolean_attribute; use crate::dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; @@ -1582,9 +1583,18 @@ pub(crate) fn handle_get_attribute( reply .send( get_known_element(documents, pipeline, node_id).map(|element| { - element - .GetAttribute(DOMString::from(name)) - .map(String::from) + if is_boolean_attribute(&name) { + // element.get_attribute_by_name(DOMString::from(name)).map(|_| String::from("true")) + if element.HasAttribute(DOMString::from(name)) { + Some(String::from("true")) + } else { + None + } + } else { + element + .GetAttribute(DOMString::from(name)) + .map(String::from) + } }), ) .unwrap(); diff --git a/tests/wpt/meta/webdriver/tests/classic/get_element_attribute/get.py.ini b/tests/wpt/meta/webdriver/tests/classic/get_element_attribute/get.py.ini index 1ec21a9e65f..bb9de0071f4 100644 --- a/tests/wpt/meta/webdriver/tests/classic/get_element_attribute/get.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/get_element_attribute/get.py.ini @@ -1,60 +1,3 @@ [get.py] [test_no_browsing_context] expected: FAIL - - [test_boolean_attribute[audio-attrs0\]] - expected: FAIL - - [test_boolean_attribute[button-attrs1\]] - expected: FAIL - - [test_boolean_attribute[details-attrs2\]] - expected: FAIL - - [test_boolean_attribute[dialog-attrs3\]] - expected: FAIL - - [test_boolean_attribute[fieldset-attrs4\]] - expected: FAIL - - [test_boolean_attribute[form-attrs5\]] - expected: FAIL - - [test_boolean_attribute[iframe-attrs6\]] - expected: FAIL - - [test_boolean_attribute[img-attrs7\]] - expected: FAIL - - [test_boolean_attribute[input-attrs8\]] - expected: FAIL - - [test_boolean_attribute[menuitem-attrs9\]] - expected: FAIL - - [test_boolean_attribute[ol-attrs10\]] - expected: FAIL - - [test_boolean_attribute[optgroup-attrs11\]] - expected: FAIL - - [test_boolean_attribute[option-attrs12\]] - expected: FAIL - - [test_boolean_attribute[script-attrs13\]] - expected: FAIL - - [test_boolean_attribute[select-attrs14\]] - expected: FAIL - - [test_boolean_attribute[textarea-attrs15\]] - expected: FAIL - - [test_boolean_attribute[track-attrs16\]] - expected: FAIL - - [test_boolean_attribute[video-attrs17\]] - expected: FAIL - - [test_global_boolean_attributes] - expected: FAIL