ladybird/Userland/Libraries/LibWeb/Fetch/BodyInit.cpp
Shannon Booth f9e5b43b7a LibWeb: Rename URL platform object to DOMURL
Along with putting functions in the URL namespace into a DOMURL
namespace.

This is done as LibWeb is in an awkward situation where it needs
two URL classes. AK::URL is the general purpose URL class which
is all that is needed in 95% of cases. URL in the Web namespace
is needed predominantly for interfacing with the javascript
interfaces.

Because of two URLs in the same namespace, AK::URL has had to be
used throughout LibWeb. If we move AK::URL into a URL namespace,
this becomes more painful - where ::URL::URL is required to
specify the constructor (and something like
::URL::create_with_url_or_path in other places).

To fix this problem - rename the class in LibWeb implementing the
URL IDL interface to DOMURL, along with moving the other Web URL
related classes into this DOMURL folder.

One could argue that this name also makes the situation a little
more clear in LibWeb for why these two URL classes need be used
in the first place.
2024-02-25 08:54:31 +01:00

148 lines
7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2022-2023, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Completion.h>
#include <LibWeb/DOMURL/URLSearchParams.h>
#include <LibWeb/Fetch/BodyInit.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/FileAPI/Blob.h>
#include <LibWeb/HTML/FormControlInfrastructure.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Streams/AbstractOperations.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/Buffers.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/XHR/FormData.h>
namespace Web::Fetch {
// https://fetch.spec.whatwg.org/#bodyinit-safely-extract
WebIDL::ExceptionOr<Infrastructure::BodyWithType> safely_extract_body(JS::Realm& realm, BodyInitOrReadableBytes const& object)
{
// 1. If object is a ReadableStream object, then:
if (auto const* stream = object.get_pointer<JS::Handle<Streams::ReadableStream>>()) {
// 1. Assert: object is neither disturbed nor locked.
VERIFY(!((*stream)->is_disturbed() || (*stream)->is_locked()));
}
// 2. Return the result of extracting object.
return extract_body(realm, object);
}
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
WebIDL::ExceptionOr<Infrastructure::BodyWithType> extract_body(JS::Realm& realm, BodyInitOrReadableBytes const& object, bool keepalive)
{
HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
auto& vm = realm.vm();
// 1. Let stream be null.
JS::GCPtr<Streams::ReadableStream> stream;
// 2. If object is a ReadableStream object, then set stream to object.
if (auto const* stream_handle = object.get_pointer<JS::Handle<Streams::ReadableStream>>()) {
stream = const_cast<Streams::ReadableStream*>(stream_handle->cell());
}
// 3. Otherwise, if object is a Blob object, set stream to the result of running objects get stream.
else if (auto const* blob_handle = object.get_pointer<JS::Handle<FileAPI::Blob>>()) {
stream = TRY(blob_handle->cell()->get_stream());
}
// 4. Otherwise, set stream to a new ReadableStream object, and set up stream with byte reading support.
else {
stream = realm.heap().allocate<Streams::ReadableStream>(realm, realm);
TRY(Streams::set_up_readable_stream_controller_with_byte_reading_support(*stream));
}
// 5. Assert: stream is a ReadableStream object.
VERIFY(stream);
// FIXME: 6. Let action be null.
// 7. Let source be null.
Infrastructure::Body::SourceType source {};
// 8. Let length be null.
Optional<u64> length {};
// 9. Let type be null.
Optional<ByteBuffer> type {};
// 10. Switch on object.
TRY(object.visit(
[&](JS::Handle<FileAPI::Blob> const& blob) -> WebIDL::ExceptionOr<void> {
// Set source to object.
source = blob;
// Set length to objects size.
length = blob->size();
// If objects type attribute is not the empty byte sequence, set type to its value.
if (!blob->type().is_empty())
type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(blob->type().bytes()));
return {};
},
[&](ReadonlyBytes bytes) -> WebIDL::ExceptionOr<void> {
// Set source to object.
source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(bytes));
return {};
},
[&](JS::Handle<WebIDL::BufferSource> const& buffer_source) -> WebIDL::ExceptionOr<void> {
// Set source to a copy of the bytes held by object.
source = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(*buffer_source->raw_object()));
return {};
},
[&](JS::Handle<XHR::FormData> const& form_data) -> WebIDL::ExceptionOr<void> {
// Set action to this step: run the multipart/form-data encoding algorithm, with objects entry list and UTF-8.
auto serialized_form_data = TRY_OR_THROW_OOM(vm, HTML::serialize_to_multipart_form_data(form_data->entry_list()));
// Set source to object.
source = serialized_form_data.serialized_data;
// FIXME: Set length to unclear, see html/6424 for improving this.
// Set type to `multipart/form-data; boundary=`, followed by the multipart/form-data boundary string generated by the multipart/form-data encoding algorithm.
type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(TRY_OR_THROW_OOM(vm, String::formatted("multipart/form-data; boundary={}"sv, serialized_form_data.boundary)).bytes()));
return {};
},
[&](JS::Handle<DOMURL::URLSearchParams> const& url_search_params) -> WebIDL::ExceptionOr<void> {
// Set source to the result of running the application/x-www-form-urlencoded serializer with objects list.
auto search_params_bytes = TRY(url_search_params->to_string()).bytes();
source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(search_params_bytes));
// Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy("application/x-www-form-urlencoded;charset=UTF-8"sv.bytes()));
return {};
},
[&](String const& scalar_value_string) -> WebIDL::ExceptionOr<void> {
// NOTE: AK::String is always UTF-8.
// Set source to the UTF-8 encoding of object.
source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(scalar_value_string.bytes()));
// Set type to `text/plain;charset=UTF-8`.
type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy("text/plain;charset=UTF-8"sv.bytes()));
return {};
},
[&](JS::Handle<Streams::ReadableStream> const& stream) -> WebIDL::ExceptionOr<void> {
// If keepalive is true, then throw a TypeError.
if (keepalive)
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot extract body from stream when keepalive is set"sv };
// If object is disturbed or locked, then throw a TypeError.
if (stream->is_disturbed() || stream->is_locked())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot extract body from disturbed or locked stream"sv };
return {};
}));
// FIXME: 11. If source is a byte sequence, then set action to a step that returns source and length to sources length.
// For now, do it synchronously.
if (source.has<ByteBuffer>())
length = source.get<ByteBuffer>().size();
// FIXME: 12. If action is non-null, then run these steps in parallel:
// 13. Let body be a body whose stream is stream, source is source, and length is length.
auto body = Infrastructure::Body::create(vm, *stream, move(source), move(length));
// 14. Return (body, type).
return Infrastructure::BodyWithType { .body = move(body), .type = move(type) };
}
}