From a6fb7c84e9602b3cf95cc3bea622f410b8bab58e Mon Sep 17 00:00:00 2001 From: Callum Law Date: Mon, 26 May 2025 16:31:43 +1200 Subject: [PATCH] LibWeb: Implement the autocapitalize attribute --- Libraries/LibWeb/HTML/AttributeNames.h | 1 + Libraries/LibWeb/HTML/HTMLElement.cpp | 93 +++ Libraries/LibWeb/HTML/HTMLElement.h | 12 + Libraries/LibWeb/HTML/HTMLElement.idl | 2 +- .../autocapitalization/autocapitalize.txt | 13 + .../autocapitalization/autocapitalize.html | 688 ++++++++++++++++++ 6 files changed, 808 insertions(+), 1 deletion(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.html diff --git a/Libraries/LibWeb/HTML/AttributeNames.h b/Libraries/LibWeb/HTML/AttributeNames.h index 3aa23cb82b4..26ab6d49c19 100644 --- a/Libraries/LibWeb/HTML/AttributeNames.h +++ b/Libraries/LibWeb/HTML/AttributeNames.h @@ -26,6 +26,7 @@ namespace AttributeNames { __ENUMERATE_HTML_ATTRIBUTE(archive, "archive") \ __ENUMERATE_HTML_ATTRIBUTE(as, "as") \ __ENUMERATE_HTML_ATTRIBUTE(async, "async") \ + __ENUMERATE_HTML_ATTRIBUTE(autocapitalize, "autocapitalize") \ __ENUMERATE_HTML_ATTRIBUTE(autocomplete, "autocomplete") \ __ENUMERATE_HTML_ATTRIBUTE(autofocus, "autofocus") \ __ENUMERATE_HTML_ATTRIBUTE(autoplay, "autoplay") \ diff --git a/Libraries/LibWeb/HTML/HTMLElement.cpp b/Libraries/LibWeb/HTML/HTMLElement.cpp index c669132d945..0988c5b7dfc 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -2232,4 +2232,97 @@ void HTMLElement::set_writing_suggestions(String const& given_value) MUST(set_attribute(HTML::AttributeNames::writingsuggestions, given_value)); } +// https://html.spec.whatwg.org/multipage/interaction.html#own-autocapitalization-hint +HTMLElement::AutocapitalizationHint HTMLElement::own_autocapitalization_hint() const +{ + // The autocapitalization processing model is based on selecting among five autocapitalization hints, defined as follows: + // + // default + // The user agent and input method should make their own determination of whether or not to enable autocapitalization. + // none + // No autocapitalization should be applied (all letters should default to lowercase). + // sentences + // The first letter of each sentence should default to a capital letter; all other letters should default to lowercase. + // words + // The first letter of each word should default to a capital letter; all other letters should default to lowercase. + // characters + // All letters should default to uppercase. + + // The autocapitalize attribute is an enumerated attribute whose states are the possible autocapitalization hints. + // The autocapitalization hint specified by the attribute's state combines with other considerations to form the + // used autocapitalization hint, which informs the behavior of the user agent. The keywords for this attribute and + // their state mappings are as follows: + + // Keyword | State + // off | none + // none | + // on | sentences + // sentences | + // words | words + // characters | characters + + // The attribute's missing value default is the default state, and its invalid value default is the sentences state. + + // To compute the own autocapitalization hint of an element element, run the following steps: + // 1. If the autocapitalize content attribute is present on element, and its value is not the empty string, return the + // state of the attribute. + auto maybe_autocapitalize_attribute = attribute(HTML::AttributeNames::autocapitalize); + + if (maybe_autocapitalize_attribute.has_value() && !maybe_autocapitalize_attribute.value().is_empty()) { + auto autocapitalize_attribute_string_view = maybe_autocapitalize_attribute.value().bytes_as_string_view(); + + if (autocapitalize_attribute_string_view.is_one_of_ignoring_ascii_case("off"sv, "none"sv)) + return AutocapitalizationHint::None; + + if (autocapitalize_attribute_string_view.equals_ignoring_ascii_case("words"sv)) + return AutocapitalizationHint::Words; + + if (autocapitalize_attribute_string_view.equals_ignoring_ascii_case("characters"sv)) + return AutocapitalizationHint::Characters; + + return AutocapitalizationHint::Sentences; + } + + // If element is an autocapitalize-and-autocorrect inheriting element and has a non-null form owner, return the own autocapitalization hint of element's form owner. + auto const* form_associated_element = as_if(this); + if (form_associated_element && form_associated_element->is_autocapitalize_and_autocorrect_inheriting() && form_associated_element->form()) + return form_associated_element->form()->own_autocapitalization_hint(); + + // 3. Return default. + return AutocapitalizationHint::Default; +} + +// https://html.spec.whatwg.org/multipage/interaction.html#attr-autocapitalize +String HTMLElement::autocapitalize() const +{ + // The autocapitalize getter steps are to: + // 1. Let state be the own autocapitalization hint of this. + auto state = own_autocapitalization_hint(); + + // 2. If state is default, then return the empty string. + // 3. If state is none, then return "none". + // 4. If state is sentences, then return "sentences". + // 5. Return the keyword value corresponding to state. + switch (state) { + case AutocapitalizationHint::Default: + return String {}; + case AutocapitalizationHint::None: + return "none"_string; + case AutocapitalizationHint::Sentences: + return "sentences"_string; + case AutocapitalizationHint::Words: + return "words"_string; + case AutocapitalizationHint::Characters: + return "characters"_string; + } + + VERIFY_NOT_REACHED(); +} + +void HTMLElement::set_autocapitalize(String const& given_value) +{ + // The autocapitalize setter steps are to set the autocapitalize content attribute to the given value. + MUST(set_attribute(HTML::AttributeNames::autocapitalize, given_value)); +} + } diff --git a/Libraries/LibWeb/HTML/HTMLElement.h b/Libraries/LibWeb/HTML/HTMLElement.h index cd2807214c9..03aea314a70 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.h +++ b/Libraries/LibWeb/HTML/HTMLElement.h @@ -123,6 +123,18 @@ public: String writing_suggestions() const; void set_writing_suggestions(String const&); + enum class AutocapitalizationHint { + Default, + None, + Sentences, + Words, + Characters + }; + + AutocapitalizationHint own_autocapitalization_hint() const; + String autocapitalize() const; + void set_autocapitalize(String const&); + bool fire_a_synthetic_pointer_event(FlyString const& type, DOM::Element& target, bool not_trusted); // https://html.spec.whatwg.org/multipage/forms.html#category-label diff --git a/Libraries/LibWeb/HTML/HTMLElement.idl b/Libraries/LibWeb/HTML/HTMLElement.idl index b2b43c346dc..ff0f1d44032 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.idl +++ b/Libraries/LibWeb/HTML/HTMLElement.idl @@ -26,7 +26,7 @@ interface HTMLElement : Element { [CEReactions] attribute boolean draggable; [CEReactions] attribute boolean spellcheck; [CEReactions] attribute DOMString writingSuggestions; - [FIXME, CEReactions] attribute DOMString autocapitalize; + [CEReactions] attribute DOMString autocapitalize; [FIXME, CEReactions] attribute boolean autocorrect; [LegacyNullToEmptyString, CEReactions] attribute Utf16DOMString innerText; diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.txt b/Tests/LibWeb/Text/expected/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.txt new file mode 100644 index 00000000000..2a0db180b52 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.txt @@ -0,0 +1,13 @@ +Harness status: OK + +Found 8 tests + +8 Pass +Pass Test that the autocapitalize is available on HTMLInputElement. +Pass Test that the autocapitalize is available on HTMLTextAreaElement. +Pass Test that the autocapitalize is available on div. +Pass Test deprecated values of autocapitalize. +Pass Test reflection of autocapitalize. +Pass Test that the IDL attribute returns the empty string if the content attribute is not set. +Pass Test inheriting values from a form. +Pass Verify that even input types that are never autocapitalized support the IDL interface. \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.html b/Tests/LibWeb/Text/input/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.html new file mode 100644 index 00000000000..50c9ffbec18 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/editing/editing-0/autocapitalization/autocapitalize.html @@ -0,0 +1,688 @@ + + + + + + + + +