1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 11:07:45 +00:00

LibWeb: A whole bunch of work towards spec-compliant <script> elements

This is still very unfinished, but there's at least a skeleton of code.
This commit is contained in:
Andreas Kling 2020-05-24 22:00:46 +02:00
parent 3a30180e1e
commit 45da08a1e6
8 changed files with 365 additions and 34 deletions

View file

@ -45,6 +45,7 @@
#include <LibWeb/DOM/HTMLBodyElement.h>
#include <LibWeb/DOM/HTMLHeadElement.h>
#include <LibWeb/DOM/HTMLHtmlElement.h>
#include <LibWeb/DOM/HTMLScriptElement.h>
#include <LibWeb/DOM/HTMLTitleElement.h>
#include <LibWeb/DOM/Text.h>
#include <LibWeb/DOM/Window.h>
@ -396,4 +397,9 @@ NonnullRefPtr<Text> Document::create_text_node(const String& data)
return adopt(*new Text(*this, data));
}
void Document::set_pending_parsing_blocking_script(Badge<HTMLScriptElement>, HTMLScriptElement* script)
{
m_pending_parsing_blocking_script = script;
}
}

View file

@ -128,6 +128,8 @@ public:
NonnullRefPtr<Element> create_element(const String& tag_name);
NonnullRefPtr<Text> create_text_node(const String& data);
void set_pending_parsing_blocking_script(Badge<HTMLScriptElement>, HTMLScriptElement*);
private:
virtual RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) const override;
@ -151,6 +153,8 @@ private:
String m_source;
OwnPtr<JS::Interpreter> m_interpreter;
RefPtr<HTMLScriptElement> m_pending_parsing_blocking_script;
};
template<>

View file

@ -43,6 +43,16 @@ HTMLScriptElement::~HTMLScriptElement()
{
}
void HTMLScriptElement::set_parser_document(Badge<HTMLDocumentParser>, Document& document)
{
m_parser_document = document.make_weak_ptr();
}
void HTMLScriptElement::set_non_blocking(Badge<HTMLDocumentParser>, bool non_blocking)
{
m_non_blocking = non_blocking;
}
void HTMLScriptElement::children_changed()
{
HTMLElement::children_changed();
@ -105,4 +115,125 @@ void HTMLScriptElement::inserted_into(Node& new_parent)
document().interpreter().run(*program);
}
void HTMLScriptElement::prepare_script(Badge<HTMLDocumentParser>)
{
if (m_already_started)
return;
RefPtr<Document> parser_document = m_parser_document.ptr();
m_parser_document = nullptr;
if (parser_document && !has_attribute("async")) {
m_non_blocking = true;
}
auto source_text = child_text_content();
if (!has_attribute("src") && source_text.is_empty())
return;
if (!is_connected())
return;
// FIXME: Check the "type" and "language" attributes
if (parser_document) {
m_parser_document = parser_document->make_weak_ptr();
m_non_blocking = false;
}
m_already_started = true;
m_preparation_time_document = document().make_weak_ptr();
if (parser_document && parser_document.ptr() != m_preparation_time_document.ptr()) {
return;
}
// FIXME: Check if scripting is disabled, if so return
// FIXME: Check the "nomodule" content attribute
// FIXME: Check CSP
// FIXME: Check "event" and "for" attributes
// FIXME: Check "charset" attribute
// FIXME: Check CORS
// FIXME: Module script credentials mode
// FIXME: Cryptographic nonce
// FIXME: Check "integrity" attribute
// FIXME: Check "referrerpolicy" attribute
m_parser_inserted = !!m_parser_document;
// FIXME: Check fetch options
if (has_attribute("src")) {
auto src = attribute("src");
if (src.is_empty()) {
// FIXME: Fire an "error" event at the element and return
ASSERT_NOT_REACHED();
}
m_from_an_external_file = true;
auto url = document().complete_url(src);
if (!url.is_valid()) {
// FIXME: Fire an "error" event at the element and return
ASSERT_NOT_REACHED();
}
// FIXME: Check classic vs. module script type
ResourceLoader::the().load(url, [this, url](auto& data, auto&) {
if (data.is_null()) {
dbg() << "HTMLScriptElement: Failed to load " << url;
return;
}
m_script_source = String::copy(data);
script_became_ready();
});
} else {
// FIXME: Check classic vs. module script type
m_script_source = source_text;
script_became_ready();
}
// FIXME: Check classic vs. module
if (has_attribute("src") && has_attribute("defer") && m_parser_inserted && !has_attribute("async")) {
// FIXME: Add the element to the end of the list of scripts that will execute
// when the document has finished parsing associated with the Document
// of the parser that created the element.
ASSERT_NOT_REACHED();
}
else if (has_attribute("src") && m_parser_inserted && !has_attribute("async")) {
document().set_pending_parsing_blocking_script({}, this);
when_the_script_is_ready([this] {
m_ready_to_be_parser_executed = true;
});
}
else if (has_attribute("src") && !has_attribute("async") && !m_non_blocking) {
ASSERT_NOT_REACHED();
}
else if (has_attribute("src")) {
ASSERT_NOT_REACHED();
}
else {
ASSERT_NOT_REACHED();
}
}
void HTMLScriptElement::script_became_ready()
{
ASSERT(m_script_ready_callback);
m_script_ready_callback();
m_script_ready_callback = nullptr;
}
void HTMLScriptElement::when_the_script_is_ready(Function<void()> callback)
{
if (m_script_ready) {
callback();
return;
}
m_script_ready_callback = move(callback);
}
}

View file

@ -26,6 +26,7 @@
#pragma once
#include <AK/Function.h>
#include <LibWeb/DOM/HTMLElement.h>
namespace Web {
@ -37,6 +38,35 @@ public:
virtual void inserted_into(Node&) override;
virtual void children_changed() override;
bool is_non_blocking() const { return m_non_blocking; }
void set_parser_document(Badge<HTMLDocumentParser>, Document&);
void set_non_blocking(Badge<HTMLDocumentParser>, bool);
void prepare_script(Badge<HTMLDocumentParser>);
private:
void script_became_ready();
void when_the_script_is_ready(Function<void()>);
WeakPtr<Document> m_parser_document;
WeakPtr<Document> m_preparation_time_document;
bool m_non_blocking { false };
bool m_already_started { false };
bool m_parser_inserted { false };
bool m_from_an_external_file { false };
bool m_script_ready { false };
bool m_ready_to_be_parser_executed { false };
Function<void()> m_script_ready_callback;
String m_script_source;
};
template<>
inline bool is<HTMLScriptElement>(const Node& node)
{
return is<Element>(node) && to<Element>(node).tag_name().equals_ignoring_case("script");
}
}