summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-04-03 23:06:09 +0200
committerAndreas Kling <kling@serenityos.org>2020-04-03 23:06:09 +0200
commit56ca91b9f8c07ae4cc4706bab379eeeda8392369 (patch)
tree1d0e2116d997ced6a259f99c2480fa9e9e294fa1
parent18d45d10822219d0e86233ae112f549512f98452 (diff)
downloadserenity-56ca91b9f8c07ae4cc4706bab379eeeda8392369.zip
LibWeb: Implement <script src> support for synchronous scripts
Scripts loaded in this way will block the parser until they finish executing. This means that they see the DOM before the whole document has been fully parsed. This is all normal, of course. To make this work, I changed the way we notify DOM nodes about tree insertion. The inserted_into() callbacks are now incrementally invoked during parse, as each node is appended to its parent. To accomodate inline scripts and inline style sheets, we now also have a children_changed() callback which is invoked on any parent when it has children added/removed.
-rw-r--r--Base/home/anon/www/welcome.html6
-rw-r--r--Base/home/anon/www/welcome.js3
-rw-r--r--Libraries/LibWeb/DOM/HTMLScriptElement.cpp38
-rw-r--r--Libraries/LibWeb/DOM/HTMLScriptElement.h1
-rw-r--r--Libraries/LibWeb/DOM/HTMLStyleElement.cpp4
-rw-r--r--Libraries/LibWeb/DOM/HTMLStyleElement.h2
-rw-r--r--Libraries/LibWeb/DOM/Node.h1
-rw-r--r--Libraries/LibWeb/Layout/LayoutNode.h1
-rw-r--r--Libraries/LibWeb/Parser/HTMLParser.cpp13
-rw-r--r--Libraries/LibWeb/TreeNode.h27
10 files changed, 69 insertions, 27 deletions
diff --git a/Base/home/anon/www/welcome.html b/Base/home/anon/www/welcome.html
index 3084977afe..e07463e859 100644
--- a/Base/home/anon/www/welcome.html
+++ b/Base/home/anon/www/welcome.html
@@ -20,14 +20,12 @@ span#ua {
color: red;
}
</style>
-<script>
-document.getElementById("ua").innerHTML = navigator.userAgent;
-</script>
+<script src="welcome.js"></script>
</head>
<body link="#44f" vlink="#c4c" background="90s-bg.png">
<h1>Welcome to the Serenity Browser!</h1>
<p>This is a very simple browser built on the LibWeb and LibJS engines.</p>
- <p>Your user agent is: <span id="ua"></span></p>
+ <p>Your user agent is: <b><span id="ua"></span></b></p>
<p>Some small test pages:</p>
<ul>
<li><a href="qsa.html">querySelectorAll test</a></li>
diff --git a/Base/home/anon/www/welcome.js b/Base/home/anon/www/welcome.js
new file mode 100644
index 0000000000..07497d0115
--- /dev/null
+++ b/Base/home/anon/www/welcome.js
@@ -0,0 +1,3 @@
+document.addEventListener("DOMContentLoaded", function() {
+ document.getElementById("ua").innerHTML = navigator.userAgent;
+});
diff --git a/Libraries/LibWeb/DOM/HTMLScriptElement.cpp b/Libraries/LibWeb/DOM/HTMLScriptElement.cpp
index 8361215704..5745878c48 100644
--- a/Libraries/LibWeb/DOM/HTMLScriptElement.cpp
+++ b/Libraries/LibWeb/DOM/HTMLScriptElement.cpp
@@ -30,6 +30,7 @@
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/HTMLScriptElement.h>
#include <LibWeb/DOM/Text.h>
+#include <LibWeb/ResourceLoader.h>
namespace Web {
@@ -42,17 +43,48 @@ HTMLScriptElement::~HTMLScriptElement()
{
}
-void HTMLScriptElement::inserted_into(Node& new_parent)
+void HTMLScriptElement::children_changed()
{
- HTMLElement::inserted_into(new_parent);
+ HTMLElement::children_changed();
+
+ if (has_attribute("src"))
+ return;
StringBuilder builder;
for_each_child([&](auto& child) {
if (is<Text>(child))
builder.append(to<Text>(child).text_content());
});
-
auto source = builder.to_string();
+ if (source.is_empty())
+ return;
+
+ auto program = JS::Parser(JS::Lexer(source)).parse_program();
+ document().interpreter().run(*program);
+}
+
+void HTMLScriptElement::inserted_into(Node& new_parent)
+{
+ HTMLElement::inserted_into(new_parent);
+
+ auto src = attribute("src");
+ if (src.is_null())
+ return;
+
+ String source;
+ URL src_url = document().complete_url(src);
+ ResourceLoader::the().load_sync(src_url, [&](auto& data) {
+ if (data.is_null()) {
+ dbg() << "HTMLScriptElement: Failed to load " << src;
+ return;
+ }
+ source = String::copy(data);
+ });
+ if (source.is_empty()) {
+ dbg() << "HTMLScriptElement: No source to parse :(";
+ return;
+ }
+
auto program = JS::Parser(JS::Lexer(source)).parse_program();
document().interpreter().run(*program);
}
diff --git a/Libraries/LibWeb/DOM/HTMLScriptElement.h b/Libraries/LibWeb/DOM/HTMLScriptElement.h
index bb9f7e139c..92f856d0a7 100644
--- a/Libraries/LibWeb/DOM/HTMLScriptElement.h
+++ b/Libraries/LibWeb/DOM/HTMLScriptElement.h
@@ -36,6 +36,7 @@ public:
virtual ~HTMLScriptElement() override;
virtual void inserted_into(Node&) override;
+ virtual void children_changed() override;
};
}
diff --git a/Libraries/LibWeb/DOM/HTMLStyleElement.cpp b/Libraries/LibWeb/DOM/HTMLStyleElement.cpp
index f3f7775348..83ae31b148 100644
--- a/Libraries/LibWeb/DOM/HTMLStyleElement.cpp
+++ b/Libraries/LibWeb/DOM/HTMLStyleElement.cpp
@@ -41,7 +41,7 @@ HTMLStyleElement::~HTMLStyleElement()
{
}
-void HTMLStyleElement::inserted_into(Node& new_parent)
+void HTMLStyleElement::children_changed()
{
StringBuilder builder;
for_each_child([&](auto& child) {
@@ -51,7 +51,7 @@ void HTMLStyleElement::inserted_into(Node& new_parent)
m_stylesheet = parse_css(builder.to_string());
if (m_stylesheet)
document().add_sheet(*m_stylesheet);
- HTMLElement::inserted_into(new_parent);
+ HTMLElement::children_changed();
}
void HTMLStyleElement::removed_from(Node& old_parent)
diff --git a/Libraries/LibWeb/DOM/HTMLStyleElement.h b/Libraries/LibWeb/DOM/HTMLStyleElement.h
index e7dac784c2..06bd7d9feb 100644
--- a/Libraries/LibWeb/DOM/HTMLStyleElement.h
+++ b/Libraries/LibWeb/DOM/HTMLStyleElement.h
@@ -37,7 +37,7 @@ public:
HTMLStyleElement(Document&, const FlyString& tag_name);
virtual ~HTMLStyleElement() override;
- virtual void inserted_into(Node&) override;
+ virtual void children_changed() override;
virtual void removed_from(Node&) override;
private:
diff --git a/Libraries/LibWeb/DOM/Node.h b/Libraries/LibWeb/DOM/Node.h
index 43359af872..479ec8960f 100644
--- a/Libraries/LibWeb/DOM/Node.h
+++ b/Libraries/LibWeb/DOM/Node.h
@@ -104,6 +104,7 @@ public:
virtual void inserted_into(Node&) {}
virtual void removed_from(Node&) {}
+ virtual void children_changed() {}
const LayoutNode* layout_node() const { return m_layout_node; }
LayoutNode* layout_node() { return m_layout_node; }
diff --git a/Libraries/LibWeb/Layout/LayoutNode.h b/Libraries/LibWeb/Layout/LayoutNode.h
index 22557a9fce..84f2f8bc14 100644
--- a/Libraries/LibWeb/Layout/LayoutNode.h
+++ b/Libraries/LibWeb/Layout/LayoutNode.h
@@ -111,6 +111,7 @@ public:
void inserted_into(LayoutNode&) {}
void removed_from(LayoutNode&) {}
+ void children_changed() {}
virtual void split_into_lines(LayoutBlock& container);
diff --git a/Libraries/LibWeb/Parser/HTMLParser.cpp b/Libraries/LibWeb/Parser/HTMLParser.cpp
index 7dfbfcdb10..c21fd30806 100644
--- a/Libraries/LibWeb/Parser/HTMLParser.cpp
+++ b/Libraries/LibWeb/Parser/HTMLParser.cpp
@@ -97,7 +97,7 @@ static bool parse_html_document(const StringView& html, Document& document, Pare
auto commit_text_node = [&] {
auto text_node = adopt(*new Text(document, text_buffer.to_string()));
- node_stack.last().append_child(text_node, false);
+ node_stack.last().append_child(text_node);
text_buffer.clear();
};
@@ -129,19 +129,20 @@ static bool parse_html_document(const StringView& html, Document& document, Pare
tag_name_buffer.clear();
new_element->set_attributes(move(attributes));
node_stack.append(new_element);
- if (node_stack.size() != 1)
- node_stack[node_stack.size() - 2].append_child(new_element, false);
+ if (node_stack.size() != 1) {
+ node_stack[node_stack.size() - 2].append_child(new_element);
+ }
if (is_self_closing_tag(new_element->tag_name()))
close_tag();
};
auto commit_doctype = [&] {
- node_stack.last().append_child(adopt(*new DocumentType(document)), false);
+ node_stack.last().append_child(adopt(*new DocumentType(document)));
};
auto commit_comment = [&] {
- node_stack.last().append_child(adopt(*new Comment(document, text_buffer.to_string())), false);
+ node_stack.last().append_child(adopt(*new Comment(document, text_buffer.to_string())));
};
auto commit_tag = [&] {
@@ -378,6 +379,7 @@ RefPtr<Document> parse_html_document(const StringView& html, const URL& url)
document->fixup();
+#if 0
Function<void(Node&)> fire_insertion_callbacks = [&](Node& node) {
for (auto* child = node.first_child(); child; child = child->next_sibling()) {
fire_insertion_callbacks(*child);
@@ -386,6 +388,7 @@ RefPtr<Document> parse_html_document(const StringView& html, const URL& url)
node.inserted_into(*node.parent());
};
fire_insertion_callbacks(document);
+#endif
document->dispatch_event(Event::create("DOMContentLoaded"));
diff --git a/Libraries/LibWeb/TreeNode.h b/Libraries/LibWeb/TreeNode.h
index 188d142ab3..5d96416b5b 100644
--- a/Libraries/LibWeb/TreeNode.h
+++ b/Libraries/LibWeb/TreeNode.h
@@ -108,9 +108,9 @@ public:
bool is_ancestor_of(const TreeNode&) const;
- void prepend_child(NonnullRefPtr<T> node, bool call_inserted_into = true);
- void append_child(NonnullRefPtr<T> node, bool call_inserted_into = true);
- NonnullRefPtr<T> remove_child(NonnullRefPtr<T> node, bool call_removed_from = true);
+ void prepend_child(NonnullRefPtr<T> node);
+ void append_child(NonnullRefPtr<T> node);
+ NonnullRefPtr<T> remove_child(NonnullRefPtr<T> node);
void donate_all_children_to(T& node);
bool is_child_allowed(const T&) const { return true; }
@@ -200,7 +200,7 @@ private:
};
template<typename T>
-inline NonnullRefPtr<T> TreeNode<T>::remove_child(NonnullRefPtr<T> node, bool call_removed_from)
+inline NonnullRefPtr<T> TreeNode<T>::remove_child(NonnullRefPtr<T> node)
{
ASSERT(node->m_parent == this);
@@ -220,16 +220,17 @@ inline NonnullRefPtr<T> TreeNode<T>::remove_child(NonnullRefPtr<T> node, bool ca
node->m_previous_sibling = nullptr;
node->m_parent = nullptr;
- if (call_removed_from)
- node->removed_from(static_cast<T&>(*this));
+ node->removed_from(static_cast<T&>(*this));
node->unref();
+ static_cast<T*>(this)->children_changed();
+
return node;
}
template<typename T>
-inline void TreeNode<T>::append_child(NonnullRefPtr<T> node, bool call_inserted_into)
+inline void TreeNode<T>::append_child(NonnullRefPtr<T> node)
{
ASSERT(!node->m_parent);
@@ -243,13 +244,14 @@ inline void TreeNode<T>::append_child(NonnullRefPtr<T> node, bool call_inserted_
m_last_child = node.ptr();
if (!m_first_child)
m_first_child = m_last_child;
- if (call_inserted_into)
- node->inserted_into(static_cast<T&>(*this));
+ node->inserted_into(static_cast<T&>(*this));
(void)node.leak_ref();
+
+ static_cast<T*>(this)->children_changed();
}
template<typename T>
-inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node, bool call_inserted_into)
+inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node)
{
ASSERT(!node->m_parent);
@@ -263,9 +265,10 @@ inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node, bool call_inserted
m_first_child = node.ptr();
if (!m_last_child)
m_last_child = m_first_child;
- if (call_inserted_into)
- node->inserted_into(static_cast<T&>(*this));
+ node->inserted_into(static_cast<T&>(*this));
(void)node.leak_ref();
+
+ static_cast<T*>(this)->children_changed();
}
template<typename T>