ladybird/Libraries/LibWeb/HTML/DOMParser.cpp
Andreas Kling b6bef6b688 Libraries: Use UTF-16 for JS-visible runtime strings
Produce JS-visible string results as UTF-16 at their source, including
numeric formatting, BigInt and BigFraction formatting, URI encoding,
console formatting, parser errors, regular expression errors, Intl and
Temporal records, LibUnicode locale boundaries, and LibWeb bindings.

Handle fractional radix formatting through the UTF-16 builder view.
2026-06-22 19:51:25 +02:00

116 lines
5.2 KiB
C++

/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/DOMParser.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/DOM/XMLDocument.h>
#include <LibWeb/HTML/DOMParser.h>
#include <LibWeb/HTML/HTMLDocument.h>
#include <LibWeb/HTML/Parser/HTMLParser.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/TrustedTypes/RequireTrustedTypesForDirective.h>
#include <LibWeb/TrustedTypes/TrustedTypePolicy.h>
#include <LibWeb/XML/XMLDocumentBuilder.h>
#include <LibXML/Parser/Parser.h>
namespace Web::HTML {
GC_DEFINE_ALLOCATOR(DOMParser);
WebIDL::ExceptionOr<GC::Ref<DOMParser>> DOMParser::construct_impl(JS::Realm& realm)
{
return realm.create<DOMParser>(realm);
}
DOMParser::DOMParser(JS::Realm& realm)
: PlatformObject(realm)
{
}
DOMParser::~DOMParser() = default;
void DOMParser::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(DOMParser);
Base::initialize(realm);
}
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring
WebIDL::ExceptionOr<GC::Ref<DOM::Document>> DOMParser::parse_from_string(TrustedTypes::TrustedHTMLOrString string, Bindings::DOMParserSupportedType type)
{
// 1. Let compliantString to the result of invoking the Get Trusted Type compliant string algorithm with
// TrustedHTML, this's relevant global object, string, "DOMParser parseFromString", and "script".
auto const compliant_string = TRY(TrustedTypes::get_trusted_type_compliant_string(
TrustedTypes::TrustedTypeName::TrustedHTML,
relevant_global_object(*this),
move(string),
TrustedTypes::InjectionSink::DOMParser_parseFromString,
TrustedTypes::Script.to_string()));
// 2. Let document be a new Document, whose content type is type and url is this's relevant global object's associated Document's URL.
GC::Ptr<DOM::Document> document;
auto& associated_document = as<HTML::Window>(relevant_global_object(*this)).associated_document();
// 3. Switch on type:
if (type == Bindings::DOMParserSupportedType::Text_Html) {
// -> "text/html"
document = HTML::HTMLDocument::create(realm(), associated_document.url());
document->set_content_type("text/html"_string);
document->set_document_type(DOM::Document::Type::HTML);
// 1. Parse HTML from a string given document and compliantString.
document->parse_html_from_a_string(compliant_string.to_utf8_but_should_be_ported_to_utf16());
} else {
// -> Otherwise
document = DOM::Document::create(realm(), associated_document.url());
switch (type) {
case Bindings::DOMParserSupportedType::Text_Xml:
document->set_content_type("text/xml"_string);
break;
case Bindings::DOMParserSupportedType::Application_Xml:
document->set_content_type("application/xml"_string);
break;
case Bindings::DOMParserSupportedType::Application_XhtmlXml:
document->set_content_type("application/xhtml+xml"_string);
break;
case Bindings::DOMParserSupportedType::Image_SvgXml:
document->set_content_type("image/svg+xml"_string);
break;
case Bindings::DOMParserSupportedType::Text_Html:
VERIFY_NOT_REACHED();
}
document->set_document_type(DOM::Document::Type::XML);
// 1. Create an XML parser parse, associated with document, and with XML scripting support disabled.
auto const utf8_complaint_string = compliant_string.to_utf8_but_should_be_ported_to_utf16();
XML::Parser parser(utf8_complaint_string, { .resolve_named_html_entity = resolve_named_html_entity });
XMLDocumentBuilder builder { *document, XMLScriptingSupport::Disabled };
// 2. Parse compliantString using parser.
auto result = parser.parse_with_listener(builder);
// 3. If the previous step resulted in an XML well-formedness or XML namespace well-formedness error, then:
if (result.is_error() || builder.has_error()) {
// NOTE: The XML parsing can produce nodes before it hits an error, just remove them.
// 1. Assert: document has no child nodes.
document->remove_all_children(true);
// 2. Let root be the result of creating an element given document, "parsererror", and "http://www.mozilla.org/newlayout/xml/parsererror.xml".
auto root = DOM::create_element(*document, "parsererror"_fly_string, "http://www.mozilla.org/newlayout/xml/parsererror.xml"_fly_string).release_value_but_fixme_should_propagate_errors();
// FIXME: 3. Optionally, add attributes or children to root to describe the nature of the parsing error.
// 4. Append root to document.
MUST(document->append_child(*root));
}
}
// AD-HOC: Setting the origin to match that of the associated document matches the behavior of existing browsers
// and avoids a crash, since we expect the origin to always be set.
// Spec issue: https://github.com/whatwg/html/issues/11429
document->set_origin(associated_document.origin());
// 3. Return document.
return GC::Ref { *document };
}
}