LibWeb: Add XMLHttpRequest Document response type

This commit is contained in:
Bastiaan van der Plaat 2023-09-14 21:07:53 +02:00 committed by Alexander Kalenik
parent b8f8b22aa5
commit 222cc29c5c
Notes: sideshowbarker 2024-07-17 08:55:54 +09:00
7 changed files with 122 additions and 12 deletions

View file

@ -146,7 +146,7 @@ static bool build_gemini_document(DOM::Document& document, ByteBuffer const& dat
return true;
}
static bool build_xml_document(DOM::Document& document, ByteBuffer const& data)
bool build_xml_document(DOM::Document& document, ByteBuffer const& data)
{
auto encoding = HTML::run_encoding_sniffing_algorithm(document, data);
auto decoder = TextCodec::decoder_for(encoding);

View file

@ -11,6 +11,7 @@
namespace Web {
bool build_xml_document(DOM::Document& document, ByteBuffer const& data);
bool parse_document(DOM::Document& document, ByteBuffer const& data);
JS::GCPtr<DOM::Document> load_document(Optional<HTML::NavigationParams> navigation_params);
JS::GCPtr<DOM::Document> create_document_for_inline_content(JS::GCPtr<HTML::Navigable> navigable, Optional<String> navigation_id, StringView content_html);

View file

@ -238,6 +238,18 @@ ErrorOr<void> MimeType::set_parameter(String name, String value)
return {};
}
// https://mimesniff.spec.whatwg.org/#xml-mime-type
bool MimeType::is_xml() const
{
return m_subtype.ends_with_bytes("+xml"sv) || essence().is_one_of("text/xml"sv, "application/xml"sv);
}
// https://mimesniff.spec.whatwg.org/#html-mime-type
bool MimeType::is_html() const
{
return essence().is_one_of("text/html"sv);
}
// https://mimesniff.spec.whatwg.org/#javascript-mime-type
bool MimeType::is_javascript() const
{

View file

@ -25,6 +25,8 @@ public:
String const& subtype() const { return m_subtype; }
OrderedHashMap<String, String> const& parameters() const { return m_parameters; }
bool is_xml() const;
bool is_html() const;
bool is_javascript() const;
ErrorOr<void> set_parameter(String name, String value);

View file

@ -18,6 +18,7 @@
#include <LibTextCodec/Decoder.h>
#include <LibWeb/Bindings/XMLHttpRequestPrototype.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/DocumentLoading.h>
#include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/IDLEventListener.h>
@ -33,6 +34,8 @@
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/Origin.h>
#include <LibWeb/HTML/Parser/HTMLEncodingDetection.h>
#include <LibWeb/HTML/Parser/HTMLParser.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Infra/ByteSequences.h>
#include <LibWeb/Infra/JSON.h>
@ -115,9 +118,35 @@ WebIDL::ExceptionOr<String> XMLHttpRequest::response_text() const
if (m_state != State::Loading && m_state != State::Done)
return String {};
// 3. Return the result of getting a text response for this.
return get_text_response();
}
// https://xhr.spec.whatwg.org/#dom-xmlhttprequest-responsexml
WebIDL::ExceptionOr<JS::GCPtr<DOM::Document>> XMLHttpRequest::response_xml()
{
// 1. If thiss response type is not the empty string or "document", then throw an "InvalidStateError" DOMException.
if (m_response_type != Bindings::XMLHttpRequestResponseType::Empty && m_response_type != Bindings::XMLHttpRequestResponseType::Document)
return WebIDL::InvalidStateError::create(realm(), "XHR responseXML can only be used for responseXML \"\" or \"document\""_fly_string);
// 2. If thiss state is not done, then return null.
if (m_state != State::Done)
return nullptr;
// 3. Assert: thiss response object is not failure.
VERIFY(!m_response_object.has<Failure>());
// 4. If thiss response object is non-null, then return it.
if (!m_response_object.has<Empty>())
return &verify_cast<DOM::Document>(m_response_object.get<JS::Value>().as_object());
// 5. Set a document response for this.
set_document_response();
// 6. Return thiss response object.
return &verify_cast<DOM::Document>(m_response_object.get<JS::Value>().as_object());
}
// https://xhr.spec.whatwg.org/#dom-xmlhttprequest-responsetype
WebIDL::ExceptionOr<void> XMLHttpRequest::set_response_type(Bindings::XMLHttpRequestResponseType response_type)
{
@ -186,8 +215,7 @@ WebIDL::ExceptionOr<JS::Value> XMLHttpRequest::response()
}
// 7. Otherwise, if thiss response type is "document", set a document response for this.
else if (m_response_type == Bindings::XMLHttpRequestResponseType::Document) {
// FIXME: Implement this.
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "XHR Document type not implemented"sv };
set_document_response();
}
// 8. Otherwise:
else {
@ -221,16 +249,8 @@ String XMLHttpRequest::get_text_response() const
// 2. Let charset be the result of get a final encoding for xhr.
auto charset = get_final_encoding().release_value_but_fixme_should_propagate_errors();
auto is_xml_mime_type = [](MimeSniff::MimeType const& mime_type) {
// An XML MIME type is any MIME type whose subtype ends in "+xml" or whose essence is "text/xml" or "application/xml". [RFC7303]
if (mime_type.essence().is_one_of("text/xml"sv, "application/xml"sv))
return true;
return mime_type.subtype().ends_with_bytes("+xml"sv);
};
// 3. If xhrs response type is the empty string, charset is null, and the result of get a final MIME type for xhr is an XML MIME type,
if (m_response_type == Bindings::XMLHttpRequestResponseType::Empty && !charset.has_value() && is_xml_mime_type(get_final_mime_type().release_value_but_fixme_should_propagate_errors())) {
if (m_response_type == Bindings::XMLHttpRequestResponseType::Empty && !charset.has_value() && get_final_mime_type().release_value_but_fixme_should_propagate_errors().is_xml()) {
// FIXME: then use the rules set forth in the XML specifications to determine the encoding. Let charset be the determined encoding. [XML] [XML-NAMES]
}
@ -247,6 +267,78 @@ String XMLHttpRequest::get_text_response() const
return TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, m_received_bytes).release_value_but_fixme_should_propagate_errors();
}
// https://xhr.spec.whatwg.org/#document-response
void XMLHttpRequest::set_document_response()
{
// 1. If xhrs responses body is null, then return.
if (!m_response->body())
return;
// 2. Let finalMIME be the result of get a final MIME type for xhr.
auto final_mine = MUST(get_final_mime_type());
// 3. If finalMIME is not an HTML MIME type or an XML MIME type, then return.
if (!final_mine.is_html() && !final_mine.is_xml())
return;
// 4. If xhrs response type is the empty string and finalMIME is an HTML MIME type, then return.
if (m_response_type == Bindings::XMLHttpRequestResponseType::Empty && final_mine.is_html())
return;
// 5. If finalMIME is an HTML MIME type, then:
Optional<StringView> charset;
JS::GCPtr<DOM::Document> document;
if (final_mine.is_html()) {
// 5.1. Let charset be the result of get a final encoding for xhr.
charset = MUST(get_final_encoding());
// 5.2. If charset is null, prescan the first 1024 bytes of xhrs received bytes and if that does not terminate unsuccessfully then let charset be the return value.
document = DOM::Document::create(realm());
if (!charset.has_value())
if (auto found_charset = HTML::run_prescan_byte_stream_algorithm(*document, m_received_bytes); found_charset.has_value())
charset = MUST(String::from_deprecated_string(found_charset.value())).bytes_as_string_view();
// 5.3. If charset is null, then set charset to UTF-8.
if (!charset.has_value())
charset = "UTF-8"sv;
// 5.4. Let document be a document that represents the result parsing xhrs received bytes following the rules set forth in the HTML Standard for an HTML parser with scripting disabled and a known definite encoding charset.
auto parser = HTML::HTMLParser::create(*document, m_received_bytes, charset.value());
parser->run(document->url());
// 5.5. Flag document as an HTML document.
document->set_document_type(DOM::Document::Type::HTML);
}
// 6. Otherwise, let document be a document that represents the result of running the XML parser with XML scripting support disabled on xhrs received bytes. If that fails (unsupported character encoding, namespace well-formedness error, etc.), then return null.
else {
document = DOM::Document::create(realm());
if (!Web::build_xml_document(*document, m_received_bytes)) {
m_response_object = JS::js_null();
return;
}
}
// 7. If charset is null, then set charset to UTF-8.
if (!charset.has_value())
charset = "UTF-8"sv;
// 8. Set documents encoding to charset.
document->set_encoding(charset->to_deprecated_string());
// 9. Set documents content type to finalMIME.
document->set_content_type(MUST(final_mine.serialized()).to_deprecated_string());
// 10. Set documents URL to xhrs responses URL.
document->set_url(m_response->url().value_or({}));
// 11. Set documents origin to xhrs relevant settings objects origin.
document->set_origin(HTML::relevant_settings_object(*this).origin());
// 12. Set xhrs response object to document.
m_response_object = JS::Value(document);
}
// https://xhr.spec.whatwg.org/#final-mime-type
ErrorOr<MimeSniff::MimeType> XMLHttpRequest::get_final_mime_type() const
{

View file

@ -48,6 +48,7 @@ public:
Fetch::Infrastructure::Status status() const;
WebIDL::ExceptionOr<String> status_text() const;
WebIDL::ExceptionOr<String> response_text() const;
WebIDL::ExceptionOr<JS::GCPtr<DOM::Document>> response_xml();
WebIDL::ExceptionOr<JS::Value> response();
Bindings::XMLHttpRequestResponseType response_type() const { return m_response_type; }
@ -86,6 +87,7 @@ private:
ErrorOr<MimeSniff::MimeType> get_final_mime_type() const;
String get_text_response() const;
void set_document_response();
WebIDL::ExceptionOr<void> handle_response_end_of_body();
WebIDL::ExceptionOr<void> handle_errors();

View file

@ -29,6 +29,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget {
readonly attribute unsigned short status;
readonly attribute ByteString statusText;
readonly attribute DOMString responseText;
readonly attribute Document? responseXML;
readonly attribute any response;
attribute XMLHttpRequestResponseType responseType;
attribute unsigned long timeout;