diff options
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibWeb/DOM/Document.h | 10 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/DocumentFragment.h | 13 | ||||
-rw-r--r-- | Libraries/LibWeb/DOMTreeModel.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Dump.cpp | 12 | ||||
-rw-r--r-- | Libraries/LibWeb/Forward.h | 1 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/HTMLTemplateElement.cpp | 21 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/HTMLTemplateElement.h | 9 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/HTMLTemplateElement.idl | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp | 78 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h | 1 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h | 6 | ||||
-rw-r--r-- | Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js | 27 | ||||
-rw-r--r-- | Libraries/LibWeb/Tests/Pages/Template.html | 14 |
14 files changed, 174 insertions, 28 deletions
diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 03ed3108b0..8f58a97353 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -167,6 +167,13 @@ public: void set_focused_element(Element*); + bool created_for_appropriate_template_contents() const { return m_created_for_appropriate_template_contents; } + void set_created_for_appropriate_template_contents(bool value) { m_created_for_appropriate_template_contents = value; } + + Document* associated_inert_template_document() { return m_associated_inert_template_document; } + const Document* associated_inert_template_document() const { return m_associated_inert_template_document; } + void set_associated_inert_template_document(Document& document) { m_associated_inert_template_document = document; } + private: virtual RefPtr<LayoutNode> create_layout_node(const CSS::StyleProperties* parent_style) override; @@ -199,6 +206,9 @@ private: bool m_editable { false }; WeakPtr<Element> m_focused_element; + + bool m_created_for_appropriate_template_contents { false }; + RefPtr<Document> m_associated_inert_template_document; }; } diff --git a/Libraries/LibWeb/DOM/DocumentFragment.h b/Libraries/LibWeb/DOM/DocumentFragment.h index b969fbde4a..b067b7e429 100644 --- a/Libraries/LibWeb/DOM/DocumentFragment.h +++ b/Libraries/LibWeb/DOM/DocumentFragment.h @@ -29,6 +29,7 @@ #include <AK/FlyString.h> #include <LibWeb/DOM/NonElementParentNode.h> #include <LibWeb/DOM/ParentNode.h> +#include <LibWeb/DOM/Element.h> namespace Web::DOM { @@ -36,10 +37,20 @@ class DocumentFragment : public ParentNode , public NonElementParentNode<DocumentFragment> { public: - DocumentFragment(Document& document); + using WrapperType = Bindings::DocumentFragmentWrapper; + + explicit DocumentFragment(Document& document); virtual ~DocumentFragment() override; virtual FlyString node_name() const override { return "#document-fragment"; } + + Element& host() { return *m_host; } + const Element& host() const { return *m_host; } + + void set_host(Element& host) { m_host = host; } + +private: + RefPtr<Element> m_host; }; } diff --git a/Libraries/LibWeb/DOMTreeModel.cpp b/Libraries/LibWeb/DOMTreeModel.cpp index a8c30e2f23..c8c6a43ce2 100644 --- a/Libraries/LibWeb/DOMTreeModel.cpp +++ b/Libraries/LibWeb/DOMTreeModel.cpp @@ -63,6 +63,8 @@ GUI::ModelIndex DOMTreeModel::parent_index(const GUI::ModelIndex& index) const if (!node.parent()) return {}; + // FIXME: Handle the template element (child elements are not stored in it, all of its children are in its document fragment "content") + // No grandparent? Parent is the document! if (!node.parent()->parent()) { return create_index(0, 0, m_document.ptr()); diff --git a/Libraries/LibWeb/Dump.cpp b/Libraries/LibWeb/Dump.cpp index f6475f8bd4..002e71795b 100644 --- a/Libraries/LibWeb/Dump.cpp +++ b/Libraries/LibWeb/Dump.cpp @@ -36,6 +36,7 @@ #include <LibWeb/DOM/Element.h> #include <LibWeb/DOM/Text.h> #include <LibWeb/Dump.h> +#include <LibWeb/HTML/HTMLTemplateElement.h> #include <LibWeb/Layout/LayoutBlock.h> #include <LibWeb/Layout/LayoutNode.h> #include <LibWeb/Layout/LayoutText.h> @@ -67,9 +68,14 @@ void dump_tree(const DOM::Node& node) } ++indent; if (is<DOM::ParentNode>(node)) { - static_cast<const DOM::ParentNode&>(node).for_each_child([](auto& child) { - dump_tree(child); - }); + if (!is<HTML::HTMLTemplateElement>(node)) { + static_cast<const DOM::ParentNode&>(node).for_each_child([](auto& child) { + dump_tree(child); + }); + } else { + auto& template_element = downcast<HTML::HTMLTemplateElement>(node); + dump_tree(template_element.content()); + } } --indent; } diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index f553a823d6..c162796c22 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -159,6 +159,7 @@ namespace Web::Bindings { class CanvasRenderingContext2DWrapper; class CharacterDataWrapper; class CommentWrapper; +class DocumentFragmentWrapper; class DocumentTypeWrapper; class DocumentWrapper; class ElementWrapper; diff --git a/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp b/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp index aeb8679a40..8a3b0812e0 100644 --- a/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <LibWeb/DOM/Document.h> #include <LibWeb/HTML/HTMLTemplateElement.h> namespace Web::HTML { @@ -31,10 +32,30 @@ namespace Web::HTML { HTMLTemplateElement::HTMLTemplateElement(DOM::Document& document, const FlyString& tag_name) : HTMLElement(document, tag_name) { + m_content = adopt(*new DOM::DocumentFragment(appropriate_template_contents_owner_document(document))); + m_content->set_host(*this); } HTMLTemplateElement::~HTMLTemplateElement() { } +DOM::Document& HTMLTemplateElement::appropriate_template_contents_owner_document(DOM::Document& document) +{ + if (!document.created_for_appropriate_template_contents()) { + if (!document.associated_inert_template_document()) { + DOM::Document new_document; + new_document.set_created_for_appropriate_template_contents(true); + + // FIXME: If doc is an HTML document, mark new doc as an HTML document also. + + document.set_associated_inert_template_document(new_document); + } + + return *document.associated_inert_template_document(); + } + + return document; +} + } diff --git a/Libraries/LibWeb/HTML/HTMLTemplateElement.h b/Libraries/LibWeb/HTML/HTMLTemplateElement.h index 9a8ceda156..47e383cf74 100644 --- a/Libraries/LibWeb/HTML/HTMLTemplateElement.h +++ b/Libraries/LibWeb/HTML/HTMLTemplateElement.h @@ -26,6 +26,7 @@ #pragma once +#include <LibWeb/DOM/DocumentFragment.h> #include <LibWeb/HTML/HTMLElement.h> namespace Web::HTML { @@ -36,6 +37,14 @@ public: HTMLTemplateElement(DOM::Document&, const FlyString& local_name); virtual ~HTMLTemplateElement() override; + + NonnullRefPtr<DOM::DocumentFragment> content() { return *m_content; } + const NonnullRefPtr<DOM::DocumentFragment> content() const { return *m_content; } + +private: + DOM::Document& appropriate_template_contents_owner_document(DOM::Document&); + + RefPtr<DOM::DocumentFragment> m_content; }; } diff --git a/Libraries/LibWeb/HTML/HTMLTemplateElement.idl b/Libraries/LibWeb/HTML/HTMLTemplateElement.idl index 2302740878..b5d2d69511 100644 --- a/Libraries/LibWeb/HTML/HTMLTemplateElement.idl +++ b/Libraries/LibWeb/HTML/HTMLTemplateElement.idl @@ -1,5 +1,5 @@ interface HTMLTemplateElement : HTMLElement { - + readonly attribute DocumentFragment content; } diff --git a/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp b/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp index ca8ab1f683..62772ce7f8 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp +++ b/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp @@ -36,6 +36,7 @@ #include <LibWeb/HTML/HTMLFormElement.h> #include <LibWeb/HTML/HTMLHeadElement.h> #include <LibWeb/HTML/HTMLScriptElement.h> +#include <LibWeb/HTML/HTMLTemplateElement.h> #include <LibWeb/HTML/Parser/HTMLDocumentParser.h> #include <LibWeb/HTML/Parser/HTMLToken.h> @@ -385,18 +386,33 @@ DOM::Element& HTMLDocumentParser::node_before_current_node() HTMLDocumentParser::AdjustedInsertionLocation HTMLDocumentParser::find_appropriate_place_for_inserting_node() { auto& target = current_node(); + HTMLDocumentParser::AdjustedInsertionLocation adjusted_insertion_location; + if (m_foster_parenting && target.local_name().is_one_of(HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) { - // FIXME: There's a bunch of steps for <template> elements here. + auto last_template = m_stack_of_open_elements.last_element_with_tag_name(HTML::TagNames::template_); auto last_table = m_stack_of_open_elements.last_element_with_tag_name(HTML::TagNames::table); - if (!last_table) { + if (last_template.element && (!last_table.element || last_template.index > last_table.index)) { + // This returns the template content, so no need to check the parent is a template. + return { downcast<HTMLTemplateElement>(last_template.element)->content(), nullptr }; + } + if (!last_table.element) { ASSERT(m_parsing_fragment); + // Guaranteed not to be a template element (it will be the html element), + // so no need to check the parent is a template. return { m_stack_of_open_elements.elements().first(), nullptr }; } - if (last_table->parent_node()) - return { last_table->parent_node(), last_table }; - return { m_stack_of_open_elements.element_before(*last_table), nullptr }; + if (last_table.element->parent_node()) + adjusted_insertion_location = { last_table.element->parent_node(), last_table.element }; + else + adjusted_insertion_location = { m_stack_of_open_elements.element_before(*last_table.element), nullptr }; + } else { + adjusted_insertion_location = { target, nullptr }; } - return { target, nullptr }; + + if (is<HTMLTemplateElement>(*adjusted_insertion_location.parent)) + return { downcast<HTMLTemplateElement>(*adjusted_insertion_location.parent).content(), nullptr }; + + return adjusted_insertion_location; } NonnullRefPtr<DOM::Element> HTMLDocumentParser::create_element_for(const HTMLToken& token) @@ -557,15 +573,29 @@ void HTMLDocumentParser::handle_in_head(HTMLToken& token) } if (token.is_start_tag() && token.tag_name() == HTML::TagNames::template_) { - // FIXME: Support this properly insert_html_element(token); + m_list_of_active_formatting_elements.add_marker(); + m_frameset_ok = false; + m_insertion_mode = InsertionMode::InTemplate; + m_stack_of_template_insertion_modes.append(InsertionMode::InTemplate); return; } if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) { - // FIXME: Support this properly - ASSERT(current_node().local_name() == HTML::TagNames::template_); - m_stack_of_open_elements.pop(); + if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) { + PARSE_ERROR(); + return; + } + + generate_all_implied_end_tags_thoroughly(); + + if (current_node().local_name() != HTML::TagNames::template_) + PARSE_ERROR(); + + m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::template_); + m_list_of_active_formatting_elements.clear_up_to_the_last_marker(); + m_stack_of_template_insertion_modes.take_last(); + reset_the_insertion_mode_appropriately(); return; } @@ -739,6 +769,12 @@ void HTMLDocumentParser::generate_implied_end_tags(const FlyString& exception) m_stack_of_open_elements.pop(); } +void HTMLDocumentParser::generate_all_implied_end_tags_thoroughly() +{ + while (current_node().local_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::colgroup, HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) + m_stack_of_open_elements.pop(); +} + void HTMLDocumentParser::close_a_p_element() { generate_implied_end_tags(HTML::TagNames::p); @@ -1084,14 +1120,17 @@ void HTMLDocumentParser::handle_in_body(HTMLToken& token) } if (token.is_end_of_file()) { - // FIXME: If the stack of template insertion modes is not empty, - // then process the token using the rules for the "in template" insertion mode. + if (!m_stack_of_template_insertion_modes.is_empty()) { + process_using_the_rules_for(InsertionMode::InTemplate, token); + return; + } - // FIXME: If there is a node in the stack of open elements that is not either - // a dd element, a dt element, an li element, an optgroup element, an option element, - // a p element, an rb element, an rp element, an rt element, an rtc element, - // a tbody element, a td element, a tfoot element, a th element, a thead element, - // a tr element, the body element, or the html element, then this is a parse error. + for (auto& node : m_stack_of_open_elements.elements()) { + if (!node.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::body, HTML::TagNames::html)) { + PARSE_ERROR(); + break; + } + } stop_parsing(); return; @@ -2697,7 +2736,8 @@ void HTMLDocumentParser::reset_the_insertion_mode_appropriately() } if (node->local_name() == HTML::TagNames::template_) { - TODO(); + m_insertion_mode = m_stack_of_template_insertion_modes.last(); + return; } if (!last && node->local_name() == HTML::TagNames::head) { @@ -2774,7 +2814,7 @@ NonnullRefPtrVector<DOM::Node> HTMLDocumentParser::parse_html_fragment(DOM::Elem parser.m_stack_of_open_elements.push(root); if (context_element.local_name() == HTML::TagNames::template_) { - TODO(); + parser.m_stack_of_template_insertion_modes.append(InsertionMode::InTemplate); } // FIXME: Create a start tag token whose name is the local name of context and whose attributes are the attributes of context. diff --git a/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h b/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h index bc75cf4990..8361eebc1c 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h +++ b/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h @@ -115,6 +115,7 @@ private: void stop_parsing() { m_stop_parsing = true; } void generate_implied_end_tags(const FlyString& exception = {}); + void generate_all_implied_end_tags_thoroughly(); bool stack_of_open_elements_has_element_with_tag_name_in_scope(const FlyString& tag_name); NonnullRefPtr<DOM::Element> create_element_for(const HTMLToken&); diff --git a/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp b/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp index bd58f25062..76c2ebef32 100644 --- a/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp +++ b/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp @@ -133,14 +133,14 @@ DOM::Element* StackOfOpenElements::topmost_special_node_below(const DOM::Element return found_element; } -DOM::Element* StackOfOpenElements::last_element_with_tag_name(const FlyString& tag_name) +StackOfOpenElements::LastElementResult StackOfOpenElements::last_element_with_tag_name(const FlyString& tag_name) { for (ssize_t i = m_elements.size() - 1; i >= 0; --i) { auto& element = m_elements[i]; if (element.local_name() == tag_name) - return &element; + return { &element, i }; } - return nullptr; + return { nullptr, -1 }; } DOM::Element* StackOfOpenElements::element_before(const DOM::Element& target) diff --git a/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h b/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h index 54a709d943..2a62bbf53e 100644 --- a/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h +++ b/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h @@ -65,7 +65,11 @@ public: DOM::Element* topmost_special_node_below(const DOM::Element&); - DOM::Element* last_element_with_tag_name(const FlyString&); + struct LastElementResult { + DOM::Element* element; + ssize_t index; + }; + LastElementResult last_element_with_tag_name(const FlyString&); DOM::Element* element_before(const DOM::Element&); private: diff --git a/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js b/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js new file mode 100644 index 0000000000..0b74b2196f --- /dev/null +++ b/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js @@ -0,0 +1,27 @@ +loadPage("file:///home/anon/web-tests/Pages/Template.html"); + +afterInitialPageLoad(() => { + test("Basic functionality", () => { + const template = document.getElementById("template"); + expect(template).not.toBeNull(); + + // The contents of a template element are not children of the actual element. + // The document fragment is not a child of the element either. + expect(template.firstChild).toBeNull(); + + // FIXME: Add this in once DocumentFragment's constructor is implemented. + //expect(template.content).toBeInstanceOf(DocumentFragment); + expect(template.content.nodeName).toBe("#document-fragment"); + + const templateDiv = template.content.getElementById("templatediv"); + expect(templateDiv.nodeName).toBe("div"); + expect(templateDiv.textContent).toBe("Hello template!"); + }); + + test("Templates are inert (e.g. scripts won't run)", () => { + // The page has a template element with a script element in it. + // Templates are inert, for example, they won't run scripts. + // That script will set "templateScriptRan" to true if it ran. + expect(window.templateScriptRan).toBeUndefined(); + }); +}); diff --git a/Libraries/LibWeb/Tests/Pages/Template.html b/Libraries/LibWeb/Tests/Pages/Template.html new file mode 100644 index 0000000000..e4478f408f --- /dev/null +++ b/Libraries/LibWeb/Tests/Pages/Template.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> +<template id="template"> + <div id="templatediv">Hello template!</div> + <script> + // I shouldn't be run. + window.templateScriptRan = true; + </script> +</template> +</body> +</html> |