diff options
author | Luke Wilde <lukew@serenityos.org> | 2021-09-26 15:24:41 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-09-26 18:59:56 +0200 |
commit | f6b24a72ee60be994917d941e30c4fe7ccccfee6 (patch) | |
tree | 201c0b4dd3604c0bab2d2f8210d77de7df9b6bf2 /Userland | |
parent | 37347cbcb64e20300155df4168412f58f33f07c6 (diff) | |
download | serenity-f6b24a72ee60be994917d941e30c4fe7ccccfee6.zip |
LibWeb: Add support for HTMLOrSVGElement.dataset
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Forward.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/DOMStringMap.cpp | 173 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/DOMStringMap.h | 54 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/DOMStringMap.idl | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLElement.cpp | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLElement.h | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLElement.idl | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/SVGElement.cpp | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/SVGElement.h | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/SVGElement.idl | 3 |
12 files changed, 259 insertions, 1 deletions
diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index 04096c8ffc..827773ce89 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -28,6 +28,8 @@ #include <LibWeb/Bindings/DOMImplementationPrototype.h> #include <LibWeb/Bindings/DOMParserConstructor.h> #include <LibWeb/Bindings/DOMParserPrototype.h> +#include <LibWeb/Bindings/DOMStringMapConstructor.h> +#include <LibWeb/Bindings/DOMStringMapPrototype.h> #include <LibWeb/Bindings/DocumentConstructor.h> #include <LibWeb/Bindings/DocumentFragmentConstructor.h> #include <LibWeb/Bindings/DocumentFragmentPrototype.h> @@ -268,6 +270,7 @@ ADD_WINDOW_OBJECT_INTERFACE(DOMException) \ ADD_WINDOW_OBJECT_INTERFACE(DOMImplementation) \ ADD_WINDOW_OBJECT_INTERFACE(DOMParser) \ + ADD_WINDOW_OBJECT_INTERFACE(DOMStringMap) \ ADD_WINDOW_OBJECT_INTERFACE(Element) \ ADD_WINDOW_OBJECT_INTERFACE(Event) \ ADD_WINDOW_OBJECT_INTERFACE(EventTarget) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 80ebbac1d9..79293e8254 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -74,6 +74,7 @@ set(SOURCES HTML/BrowsingContextContainer.cpp HTML/CanvasRenderingContext2D.cpp HTML/DOMParser.cpp + HTML/DOMStringMap.cpp HTML/EventLoop/EventLoop.cpp HTML/EventLoop/Task.cpp HTML/EventLoop/TaskQueue.cpp @@ -344,6 +345,7 @@ libweb_js_wrapper(DOM/Text) libweb_js_wrapper(HTML/CanvasRenderingContext2D) libweb_js_wrapper(HTML/CloseEvent) libweb_js_wrapper(HTML/DOMParser) +libweb_js_wrapper(HTML/DOMStringMap) libweb_js_wrapper(HTML/History) libweb_js_wrapper(HTML/HTMLAnchorElement) libweb_js_wrapper(HTML/HTMLAreaElement) diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index fcc00aca80..4683bced38 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -94,6 +94,7 @@ namespace Web::HTML { class CanvasRenderingContext2D; class CloseEvent; class DOMParser; +class DOMStringMap; struct EventHandler; class EventLoop; class HTMLAnchorElement; @@ -260,6 +261,7 @@ class DocumentWrapper; class DOMExceptionWrapper; class DOMImplementationWrapper; class DOMParserWrapper; +class DOMStringMapWrapper; class ElementWrapper; class EventListenerWrapper; class EventTargetWrapper; diff --git a/Userland/Libraries/LibWeb/HTML/DOMStringMap.cpp b/Userland/Libraries/LibWeb/HTML/DOMStringMap.cpp new file mode 100644 index 0000000000..7eb7e5e67b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/DOMStringMap.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/CharacterTypes.h> +#include <LibWeb/DOM/Element.h> +#include <LibWeb/HTML/DOMStringMap.h> + +namespace Web::HTML { + +DOMStringMap::DOMStringMap(DOM::Element& associated_element) + : m_associated_element(associated_element) +{ +} + +DOMStringMap::~DOMStringMap() +{ +} + +// https://html.spec.whatwg.org/multipage/dom.html#concept-domstringmap-pairs +Vector<DOMStringMap::NameValuePair> DOMStringMap::get_name_value_pairs() const +{ + // 1. Let list be an empty list of name-value pairs. + Vector<NameValuePair> list; + + // 2. For each content attribute on the DOMStringMap's associated element whose first five characters are the string "data-" and whose remaining characters (if any) do not include any ASCII upper alphas, + // in the order that those attributes are listed in the element's attribute list, add a name-value pair to list whose name is the attribute's name with the first five characters removed and whose value + // is the attribute's value. + m_associated_element->for_each_attribute([&](auto& name, auto& value) { + if (!name.starts_with("data-")) + return; + + auto name_after_starting_data = name.view().substring_view(5); + + for (auto character : name_after_starting_data) { + if (is_ascii_upper_alpha(character)) + return; + } + + // 3. For each name in list, for each U+002D HYPHEN-MINUS character (-) in the name that is followed by an ASCII lower alpha, remove the U+002D HYPHEN-MINUS character (-) and replace the character + // that followed it by the same character converted to ASCII uppercase. + StringBuilder builder; + for (size_t character_index = 0; character_index < name_after_starting_data.length(); ++character_index) { + auto current_character = name_after_starting_data[character_index]; + + if (character_index + 1 < name_after_starting_data.length() && current_character == '-') { + auto next_character = name_after_starting_data[character_index + 1]; + + if (is_ascii_lower_alpha(next_character)) { + builder.append(to_ascii_uppercase(next_character)); + + // Skip the next character + ++character_index; + + return; + } + } + + builder.append(current_character); + } + + list.append({ builder.to_string(), value }); + }); + + // 4. Return list. + return list; +} + +// https://html.spec.whatwg.org/multipage/dom.html#concept-domstringmap-pairs +// NOTE: There isn't a direct link to this, so the link is to one of the algorithms above it. +Vector<String> DOMStringMap::supported_property_names() const +{ + // The supported property names on a DOMStringMap object at any instant are the names of each pair returned from getting the DOMStringMap's name-value pairs at that instant, in the order returned. + Vector<String> names; + auto name_value_pairs = get_name_value_pairs(); + for (auto& name_value_pair : name_value_pairs) { + names.append(name_value_pair.name); + } + return names; +} + +// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-nameditem +String DOMStringMap::determine_value_of_named_property(String const& name) const +{ + // To determine the value of a named property name for a DOMStringMap, return the value component of the name-value pair whose name component is name in the list returned from getting the + // DOMStringMap's name-value pairs. + auto name_value_pairs = get_name_value_pairs(); + auto optional_value = name_value_pairs.first_matching([&name](NameValuePair& name_value_pair) { + return name_value_pair.name == name; + }); + + // NOTE: determine_value_of_named_property is only called if `name` is in supported_property_names. + VERIFY(optional_value.has_value()); + + return optional_value->value; +} + +// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-setitem +DOM::ExceptionOr<void> DOMStringMap::set_value_of_new_named_property(String const& name, String const& value) +{ + AK::StringBuilder builder; + + // 3. Insert the string data- at the front of name. + // NOTE: This is done out of order because StringBuilder doesn't have prepend. + builder.append("data-"); + + for (size_t character_index = 0; character_index < name.length(); ++character_index) { + // 1. If name contains a U+002D HYPHEN-MINUS character (-) followed by an ASCII lower alpha, then throw a "SyntaxError" DOMException. + auto current_character = name[character_index]; + + if (current_character == '-' && character_index + 1 < name.length()) { + auto next_character = name[character_index + 1]; + if (is_ascii_lower_alpha(next_character)) + return DOM::SyntaxError::create("Name cannot contain a '-' followed by a lowercase character."); + } + + // 2. For each ASCII upper alpha in name, insert a U+002D HYPHEN-MINUS character (-) before the character and replace the character with the same character converted to ASCII lowercase. + if (is_ascii_upper_alpha(current_character)) { + builder.append('-'); + builder.append(to_ascii_lowercase(current_character)); + continue; + } + + builder.append(current_character); + } + + auto data_name = builder.to_string(); + + // FIXME: 4. If name does not match the XML Name production, throw an "InvalidCharacterError" DOMException. + + // 5. Set an attribute value for the DOMStringMap's associated element using name and value. + m_associated_element->set_attribute(data_name, value); + + return {}; +} + +// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-setitem +DOM::ExceptionOr<void> DOMStringMap::set_value_of_existing_named_property(String const& name, String const& value) +{ + return set_value_of_new_named_property(name, value); +} + +// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-removeitem +bool DOMStringMap::delete_existing_named_property(String const& name) +{ + AK::StringBuilder builder; + + // 2. Insert the string data- at the front of name. + // NOTE: This is done out of order because StringBuilder doesn't have prepend. + builder.append("data-"); + + for (auto character : name) { + // 1. For each ASCII upper alpha in name, insert a U+002D HYPHEN-MINUS character (-) before the character and replace the character with the same character converted to ASCII lowercase. + if (is_ascii_upper_alpha(character)) { + builder.append('-'); + builder.append(to_ascii_lowercase(character)); + continue; + } + + builder.append(character); + } + + // Remove an attribute by name given name and the DOMStringMap's associated element. + auto data_name = builder.to_string(); + m_associated_element->remove_attribute(data_name); + + // The spec doesn't have the step. This indicates that the deletion was successful. + return true; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/DOMStringMap.h b/Userland/Libraries/LibWeb/HTML/DOMStringMap.h new file mode 100644 index 0000000000..a29c85faa4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/DOMStringMap.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/RefCounted.h> +#include <AK/Weakable.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/Forward.h> + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/dom.html#domstringmap +class DOMStringMap final + : public RefCounted<DOMStringMap> + , public Weakable<DOMStringMap> + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::DOMStringMapWrapper; + + static NonnullRefPtr<DOMStringMap> create(DOM::Element& associated_element) + { + return adopt_ref(*new DOMStringMap(associated_element)); + } + + virtual ~DOMStringMap() override; + + Vector<String> supported_property_names() const; + + String determine_value_of_named_property(String const&) const; + + DOM::ExceptionOr<void> set_value_of_new_named_property(String const&, String const&); + DOM::ExceptionOr<void> set_value_of_existing_named_property(String const&, String const&); + + bool delete_existing_named_property(String const&); + +private: + DOMStringMap(DOM::Element&); + + struct NameValuePair { + String name; + String value; + }; + + Vector<NameValuePair> get_name_value_pairs() const; + + // https://html.spec.whatwg.org/multipage/dom.html#concept-domstringmap-element + NonnullRefPtr<DOM::Element> m_associated_element; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/DOMStringMap.idl b/Userland/Libraries/LibWeb/HTML/DOMStringMap.idl new file mode 100644 index 0000000000..191e59d49c --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/DOMStringMap.idl @@ -0,0 +1,8 @@ +[Exposed=Window, LegacyOverrideBuiltIns] +interface DOMStringMap { + getter DOMString (DOMString name); + + // FIXME: Extended attributes are currently not applied to special operations. + [CEReactions] setter undefined (DOMString name, DOMString value); + [CEReactions] deleter undefined (DOMString name); +}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp index c6d5ba6205..2f32a0a58d 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -23,6 +23,7 @@ namespace Web::HTML { HTMLElement::HTMLElement(DOM::Document& document, QualifiedName qualified_name) : Element(document, move(qualified_name)) + , m_dataset(DOMStringMap::create(*this)) { } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.h b/Userland/Libraries/LibWeb/HTML/HTMLElement.h index c0e24322fa..cbd70942ad 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.h @@ -7,6 +7,7 @@ #pragma once #include <LibWeb/DOM/Element.h> +#include <LibWeb/HTML/DOMStringMap.h> #include <LibWeb/HTML/EventNames.h> #include <LibWeb/HTML/GlobalEventHandlers.h> @@ -35,6 +36,8 @@ public: bool cannot_navigate() const; + NonnullRefPtr<DOMStringMap> dataset() const { return m_dataset; } + protected: virtual void parse_attribute(const FlyString& name, const String& value) override; @@ -48,6 +51,8 @@ private: Inherit, }; ContentEditableState content_editable_state() const; + + NonnullRefPtr<DOMStringMap> m_dataset; }; } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl index 5131df71c3..d9db6724b1 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl @@ -12,6 +12,9 @@ interface HTMLElement : Element { readonly attribute long offsetTop; readonly attribute long offsetLeft; + // FIXME: This should come from a HTMLOrSVGElement mixin + [SameObject] readonly attribute DOMStringMap dataset; + // FIXME: These should all come from a GlobalEventHandlers mixin attribute EventHandler onabort; attribute EventHandler onauxclick; diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGElement.cpp index 1167d01245..941fdd9add 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGElement.cpp @@ -10,6 +10,7 @@ namespace Web::SVG { SVGElement::SVGElement(DOM::Document& document, QualifiedName qualified_name) : Element(document, move(qualified_name)) + , m_dataset(HTML::DOMStringMap::create(*this)) { } diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.h b/Userland/Libraries/LibWeb/SVG/SVGElement.h index 221d88d552..a1827998ff 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGElement.h @@ -7,6 +7,7 @@ #pragma once #include <LibWeb/DOM/Element.h> +#include <LibWeb/HTML/DOMStringMap.h> namespace Web::SVG { @@ -16,8 +17,12 @@ public: virtual bool requires_svg_container() const override { return true; } + NonnullRefPtr<HTML::DOMStringMap> dataset() const { return m_dataset; } + protected: SVGElement(DOM::Document&, QualifiedName); + + NonnullRefPtr<HTML::DOMStringMap> m_dataset; }; } diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.idl b/Userland/Libraries/LibWeb/SVG/SVGElement.idl index 1eb7f7f18c..3729757766 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGElement.idl +++ b/Userland/Libraries/LibWeb/SVG/SVGElement.idl @@ -1,3 +1,4 @@ interface SVGElement : Element { - + // FIXME: This should come from a HTMLOrSVGElement mixin + [SameObject] readonly attribute DOMStringMap dataset; }; |