summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
Diffstat (limited to 'Libraries')
-rw-r--r--Libraries/LibWeb/DOM/Document.h10
-rw-r--r--Libraries/LibWeb/DOM/DocumentFragment.h13
-rw-r--r--Libraries/LibWeb/DOMTreeModel.cpp2
-rw-r--r--Libraries/LibWeb/Dump.cpp12
-rw-r--r--Libraries/LibWeb/Forward.h1
-rw-r--r--Libraries/LibWeb/HTML/HTMLTemplateElement.cpp21
-rw-r--r--Libraries/LibWeb/HTML/HTMLTemplateElement.h9
-rw-r--r--Libraries/LibWeb/HTML/HTMLTemplateElement.idl2
-rw-r--r--Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp78
-rw-r--r--Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h1
-rw-r--r--Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp6
-rw-r--r--Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h6
-rw-r--r--Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js27
-rw-r--r--Libraries/LibWeb/Tests/Pages/Template.html14
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>