Explorar el Código

LibWeb: Implement Element.insertAdjacentHTML() from DOM Parsing

One edge case is left as a TODO() for now, since I'm not entirely sure
how to construct an element to those specifications.

With this patch, we can now run the Speedometer benchmark! :^)
Andreas Kling hace 2 años
padre
commit
aa4dd6c1bc

+ 73 - 0
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -730,4 +730,77 @@ void Element::serialize_pseudo_elements_as_json(JsonArraySerializer<StringBuilde
     }
     }
 }
 }
 
 
+// https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml
+DOM::ExceptionOr<void> Element::insert_adjacent_html(String position, String text)
+{
+    JS::GCPtr<Node> context;
+    // 1. Use the first matching item from this list:
+    // - If position is an ASCII case-insensitive match for the string "beforebegin"
+    // - If position is an ASCII case-insensitive match for the string "afterend"
+    if (position.equals_ignoring_case("beforebegin"sv) || position.equals_ignoring_case("afterend"sv)) {
+        // Let context be the context object's parent.
+        context = this->parent();
+
+        // If context is null or a Document, throw a "NoModificationAllowedError" DOMException.
+        if (!context || context->is_document())
+            return NoModificationAllowedError::create(window(), "insertAdjacentHTML: context is null or a Document"sv);
+    }
+    // - If position is an ASCII case-insensitive match for the string "afterbegin"
+    // - If position is an ASCII case-insensitive match for the string "beforeend"
+    else if (position.equals_ignoring_case("afterbegin"sv) || position.equals_ignoring_case("beforeend"sv)) {
+        // Let context be the context object.
+        context = this;
+    }
+    // Otherwise
+    else {
+        // Throw a "SyntaxError" DOMException.
+        return SyntaxError::create(window(), "insertAdjacentHTML: invalid position argument"sv);
+    }
+
+    // 2. If context is not an Element or the following are all true:
+    //    - context's node document is an HTML document,
+    //    - context's local name is "html", and
+    //    - context's namespace is the HTML namespace;
+    if (!is<Element>(*context)
+        || (context->document().document_type() == Document::Type::HTML
+            && static_cast<Element const&>(*context).local_name() == "html"sv
+            && static_cast<Element const&>(*context).namespace_() == Namespace::HTML)) {
+        // FIXME: let context be a new Element with
+        //        - body as its local name,
+        //        - The HTML namespace as its namespace, and
+        //        - The context object's node document as its node document.
+        TODO();
+    }
+
+    // 3. Let fragment be the result of invoking the fragment parsing algorithm with text as markup, and context as the context element.
+    auto fragment = TRY(DOMParsing::parse_fragment(text, verify_cast<Element>(*context)));
+
+    // 4. Use the first matching item from this list:
+
+    // - If position is an ASCII case-insensitive match for the string "beforebegin"
+    if (position.equals_ignoring_case("beforebegin"sv)) {
+        // Insert fragment into the context object's parent before the context object.
+        parent()->insert_before(fragment, this);
+    }
+
+    // - If position is an ASCII case-insensitive match for the string "afterbegin"
+    else if (position.equals_ignoring_case("afterbegin"sv)) {
+        // Insert fragment into the context object before its first child.
+        insert_before(fragment, first_child());
+    }
+
+    // - If position is an ASCII case-insensitive match for the string "beforeend"
+    else if (position.equals_ignoring_case("beforeend"sv)) {
+        // Append fragment to the context object.
+        TRY(append_child(fragment));
+    }
+
+    // - If position is an ASCII case-insensitive match for the string "afterend"
+    else if (position.equals_ignoring_case("afterend"sv)) {
+        // Insert fragment into the context object's parent before the context object's next sibling.
+        parent()->insert_before(fragment, next_sibling());
+    }
+    return {};
+}
+
 }
 }

+ 2 - 0
Userland/Libraries/LibWeb/DOM/Element.h

@@ -108,6 +108,8 @@ public:
     String inner_html() const;
     String inner_html() const;
     ExceptionOr<void> set_inner_html(String const&);
     ExceptionOr<void> set_inner_html(String const&);
 
 
+    ExceptionOr<void> insert_adjacent_html(String position, String text);
+
     bool is_focused() const;
     bool is_focused() const;
     bool is_active() const;
     bool is_active() const;
 
 

+ 2 - 0
Userland/Libraries/LibWeb/DOM/Element.idl

@@ -52,6 +52,8 @@ interface Element : Node {
     readonly attribute long clientLeft;
     readonly attribute long clientLeft;
     readonly attribute long clientWidth;
     readonly attribute long clientWidth;
     readonly attribute long clientHeight;
     readonly attribute long clientHeight;
+
+    [CEReactions] undefined insertAdjacentHTML(DOMString position, DOMString text);
 };
 };
 
 
 Element includes ParentNode;
 Element includes ParentNode;

+ 1 - 1
Userland/Libraries/LibWeb/DOMParsing/InnerHTML.cpp

@@ -12,7 +12,7 @@
 namespace Web::DOMParsing {
 namespace Web::DOMParsing {
 
 
 // https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm
 // https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm
-static DOM::ExceptionOr<JS::NonnullGCPtr<DOM::DocumentFragment>> parse_fragment(String const& markup, DOM::Element& context_element)
+DOM::ExceptionOr<JS::NonnullGCPtr<DOM::DocumentFragment>> parse_fragment(String const& markup, DOM::Element& context_element)
 {
 {
     // FIXME: Handle XML documents.
     // FIXME: Handle XML documents.
 
 

+ 2 - 0
Userland/Libraries/LibWeb/DOMParsing/InnerHTML.h

@@ -16,4 +16,6 @@ namespace Web::DOMParsing {
 // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml
 // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml
 DOM::ExceptionOr<void> inner_html_setter(JS::NonnullGCPtr<DOM::Node> context_object, String const& value);
 DOM::ExceptionOr<void> inner_html_setter(JS::NonnullGCPtr<DOM::Node> context_object, String const& value);
 
 
+DOM::ExceptionOr<JS::NonnullGCPtr<DOM::DocumentFragment>> parse_fragment(String const& markup, DOM::Element& context_element);
+
 }
 }