|
@@ -0,0 +1,174 @@
|
|
|
+
|
|
|
+ * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: BSD-2-Clause
|
|
|
+ */
|
|
|
+
|
|
|
+#include <LibJS/Runtime/ArrayBuffer.h>
|
|
|
+#include <LibJS/Runtime/Error.h>
|
|
|
+#include <LibJS/Runtime/PromiseReaction.h>
|
|
|
+#include <LibWeb/Bindings/MainThreadVM.h>
|
|
|
+#include <LibWeb/Fetch/Body.h>
|
|
|
+#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
|
|
|
+#include <LibWeb/FileAPI/Blob.h>
|
|
|
+#include <LibWeb/HTML/Window.h>
|
|
|
+#include <LibWeb/Infra/JSON.h>
|
|
|
+#include <LibWeb/MimeSniff/MimeType.h>
|
|
|
+#include <LibWeb/Streams/ReadableStream.h>
|
|
|
+#include <LibWeb/WebIDL/Promise.h>
|
|
|
+
|
|
|
+namespace Web::Fetch {
|
|
|
+
|
|
|
+BodyMixin::~BodyMixin() = default;
|
|
|
+
|
|
|
+
|
|
|
+bool BodyMixin::is_unusable() const
|
|
|
+{
|
|
|
+
|
|
|
+ auto const& body = body_impl();
|
|
|
+ return body.has_value() && (body->stream()->is_disturbed() || body->stream()->is_locked());
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+JS::GCPtr<Streams::ReadableStream> BodyMixin::body() const
|
|
|
+{
|
|
|
+
|
|
|
+ auto const& body = body_impl();
|
|
|
+ return body.has_value() ? body->stream().ptr() : nullptr;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+bool BodyMixin::body_used() const
|
|
|
+{
|
|
|
+
|
|
|
+ auto const& body = body_impl();
|
|
|
+ return body.has_value() && body->stream()->is_disturbed();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+JS::NonnullGCPtr<JS::Promise> BodyMixin::array_buffer() const
|
|
|
+{
|
|
|
+ auto& vm = Bindings::main_thread_vm();
|
|
|
+ auto& realm = *vm.current_realm();
|
|
|
+
|
|
|
+
|
|
|
+ return consume_body(realm, *this, PackageDataType::ArrayBuffer);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+JS::NonnullGCPtr<JS::Promise> BodyMixin::blob() const
|
|
|
+{
|
|
|
+ auto& vm = Bindings::main_thread_vm();
|
|
|
+ auto& realm = *vm.current_realm();
|
|
|
+
|
|
|
+
|
|
|
+ return consume_body(realm, *this, PackageDataType::Blob);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+JS::NonnullGCPtr<JS::Promise> BodyMixin::form_data() const
|
|
|
+{
|
|
|
+ auto& vm = Bindings::main_thread_vm();
|
|
|
+ auto& realm = *vm.current_realm();
|
|
|
+
|
|
|
+
|
|
|
+ return consume_body(realm, *this, PackageDataType::FormData);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+JS::NonnullGCPtr<JS::Promise> BodyMixin::json() const
|
|
|
+{
|
|
|
+ auto& vm = Bindings::main_thread_vm();
|
|
|
+ auto& realm = *vm.current_realm();
|
|
|
+
|
|
|
+
|
|
|
+ return consume_body(realm, *this, PackageDataType::JSON);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+JS::NonnullGCPtr<JS::Promise> BodyMixin::text() const
|
|
|
+{
|
|
|
+ auto& vm = Bindings::main_thread_vm();
|
|
|
+ auto& realm = *vm.current_realm();
|
|
|
+
|
|
|
+
|
|
|
+ return consume_body(realm, *this, PackageDataType::Text);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+WebIDL::ExceptionOr<JS::Value> package_data(JS::Realm& realm, ByteBuffer bytes, PackageDataType type, Optional<MimeSniff::MimeType> const& mime_type)
|
|
|
+{
|
|
|
+ auto& vm = realm.vm();
|
|
|
+ auto& window = verify_cast<HTML::Window>(realm.global_object());
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case PackageDataType::ArrayBuffer:
|
|
|
+
|
|
|
+ return JS::ArrayBuffer::create(realm, move(bytes));
|
|
|
+ case PackageDataType::Blob: {
|
|
|
+
|
|
|
+
|
|
|
+ auto mime_type_string = mime_type.has_value() ? mime_type->serialized() : String::empty();
|
|
|
+ return FileAPI::Blob::create(window, move(bytes), move(mime_type_string));
|
|
|
+ }
|
|
|
+ case PackageDataType::FormData:
|
|
|
+
|
|
|
+ if (mime_type.has_value() && mime_type->essence() == "multipart/form-data"sv) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return JS::js_null();
|
|
|
+ }
|
|
|
+
|
|
|
+ else if (mime_type.has_value() && mime_type->essence() == "application/x-www-form-urlencoded"sv) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return JS::js_null();
|
|
|
+ }
|
|
|
+
|
|
|
+ else {
|
|
|
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Mime type must be 'multipart/form-data' or 'application/x-www-form-urlencoded'"sv };
|
|
|
+ }
|
|
|
+ case PackageDataType::JSON:
|
|
|
+
|
|
|
+ return Infra::parse_json_bytes_to_javascript_value(vm, bytes);
|
|
|
+ case PackageDataType::Text:
|
|
|
+
|
|
|
+ return JS::js_string(vm, String::copy(bytes));
|
|
|
+ default:
|
|
|
+ VERIFY_NOT_REACHED();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+JS::NonnullGCPtr<JS::Promise> consume_body(JS::Realm& realm, BodyMixin const& object, PackageDataType type)
|
|
|
+{
|
|
|
+ auto& vm = realm.vm();
|
|
|
+
|
|
|
+
|
|
|
+ if (object.is_unusable()) {
|
|
|
+ auto promise_capability = WebIDL::create_rejected_promise(realm, JS::TypeError::create(realm, "Body is unusable"sv));
|
|
|
+ return static_cast<JS::Promise&>(*promise_capability.promise);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ auto promise = WebIDL::create_resolved_promise(realm, JS::js_string(vm, String::empty()));
|
|
|
+
|
|
|
+
|
|
|
+ auto const& body = object.body_impl();
|
|
|
+ if (body.has_value())
|
|
|
+ promise = body->fully_read_as_promise();
|
|
|
+
|
|
|
+
|
|
|
+ auto steps = [&realm, &object, type](JS::Value value) -> WebIDL::ExceptionOr<JS::Value> {
|
|
|
+ VERIFY(value.is_string());
|
|
|
+ auto bytes = TRY_OR_RETURN_OOM(realm.global_object(), ByteBuffer::copy(value.as_string().string().bytes()));
|
|
|
+ return package_data(realm, move(bytes), type, object.mime_type_impl());
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ return WebIDL::upon_fulfillment(promise, move(steps));
|
|
|
+}
|
|
|
+
|
|
|
+}
|