From ecaacd2adfb133cb6f0a307592c33e0073137323 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 6 Nov 2024 18:31:04 -0500 Subject: [PATCH] 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. --- .../Libraries/LibWeb/HTML/AttributeNames.cpp | 69 +++++++++++-------- .../Libraries/LibWeb/HTML/AttributeNames.h | 8 +++ .../WebContent/WebDriverConnection.cpp | 23 +++---- 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp b/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp index a9e7b64c690..2e73cb25113 100644 --- a/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp +++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp @@ -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); } } diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.h b/Userland/Libraries/LibWeb/HTML/AttributeNames.h index 0f2544f633c..6dc4c95cbca 100644 --- a/Userland/Libraries/LibWeb/HTML/AttributeNames.h +++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.h @@ -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; diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index ecd32df6c1e..f230f00ae81 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -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 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 {};