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
bool is_boolean_attribute(FlyString const& attribute)
{
// NOTE: This is the list of attributes from https://html.spec.whatwg.org/#attributes-3
// with a Value column value of "Boolean attribute".
return attribute.is_one_of(
AttributeNames::allowfullscreen,
AttributeNames::async,
AttributeNames::autofocus,
AttributeNames::autoplay,
AttributeNames::checked,
AttributeNames::controls,
AttributeNames::default_,
AttributeNames::defer,
AttributeNames::disabled,
AttributeNames::formnovalidate,
AttributeNames::inert,
AttributeNames::ismap,
AttributeNames::itemscope,
AttributeNames::loop,
AttributeNames::multiple,
AttributeNames::muted,
AttributeNames::nomodule,
AttributeNames::novalidate,
AttributeNames::open,
AttributeNames::playsinline,
AttributeNames::readonly,
AttributeNames::required,
AttributeNames::reversed,
AttributeNames::selected);
// NOTE: For web compatibility, this matches the list of attributes which Chromium considers to be booleans,
// excluding attributes that are only used by Chromium itself:
// https://source.chromium.org/chromium/chromium/src/+/460b7c003cf89fc9493e721701906f19e5f6a387:chrome/test/chromedriver/element_commands.cc;l=48-94
return attribute.equals_ignoring_ascii_case(AttributeNames::allowfullscreen)
|| attribute.equals_ignoring_ascii_case(AttributeNames::async)
|| attribute.equals_ignoring_ascii_case(AttributeNames::autofocus)
|| attribute.equals_ignoring_ascii_case(AttributeNames::autoplay)
|| attribute.equals_ignoring_ascii_case(AttributeNames::checked)
|| attribute.equals_ignoring_ascii_case(AttributeNames::compact)
|| attribute.equals_ignoring_ascii_case(AttributeNames::controls)
|| attribute.equals_ignoring_ascii_case(AttributeNames::declare)
|| attribute.equals_ignoring_ascii_case(AttributeNames::default_)
|| attribute.equals_ignoring_ascii_case(AttributeNames::defaultchecked)
|| attribute.equals_ignoring_ascii_case(AttributeNames::defaultselected)
|| attribute.equals_ignoring_ascii_case(AttributeNames::defer)
|| attribute.equals_ignoring_ascii_case(AttributeNames::disabled)
|| attribute.equals_ignoring_ascii_case(AttributeNames::ended)
|| attribute.equals_ignoring_ascii_case(AttributeNames::formnovalidate)
|| attribute.equals_ignoring_ascii_case(AttributeNames::hidden)
|| attribute.equals_ignoring_ascii_case(AttributeNames::indeterminate)
|| attribute.equals_ignoring_ascii_case(AttributeNames::iscontenteditable)
|| attribute.equals_ignoring_ascii_case(AttributeNames::ismap)
|| attribute.equals_ignoring_ascii_case(AttributeNames::itemscope)
|| attribute.equals_ignoring_ascii_case(AttributeNames::loop)
|| attribute.equals_ignoring_ascii_case(AttributeNames::multiple)
|| attribute.equals_ignoring_ascii_case(AttributeNames::muted)
|| 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(declare) \
__ENUMERATE_HTML_ATTRIBUTE(default_) \
__ENUMERATE_HTML_ATTRIBUTE(defaultchecked) \
__ENUMERATE_HTML_ATTRIBUTE(defaultselected) \
__ENUMERATE_HTML_ATTRIBUTE(defer) \
__ENUMERATE_HTML_ATTRIBUTE(dir) \
__ENUMERATE_HTML_ATTRIBUTE(direction) \
@ -67,6 +69,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(disabled) \
__ENUMERATE_HTML_ATTRIBUTE(download) \
__ENUMERATE_HTML_ATTRIBUTE(enctype) \
__ENUMERATE_HTML_ATTRIBUTE(ended) \
__ENUMERATE_HTML_ATTRIBUTE(event) \
__ENUMERATE_HTML_ATTRIBUTE(face) \
__ENUMERATE_HTML_ATTRIBUTE(fetchpriority) \
@ -90,9 +93,11 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(id) \
__ENUMERATE_HTML_ATTRIBUTE(imagesizes) \
__ENUMERATE_HTML_ATTRIBUTE(imagesrcset) \
__ENUMERATE_HTML_ATTRIBUTE(indeterminate) \
__ENUMERATE_HTML_ATTRIBUTE(inert) \
__ENUMERATE_HTML_ATTRIBUTE(integrity) \
__ENUMERATE_HTML_ATTRIBUTE(is) \
__ENUMERATE_HTML_ATTRIBUTE(iscontenteditable) \
__ENUMERATE_HTML_ATTRIBUTE(ismap) \
__ENUMERATE_HTML_ATTRIBUTE(itemscope) \
__ENUMERATE_HTML_ATTRIBUTE(kind) \
@ -212,6 +217,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(open) \
__ENUMERATE_HTML_ATTRIBUTE(optimum) \
__ENUMERATE_HTML_ATTRIBUTE(pattern) \
__ENUMERATE_HTML_ATTRIBUTE(paused) \
__ENUMERATE_HTML_ATTRIBUTE(ping) \
__ENUMERATE_HTML_ATTRIBUTE(placeholder) \
__ENUMERATE_HTML_ATTRIBUTE(playsinline) \
@ -232,6 +238,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(scrollamount) \
__ENUMERATE_HTML_ATTRIBUTE(scrolldelay) \
__ENUMERATE_HTML_ATTRIBUTE(scrolling) \
__ENUMERATE_HTML_ATTRIBUTE(seeking) \
__ENUMERATE_HTML_ATTRIBUTE(selected) \
__ENUMERATE_HTML_ATTRIBUTE(shadowrootclonable) \
__ENUMERATE_HTML_ATTRIBUTE(shadowrootdelegatesfocus) \
@ -265,6 +272,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(vlink) \
__ENUMERATE_HTML_ATTRIBUTE(vspace) \
__ENUMERATE_HTML_ATTRIBUTE(width) \
__ENUMERATE_HTML_ATTRIBUTE(willvalidate) \
__ENUMERATE_HTML_ATTRIBUTE(wrap)
#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
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());
// 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]() {
// 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));
// 4. Let result be the result of the first matching condition:
Optional<ByteString> result;
// 4. Let name be URL variables["name"].
// 5. Let result be the result of the first matching condition:
String result {};
// -> If name is a boolean attribute
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))
result = "true"sv;
result = "true"_string;
}
// -> Otherwise
else {
// The result of getting an attribute by name name.
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.
if (result.has_value()) {
async_driver_execution_complete({ result.release_value() });
return;
}
async_driver_execution_complete(JsonValue {});
async_driver_execution_complete({ result.to_byte_string() });
});
return JsonValue {};