LibWeb+WebContent: Update our list of boolean attributes for WebDriver

Chromium has a larger list of boolean attributes, and WPT depends on it.
We must also compare attribute names case-insensitively.

Note this method is only used by WebDriver.
This commit is contained in:
Timothy Flynn 2024-11-06 18:31:04 -05:00 committed by Tim Flynn
commit ecaacd2adf
Notes: github-actions[bot] 2024-11-07 01:45:38 +00:00
3 changed files with 60 additions and 40 deletions

View file

@ -41,33 +41,48 @@ void initialize_strings()
// https://html.spec.whatwg.org/#boolean-attribute // https://html.spec.whatwg.org/#boolean-attribute
bool is_boolean_attribute(FlyString const& attribute) bool is_boolean_attribute(FlyString const& attribute)
{ {
// NOTE: This is the list of attributes from https://html.spec.whatwg.org/#attributes-3 // NOTE: For web compatibility, this matches the list of attributes which Chromium considers to be booleans,
// with a Value column value of "Boolean attribute". // excluding attributes that are only used by Chromium itself:
return attribute.is_one_of( // https://source.chromium.org/chromium/chromium/src/+/460b7c003cf89fc9493e721701906f19e5f6a387:chrome/test/chromedriver/element_commands.cc;l=48-94
AttributeNames::allowfullscreen, return attribute.equals_ignoring_ascii_case(AttributeNames::allowfullscreen)
AttributeNames::async, || attribute.equals_ignoring_ascii_case(AttributeNames::async)
AttributeNames::autofocus, || attribute.equals_ignoring_ascii_case(AttributeNames::autofocus)
AttributeNames::autoplay, || attribute.equals_ignoring_ascii_case(AttributeNames::autoplay)
AttributeNames::checked, || attribute.equals_ignoring_ascii_case(AttributeNames::checked)
AttributeNames::controls, || attribute.equals_ignoring_ascii_case(AttributeNames::compact)
AttributeNames::default_, || attribute.equals_ignoring_ascii_case(AttributeNames::controls)
AttributeNames::defer, || attribute.equals_ignoring_ascii_case(AttributeNames::declare)
AttributeNames::disabled, || attribute.equals_ignoring_ascii_case(AttributeNames::default_)
AttributeNames::formnovalidate, || attribute.equals_ignoring_ascii_case(AttributeNames::defaultchecked)
AttributeNames::inert, || attribute.equals_ignoring_ascii_case(AttributeNames::defaultselected)
AttributeNames::ismap, || attribute.equals_ignoring_ascii_case(AttributeNames::defer)
AttributeNames::itemscope, || attribute.equals_ignoring_ascii_case(AttributeNames::disabled)
AttributeNames::loop, || attribute.equals_ignoring_ascii_case(AttributeNames::ended)
AttributeNames::multiple, || attribute.equals_ignoring_ascii_case(AttributeNames::formnovalidate)
AttributeNames::muted, || attribute.equals_ignoring_ascii_case(AttributeNames::hidden)
AttributeNames::nomodule, || attribute.equals_ignoring_ascii_case(AttributeNames::indeterminate)
AttributeNames::novalidate, || attribute.equals_ignoring_ascii_case(AttributeNames::iscontenteditable)
AttributeNames::open, || attribute.equals_ignoring_ascii_case(AttributeNames::ismap)
AttributeNames::playsinline, || attribute.equals_ignoring_ascii_case(AttributeNames::itemscope)
AttributeNames::readonly, || attribute.equals_ignoring_ascii_case(AttributeNames::loop)
AttributeNames::required, || attribute.equals_ignoring_ascii_case(AttributeNames::multiple)
AttributeNames::reversed, || attribute.equals_ignoring_ascii_case(AttributeNames::muted)
AttributeNames::selected); || attribute.equals_ignoring_ascii_case(AttributeNames::nohref)
|| attribute.equals_ignoring_ascii_case(AttributeNames::nomodule)
|| attribute.equals_ignoring_ascii_case(AttributeNames::noresize)
|| attribute.equals_ignoring_ascii_case(AttributeNames::noshade)
|| attribute.equals_ignoring_ascii_case(AttributeNames::novalidate)
|| attribute.equals_ignoring_ascii_case(AttributeNames::nowrap)
|| attribute.equals_ignoring_ascii_case(AttributeNames::open)
|| attribute.equals_ignoring_ascii_case(AttributeNames::paused)
|| attribute.equals_ignoring_ascii_case(AttributeNames::playsinline)
|| attribute.equals_ignoring_ascii_case(AttributeNames::readonly)
|| attribute.equals_ignoring_ascii_case(AttributeNames::required)
|| attribute.equals_ignoring_ascii_case(AttributeNames::reversed)
|| attribute.equals_ignoring_ascii_case(AttributeNames::seeking)
|| attribute.equals_ignoring_ascii_case(AttributeNames::selected)
|| attribute.equals_ignoring_ascii_case(AttributeNames::truespeed)
|| attribute.equals_ignoring_ascii_case(AttributeNames::willvalidate);
} }
} }

View file

@ -60,6 +60,8 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(datetime) \ __ENUMERATE_HTML_ATTRIBUTE(datetime) \
__ENUMERATE_HTML_ATTRIBUTE(declare) \ __ENUMERATE_HTML_ATTRIBUTE(declare) \
__ENUMERATE_HTML_ATTRIBUTE(default_) \ __ENUMERATE_HTML_ATTRIBUTE(default_) \
__ENUMERATE_HTML_ATTRIBUTE(defaultchecked) \
__ENUMERATE_HTML_ATTRIBUTE(defaultselected) \
__ENUMERATE_HTML_ATTRIBUTE(defer) \ __ENUMERATE_HTML_ATTRIBUTE(defer) \
__ENUMERATE_HTML_ATTRIBUTE(dir) \ __ENUMERATE_HTML_ATTRIBUTE(dir) \
__ENUMERATE_HTML_ATTRIBUTE(direction) \ __ENUMERATE_HTML_ATTRIBUTE(direction) \
@ -67,6 +69,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(disabled) \ __ENUMERATE_HTML_ATTRIBUTE(disabled) \
__ENUMERATE_HTML_ATTRIBUTE(download) \ __ENUMERATE_HTML_ATTRIBUTE(download) \
__ENUMERATE_HTML_ATTRIBUTE(enctype) \ __ENUMERATE_HTML_ATTRIBUTE(enctype) \
__ENUMERATE_HTML_ATTRIBUTE(ended) \
__ENUMERATE_HTML_ATTRIBUTE(event) \ __ENUMERATE_HTML_ATTRIBUTE(event) \
__ENUMERATE_HTML_ATTRIBUTE(face) \ __ENUMERATE_HTML_ATTRIBUTE(face) \
__ENUMERATE_HTML_ATTRIBUTE(fetchpriority) \ __ENUMERATE_HTML_ATTRIBUTE(fetchpriority) \
@ -90,9 +93,11 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(id) \ __ENUMERATE_HTML_ATTRIBUTE(id) \
__ENUMERATE_HTML_ATTRIBUTE(imagesizes) \ __ENUMERATE_HTML_ATTRIBUTE(imagesizes) \
__ENUMERATE_HTML_ATTRIBUTE(imagesrcset) \ __ENUMERATE_HTML_ATTRIBUTE(imagesrcset) \
__ENUMERATE_HTML_ATTRIBUTE(indeterminate) \
__ENUMERATE_HTML_ATTRIBUTE(inert) \ __ENUMERATE_HTML_ATTRIBUTE(inert) \
__ENUMERATE_HTML_ATTRIBUTE(integrity) \ __ENUMERATE_HTML_ATTRIBUTE(integrity) \
__ENUMERATE_HTML_ATTRIBUTE(is) \ __ENUMERATE_HTML_ATTRIBUTE(is) \
__ENUMERATE_HTML_ATTRIBUTE(iscontenteditable) \
__ENUMERATE_HTML_ATTRIBUTE(ismap) \ __ENUMERATE_HTML_ATTRIBUTE(ismap) \
__ENUMERATE_HTML_ATTRIBUTE(itemscope) \ __ENUMERATE_HTML_ATTRIBUTE(itemscope) \
__ENUMERATE_HTML_ATTRIBUTE(kind) \ __ENUMERATE_HTML_ATTRIBUTE(kind) \
@ -212,6 +217,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(open) \ __ENUMERATE_HTML_ATTRIBUTE(open) \
__ENUMERATE_HTML_ATTRIBUTE(optimum) \ __ENUMERATE_HTML_ATTRIBUTE(optimum) \
__ENUMERATE_HTML_ATTRIBUTE(pattern) \ __ENUMERATE_HTML_ATTRIBUTE(pattern) \
__ENUMERATE_HTML_ATTRIBUTE(paused) \
__ENUMERATE_HTML_ATTRIBUTE(ping) \ __ENUMERATE_HTML_ATTRIBUTE(ping) \
__ENUMERATE_HTML_ATTRIBUTE(placeholder) \ __ENUMERATE_HTML_ATTRIBUTE(placeholder) \
__ENUMERATE_HTML_ATTRIBUTE(playsinline) \ __ENUMERATE_HTML_ATTRIBUTE(playsinline) \
@ -232,6 +238,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(scrollamount) \ __ENUMERATE_HTML_ATTRIBUTE(scrollamount) \
__ENUMERATE_HTML_ATTRIBUTE(scrolldelay) \ __ENUMERATE_HTML_ATTRIBUTE(scrolldelay) \
__ENUMERATE_HTML_ATTRIBUTE(scrolling) \ __ENUMERATE_HTML_ATTRIBUTE(scrolling) \
__ENUMERATE_HTML_ATTRIBUTE(seeking) \
__ENUMERATE_HTML_ATTRIBUTE(selected) \ __ENUMERATE_HTML_ATTRIBUTE(selected) \
__ENUMERATE_HTML_ATTRIBUTE(shadowrootclonable) \ __ENUMERATE_HTML_ATTRIBUTE(shadowrootclonable) \
__ENUMERATE_HTML_ATTRIBUTE(shadowrootdelegatesfocus) \ __ENUMERATE_HTML_ATTRIBUTE(shadowrootdelegatesfocus) \
@ -265,6 +272,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(vlink) \ __ENUMERATE_HTML_ATTRIBUTE(vlink) \
__ENUMERATE_HTML_ATTRIBUTE(vspace) \ __ENUMERATE_HTML_ATTRIBUTE(vspace) \
__ENUMERATE_HTML_ATTRIBUTE(width) \ __ENUMERATE_HTML_ATTRIBUTE(width) \
__ENUMERATE_HTML_ATTRIBUTE(willvalidate) \
__ENUMERATE_HTML_ATTRIBUTE(wrap) __ENUMERATE_HTML_ATTRIBUTE(wrap)
#define __ENUMERATE_HTML_ATTRIBUTE(name) extern FlyString name; #define __ENUMERATE_HTML_ATTRIBUTE(name) extern FlyString name;

View file

@ -1302,36 +1302,33 @@ Messages::WebDriverClient::IsElementSelectedResponse WebDriverConnection::is_ele
// 12.4.2 Get Element Attribute, https://w3c.github.io/webdriver/#dfn-get-element-attribute // 12.4.2 Get Element Attribute, https://w3c.github.io/webdriver/#dfn-get-element-attribute
Messages::WebDriverClient::GetElementAttributeResponse WebDriverConnection::get_element_attribute(String const& element_id, String const& name) Messages::WebDriverClient::GetElementAttributeResponse WebDriverConnection::get_element_attribute(String const& element_id, String const& name)
{ {
// 1. If the current browsing context is no longer open, return error with error code no such window. // 1. If session's current browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_browsing_context_is_open()); TRY(ensure_current_browsing_context_is_open());
// 2. Handle any user prompts and return its value if it is an error. // 2. Try to handle any user prompts with session.
handle_any_user_prompts([this, element_id, name]() { handle_any_user_prompts([this, element_id, name]() {
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known element with session and URL variables' element id.
auto element = WEBDRIVER_TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id)); auto element = WEBDRIVER_TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let result be the result of the first matching condition: // 4. Let name be URL variables["name"].
Optional<ByteString> result; // 5. Let result be the result of the first matching condition:
String result {};
// -> If name is a boolean attribute // -> If name is a boolean attribute
if (Web::HTML::is_boolean_attribute(name)) { if (Web::HTML::is_boolean_attribute(name)) {
// "true" (string) if the element has the attribute, otherwise null. // "true" (string) if the element hasAttribute() with name, otherwise null.
if (element->has_attribute(name)) if (element->has_attribute(name))
result = "true"sv; result = "true"_string;
} }
// -> Otherwise // -> Otherwise
else { else {
// The result of getting an attribute by name name. // The result of getting an attribute by name name.
if (auto attr = element->get_attribute(name); attr.has_value()) if (auto attr = element->get_attribute(name); attr.has_value())
result = attr->to_byte_string(); result = attr.release_value();
} }
// 5. Return success with data result. // 5. Return success with data result.
if (result.has_value()) { async_driver_execution_complete({ result.to_byte_string() });
async_driver_execution_complete({ result.release_value() });
return;
}
async_driver_execution_complete(JsonValue {});
}); });
return JsonValue {}; return JsonValue {};