mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-23 09:46:04 -05:00
5bed8f4055
URL::basic_parse has a subtle bug where the resulting URL is not set to valid when StateOveride is provided and the URL parser early returns a valid URL. This has not surfaced as a problem so far, as the only users of the state override API provide an already valid URL buffer and also ignore the result of basic parsing with a state override. However, this bug surfaces implementing the URL pattern spec, which as part of URL canonicalization: * Provides a dummy URL record * Basic URL parses that URL with state override * Checks the result of the URL parser to validate the URL While we could set URL validity on every early return of the URL parser during state override, it has been a long standing FIXME around the code to try and remove the awkward validity state of the URL class. So this commit makes the first stage of this change by migrating the basic parser API to return Optional, which also happens to make this subtle issue not a problem any more.
95 lines
5 KiB
C++
95 lines
5 KiB
C++
/*
|
|
* Copyright (c) 2023, Bastiaan van der Plaat <bastiaan.v.d.plaat@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibURL/Parser.h>
|
|
#include <LibWeb/DOM/Document.h>
|
|
#include <LibWeb/Fetch/Fetching/Fetching.h>
|
|
#include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
|
|
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
|
|
#include <LibWeb/HTML/Navigator.h>
|
|
#include <LibWeb/HTML/NavigatorBeacon.h>
|
|
#include <LibWeb/HTML/Scripting/Environments.h>
|
|
#include <LibWeb/HTML/Window.h>
|
|
|
|
namespace Web::HTML {
|
|
|
|
// https://w3c.github.io/beacon/#sendbeacon-method
|
|
WebIDL::ExceptionOr<bool> NavigatorBeaconMixin::send_beacon(String const& url, Optional<Fetch::BodyInit> const& data)
|
|
{
|
|
auto& navigator = verify_cast<Navigator>(*this);
|
|
auto& realm = navigator.realm();
|
|
auto& vm = realm.vm();
|
|
auto& relevant_settings_object = HTML::relevant_settings_object(navigator);
|
|
|
|
// 1. Set base to this's relevant settings object's API base URL.
|
|
auto base_url = relevant_settings_object.api_base_url();
|
|
|
|
// 2. Set origin to this's relevant settings object's origin.
|
|
auto origin = relevant_settings_object.origin();
|
|
|
|
// 3. Set parsedUrl to the result of the URL parser steps with url and base. If the algorithm returns an error, or if parsedUrl's scheme is not "http" or "https", throw a "TypeError" exception and terminate these steps.
|
|
auto parsed_url = URL::Parser::basic_parse(url, base_url);
|
|
if (!parsed_url.has_value())
|
|
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Beacon URL {} is invalid.", url)) };
|
|
if (parsed_url->scheme() != "http" && parsed_url->scheme() != "https")
|
|
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Beacon URL {} must be either http:// or https://.", url)) };
|
|
|
|
// 4. Let headerList be an empty list.
|
|
auto header_list = Fetch::Infrastructure::HeaderList::create(vm);
|
|
|
|
// 5. Let corsMode be "no-cors".
|
|
auto cors_mode = Fetch::Infrastructure::Request::Mode::NoCORS;
|
|
|
|
// 6. If data is not null:
|
|
GC::Ptr<Fetch::Infrastructure::Body> transmitted_data;
|
|
if (data.has_value()) {
|
|
// 6.1 Set transmittedData and contentType to the result of extracting data's byte stream with the keepalive flag set.
|
|
auto body_with_type = TRY(Fetch::extract_body(realm, data.value(), true));
|
|
transmitted_data = body_with_type.body;
|
|
auto& content_type = body_with_type.type;
|
|
|
|
// 6.2 If the amount of data that can be queued to be sent by keepalive enabled requests is exceeded by the size of transmittedData (as defined in HTTP-network-or-cache fetch), set the return value to false and terminate these steps.
|
|
if (transmitted_data->length().has_value() && transmitted_data->length().value() > Fetch::Fetching::keepalive_maximum_size)
|
|
return false;
|
|
|
|
// 6.3 If contentType is not null:
|
|
if (content_type.has_value()) {
|
|
// Set corsMode to "cors".
|
|
cors_mode = Fetch::Infrastructure::Request::Mode::CORS;
|
|
|
|
// If contentType value is a CORS-safelisted request-header value for the Content-Type header, set corsMode to "no-cors".
|
|
auto content_type_header = Fetch::Infrastructure::Header::from_string_pair("Content-Type"sv, content_type.value());
|
|
if (Fetch::Infrastructure::is_cors_safelisted_request_header(content_type_header))
|
|
cors_mode = Fetch::Infrastructure::Request::Mode::NoCORS;
|
|
|
|
// Append a Content-Type header with value contentType to headerList.
|
|
header_list->append(content_type_header);
|
|
}
|
|
}
|
|
|
|
// FIXME: 7. Set the return value to true, return the sendBeacon() call, and continue to run the following steps in parallel:
|
|
|
|
// 7.1 Let req be a new request, initialized as follows:
|
|
auto req = Fetch::Infrastructure::Request::create(vm);
|
|
req->set_method(MUST(ByteBuffer::copy("POST"sv.bytes()))); // method: POST
|
|
req->set_client(&relevant_settings_object); // client: this's relevant settings object
|
|
req->set_url_list({ parsed_url.release_value() }); // url: parsedUrl
|
|
req->set_header_list(header_list); // header list: headerList
|
|
req->set_origin(origin); // origin: origin
|
|
req->set_keepalive(true); // keepalive: true
|
|
if (transmitted_data)
|
|
req->set_body(GC::Ref<Fetch::Infrastructure::Body> { *transmitted_data }); // body: transmittedData
|
|
req->set_mode(cors_mode); // mode: corsMode
|
|
req->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include); // credentials mode: include
|
|
req->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Beacon); // initiator type: "beacon"
|
|
|
|
// 7.2 Fetch req.
|
|
(void)Fetch::Fetching::fetch(realm, req, Fetch::Infrastructure::FetchAlgorithms::create(vm, {}));
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|