LibXML+LibWeb: Use existing HTML entities table for XML parsing too

For XHTML documents, resolve named character entities (e.g.,  )
using the HTML entity table via a getEntity SAX callback. This avoids
parsing a large embedded DTD on every document and matches the approach
used by Blink and WebKit.

This also removes the now-unused DTD infrastructure:

- Remove resolve_external_resource callback from Parser::Options
- Remove resolve_xml_resource() function and its ~60KB embedded DTD
- Remove all call sites passing the unused callback
This commit is contained in:
sideshowbarker 2026-01-09 04:32:12 +09:00 committed by Tim Ledbetter
parent 35bb1e20ee
commit 1b41659efd
43 changed files with 28321 additions and 55 deletions

View file

@ -56,7 +56,7 @@ bool build_xml_document(DOM::Document& document, ByteBuffer const& data, Optiona
return false; return false;
} }
auto source = decoder->to_utf8(data).release_value_but_fixme_should_propagate_errors(); auto source = decoder->to_utf8(data).release_value_but_fixme_should_propagate_errors();
XML::Parser parser(source, { .resolve_external_resource = resolve_xml_resource }); XML::Parser parser(source, { .resolve_named_html_entity = resolve_named_html_entity });
XMLDocumentBuilder builder { document }; XMLDocumentBuilder builder { document };
auto result = parser.parse_with_listener(builder); auto result = parser.parse_with_listener(builder);
return !result.is_error() && !builder.has_error(); return !result.is_error() && !builder.has_error();
@ -189,7 +189,7 @@ static WebIDL::ExceptionOr<GC::Ref<DOM::Document>> load_xml_document(HTML::Navig
} }
// NB: If document is part of session history traversal, resolve the signal_to_continue_session_history_processing. // NB: If document is part of session history traversal, resolve the signal_to_continue_session_history_processing.
signal_to_continue_session_history_processing->resolve({}); signal_to_continue_session_history_processing->resolve({});
XML::Parser parser(source.value(), { .preserve_cdata = true, .preserve_comments = true, .resolve_external_resource = resolve_xml_resource }); XML::Parser parser(source.value(), { .preserve_cdata = true, .preserve_comments = true, .resolve_named_html_entity = resolve_named_html_entity });
XMLDocumentBuilder builder { document }; XMLDocumentBuilder builder { document };
auto result = parser.parse_with_listener(builder); auto result = parser.parse_with_listener(builder);
if (result.is_error()) { if (result.is_error()) {

View file

@ -72,7 +72,7 @@ WebIDL::ExceptionOr<GC::Root<DOM::Document>> DOMParser::parse_from_string(Truste
// 1. Create an XML parser parse, associated with document, and with XML scripting support disabled. // 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(); auto const utf8_complaint_string = compliant_string.to_utf8_but_should_be_ported_to_utf16();
XML::Parser parser(utf8_complaint_string, { .resolve_external_resource = resolve_xml_resource }); XML::Parser parser(utf8_complaint_string, { .resolve_named_html_entity = resolve_named_html_entity });
XMLDocumentBuilder builder { *document, XMLScriptingSupport::Disabled }; XMLDocumentBuilder builder { *document, XMLScriptingSupport::Disabled };
// 2. Parse compliantString using parser. // 2. Parse compliantString using parser.
auto result = parser.parse_with_listener(builder); auto result = parser.parse_with_listener(builder);

View file

@ -62,7 +62,7 @@ ErrorOr<GC::Ref<SVGDecodedImageData>> SVGDecodedImageData::create(JS::Realm& rea
auto& window = as<HTML::Window>(HTML::relevant_global_object(document)); auto& window = as<HTML::Window>(HTML::relevant_global_object(document));
document->browsing_context()->window_proxy()->set_window(window); document->browsing_context()->window_proxy()->set_window(window);
XML::Parser parser(data, { .resolve_external_resource = resolve_xml_resource }); XML::Parser parser(data, { .resolve_named_html_entity = resolve_named_html_entity });
XMLDocumentBuilder builder { document }; XMLDocumentBuilder builder { document };
auto result = parser.parse_with_listener(builder); auto result = parser.parse_with_listener(builder);
(void)result; (void)result;

File diff suppressed because one or more lines are too long

View file

@ -21,7 +21,7 @@ enum class XMLScriptingSupport {
Enabled, Enabled,
}; };
ErrorOr<Variant<ByteString, Vector<XML::MarkupDeclaration>>> resolve_xml_resource(XML::SystemID const&, Optional<XML::PublicID> const&); Optional<String> resolve_named_html_entity(StringView entity_name);
class XMLDocumentBuilder final : public XML::Listener { class XMLDocumentBuilder final : public XML::Listener {
public: public:

View file

@ -72,7 +72,7 @@ WebIDL::ExceptionOr<Vector<GC::Root<DOM::Node>>> XMLFragmentParser::parse_xml_fr
if (allow_declarative_shadow_roots == HTML::HTMLParser::AllowDeclarativeShadowRoots::Yes) if (allow_declarative_shadow_roots == HTML::HTMLParser::AllowDeclarativeShadowRoots::Yes)
document->set_allow_declarative_shadow_roots(true); document->set_allow_declarative_shadow_roots(true);
XML::Parser parser(feed.string_view()); XML::Parser parser(feed.string_view(), { .resolve_named_html_entity = resolve_named_html_entity });
XMLDocumentBuilder builder { *document, XMLScriptingSupport::Disabled }; XMLDocumentBuilder builder { *document, XMLScriptingSupport::Disabled };
auto result = parser.parse_with_listener(builder); auto result = parser.parse_with_listener(builder);

View file

@ -29,6 +29,8 @@ struct ParserContext {
Vector<ParseError> parse_errors; Vector<ParseError> parse_errors;
Parser::Options const* options { nullptr };
bool is_xhtml_document { false };
int depth { 0 }; int depth { 0 };
}; };
@ -53,6 +55,82 @@ static StringView xml_char_to_string_view(xmlChar const* str)
return StringView(reinterpret_cast<char const*>(str), strlen(reinterpret_cast<char const*>(str))); return StringView(reinterpret_cast<char const*>(str), strlen(reinterpret_cast<char const*>(str)));
} }
static bool is_known_xhtml_public_id(StringView public_id)
{
return public_id.is_one_of(
"-//W3C//DTD XHTML 1.0 Transitional//EN"sv,
"-//W3C//DTD XHTML 1.1//EN"sv,
"-//W3C//DTD XHTML 1.0 Strict//EN"sv,
"-//W3C//DTD XHTML 1.0 Frameset//EN"sv,
"-//W3C//DTD XHTML Basic 1.0//EN"sv,
"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"sv,
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"sv,
"-//W3C//DTD MathML 2.0//EN"sv,
"-//WAPFORUM//DTD XHTML Mobile 1.0//EN"sv,
"-//WAPFORUM//DTD XHTML Mobile 1.1//EN"sv,
"-//WAPFORUM//DTD XHTML Mobile 1.2//EN"sv);
}
static void external_subset_handler(void* ctx, xmlChar const*, xmlChar const* external_id, xmlChar const*)
{
auto* parser_ctx = static_cast<xmlParserCtxtPtr>(ctx);
auto* context = static_cast<ParserContext*>(parser_ctx->_private);
if (!context || !external_id)
return;
auto public_id = xml_char_to_string_view(external_id);
if (is_known_xhtml_public_id(public_id))
context->is_xhtml_document = true;
}
static xmlEntity s_xhtml_entity_result;
static char s_xhtml_entity_utf8_buffer[32];
static xmlEntityPtr get_entity_handler(void* ctx, xmlChar const* name)
{
auto* parser_ctx = static_cast<xmlParserCtxtPtr>(ctx);
auto* predefined = xmlGetPredefinedEntity(name);
if (predefined)
return predefined;
if (parser_ctx->myDoc) {
auto* doc_entity = xmlGetDocEntity(parser_ctx->myDoc, name);
if (doc_entity)
return doc_entity;
}
auto* context = static_cast<ParserContext*>(parser_ctx->_private);
if (!context || !context->is_xhtml_document)
return nullptr;
// For XHTML documents, resolve named character entities (e.g., &nbsp;) using the
// HTML entity table. This avoids parsing a large embedded DTD on every document
// and matches the approach used by Blink and WebKit.
if (!context->options || !context->options->resolve_named_html_entity)
return nullptr;
auto entity_name = xml_char_to_string_view(name);
auto resolved = context->options->resolve_named_html_entity(entity_name);
if (!resolved.has_value())
return nullptr;
auto utf8_bytes = resolved->bytes_as_string_view();
if (utf8_bytes.length() >= sizeof(s_xhtml_entity_utf8_buffer))
return nullptr;
(void)utf8_bytes.copy_characters_to_buffer(s_xhtml_entity_utf8_buffer, sizeof(s_xhtml_entity_utf8_buffer));
s_xhtml_entity_result = {};
s_xhtml_entity_result.type = XML_ENTITY_DECL;
s_xhtml_entity_result.name = name;
s_xhtml_entity_result.content = reinterpret_cast<xmlChar*>(s_xhtml_entity_utf8_buffer);
s_xhtml_entity_result.length = static_cast<int>(utf8_bytes.length());
s_xhtml_entity_result.etype = XML_INTERNAL_PREDEFINED_ENTITY;
return &s_xhtml_entity_result;
}
static void start_document_handler(void* ctx) static void start_document_handler(void* ctx)
{ {
auto* parser_ctx = static_cast<xmlParserCtxtPtr>(ctx); auto* parser_ctx = static_cast<xmlParserCtxtPtr>(ctx);
@ -345,7 +423,7 @@ static void structured_error_handler(void* ctx, xmlError const* error)
context->error = move(parse_error); context->error = move(parse_error);
} }
static xmlSAXHandler create_sax_handler(bool preserve_comments) static xmlSAXHandler create_sax_handler(bool preserve_comments, bool resolve_html_entities)
{ {
xmlSAXHandler handler = {}; xmlSAXHandler handler = {};
handler.initialized = XML_SAX2_MAGIC; handler.initialized = XML_SAX2_MAGIC;
@ -360,6 +438,10 @@ static xmlSAXHandler create_sax_handler(bool preserve_comments)
handler.serror = structured_error_handler; handler.serror = structured_error_handler;
if (preserve_comments) if (preserve_comments)
handler.comment = comment_handler; handler.comment = comment_handler;
if (resolve_html_entities) {
handler.externalSubset = external_subset_handler;
handler.getEntity = get_entity_handler;
}
return handler; return handler;
} }
@ -371,8 +453,10 @@ ErrorOr<void, ParseError> Parser::parse_with_listener(Listener& listener)
ParserContext context; ParserContext context;
context.listener = &listener; context.listener = &listener;
context.options = &m_options;
auto sax_handler = create_sax_handler(m_options.preserve_comments); bool resolve_html_entities = static_cast<bool>(m_options.resolve_named_html_entity);
auto sax_handler = create_sax_handler(m_options.preserve_comments, resolve_html_entities);
int options = XML_PARSE_NONET | XML_PARSE_NOWARNING; int options = XML_PARSE_NONET | XML_PARSE_NOWARNING;
if (!m_options.preserve_cdata) if (!m_options.preserve_cdata)
@ -412,8 +496,10 @@ ErrorOr<void, ParseError> Parser::parse_with_listener(Listener& listener)
ErrorOr<Document, ParseError> Parser::parse() ErrorOr<Document, ParseError> Parser::parse()
{ {
ParserContext context; ParserContext context;
context.options = &m_options;
auto sax_handler = create_sax_handler(m_options.preserve_comments); bool resolve_html_entities = static_cast<bool>(m_options.resolve_named_html_entity);
auto sax_handler = create_sax_handler(m_options.preserve_comments, resolve_html_entities);
int options = XML_PARSE_NONET | XML_PARSE_NOWARNING; int options = XML_PARSE_NONET | XML_PARSE_NOWARNING;
if (!m_options.preserve_cdata) if (!m_options.preserve_cdata)

View file

@ -11,7 +11,9 @@
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/GenericLexer.h> #include <AK/GenericLexer.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h> #include <AK/OwnPtr.h>
#include <AK/String.h>
#include <LibXML/DOM/Document.h> #include <LibXML/DOM/Document.h>
#include <LibXML/DOM/DocumentTypeDeclaration.h> #include <LibXML/DOM/DocumentTypeDeclaration.h>
#include <LibXML/DOM/Node.h> #include <LibXML/DOM/Node.h>
@ -51,7 +53,7 @@ public:
bool preserve_cdata { true }; bool preserve_cdata { true };
bool preserve_comments { false }; bool preserve_comments { false };
bool treat_errors_as_fatal { true }; bool treat_errors_as_fatal { true };
Function<ErrorOr<Variant<ByteString, Vector<MarkupDeclaration>>>(SystemID const&, Optional<PublicID> const&)> resolve_external_resource {}; Function<Optional<String>(StringView)> resolve_named_html_entity {};
}; };
Parser(StringView source, Options options) Parser(StringView source, Options options)

View file

@ -0,0 +1,9 @@
<!doctype html>
<title>Test reference</title>
<style>
html, body { margin: 0 }
</style>
<iframe src="about:blank"></iframe>
<div>
PASS
</div>

View file

@ -0,0 +1,4 @@
<!DOCTYPE html>
Test passes if it correctly shows &Aacute; in the subframe.
<hr>
<iframe srcdoc="&amp;Aacute"></iframe>

View file

@ -0,0 +1,19 @@
<!doctype html>
<title>Appending from the parser after adopting in an XML document doesn't miss notifications</title>
<link rel="match" href="../../../../../expected/wpt-import/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001-ref.html">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1511329">
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<style>
html, body { margin: 0 }
</style>
<script>
// If we don't get notified of the <div> insertion, the PASS text will never appear.
function parsingInterrupted() {
let frameDoc = document.querySelector("iframe").contentDocument;
let root = frameDoc.documentElement;
document.documentElement.appendChild(root);
root.offsetTop;
}
</script>
<iframe src="support/adopt-while-parsing.xhtml"></iframe>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>
Test that an XHTML document with a data: URL still handles the XHTML DTD
properly even if the DTD URL is given as a relative URL.
</title>
<link rel="author" title="Boris Zbarsky" href="bzbarsky@mit.edu">
<link rel="match" href="../../../../../expected/wpt-import/html/the-xhtml-syntax/parsing-xhtml-documents/data-xhtml-with-dtd-ref.html">
</head>
<body>
Test passes if it correctly shows &Aacute; in the subframe.
<hr>
<!-- Document in the subframe is:
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
&Aacute;
</body>
</html>
-->
<iframe src='data:application/xml,%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3C!DOCTYPE%20html%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20XHTML%201.0%20Strict%2F%2FEN%22%20%22DTD%2Fxhtml1-strict.dtd%22%3E%0A%3Chtml%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml%22%3E%0A%20%20%3Cbody%3E%0A%20%20%20%20%26Aacute%3B%0A%20%20%3C%2Fbody%3E%0A%3C%2Fhtml%3E%0A'></iframe>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<script>
window.parent.parsingInterrupted();
</script>
<div>
PASS
</div>
</body>
</html>

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Fail
Fail xml-stylesheet blocks script execution and rendering

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<meta charset=utf-8>
<script>
var parser = new DOMParser();
var parse = parser.parseFromString.bind(parser);
function generateTestFunction(entitystring, expectedString, publicId, systemId, mimeType, friendlyMime) {
return function () {
var doctypeString = '<!DOCTYPE html';
if (publicId != null)
doctypeString += ' PUBLIC "' + publicId + '" "' + systemId + '">';
else if (systemId != null)
doctypeString += ' SYSTEM "' + systemId + '">';
else // both are null
doctypeString += '>';
var doc = parse(doctypeString + "<html><head></head><body id='test'>"+entitystring+"</body></html>", mimeType);
var root = doc.getElementById('test');
parent.assert_not_equals(root, null, friendlyMime + " parsing the entity reference caused a parse error;");
parent.assert_true(!!root.firstChild);
// Next line because some browsers include the partial parsed result in the parser error returned document.
parent.assert_equals(root.firstChild.nodeType, 3/*Text*/, friendlyMime + " parsing the entity reference caused a parse error;");
var text = root.firstChild.data;
for (var i = 0, len = expectedString.length; i < len; i++) {
parent.assert_equals(text.charCodeAt(i),expectedString.charCodeAt(i));
}
}
}
function setupTests(jsonEntities, publicId, systemId, mimeType, friendlyMime) {
for (entityName in jsonEntities) {
if ((mimeType == "text/html") || /;$/.test(entityName)) {
parent.test(generateTestFunction(entityName, jsonEntities[entityName].characters, publicId, systemId, mimeType, friendlyMime), friendlyMime + " parsing " + entityName);
}
}
}
parent.setup(function() {}, {explicit_done: true});
function run(row) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "entities.json");
xhr.onload = function () {
var entitiesJSON = JSON.parse(xhr.response);
setupTests(entitiesJSON, row[1], row[2], row[0], row[3]);
parent.done();
}
xhr.send();
}
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD XHTML 1.0 Transitional//EN", "foo", "XHTML1.0 Transitional"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["text/html", null, null, "HTML"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//WAPFORUM//DTD XHTML Mobile 1.1//EN", "foo", "XHTML Mobile 1.1"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//WAPFORUM//DTD XHTML Mobile 1.2//EN", "foo", "XHTML Mobile 1.2"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD XHTML 1.1//EN", "foo", "XHTML1.1"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD XHTML 1.0 Strict//EN", "foo", "XHTML1.0 Strict"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD XHTML 1.0 Frameset//EN", "foo", "XHTML1.0 Frameset"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD XHTML Basic 1.0//EN", "foo", "XHTML Basic"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN", "foo", "XHTML1.1+MathML"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN", "foo", "XHTML1.1+MathML+SVG"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//W3C//DTD MathML 2.0//EN", "foo", "MathML"]);
</script>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta name=timeout content=long>
<title>HTML entities for various XHTML Doctype</title>
<link rel=help href="http://w3c.github.io/html/xhtml.html#parsing-xhtml-documents">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<iframe id="test" src="support/xhtml-mathml-dtd-entity.htm"></iframe>
<script>
onload = () => document.getElementById("test").contentWindow.run(
["application/xhtml+xml", "-//WAPFORUM//DTD XHTML Mobile 1.0//EN", "foo", "XHTML Mobile"]);
</script>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="support/simple-style.css?pipe=trickle(d2)"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script><![CDATA[
window.observedZIndex = getComputedStyle(document.documentElement).zIndex;
]]></script>
<title>xml-stylesheet blocks script execution and rendering</title>
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1986042" />
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io" />
<link rel="author" title="Mozilla" href="https://mozilla.org" />
<script src="../../../resources/testharness.js"/>
<script src="../../../resources/testharnessreport.js"/>
</head>
<body>
<script><![CDATA[
test(function() {
assert_equals(window.observedZIndex, "3", "XML processing instruction should've blocked script execution and rendering");
});
]]></script>
</body>
</html>

View file

@ -362,18 +362,6 @@ static auto parse(StringView contents)
contents, contents,
{ {
.preserve_comments = true, .preserve_comments = true,
.resolve_external_resource = [&](XML::SystemID const& system_id, Optional<XML::PublicID> const&) -> ErrorOr<Variant<ByteString, Vector<XML::MarkupDeclaration>>> {
auto base = URL::create_with_file_scheme(s_path);
auto url = URL::Parser::basic_parse(system_id.system_literal, base);
if (!url.has_value())
return Error::from_string_literal("Invalid URL");
if (url->scheme() != "file")
return Error::from_string_literal("NYI: Nonlocal entity");
auto file = TRY(Core::File::open(URL::percent_decode(url->serialize_path()), Core::File::OpenMode::Read));
return ByteString::copy(TRY(file->read_until_eof()));
},
}, },
}; };
} }