1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-15 06:54:57 +00:00

LibWeb: Add basic support for dynamic markup insertion

This implements basic support for dynamic markup insertion, adding
 * Document::open()
 * Document::write(Vector<String> const&)
 * Document::writeln(Vector<String> const&)
 * Document::close()

The HTMLParser is modified to make it possible to create a
script-created parser which initially only contains a HTMLTokenizer
without any data. Aditionally the HTMLParser::run method gains an
overload which does not modify the Document and does not run
HTMLParser::the_end() so that we can reenter the parser at a later time.
Furthermore all FIXMEs that consern the insertion point are implemented
wich is defined in the HTMLTokenizer. Additionally the following
member-variables of the HTMLParser are now exposed by getter funcions:
 * m_tokenizer
 * m_aborted
 * m_script_nesting_level

The HTMLTokenizer is modified so that it contains an insertion
point which keeps track of where the next input from the Document::write
functions will be inserted. The insertion point is implemented as the
charakter offset into m_decoded_input and a boolean describing if the
insertion point is defined. Functions to update, check and {re}store the
insertion point are also added.
The function HTMLTokenizer::insert_eof is added to tell a script-created
parser that document::close was called and HTMLParser::the_end() should
be called.
Lastly an explicit default constructor is added to HTMLTokenizer to
create a empty HTMLTokenizer into which data can be inserted.
This commit is contained in:
Lorenz Steinert 2022-02-19 15:58:21 +01:00 committed by Andreas Kling
parent d29d9462e9
commit db789813c9
7 changed files with 282 additions and 19 deletions

View file

@ -137,17 +137,24 @@ HTMLParser::HTMLParser(DOM::Document& document, StringView input, const String&
m_document->set_encoding(standardized_encoding.value());
}
HTMLParser::HTMLParser(DOM::Document& document)
: m_document(document)
{
m_tokenizer.set_parser({}, *this);
}
HTMLParser::~HTMLParser()
{
m_document->set_should_invalidate_styles_on_attribute_changes(true);
}
void HTMLParser::run(const AK::URL& url)
void HTMLParser::run()
{
m_document->set_url(url);
m_document->set_source(m_tokenizer.source());
for (;;) {
// FIXME: Find a better way to say that we come from Document::close() and want to process EOF.
if (!m_tokenizer.is_eof_inserted() && m_tokenizer.is_insertion_point_reached())
return;
auto optional_token = m_tokenizer.next_token();
if (!optional_token.has_value())
break;
@ -186,7 +193,13 @@ void HTMLParser::run(const AK::URL& url)
}
flush_character_insertions();
}
void HTMLParser::run(const AK::URL& url)
{
m_document->set_url(url);
m_document->set_source(m_tokenizer.source());
run();
the_end();
}
@ -197,7 +210,8 @@ void HTMLParser::the_end()
// FIXME: 1. If the active speculative HTML parser is not null, then stop the speculative HTML parser and return.
// FIXME: 2. Set the insertion point to undefined.
// 2. Set the insertion point to undefined.
m_tokenizer.undefine_insertion_point();
// 3. Update the current document readiness to "interactive".
m_document->update_readiness(HTML::DocumentReadyState::Interactive);
@ -2003,6 +2017,7 @@ void HTMLParser::decrement_script_nesting_level()
--m_script_nesting_level;
}
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incdata
void HTMLParser::handle_text(HTMLToken& token)
{
if (token.is_character()) {
@ -2025,13 +2040,18 @@ void HTMLParser::handle_text(HTMLToken& token)
NonnullRefPtr<HTMLScriptElement> script = verify_cast<HTMLScriptElement>(current_node());
(void)m_stack_of_open_elements.pop();
m_insertion_mode = m_original_insertion_mode;
// FIXME: Handle tokenizer insertion point stuff here.
// Let the old insertion point have the same value as the current insertion point.
m_tokenizer.store_insertion_point();
// Let the insertion point be just before the next input character.
m_tokenizer.update_insertion_point();
increment_script_nesting_level();
// FIXME: Check if active speculative HTML parser is null.
script->prepare_script({});
decrement_script_nesting_level();
if (script_nesting_level() == 0)
m_parser_pause_flag = false;
// FIXME: Handle tokenizer insertion point stuff here too.
// Let the insertion point have the value of the old insertion point.
m_tokenizer.restore_insertion_point();
while (document().pending_parsing_blocking_script()) {
if (script_nesting_level() != 0) {
@ -2065,7 +2085,8 @@ void HTMLParser::handle_text(HTMLToken& token)
m_tokenizer.set_blocked(false);
// FIXME: Handle tokenizer insertion point stuff here too.
// Let the insertion point be just before the next input character.
m_tokenizer.update_insertion_point();
VERIFY(script_nesting_level() == 0);
increment_script_nesting_level();
@ -2076,7 +2097,8 @@ void HTMLParser::handle_text(HTMLToken& token)
VERIFY(script_nesting_level() == 0);
m_parser_pause_flag = false;
// FIXME: Handle tokenizer insertion point stuff here too.
// Let the insertion point be undefined again.
m_tokenizer.undefine_insertion_point();
}
}
return;
@ -2986,8 +3008,26 @@ void HTMLParser::process_using_the_rules_for_foreign_content(HTMLToken& token)
if (token.is_end_tag() && current_node().namespace_() == Namespace::SVG && current_node().tag_name() == SVG::TagNames::script) {
ScriptEndTag:
// Pop the current node off the stack of open elements.
(void)m_stack_of_open_elements.pop();
// Let the old insertion point have the same value as the current insertion point.
m_tokenizer.store_insertion_point();
// Let the insertion point be just before the next input character.
m_tokenizer.update_insertion_point();
// Increment the parser's script nesting level by one.
increment_script_nesting_level();
// Set the parser pause flag to true.
m_parser_pause_flag = true;
// FIXME: Implement SVG script parsing.
TODO();
// Decrement the parser's script nesting level by one.
decrement_script_nesting_level();
// If the parser's script nesting level is zero, then set the parser pause flag to false.
if (script_nesting_level() == 0)
m_parser_pause_flag = false;
// Let the insertion point have the value of the old insertion point.
m_tokenizer.restore_insertion_point();
}
if (token.is_end_tag()) {