LibWeb: Update Document.write spec steps
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

The spec changes seem to mostly be about introducing a TrustedHTML type
which we do not yet support, so we have a couple of FIXMEs.

TrustedTypes::InjectionSink is an attempt at matching the spec, but it's
not entirely clear to me how it should work. I'm sure it'll get
revisited once we start implementing trusted types.
This commit is contained in:
Sam Atkins 2025-02-24 12:53:37 +00:00 committed by Jelle Raaijmakers
parent abe1e14812
commit 03a058ba5e
Notes: github-actions[bot] 2025-02-25 14:20:38 +00:00
4 changed files with 67 additions and 23 deletions

View file

@ -603,40 +603,59 @@ GC::Ptr<Selection::Selection> Document::get_selection() const
}
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-write
WebIDL::ExceptionOr<void> Document::write(Vector<String> const& strings)
WebIDL::ExceptionOr<void> Document::write(Vector<String> const& text)
{
StringBuilder builder;
builder.join(""sv, strings);
return run_the_document_write_steps(builder.string_view());
// The document.write(...text) method steps are to run the document write steps with this, text, false, and "Document write".
return run_the_document_write_steps(text, AddLineFeed::No, TrustedTypes::InjectionSink::DocumentWrite);
}
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-writeln
WebIDL::ExceptionOr<void> Document::writeln(Vector<String> const& strings)
WebIDL::ExceptionOr<void> Document::writeln(Vector<String> const& text)
{
StringBuilder builder;
builder.join(""sv, strings);
builder.append("\n"sv);
return run_the_document_write_steps(builder.string_view());
// The document.writeln(...text) method steps are to run the document write steps with this, text, true, and "Document writeln".
return run_the_document_write_steps(text, AddLineFeed::Yes, TrustedTypes::InjectionSink::DocumentWriteln);
}
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
WebIDL::ExceptionOr<void> Document::run_the_document_write_steps(StringView input)
WebIDL::ExceptionOr<void> Document::run_the_document_write_steps(Vector<String> const& text, AddLineFeed line_feed, TrustedTypes::InjectionSink sink)
{
// 1. If document is an XML document, then throw an "InvalidStateError" DOMException.
// 1. Let string be the empty string.
StringBuilder string;
// 2. Let isTrusted be false if text contains a string; otherwise true.
// FIXME: We currently only accept strings. Revisit this once we support the TrustedHTML type.
auto is_trusted = true;
// 3. For each value of text:
for (auto const& value : text) {
// FIXME: 1. If value is a TrustedHTML object, then append value's associated data to string.
// 2. Otherwise, append value to string.
string.append(value);
}
// FIXME: 4. If isTrusted is false, set string to the result of invoking the Get Trusted Type compliant string algorithm
// with TrustedHTML, this's relevant global object, string, sink, and "script".
(void)is_trusted;
(void)sink;
// 5. If lineFeed is true, append U+000A LINE FEED to string.
if (line_feed == AddLineFeed::Yes)
string.append('\n');
// 6. If document is an XML document, then throw an "InvalidStateError" DOMException.
if (m_type == Type::XML)
return WebIDL::InvalidStateError::create(realm(), "write() called on XML document."_string);
// 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
// 7. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
if (m_throw_on_dynamic_markup_insertion_counter > 0)
return WebIDL::InvalidStateError::create(realm(), "throw-on-dynamic-markup-insertion-counter greater than zero."_string);
// 3. If document's active parser was aborted is true, then return.
// 8. If document's active parser was aborted is true, then return.
if (m_active_parser_was_aborted)
return {};
// 4. If the insertion point is undefined, then:
// 9. If the insertion point is undefined, then:
if (!(m_parser && m_parser->tokenizer().is_insertion_point_defined())) {
// 1. If document's unload counter is greater than 0 or document's ignore-destructive-writes counter is greater than 0, then return.
if (m_unload_counter > 0 || m_ignore_destructive_writes_counter > 0)
@ -646,13 +665,13 @@ WebIDL::ExceptionOr<void> Document::run_the_document_write_steps(StringView inpu
TRY(open());
}
// 5. Insert input into the input stream just before the insertion point.
m_parser->tokenizer().insert_input_at_insertion_point(input);
// 10. Insert string into the input stream just before the insertion point.
m_parser->tokenizer().insert_input_at_insertion_point(string.string_view());
// 6. If there is no pending parsing-blocking script, have the HTML parser process input, one code point at a time,
// processing resulting tokens as they are emitted, and stopping when the tokenizer reaches the insertion point
// or when the processing of the tokenizer is aborted by the tree construction stage (this can happen if a script
// end tag token is emitted by the tokenizer).
// 11. If document's pending parsing-blocking script is null, then have the HTML parser process string, one code
// point at a time, processing resulting tokens as they are emitted, and stopping when the tokenizer reaches
// the insertion point or when the processing of the tokenizer is aborted by the tree construction stage (this
// can happen if a script end tag token is emitted by the tokenizer).
if (!pending_parsing_blocking_script())
m_parser->run(HTML::HTMLTokenizer::StopAtInsertionPoint::Yes);

View file

@ -38,6 +38,7 @@
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/VisibilityState.h>
#include <LibWeb/InvalidateDisplayList.h>
#include <LibWeb/TrustedTypes/InjectionSink.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/WebIDL/ObservableArray.h>
@ -840,7 +841,11 @@ private:
void evaluate_media_rules();
WebIDL::ExceptionOr<void> run_the_document_write_steps(StringView);
enum class AddLineFeed {
Yes,
No,
};
WebIDL::ExceptionOr<void> run_the_document_write_steps(Vector<String> const& text, AddLineFeed line_feed, TrustedTypes::InjectionSink sink);
void queue_intersection_observer_task();
void queue_an_intersection_observer_entry(IntersectionObserver::IntersectionObserver&, HighResolutionTime::DOMHighResTimeStamp time, GC::Ref<Geometry::DOMRectReadOnly> root_bounds, GC::Ref<Geometry::DOMRectReadOnly> bounding_client_rect, GC::Ref<Geometry::DOMRectReadOnly> intersection_rect, bool is_intersecting, double intersection_ratio, GC::Ref<Element> target);

View file

@ -53,7 +53,9 @@ interface Document : Node {
[CEReactions] Document open(optional DOMString unused1, optional DOMString unused2);
WindowProxy? open(USVString url, DOMString name, DOMString features);
[CEReactions] undefined close();
// FIXME: [CEReactions] undefined write((TrustedHTML or DOMString)... text);
[CEReactions] undefined write(DOMString... text);
// FIXME: [CEReactions] undefined writeln((TrustedHTML or DOMString)... text);
[CEReactions] undefined writeln(DOMString... text);
// FIXME: static Document parseHTMLUnsafe((TrustedHTML or DOMString) html);

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace Web::TrustedTypes {
// https://w3c.github.io/trusted-types/dist/spec/#injection-sink
enum class InjectionSink {
DocumentWrite,
DocumentWriteln,
Function,
};
}