2023-09-18 18:29:51 +02:00
/*
* Copyright ( c ) 2023 , Bastiaan van der Plaat < bastiaan . v . d . plaat @ gmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-10-05 16:33:30 +13:00
# include <LibURL/Parser.h>
2023-09-18 18:29:51 +02:00
# include <LibWeb/DOM/Document.h>
# include <LibWeb/Fetch/Fetching/Fetching.h>
# include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
2024-08-10 09:27:28 +01:00
# include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
2023-09-18 18:29:51 +02:00
# 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
2025-07-22 12:54:35 +02:00
WebIDL : : ExceptionOr < bool > NavigatorBeaconPartial : : send_beacon ( String const & url , Optional < Fetch : : BodyInit > const & data )
2023-09-18 18:29:51 +02:00
{
2025-01-21 09:12:05 -05:00
auto & navigator = as < Navigator > ( * this ) ;
2023-09-18 18:29:51 +02:00
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.
2024-03-18 16:22:27 +13:00
auto parsed_url = URL : : Parser : : basic_parse ( url , base_url ) ;
2025-01-10 04:50:34 +13:00
if ( ! parsed_url . has_value ( ) )
2023-09-18 18:29:51 +02:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , MUST ( String : : formatted ( " Beacon URL {} is invalid. " , url ) ) } ;
2025-01-10 04:50:34 +13:00
if ( parsed_url - > scheme ( ) ! = " http " & & parsed_url - > scheme ( ) ! = " https " )
2023-09-18 18:29:51 +02:00
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:
2024-11-15 04:01:23 +13:00
GC : : Ptr < Fetch : : Infrastructure : : Body > transmitted_data ;
2023-09-18 18:29:51 +02:00
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.
2024-08-10 09:27:28 +01:00
if ( transmitted_data - > length ( ) . has_value ( ) & & transmitted_data - > length ( ) . value ( ) > Fetch : : Fetching : : keepalive_maximum_size )
return false ;
2023-09-18 18:29:51 +02:00
// 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".
2024-04-26 13:24:20 -04:00
auto content_type_header = Fetch : : Infrastructure : : Header : : from_string_pair ( " Content-Type " sv , content_type . value ( ) ) ;
2023-09-18 18:29:51 +02:00
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.
2024-04-26 13:24:20 -04:00
header_list - > append ( content_type_header ) ;
2023-09-18 18:29:51 +02:00
}
}
// 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
2025-01-10 04:50:34 +13:00
req - > set_url_list ( { parsed_url . release_value ( ) } ) ; // url: parsedUrl
2023-09-18 18:29:51 +02:00
req - > set_header_list ( header_list ) ; // header list: headerList
req - > set_origin ( origin ) ; // origin: origin
req - > set_keepalive ( true ) ; // keepalive: true
2024-04-07 15:40:20 +02:00
if ( transmitted_data )
2024-11-15 04:01:23 +13:00
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"
2023-09-18 18:29:51 +02:00
// 7.2 Fetch req.
( void ) Fetch : : Fetching : : fetch ( realm , req , Fetch : : Infrastructure : : FetchAlgorithms : : create ( vm , { } ) ) ;
return true ;
}
}