From aa4dd6c1bc4dc28cf096aff76b3487501fe7af1f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 20 Sep 2022 18:28:41 +0200 Subject: 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! :^) --- Userland/Libraries/LibWeb/DOM/Element.cpp | 73 ++++++++++++++++++++++ Userland/Libraries/LibWeb/DOM/Element.h | 2 + Userland/Libraries/LibWeb/DOM/Element.idl | 2 + Userland/Libraries/LibWeb/DOMParsing/InnerHTML.cpp | 2 +- Userland/Libraries/LibWeb/DOMParsing/InnerHTML.h | 2 + 5 files changed, 80 insertions(+), 1 deletion(-) (limited to 'Userland/Libraries/LibWeb') diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index a65dc23c56..c950801065 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -730,4 +730,77 @@ void Element::serialize_pseudo_elements_as_json(JsonArraySerializer Element::insert_adjacent_html(String position, String text) +{ + JS::GCPtr 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(*context) + || (context->document().document_type() == Document::Type::HTML + && static_cast(*context).local_name() == "html"sv + && static_cast(*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(*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 {}; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 020fea56b1..603278c7d2 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -108,6 +108,8 @@ public: String inner_html() const; ExceptionOr set_inner_html(String const&); + ExceptionOr insert_adjacent_html(String position, String text); + bool is_focused() const; bool is_active() const; diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl index db169a4869..9d25de71b2 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.idl +++ b/Userland/Libraries/LibWeb/DOM/Element.idl @@ -52,6 +52,8 @@ interface Element : Node { readonly attribute long clientLeft; readonly attribute long clientWidth; readonly attribute long clientHeight; + + [CEReactions] undefined insertAdjacentHTML(DOMString position, DOMString text); }; Element includes ParentNode; diff --git a/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.cpp b/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.cpp index a92e187aa2..245dbeb049 100644 --- a/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.cpp +++ b/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.cpp @@ -12,7 +12,7 @@ namespace Web::DOMParsing { // https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm -static DOM::ExceptionOr> parse_fragment(String const& markup, DOM::Element& context_element) +DOM::ExceptionOr> parse_fragment(String const& markup, DOM::Element& context_element) { // FIXME: Handle XML documents. diff --git a/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.h b/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.h index 9689d4edcc..9619254e14 100644 --- a/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.h +++ b/Userland/Libraries/LibWeb/DOMParsing/InnerHTML.h @@ -16,4 +16,6 @@ namespace Web::DOMParsing { // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml DOM::ExceptionOr inner_html_setter(JS::NonnullGCPtr context_object, String const& value); +DOM::ExceptionOr> parse_fragment(String const& markup, DOM::Element& context_element); + } -- cgit v1.2.3