LibWeb: Make the HTMLParser GC-allocated

This prevents a reference cycle between a HTMLParser opened via
document.open() and the document. It was one of many things keeping
some documents alive indefinitely.
This commit is contained in:
Andreas Kling 2022-10-17 10:46:11 +02:00
parent 68452c749a
commit 6e0f80fbe0
8 changed files with 71 additions and 35 deletions

View file

@ -340,6 +340,8 @@ void Document::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_all);
visitor.visit(m_selection);
visitor.visit(m_parser);
for (auto& script : m_scripts_to_execute_when_parsing_has_finished)
visitor.visit(script.ptr());
for (auto& script : m_scripts_to_execute_as_soon_as_possible)
@ -2149,7 +2151,7 @@ void Document::abort()
}
// https://html.spec.whatwg.org/multipage/dom.html#active-parser
RefPtr<HTML::HTMLParser> Document::active_parser()
JS::GCPtr<HTML::HTMLParser> Document::active_parser()
{
if (!m_parser)
return nullptr;

View file

@ -428,7 +428,7 @@ public:
void unload(bool recursive_flag = false, Optional<DocumentUnloadTimingInfo> = {});
// https://html.spec.whatwg.org/multipage/dom.html#active-parser
RefPtr<HTML::HTMLParser> active_parser();
JS::GCPtr<HTML::HTMLParser> active_parser();
// https://html.spec.whatwg.org/multipage/dom.html#load-timing-info
DocumentLoadTimingInfo& load_timing_info() { return m_load_timing_info; }
@ -478,7 +478,7 @@ private:
RefPtr<Platform::Timer> m_style_update_timer;
RefPtr<Platform::Timer> m_layout_update_timer;
RefPtr<HTML::HTMLParser> m_parser;
JS::GCPtr<HTML::HTMLParser> m_parser;
bool m_active_parser_was_aborted { false };
String m_source;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -147,6 +147,19 @@ HTMLParser::~HTMLParser()
m_document->set_should_invalidate_styles_on_attribute_changes(true);
}
void HTMLParser::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_document);
visitor.visit(m_head_element);
visitor.visit(m_form_element);
visitor.visit(m_context_element);
visitor.visit(m_character_insertion_node);
m_stack_of_open_elements.visit_edges(visitor);
m_list_of_active_formatting_elements.visit_edges(visitor);
}
void HTMLParser::run()
{
for (;;) {
@ -3426,23 +3439,23 @@ Vector<JS::Handle<DOM::Node>> HTMLParser::parse_html_fragment(DOM::Element& cont
return children;
}
NonnullRefPtr<HTMLParser> HTMLParser::create_for_scripting(DOM::Document& document)
JS::NonnullGCPtr<HTMLParser> HTMLParser::create_for_scripting(DOM::Document& document)
{
return adopt_ref(*new HTMLParser(document));
return *document.heap().allocate_without_realm<HTMLParser>(document);
}
NonnullRefPtr<HTMLParser> HTMLParser::create_with_uncertain_encoding(DOM::Document& document, ByteBuffer const& input)
JS::NonnullGCPtr<HTMLParser> HTMLParser::create_with_uncertain_encoding(DOM::Document& document, ByteBuffer const& input)
{
if (document.has_encoding())
return adopt_ref(*new HTMLParser(document, input, document.encoding().value()));
return *document.heap().allocate_without_realm<HTMLParser>(document, input, document.encoding().value());
auto encoding = run_encoding_sniffing_algorithm(document, input);
dbgln_if(HTML_PARSER_DEBUG, "The encoding sniffing algorithm returned encoding '{}'", encoding);
return adopt_ref(*new HTMLParser(document, input, encoding));
return *document.heap().allocate_without_realm<HTMLParser>(document, input, encoding);
}
NonnullRefPtr<HTMLParser> HTMLParser::create(DOM::Document& document, StringView input, String const& encoding)
JS::NonnullGCPtr<HTMLParser> HTMLParser::create(DOM::Document& document, StringView input, String const& encoding)
{
return adopt_ref(*new HTMLParser(document, input, encoding));
return *document.heap().allocate_without_realm<HTMLParser>(document, input, encoding);
}
// https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-serialisation-algorithm

View file

@ -1,11 +1,12 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Heap/Cell.h>
#include <LibWeb/DOM/Node.h>
#include <LibWeb/HTML/Parser/HTMLTokenizer.h>
#include <LibWeb/HTML/Parser/ListOfActiveFormattingElements.h>
@ -38,15 +39,17 @@ namespace Web::HTML {
__ENUMERATE_INSERTION_MODE(AfterAfterBody) \
__ENUMERATE_INSERTION_MODE(AfterAfterFrameset)
class HTMLParser : public RefCounted<HTMLParser> {
class HTMLParser final : public JS::Cell {
JS_CELL(HTMLParser, JS::Cell);
friend class HTMLTokenizer;
public:
~HTMLParser();
static NonnullRefPtr<HTMLParser> create_for_scripting(DOM::Document&);
static NonnullRefPtr<HTMLParser> create_with_uncertain_encoding(DOM::Document&, ByteBuffer const& input);
static NonnullRefPtr<HTMLParser> create(DOM::Document&, StringView input, String const& encoding);
static JS::NonnullGCPtr<HTMLParser> create_for_scripting(DOM::Document&);
static JS::NonnullGCPtr<HTMLParser> create_with_uncertain_encoding(DOM::Document&, ByteBuffer const& input);
static JS::NonnullGCPtr<HTMLParser> create(DOM::Document&, StringView input, String const& encoding);
void run();
void run(const AK::URL&);
@ -80,6 +83,8 @@ private:
HTMLParser(DOM::Document&, StringView input, String const& encoding);
HTMLParser(DOM::Document&);
virtual void visit_edges(Cell::Visitor&) override;
char const* insertion_mode_name() const;
DOM::QuirksMode which_quirks_mode(HTMLToken const&) const;
@ -182,14 +187,14 @@ private:
JS::Realm& realm();
JS::Handle<DOM::Document> m_document;
JS::Handle<HTMLHeadElement> m_head_element;
JS::Handle<HTMLFormElement> m_form_element;
JS::Handle<DOM::Element> m_context_element;
JS::GCPtr<DOM::Document> m_document;
JS::GCPtr<HTMLHeadElement> m_head_element;
JS::GCPtr<HTMLFormElement> m_form_element;
JS::GCPtr<DOM::Element> m_context_element;
Vector<HTMLToken> m_pending_table_character_tokens;
JS::Handle<DOM::Text> m_character_insertion_node;
JS::GCPtr<DOM::Text> m_character_insertion_node;
StringBuilder m_character_insertion_builder;
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -11,15 +11,21 @@ namespace Web::HTML {
ListOfActiveFormattingElements::~ListOfActiveFormattingElements() = default;
void ListOfActiveFormattingElements::visit_edges(JS::Cell::Visitor& visitor)
{
for (auto& entry : m_entries)
visitor.visit(entry.element);
}
void ListOfActiveFormattingElements::add(DOM::Element& element)
{
// FIXME: Implement the Noah's Ark clause https://html.spec.whatwg.org/multipage/parsing.html#push-onto-the-list-of-active-formatting-elements
m_entries.append({ JS::make_handle(element) });
m_entries.append({ element });
}
void ListOfActiveFormattingElements::add_marker()
{
m_entries.append({ JS::make_handle<DOM::Element>(nullptr) });
m_entries.append({ nullptr });
}
bool ListOfActiveFormattingElements::contains(const DOM::Element& element) const
@ -78,7 +84,7 @@ void ListOfActiveFormattingElements::replace(DOM::Element& to_remove, DOM::Eleme
void ListOfActiveFormattingElements::insert_at(size_t index, DOM::Element& element)
{
m_entries.insert(index, { JS::make_handle(element) });
m_entries.insert(index, { element });
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -20,7 +20,7 @@ public:
struct Entry {
bool is_marker() const { return !element; }
JS::Handle<DOM::Element> element;
JS::GCPtr<DOM::Element> element;
};
bool is_empty() const { return m_entries.is_empty(); }
@ -43,6 +43,8 @@ public:
Optional<size_t> find_index(DOM::Element const&) const;
void visit_edges(JS::Cell::Visitor&);
private:
Vector<Entry> m_entries;
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -14,6 +14,12 @@ static Vector<FlyString> s_base_list { "applet", "caption", "html", "table", "td
StackOfOpenElements::~StackOfOpenElements() = default;
void StackOfOpenElements::visit_edges(JS::Cell::Visitor& visitor)
{
for (auto& element : m_elements)
visitor.visit(element);
}
bool StackOfOpenElements::has_in_scope_impl(FlyString const& tag_name, Vector<FlyString> const& list) const
{
for (auto const& element : m_elements.in_reverse()) {
@ -162,7 +168,7 @@ void StackOfOpenElements::replace(DOM::Element const& to_remove, JS::NonnullGCPt
for (size_t i = 0; i < m_elements.size(); i++) {
if (m_elements[i].ptr() == &to_remove) {
m_elements.remove(i);
m_elements.insert(i, JS::make_handle(*to_add));
m_elements.insert(i, to_add);
break;
}
}
@ -172,7 +178,7 @@ void StackOfOpenElements::insert_immediately_below(JS::NonnullGCPtr<DOM::Element
{
for (size_t i = 0; i < m_elements.size(); i++) {
if (m_elements[i].ptr() == &target) {
m_elements.insert(i + 1, JS::make_handle(*element_to_add));
m_elements.insert(i + 1, element_to_add);
break;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -27,7 +27,7 @@ public:
DOM::Element& last() { return *m_elements.last(); }
bool is_empty() const { return m_elements.is_empty(); }
void push(JS::NonnullGCPtr<DOM::Element> element) { m_elements.append(JS::make_handle(*element)); }
void push(JS::NonnullGCPtr<DOM::Element> element) { m_elements.append(element); }
JS::NonnullGCPtr<DOM::Element> pop() { return *m_elements.take_last(); }
void remove(DOM::Element const& element);
void replace(DOM::Element const& to_remove, JS::NonnullGCPtr<DOM::Element> to_add);
@ -47,8 +47,8 @@ public:
bool contains(const DOM::Element&) const;
bool contains(FlyString const& tag_name) const;
Vector<JS::Handle<DOM::Element>> const& elements() const { return m_elements; }
Vector<JS::Handle<DOM::Element>>& elements() { return m_elements; }
auto const& elements() const { return m_elements; }
auto& elements() { return m_elements; }
void pop_until_an_element_with_tag_name_has_been_popped(FlyString const&);
@ -61,11 +61,13 @@ public:
LastElementResult last_element_with_tag_name(FlyString const&);
JS::GCPtr<DOM::Element> element_immediately_above(DOM::Element const&);
void visit_edges(JS::Cell::Visitor&);
private:
bool has_in_scope_impl(FlyString const& tag_name, Vector<FlyString> const&) const;
bool has_in_scope_impl(const DOM::Element& target_node, Vector<FlyString> const&) const;
Vector<JS::Handle<DOM::Element>> m_elements;
Vector<JS::NonnullGCPtr<DOM::Element>> m_elements;
};
}