Everywhere: Hoist the Libraries folder to the top-level

This commit is contained in:
Timothy Flynn 2024-11-09 12:25:08 -05:00 committed by Andreas Kling
parent 950e819ee7
commit 93712b24bf
Notes: github-actions[bot] 2024-11-10 11:51:52 +00:00
4547 changed files with 104 additions and 113 deletions

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/XHR/EventNames.h>
namespace Web::XHR::EventNames {
#define __ENUMERATE_XHR_EVENT(name) FlyString name;
ENUMERATE_XHR_EVENTS
#undef __ENUMERATE_XHR_EVENT
void initialize_strings()
{
static bool s_initialized = false;
VERIFY(!s_initialized);
#define __ENUMERATE_XHR_EVENT(name) \
name = #name##_fly_string;
ENUMERATE_XHR_EVENTS
#undef __ENUMERATE_XHR_EVENT
s_initialized = true;
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Error.h>
#include <AK/FlyString.h>
namespace Web::XHR::EventNames {
#define ENUMERATE_XHR_EVENTS \
__ENUMERATE_XHR_EVENT(readystatechange) \
__ENUMERATE_XHR_EVENT(loadstart) \
__ENUMERATE_XHR_EVENT(progress) \
__ENUMERATE_XHR_EVENT(abort) \
__ENUMERATE_XHR_EVENT(error) \
__ENUMERATE_XHR_EVENT(load) \
__ENUMERATE_XHR_EVENT(timeout) \
__ENUMERATE_XHR_EVENT(loadend)
#define __ENUMERATE_XHR_EVENT(name) extern FlyString name;
ENUMERATE_XHR_EVENTS
#undef __ENUMERATE_XHR_EVENT
void initialize_strings();
}

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/Completion.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/FileAPI/Blob.h>
#include <LibWeb/FileAPI/File.h>
#include <LibWeb/HTML/FormControlInfrastructure.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/XHR/FormData.h>
namespace Web::XHR {
JS_DEFINE_ALLOCATOR(FormData);
// https://xhr.spec.whatwg.org/#dom-formdata
WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> FormData::construct_impl(JS::Realm& realm, JS::GCPtr<HTML::HTMLFormElement> form)
{
Vector<FormDataEntry> list;
// 1. If form is given, then:
if (form) {
// 1. Let list be the result of constructing the entry list for form.
auto entry_list = TRY(construct_entry_list(realm, *form));
// 2. If list is null, then throw an "InvalidStateError" DOMException.
if (!entry_list.has_value())
return WebIDL::InvalidStateError::create(realm, "Form element does not contain any entries."_string);
// 3. Set thiss entry list to list.
list = entry_list.release_value();
}
return construct_impl(realm, move(list));
}
WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> FormData::construct_impl(JS::Realm& realm, Vector<FormDataEntry> entry_list)
{
return realm.heap().allocate<FormData>(realm, realm, move(entry_list));
}
WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> FormData::create(JS::Realm& realm, Vector<DOMURL::QueryParam> entry_list)
{
Vector<FormDataEntry> list;
list.ensure_capacity(entry_list.size());
for (auto& entry : entry_list)
list.unchecked_append({ .name = move(entry.name), .value = move(entry.value) });
return construct_impl(realm, move(list));
}
FormData::FormData(JS::Realm& realm, Vector<FormDataEntry> entry_list)
: PlatformObject(realm)
, m_entry_list(move(entry_list))
{
}
FormData::~FormData() = default;
void FormData::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(FormData);
}
// https://xhr.spec.whatwg.org/#dom-formdata-append
WebIDL::ExceptionOr<void> FormData::append(String const& name, String const& value)
{
return append_impl(name, value);
}
// https://xhr.spec.whatwg.org/#dom-formdata-append-blob
WebIDL::ExceptionOr<void> FormData::append(String const& name, JS::NonnullGCPtr<FileAPI::Blob> const& blob_value, Optional<String> const& filename)
{
auto inner_filename = filename.has_value() ? filename.value() : Optional<String> {};
return append_impl(name, blob_value, inner_filename);
}
// https://xhr.spec.whatwg.org/#dom-formdata-append
// https://xhr.spec.whatwg.org/#dom-formdata-append-blob
WebIDL::ExceptionOr<void> FormData::append_impl(String const& name, Variant<JS::NonnullGCPtr<FileAPI::Blob>, String> const& value, Optional<String> const& filename)
{
auto& realm = this->realm();
auto& vm = realm.vm();
// 1. Let value be value if given; otherwise blobValue.
// 2. Let entry be the result of creating an entry with name, value, and filename if given.
auto entry = TRY(HTML::create_entry(realm, name, value, filename));
// 3. Append entry to thiss entry list.
TRY_OR_THROW_OOM(vm, m_entry_list.try_append(move(entry)));
return {};
}
// https://xhr.spec.whatwg.org/#dom-formdata-delete
void FormData::delete_(String const& name)
{
// The delete(name) method steps are to remove all entries whose name is name from thiss entry list.
m_entry_list.remove_all_matching([&name](FormDataEntry const& entry) {
return entry.name == name;
});
}
// https://xhr.spec.whatwg.org/#dom-formdata-get
Variant<JS::Handle<FileAPI::File>, String, Empty> FormData::get(String const& name)
{
// 1. If there is no entry whose name is name in thiss entry list, then return null.
auto entry_iterator = m_entry_list.find_if([&name](FormDataEntry const& entry) {
return entry.name == name;
});
if (entry_iterator.is_end())
return Empty {};
// 2. Return the value of the first entry whose name is name from thiss entry list.
return entry_iterator->value;
}
// https://xhr.spec.whatwg.org/#dom-formdata-getall
WebIDL::ExceptionOr<Vector<FormDataEntryValue>> FormData::get_all(String const& name)
{
// 1. If there is no entry whose name is name in thiss entry list, then return the empty list.
// 2. Return the values of all entries whose name is name, in order, from thiss entry list.
Vector<FormDataEntryValue> values;
for (auto const& entry : m_entry_list) {
if (entry.name == name)
TRY_OR_THROW_OOM(vm(), values.try_append(entry.value));
}
return values;
}
// https://xhr.spec.whatwg.org/#dom-formdata-has
bool FormData::has(String const& name)
{
// The has(name) method steps are to return true if there is an entry whose name is name in thiss entry list; otherwise false.
return !m_entry_list.find_if([&name](auto& entry) {
return entry.name == name;
})
.is_end();
}
// https://xhr.spec.whatwg.org/#dom-formdata-set
WebIDL::ExceptionOr<void> FormData::set(String const& name, String const& value)
{
return set_impl(name, value);
}
// https://xhr.spec.whatwg.org/#dom-formdata-set-blob
WebIDL::ExceptionOr<void> FormData::set(String const& name, JS::NonnullGCPtr<FileAPI::Blob> const& blob_value, Optional<String> const& filename)
{
auto inner_filename = filename.has_value() ? filename.value() : Optional<String> {};
return set_impl(name, blob_value, inner_filename);
}
// https://xhr.spec.whatwg.org/#dom-formdata-set
// https://xhr.spec.whatwg.org/#dom-formdata-set-blob
WebIDL::ExceptionOr<void> FormData::set_impl(String const& name, Variant<JS::NonnullGCPtr<FileAPI::Blob>, String> const& value, Optional<String> const& filename)
{
auto& realm = this->realm();
auto& vm = realm.vm();
// 1. Let value be value if given; otherwise blobValue.
// 2. Let entry be the result of creating an entry with name, value, and filename if given.
auto entry = TRY(HTML::create_entry(realm, name, value, filename));
auto existing = m_entry_list.find_if([&name](auto& entry) {
return entry.name == name;
});
// 3. If there are entries in thiss entry list whose name is name, then replace the first such entry with entry and remove the others.
if (!existing.is_end()) {
existing->value = entry.value;
m_entry_list.remove_all_matching([&name, &existing](auto& entry) {
return &entry != &*existing && entry.name == name;
});
}
// 4. Otherwise, append entry to thiss entry list.
else {
TRY_OR_THROW_OOM(vm, m_entry_list.try_append(move(entry)));
}
return {};
}
JS::ThrowCompletionOr<void> FormData::for_each(ForEachCallback callback)
{
for (auto i = 0u; i < m_entry_list.size(); ++i) {
auto& entry = m_entry_list[i];
TRY(callback(entry.name, entry.value));
}
return {};
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Bindings/FormDataPrototype.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/DOMURL/URLSearchParams.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/HTMLFormElement.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/XHR/FormDataEntry.h>
namespace Web::XHR {
// https://xhr.spec.whatwg.org/#interface-formdata
class FormData : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(FormData, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(FormData);
public:
virtual ~FormData() override;
static WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> construct_impl(JS::Realm&, JS::GCPtr<HTML::HTMLFormElement> form = {});
static WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> construct_impl(JS::Realm&, Vector<FormDataEntry> entry_list);
static WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> create(JS::Realm&, Vector<DOMURL::QueryParam> entry_list);
WebIDL::ExceptionOr<void> append(String const& name, String const& value);
WebIDL::ExceptionOr<void> append(String const& name, JS::NonnullGCPtr<FileAPI::Blob> const& blob_value, Optional<String> const& filename = {});
void delete_(String const& name);
Variant<JS::Handle<FileAPI::File>, String, Empty> get(String const& name);
WebIDL::ExceptionOr<Vector<FormDataEntryValue>> get_all(String const& name);
bool has(String const& name);
WebIDL::ExceptionOr<void> set(String const& name, String const& value);
WebIDL::ExceptionOr<void> set(String const& name, JS::NonnullGCPtr<FileAPI::Blob> const& blob_value, Optional<String> const& filename = {});
Vector<FormDataEntry> const& entry_list() const { return m_entry_list; }
using ForEachCallback = Function<JS::ThrowCompletionOr<void>(String const&, FormDataEntryValue const&)>;
JS::ThrowCompletionOr<void> for_each(ForEachCallback);
private:
friend class FormDataIterator;
explicit FormData(JS::Realm&, Vector<FormDataEntry> entry_list = {});
virtual void initialize(JS::Realm&) override;
WebIDL::ExceptionOr<void> append_impl(String const& name, Variant<JS::NonnullGCPtr<FileAPI::Blob>, String> const& value, Optional<String> const& filename = {});
WebIDL::ExceptionOr<void> set_impl(String const& name, Variant<JS::NonnullGCPtr<FileAPI::Blob>, String> const& value, Optional<String> const& filename = {});
Vector<FormDataEntry> m_entry_list;
};
}

View file

@ -0,0 +1,27 @@
#import <FileAPI/Blob.idl>
#import <FileAPI/File.idl>
// FIXME: This #import currently gives the following error after XHR/FormData.idl was #imported in Fetch/BodyInit.idl:
// "LibWeb/HTML/Window.idl:114: error: Mixin 'WindowOrWorkerGlobalScope' was never defined."
// XHR/FormData.idl needs to be #imported in Fetch/BodyInit.idl while removing #import HTML/HTMLFormElement.idl
// currently makes no difference.
// #import <HTML/HTMLFormElement.idl>
typedef (File or USVString) FormDataEntryValue;
// https://xhr.spec.whatwg.org/#interface-formdata
[Exposed=(Window,Worker)]
interface FormData {
constructor(optional HTMLFormElement form);
undefined append(USVString name, USVString value);
undefined append(USVString name, Blob blobValue, optional USVString filename);
undefined delete(USVString name);
// FIXME: The BindingsGenerator is not able to resolve the Variant's visit for FormDataEntryValue when
// the return value for one function returns an optional FormDataEntryValue while the others does not.
(File or USVString)? get(USVString name);
sequence<FormDataEntryValue> getAll(USVString name);
boolean has(USVString name);
undefined set(USVString name, USVString value);
undefined set(USVString name, Blob blobValue, optional USVString filename);
iterable<USVString, FormDataEntryValue>;
};

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Variant.h>
#include <LibJS/Heap/Handle.h>
#include <LibWeb/Forward.h>
namespace Web::XHR {
// https://xhr.spec.whatwg.org/#formdataentryvalue
using FormDataEntryValue = Variant<JS::Handle<FileAPI::File>, String>;
struct FormDataEntry {
String name;
FormDataEntryValue value;
};
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Iterator.h>
#include <LibWeb/Bindings/FormDataIteratorPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/FileAPI/File.h>
#include <LibWeb/XHR/FormDataIterator.h>
namespace Web::Bindings {
template<>
void Intrinsics::create_web_prototype_and_constructor<FormDataIteratorPrototype>(JS::Realm& realm)
{
auto prototype = heap().allocate<FormDataIteratorPrototype>(realm, realm);
m_prototypes.set("FormDataIterator"_fly_string, prototype);
}
}
namespace Web::XHR {
JS_DEFINE_ALLOCATOR(FormDataIterator);
JS::NonnullGCPtr<FormDataIterator> FormDataIterator::create(FormData const& form_data, JS::Object::PropertyKind iterator_kind)
{
return form_data.heap().allocate<FormDataIterator>(form_data.realm(), form_data, iterator_kind);
}
FormDataIterator::FormDataIterator(Web::XHR::FormData const& form_data, JS::Object::PropertyKind iterator_kind)
: PlatformObject(form_data.realm())
, m_form_data(form_data)
, m_iterator_kind(iterator_kind)
{
}
FormDataIterator::~FormDataIterator() = default;
void FormDataIterator::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(FormDataIterator);
}
void FormDataIterator::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_form_data);
}
JS::Object* FormDataIterator::next()
{
auto& vm = this->vm();
if (m_index >= m_form_data->m_entry_list.size())
return create_iterator_result_object(vm, JS::js_undefined(), true);
auto entry = m_form_data->m_entry_list[m_index++];
if (m_iterator_kind == JS::Object::PropertyKind::Key)
return create_iterator_result_object(vm, JS::PrimitiveString::create(vm, entry.name), false);
auto entry_value = entry.value.visit(
[&](JS::Handle<FileAPI::File> const& file) -> JS::Value {
return file.cell();
},
[&](String const& string) -> JS::Value {
return JS::PrimitiveString::create(vm, string);
});
if (m_iterator_kind == JS::Object::PropertyKind::Value)
return create_iterator_result_object(vm, entry_value, false);
return create_iterator_result_object(vm, JS::Array::create_from(realm(), { JS::PrimitiveString::create(vm, entry.name), entry_value }), false).ptr();
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/XHR/FormData.h>
namespace Web::XHR {
class FormDataIterator : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(FormDataIterator, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(FormDataIterator);
public:
[[nodiscard]] static JS::NonnullGCPtr<FormDataIterator> create(FormData const&, JS::Object::PropertyKind iterator_kind);
virtual ~FormDataIterator() override;
JS::Object* next();
private:
FormDataIterator(FormData const&, JS::Object::PropertyKind iterator_kind);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
JS::NonnullGCPtr<FormData const> m_form_data;
JS::Object::PropertyKind m_iterator_kind;
size_t m_index { 0 };
};
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/ProgressEventPrototype.h>
#include <LibWeb/XHR/ProgressEvent.h>
namespace Web::XHR {
JS_DEFINE_ALLOCATOR(ProgressEvent);
JS::NonnullGCPtr<ProgressEvent> ProgressEvent::create(JS::Realm& realm, FlyString const& event_name, ProgressEventInit const& event_init)
{
return realm.heap().allocate<ProgressEvent>(realm, realm, event_name, event_init);
}
WebIDL::ExceptionOr<JS::NonnullGCPtr<ProgressEvent>> ProgressEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, ProgressEventInit const& event_init)
{
return create(realm, event_name, event_init);
}
ProgressEvent::ProgressEvent(JS::Realm& realm, FlyString const& event_name, ProgressEventInit const& event_init)
: Event(realm, event_name, event_init)
, m_length_computable(event_init.length_computable)
, m_loaded(event_init.loaded)
, m_total(event_init.total)
{
}
ProgressEvent::~ProgressEvent() = default;
void ProgressEvent::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(ProgressEvent);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <LibWeb/DOM/Event.h>
#include <LibWeb/WebIDL/Types.h>
namespace Web::XHR {
struct ProgressEventInit : public DOM::EventInit {
bool length_computable { false };
WebIDL::UnsignedLongLong loaded { 0 };
WebIDL::UnsignedLongLong total { 0 };
};
class ProgressEvent final : public DOM::Event {
WEB_PLATFORM_OBJECT(ProgressEvent, DOM::Event);
JS_DECLARE_ALLOCATOR(ProgressEvent);
public:
[[nodiscard]] static JS::NonnullGCPtr<ProgressEvent> create(JS::Realm&, FlyString const& event_name, ProgressEventInit const& event_init);
static WebIDL::ExceptionOr<JS::NonnullGCPtr<ProgressEvent>> construct_impl(JS::Realm&, FlyString const& event_name, ProgressEventInit const& event_init);
virtual ~ProgressEvent() override;
bool length_computable() const { return m_length_computable; }
WebIDL::UnsignedLongLong loaded() const { return m_loaded; }
WebIDL::UnsignedLongLong total() const { return m_total; }
private:
ProgressEvent(JS::Realm&, FlyString const& event_name, ProgressEventInit const& event_init);
virtual void initialize(JS::Realm&) override;
bool m_length_computable { false };
WebIDL::UnsignedLongLong m_loaded { 0 };
WebIDL::UnsignedLongLong m_total { 0 };
};
}

View file

@ -0,0 +1,17 @@
#import <DOM/Event.idl>
// https://xhr.spec.whatwg.org/#interface-progressevent
[Exposed=(Window,Worker)]
interface ProgressEvent : Event {
constructor(DOMString type, optional ProgressEventInit eventInitDict = {});
readonly attribute boolean lengthComputable;
readonly attribute unsigned long long loaded;
readonly attribute unsigned long long total;
};
dictionary ProgressEventInit : EventInit {
boolean lengthComputable = false;
unsigned long long loaded = 0;
unsigned long long total = 0;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2020-2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2022, Kenneth Myhra <kennethmyhra@serenityos.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/RefCounted.h>
#include <AK/Weakable.h>
#include <LibURL/URL.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/DOMURL/URLSearchParams.h>
#include <LibWeb/Fetch/BodyInit.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Statuses.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/MimeSniff/MimeType.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/XHR/XMLHttpRequestEventTarget.h>
namespace Web::XHR {
// https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit
using DocumentOrXMLHttpRequestBodyInit = Variant<JS::Handle<Web::DOM::Document>, JS::Handle<Web::FileAPI::Blob>, JS::Handle<WebIDL::BufferSource>, JS::Handle<XHR::FormData>, JS::Handle<Web::DOMURL::URLSearchParams>, AK::String>;
class XMLHttpRequest final : public XMLHttpRequestEventTarget {
WEB_PLATFORM_OBJECT(XMLHttpRequest, XMLHttpRequestEventTarget);
JS_DECLARE_ALLOCATOR(XMLHttpRequest);
public:
enum class State : u16 {
Unsent = 0,
Opened = 1,
HeadersReceived = 2,
Loading = 3,
Done = 4,
};
static WebIDL::ExceptionOr<JS::NonnullGCPtr<XMLHttpRequest>> construct_impl(JS::Realm&);
virtual ~XMLHttpRequest() override;
State ready_state() const { return m_state; }
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; }
String response_url();
WebIDL::ExceptionOr<void> open(String const& method, String const& url);
WebIDL::ExceptionOr<void> open(String const& method, String const& url, bool async, Optional<String> const& username = Optional<String> {}, Optional<String> const& password = Optional<String> {});
WebIDL::ExceptionOr<void> send(Optional<DocumentOrXMLHttpRequestBodyInit> body);
WebIDL::ExceptionOr<void> set_request_header(String const& header, String const& value);
WebIDL::ExceptionOr<void> set_response_type(Bindings::XMLHttpRequestResponseType);
WebIDL::ExceptionOr<Optional<String>> get_response_header(String const& name) const;
WebIDL::ExceptionOr<String> get_all_response_headers() const;
WebIDL::CallbackType* onreadystatechange();
void set_onreadystatechange(WebIDL::CallbackType*);
WebIDL::ExceptionOr<void> override_mime_type(String const& mime);
u32 timeout() const;
WebIDL::ExceptionOr<void> set_timeout(u32 timeout);
bool with_credentials() const;
WebIDL::ExceptionOr<void> set_with_credentials(bool);
void abort();
JS::NonnullGCPtr<XMLHttpRequestUpload> upload() const;
private:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
virtual bool must_survive_garbage_collection() const override;
[[nodiscard]] MimeSniff::MimeType get_response_mime_type() const;
[[nodiscard]] Optional<StringView> get_final_encoding() const;
[[nodiscard]] 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();
JS::ThrowCompletionOr<void> request_error_steps(FlyString const& event_name, JS::GCPtr<WebIDL::DOMException> exception = nullptr);
XMLHttpRequest(JS::Realm&, XMLHttpRequestUpload&, Fetch::Infrastructure::HeaderList&, Fetch::Infrastructure::Response&, Fetch::Infrastructure::FetchController&);
// https://xhr.spec.whatwg.org/#upload-object
// upload object
// An XMLHttpRequestUpload object.
JS::NonnullGCPtr<XMLHttpRequestUpload> m_upload_object;
// https://xhr.spec.whatwg.org/#concept-xmlhttprequest-state
// state
// One of unsent, opened, headers received, loading, and done; initially unsent.
State m_state { State::Unsent };
// https://xhr.spec.whatwg.org/#send-flag
// send() flag
// A flag, initially unset.
bool m_send { false };
// https://xhr.spec.whatwg.org/#timeout
// timeout
// An unsigned integer, initially 0.
u32 m_timeout { 0 };
// https://xhr.spec.whatwg.org/#cross-origin-credentials
// cross-origin credentials
// A boolean, initially false.
bool m_cross_origin_credentials { false };
// https://xhr.spec.whatwg.org/#request-method
// request method
// A method.
ByteString m_request_method;
// https://xhr.spec.whatwg.org/#request-url
// request URL
// A URL.
URL::URL m_request_url;
// https://xhr.spec.whatwg.org/#author-request-headers
// author request headers
// A header list, initially empty.
JS::NonnullGCPtr<Fetch::Infrastructure::HeaderList> m_author_request_headers;
// https://xhr.spec.whatwg.org/#request-body
// request body
// Initially null.
JS::GCPtr<Fetch::Infrastructure::Body> m_request_body;
// https://xhr.spec.whatwg.org/#synchronous-flag
// synchronous flag
// A flag, initially unset.
bool m_synchronous { false };
// https://xhr.spec.whatwg.org/#upload-complete-flag
// upload complete flag
// A flag, initially unset.
bool m_upload_complete { false };
// https://xhr.spec.whatwg.org/#upload-listener-flag
// upload listener flag
// A flag, initially unset.
bool m_upload_listener { false };
// https://xhr.spec.whatwg.org/#timed-out-flag
// timed out flag
// A flag, initially unset.
bool m_timed_out { false };
// https://xhr.spec.whatwg.org/#response
// response
// A response, initially a network error.
JS::NonnullGCPtr<Fetch::Infrastructure::Response> m_response;
// https://xhr.spec.whatwg.org/#received-bytes
// received bytes
// A byte sequence, initially the empty byte sequence.
ByteBuffer m_received_bytes;
// https://xhr.spec.whatwg.org/#response-type
// response type
// One of the empty string, "arraybuffer", "blob", "document", "json", and "text"; initially the empty string.
Bindings::XMLHttpRequestResponseType m_response_type;
enum class Failure {
/// ????
};
// https://xhr.spec.whatwg.org/#response-object
// response object
// An object, failure, or null, initially null.
// NOTE: This needs to be a JS::Value as the JSON response might not actually be an object.
Variant<JS::NonnullGCPtr<JS::Object>, Failure, Empty> m_response_object;
// https://xhr.spec.whatwg.org/#xmlhttprequest-fetch-controller
// fetch controller
// A fetch controller, initially a new fetch controller.
// NOTE: The send() method sets it to a useful fetch controller, but for simplicity it always holds a fetch controller.
JS::NonnullGCPtr<Fetch::Infrastructure::FetchController> m_fetch_controller;
// https://xhr.spec.whatwg.org/#override-mime-type
// override MIME type
// A MIME type or null, initially null.
// NOTE: Can get a value when overrideMimeType() is invoked.
Optional<MimeSniff::MimeType> m_override_mime_type;
// Non-standard, see async path in `send()`
u64 m_request_body_transmitted { 0 };
};
}

View file

@ -0,0 +1,55 @@
#import <DOM/Document.idl>
#import <DOM/EventHandler.idl>
#import <Fetch/BodyInit.idl>
#import <XHR/XMLHttpRequestEventTarget.idl>
#import <XHR/XMLHttpRequestUpload.idl>
enum XMLHttpRequestResponseType {
"",
"arraybuffer",
"blob",
"document",
"json",
"text"
};
// https://xhr.spec.whatwg.org/#xmlhttprequest
[Exposed=(Window,DedicatedWorker,SharedWorker)]
interface XMLHttpRequest : XMLHttpRequestEventTarget {
constructor();
// event handler
attribute EventHandler onreadystatechange;
// states
const unsigned short UNSENT = 0;
const unsigned short OPENED = 1;
const unsigned short HEADERS_RECEIVED = 2;
const unsigned short LOADING = 3;
const unsigned short DONE = 4;
readonly attribute unsigned short readyState;
// request
undefined open(DOMString method, DOMString url);
undefined open(ByteString method, USVString url, boolean async, optional USVString? username = null, optional USVString? password = null);
undefined setRequestHeader(DOMString name, DOMString value);
attribute unsigned long timeout;
attribute boolean withCredentials;
[SameObject] readonly attribute XMLHttpRequestUpload upload;
undefined send(optional (Document or XMLHttpRequestBodyInit)? body = null);
undefined abort();
// response
readonly attribute USVString responseURL;
readonly attribute unsigned short status;
readonly attribute ByteString statusText;
ByteString? getResponseHeader(ByteString name);
ByteString getAllResponseHeaders();
undefined overrideMimeType(DOMString mime);
attribute XMLHttpRequestResponseType responseType;
readonly attribute any response;
readonly attribute DOMString responseText;
readonly attribute Document? responseXML;
};

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/XMLHttpRequestEventTargetPrototype.h>
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/XHR/EventNames.h>
#include <LibWeb/XHR/XMLHttpRequestEventTarget.h>
namespace Web::XHR {
#undef __ENUMERATE
#define __ENUMERATE(attribute_name, event_name) \
void XMLHttpRequestEventTarget::set_##attribute_name(WebIDL::CallbackType* value) \
{ \
set_event_handler_attribute(event_name, value); \
} \
WebIDL::CallbackType* XMLHttpRequestEventTarget::attribute_name() \
{ \
return event_handler_attribute(event_name); \
}
ENUMERATE_XML_HTTP_REQUEST_EVENT_TARGET_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventTarget.h>
namespace Web::XHR {
#define ENUMERATE_XML_HTTP_REQUEST_EVENT_TARGET_EVENT_HANDLERS(E) \
E(onloadstart, XHR::EventNames::loadstart) \
E(onprogress, XHR::EventNames::progress) \
E(onabort, XHR::EventNames::abort) \
E(onerror, XHR::EventNames::error) \
E(onload, XHR::EventNames::load) \
E(ontimeout, XHR::EventNames::timeout) \
E(onloadend, XHR::EventNames::loadend)
class XMLHttpRequestEventTarget : public DOM::EventTarget {
WEB_PLATFORM_OBJECT(XMLHttpRequestEventTarget, DOM::EventTarget);
public:
virtual ~XMLHttpRequestEventTarget() override {};
#undef __ENUMERATE
#define __ENUMERATE(attribute_name, event_name) \
void set_##attribute_name(WebIDL::CallbackType*); \
WebIDL::CallbackType* attribute_name();
ENUMERATE_XML_HTTP_REQUEST_EVENT_TARGET_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
protected:
XMLHttpRequestEventTarget(JS::Realm& realm)
: DOM::EventTarget(realm)
{
}
};
}

View file

@ -0,0 +1,17 @@
#import <DOM/EventTarget.idl>
#import <DOM/EventHandler.idl>
// https://xhr.spec.whatwg.org/#xmlhttprequesteventtarget
[Exposed=(Window,DedicatedWorker,SharedWorker)]
interface XMLHttpRequestEventTarget : EventTarget {
// event handlers
attribute EventHandler onloadstart;
attribute EventHandler onprogress;
attribute EventHandler onabort;
attribute EventHandler onerror;
attribute EventHandler onload;
attribute EventHandler ontimeout;
attribute EventHandler onloadend;
};

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/XMLHttpRequestUploadPrototype.h>
#include <LibWeb/XHR/XMLHttpRequestUpload.h>
namespace Web::XHR {
JS_DEFINE_ALLOCATOR(XMLHttpRequestUpload);
XMLHttpRequestUpload::XMLHttpRequestUpload(JS::Realm& realm)
: XMLHttpRequestEventTarget(realm)
{
}
XMLHttpRequestUpload::~XMLHttpRequestUpload() = default;
void XMLHttpRequestUpload::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(XMLHttpRequestUpload);
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/XHR/XMLHttpRequestEventTarget.h>
namespace Web::XHR {
class XMLHttpRequestUpload : public XMLHttpRequestEventTarget {
WEB_PLATFORM_OBJECT(XMLHttpRequestUpload, XMLHttpRequestEventTarget);
JS_DECLARE_ALLOCATOR(XMLHttpRequestUpload);
public:
virtual ~XMLHttpRequestUpload() override;
private:
XMLHttpRequestUpload(JS::Realm&);
virtual void initialize(JS::Realm&) override;
};
}

View file

@ -0,0 +1,6 @@
#import <XHR/XMLHttpRequestEventTarget.idl>
// https://xhr.spec.whatwg.org/#xmlhttprequestupload
[Exposed=(Window,DedicatedWorker,SharedWorker)]
interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {
};