LibWeb: Generate a simple error page when XML decode/parse fails

This fixes a regression on Acid3, since we are not expected to "best
effort" parse XML. The test specifically checks that you don't create an
incomplete, incorrect DOM.
This commit is contained in:
Andreas Kling 2024-04-19 10:47:57 +02:00
parent 5a3efb8842
commit 5ef6df79ed
Notes: sideshowbarker 2024-07-16 23:38:54 +09:00
4 changed files with 41 additions and 2 deletions

View file

@ -0,0 +1 @@
<this-will-not-parse

View file

@ -0,0 +1,3 @@
Got load event
[object HTMLDocument]
Failed to parse XML document: Expected '>' at offset 21

View file

@ -0,0 +1,13 @@
<script src="../include.js"></script>
<iframe id="i1"></iframe>
<script>
asyncTest((done) => {
i1.src = '../../data/bad.xml';
i1.onload = function() {
println("Got load event");
println(i1.contentDocument);
println(i1.contentDocument.documentElement.innerText);
done();
}
});
</script>

View file

@ -26,6 +26,17 @@
namespace Web { namespace Web {
// Replaces a document's content with a simple error message.
static void convert_to_xml_error_document(DOM::Document& document, String error_string)
{
auto html_element = MUST(DOM::create_element(document, HTML::TagNames::html, Namespace::HTML));
auto body_element = MUST(DOM::create_element(document, HTML::TagNames::body, Namespace::HTML));
MUST(html_element->append_child(body_element));
MUST(body_element->append_child(document.heap().allocate<DOM::Text>(document.realm(), document, error_string)));
document.remove_all_children();
MUST(document.append_child(html_element));
}
static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_markdown_document(HTML::NavigationParams const& navigation_params) static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_markdown_document(HTML::NavigationParams const& navigation_params)
{ {
auto extra_head_contents = R"~~~( auto extra_head_contents = R"~~~(
@ -108,8 +119,10 @@ bool build_xml_document(DOM::Document& document, ByteBuffer const& data, Optiona
} }
VERIFY(decoder.has_value()); VERIFY(decoder.has_value());
// Well-formed XML documents contain only properly encoded characters // Well-formed XML documents contain only properly encoded characters
if (!decoder->validate(data)) if (!decoder->validate(data)) {
convert_to_xml_error_document(document, "XML Document contains improperly-encoded characters"_string);
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_external_resource = resolve_xml_resource });
XMLDocumentBuilder builder { document }; XMLDocumentBuilder builder { document };
@ -198,7 +211,7 @@ static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_xml_document(HT
// FIXME: Actually follow the spec! This is just the ad-hoc code we had before, modified somewhat. // FIXME: Actually follow the spec! This is just the ad-hoc code we had before, modified somewhat.
auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::XML, "application/xhtml+xml"_string, navigation_params)); auto document = TRY(DOM::Document::create_and_initialize(DOM::Document::Type::XML, type.essence(), navigation_params));
Optional<String> content_encoding; Optional<String> content_encoding;
if (auto maybe_encoding = type.parameters().get("charset"sv); maybe_encoding.has_value()) if (auto maybe_encoding = type.parameters().get("charset"sv); maybe_encoding.has_value())
@ -219,6 +232,9 @@ static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_xml_document(HT
if (!decoder->validate(data)) { if (!decoder->validate(data)) {
// FIXME: Insert error message into the document. // FIXME: Insert error message into the document.
dbgln("XML Document contains improperly-encoded characters"); dbgln("XML Document contains improperly-encoded characters");
convert_to_xml_error_document(document, "XML Document contains improperly-encoded characters"_string);
// NOTE: This ensures that the `load` event gets fired for the frame loading this document.
document->completely_finish_loading(); document->completely_finish_loading();
return; return;
} }
@ -226,6 +242,9 @@ static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_xml_document(HT
if (source.is_error()) { if (source.is_error()) {
// FIXME: Insert error message into the document. // FIXME: Insert error message into the document.
dbgln("Failed to decode XML document: {}", source.error()); dbgln("Failed to decode XML document: {}", source.error());
convert_to_xml_error_document(document, MUST(String::formatted("Failed to decode XML document: {}", source.error())));
// NOTE: This ensures that the `load` event gets fired for the frame loading this document.
document->completely_finish_loading(); document->completely_finish_loading();
return; return;
} }
@ -235,6 +254,9 @@ static WebIDL::ExceptionOr<JS::NonnullGCPtr<DOM::Document>> load_xml_document(HT
if (result.is_error()) { if (result.is_error()) {
// FIXME: Insert error message into the document. // FIXME: Insert error message into the document.
dbgln("Failed to parse XML document: {}", result.error()); dbgln("Failed to parse XML document: {}", result.error());
convert_to_xml_error_document(document, MUST(String::formatted("Failed to parse XML document: {}", result.error())));
// NOTE: XMLDocumentBuilder ensures that the `load` event gets fired. We don't need to do anything else here.
} }
}; };