2021-09-13 01:00:47 +03:00
/*
* Copyright ( c ) 2021 , Idan Horowitz < idan . horowitz @ serenityos . org >
2025-03-15 15:38:09 +13:00
* Copyright ( c ) 2023 - 2025 , Shannon Booth < shannon @ serenityos . org >
2021-09-13 01:00:47 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/QuickSort.h>
2021-09-13 21:50:05 +03:00
# include <AK/StringBuilder.h>
2021-09-13 01:00:47 +03:00
# include <AK/Utf8View.h>
2023-06-25 14:15:24 +12:00
# include <LibTextCodec/Decoder.h>
2024-08-05 16:03:53 +01:00
# include <LibTextCodec/Encoder.h>
2024-03-18 16:22:27 +13:00
# include <LibURL/Parser.h>
2023-03-01 20:10:01 +01:00
# include <LibWeb/Bindings/ExceptionOrUtils.h>
LibWeb: Remove unecessary dependence on Window from assorted classes
These classes only needed Window to get at its realm. Pass a realm
directly to construct Crypto, Encoding, HRT, IntersectionObserver,
NavigationTiming, Page, RequestIdleCallback, Selection, Streams, URL,
and XML classes.
2022-09-25 18:11:21 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/URLSearchParamsPrototype.h>
2024-02-11 19:48:56 +13:00
# include <LibWeb/DOMURL/DOMURL.h>
# include <LibWeb/DOMURL/URLSearchParams.h>
2025-05-12 20:01:56 +12:00
# include <LibWeb/Infra/Strings.h>
2021-09-13 01:00:47 +03:00
2024-02-11 19:48:56 +13:00
namespace Web : : DOMURL {
2021-09-13 01:00:47 +03:00
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( URLSearchParams ) ;
2023-11-19 19:47:52 +01:00
LibWeb: Remove unecessary dependence on Window from assorted classes
These classes only needed Window to get at its realm. Pass a realm
directly to construct Crypto, Encoding, HRT, IntersectionObserver,
NavigationTiming, Page, RequestIdleCallback, Selection, Streams, URL,
and XML classes.
2022-09-25 18:11:21 -06:00
URLSearchParams : : URLSearchParams ( JS : : Realm & realm , Vector < QueryParam > list )
: PlatformObject ( realm )
2022-09-04 14:04:42 +02:00
, m_list ( move ( list ) )
{
}
URLSearchParams : : ~ URLSearchParams ( ) = default ;
2023-08-07 08:41:28 +02:00
void URLSearchParams : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( URLSearchParams ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2023-01-10 06:28:20 -05:00
}
2022-09-04 14:04:42 +02:00
void URLSearchParams : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_url ) ;
}
2023-06-25 14:15:24 +12:00
// https://url.spec.whatwg.org/#concept-urlencoded-serializer
// The application/x-www-form-urlencoded serializer takes a list of name-value tuples tuples, with an optional encoding encoding (default UTF-8), and then runs these steps. They return an ASCII string.
2024-08-11 00:24:54 +12:00
String url_encode ( Vector < QueryParam > const & tuples , StringView encoding )
2021-09-13 21:50:05 +03:00
{
2023-06-25 14:15:24 +12:00
// 1. Set encoding to the result of getting an output encoding from encoding.
encoding = TextCodec : : get_output_encoding ( encoding ) ;
2024-08-05 16:03:53 +01:00
auto encoder = TextCodec : : encoder_for ( encoding ) ;
if ( ! encoder . has_value ( ) ) {
// NOTE: Fallback to default utf-8 encoder.
encoder = TextCodec : : encoder_for ( " utf-8 " sv ) ;
}
2023-06-25 14:15:24 +12:00
// 2. Let output be the empty string.
StringBuilder output ;
// 3. For each tuple of tuples:
for ( auto const & tuple : tuples ) {
// 1. Assert: tuple’ s name and tuple’ s value are scalar value strings.
// 2. Let name be the result of running percent-encode after encoding with encoding, tuple’ s name, the application/x-www-form-urlencoded percent-encode set, and true.
2024-08-11 00:05:22 +12:00
auto name = URL : : Parser : : percent_encode_after_encoding ( * encoder , tuple . name , URL : : PercentEncodeSet : : ApplicationXWWWFormUrlencoded , true ) ;
2023-06-25 14:15:24 +12:00
// 3. Let value be the result of running percent-encode after encoding with encoding, tuple’ s value, the application/x-www-form-urlencoded percent-encode set, and true.
2024-08-11 00:05:22 +12:00
auto value = URL : : Parser : : percent_encode_after_encoding ( * encoder , tuple . value , URL : : PercentEncodeSet : : ApplicationXWWWFormUrlencoded , true ) ;
2023-06-25 14:15:24 +12:00
// 4. If output is not the empty string, then append U+0026 (&) to output.
if ( ! output . is_empty ( ) )
2024-08-11 00:24:54 +12:00
output . append ( ' & ' ) ;
2023-06-25 14:15:24 +12:00
// 5. Append name, followed by U+003D (=), followed by value, to output.
2024-08-11 00:24:54 +12:00
output . append ( name ) ;
output . append ( ' = ' ) ;
output . append ( value ) ;
2021-09-13 21:50:05 +03:00
}
2023-06-25 14:15:24 +12:00
// 4. Return output.
2024-08-11 00:24:54 +12:00
return MUST ( output . to_string ( ) ) ;
2021-09-13 21:50:05 +03:00
}
2023-06-25 14:12:34 +12:00
// https://url.spec.whatwg.org/#concept-urlencoded-parser
// The application/x-www-form-urlencoded parser takes a byte sequence input, and then runs these steps:
2024-08-11 00:24:54 +12:00
Vector < QueryParam > url_decode ( StringView input )
2021-09-13 21:50:05 +03:00
{
// 1. Let sequences be the result of splitting input on 0x26 (&).
auto sequences = input . split_view ( ' & ' ) ;
// 2. Let output be an initially empty list of name-value tuples where both name and value hold a string.
Vector < QueryParam > output ;
// 3. For each byte sequence bytes in sequences:
for ( auto bytes : sequences ) {
// 1. If bytes is the empty byte sequence, then continue.
if ( bytes . is_empty ( ) )
continue ;
StringView name ;
StringView value ;
// 2. If bytes contains a 0x3D (=), then let name be the bytes from the start of bytes up to but excluding its first 0x3D (=), and let value be the bytes, if any, after the first 0x3D (=) up to the end of bytes. If 0x3D (=) is the first byte, then name will be the empty byte sequence. If it is the last, then value will be the empty byte sequence.
if ( auto index = bytes . find ( ' = ' ) ; index . has_value ( ) ) {
name = bytes . substring_view ( 0 , * index ) ;
value = bytes . substring_view ( * index + 1 ) ;
}
// 3. Otherwise, let name have the value of bytes and let value be the empty byte sequence.
else {
name = bytes ;
value = " " sv ;
}
// 4. Replace any 0x2B (+) in name and value with 0x20 (SP).
2022-07-05 22:33:15 +02:00
auto space_decoded_name = name . replace ( " + " sv , " " sv , ReplaceMode : : All ) ;
2024-07-28 16:23:59 -03:00
auto space_decoded_value = value . replace ( " + " sv , " " sv , ReplaceMode : : All ) ;
2021-09-13 21:50:05 +03:00
// 5. Let nameString and valueString be the result of running UTF-8 decode without BOM on the percent-decoding of name and value, respectively.
2024-08-11 15:19:47 +12:00
auto name_string = String : : from_utf8_with_replacement_character ( URL : : percent_decode ( space_decoded_name ) , String : : WithBOMHandling : : No ) ;
auto value_string = String : : from_utf8_with_replacement_character ( URL : : percent_decode ( space_decoded_value ) , String : : WithBOMHandling : : No ) ;
2021-09-13 21:50:05 +03:00
2024-08-11 00:24:54 +12:00
output . empend ( move ( name_string ) , move ( value_string ) ) ;
2021-09-13 21:50:05 +03:00
}
return output ;
}
2024-11-15 04:01:23 +13:00
GC : : Ref < URLSearchParams > URLSearchParams : : create ( JS : : Realm & realm , Vector < QueryParam > list )
2022-09-04 14:04:42 +02:00
{
2024-11-14 05:50:17 +13:00
return realm . create < URLSearchParams > ( realm , move ( list ) ) ;
2022-09-04 14:04:42 +02:00
}
2024-08-06 23:27:11 +12:00
// https://url.spec.whatwg.org/#urlsearchparams-initialize
2024-11-15 04:01:23 +13:00
GC : : Ref < URLSearchParams > URLSearchParams : : create ( JS : : Realm & realm , StringView init )
2024-08-06 23:27:11 +12:00
{
// NOTE: We skip the other steps since we know it is a string at this point.
// b. Set query’ s list to the result of parsing init.
2024-08-11 00:24:54 +12:00
return URLSearchParams : : create ( realm , url_decode ( init ) ) ;
2024-08-06 23:27:11 +12:00
}
2022-02-14 06:38:03 +00:00
// https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams
// https://url.spec.whatwg.org/#urlsearchparams-initialize
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < URLSearchParams > > URLSearchParams : : construct_impl ( JS : : Realm & realm , Variant < Vector < Vector < String > > , OrderedHashMap < String , String > , String > const & init )
2021-09-13 01:00:47 +03:00
{
2023-03-01 20:10:01 +01:00
auto & vm = realm . vm ( ) ;
2021-09-13 01:00:47 +03:00
// 1. If init is a string and starts with U+003F (?), then remove the first code point from init.
2022-01-25 20:38:55 +00:00
// NOTE: We do this when we know that it's a string on step 3 of initialization.
2021-09-13 01:00:47 +03:00
// 2. Initialize this with init.
// URLSearchParams init from this point forward
// 1. If init is a sequence, then for each pair in init:
2023-03-01 20:10:01 +01:00
if ( init . has < Vector < Vector < String > > > ( ) ) {
auto const & init_sequence = init . get < Vector < Vector < String > > > ( ) ;
2022-01-25 20:38:55 +00:00
Vector < QueryParam > list ;
list . ensure_capacity ( init_sequence . size ( ) ) ;
for ( auto const & pair : init_sequence ) {
// a. If pair does not contain exactly two items, then throw a TypeError.
if ( pair . size ( ) ! = 2 )
2023-03-03 18:04:58 +00:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , TRY_OR_THROW_OOM ( vm , String : : formatted ( " Expected only 2 items in pair, got {} " , pair . size ( ) ) ) } ;
2022-01-25 20:38:55 +00:00
// b. Append a new name-value pair whose name is pair’ s first item, and value is pair’ s second item, to query’ s list.
list . append ( QueryParam { . name = pair [ 0 ] , . value = pair [ 1 ] } ) ;
}
LibWeb: Remove unecessary dependence on Window from assorted classes
These classes only needed Window to get at its realm. Pass a realm
directly to construct Crypto, Encoding, HRT, IntersectionObserver,
NavigationTiming, Page, RequestIdleCallback, Selection, Streams, URL,
and XML classes.
2022-09-25 18:11:21 -06:00
return URLSearchParams : : create ( realm , move ( list ) ) ;
2022-01-25 20:38:55 +00:00
}
2021-09-13 01:00:47 +03:00
// 2. Otherwise, if init is a record, then for each name → value of init, append a new name-value pair whose name is name and value is value, to query’ s list.
2023-03-01 20:10:01 +01:00
if ( init . has < OrderedHashMap < String , String > > ( ) ) {
auto const & init_record = init . get < OrderedHashMap < String , String > > ( ) ;
2022-02-14 06:38:03 +00:00
Vector < QueryParam > list ;
list . ensure_capacity ( init_record . size ( ) ) ;
for ( auto const & pair : init_record )
list . append ( QueryParam { . name = pair . key , . value = pair . value } ) ;
LibWeb: Remove unecessary dependence on Window from assorted classes
These classes only needed Window to get at its realm. Pass a realm
directly to construct Crypto, Encoding, HRT, IntersectionObserver,
NavigationTiming, Page, RequestIdleCallback, Selection, Streams, URL,
and XML classes.
2022-09-25 18:11:21 -06:00
return URLSearchParams : : create ( realm , move ( list ) ) ;
2022-02-14 06:38:03 +00:00
}
2021-09-13 01:00:47 +03:00
// 3. Otherwise:
// a. Assert: init is a string.
2022-01-25 20:38:55 +00:00
// NOTE: `get` performs `VERIFY(has<T>())`
2023-03-01 20:10:01 +01:00
auto const & init_string = init . get < String > ( ) ;
2022-01-25 20:38:55 +00:00
// See NOTE at the start of this function.
2023-03-01 20:10:01 +01:00
auto init_string_view = init_string . bytes_as_string_view ( ) ;
auto stripped_init = init_string_view . substring_view ( init_string_view . starts_with ( ' ? ' ) ) ;
2022-01-25 20:38:55 +00:00
2021-09-13 01:00:47 +03:00
// b. Set query’ s list to the result of parsing init.
2024-08-06 23:27:11 +12:00
return URLSearchParams : : create ( realm , stripped_init ) ;
2021-09-13 01:00:47 +03:00
}
2023-02-23 13:32:08 +00:00
// https://url.spec.whatwg.org/#dom-urlsearchparams-size
size_t URLSearchParams : : size ( ) const
{
// The size getter steps are to return this’ s list’ s size.
return m_list . size ( ) ;
}
2024-08-11 00:24:54 +12:00
// https://url.spec.whatwg.org/#dom-urlsearchparams-append
void URLSearchParams : : append ( String const & name , String const & value )
2021-09-13 01:00:47 +03:00
{
// 1. Append a new name-value pair whose name is name and value is value, to list.
2024-08-11 00:24:54 +12:00
m_list . empend ( name , value ) ;
2023-03-01 20:10:01 +01:00
2024-08-11 00:24:54 +12:00
// 2. Update this.
update ( ) ;
2021-09-13 01:00:47 +03:00
}
2024-08-11 00:39:07 +12:00
// https://url.spec.whatwg.org/#concept-urlsearchparams-update
2024-08-11 00:24:54 +12:00
void URLSearchParams : : update ( )
2021-09-13 01:00:47 +03:00
{
// 1. If query’ s URL object is null, then return.
2022-09-04 14:04:42 +02:00
if ( ! m_url )
2024-08-11 00:24:54 +12:00
return ;
2021-09-13 01:00:47 +03:00
// 2. Let serializedQuery be the serialization of query’ s list.
2024-08-11 00:39:07 +12:00
Optional < String > serialized_query = to_string ( ) ;
2024-08-11 00:24:54 +12:00
2021-09-13 01:00:47 +03:00
// 3. If serializedQuery is the empty string, then set serializedQuery to null.
2024-08-11 00:39:07 +12:00
if ( serialized_query = = String { } )
2021-09-14 00:10:22 +03:00
serialized_query = { } ;
2024-08-11 00:24:54 +12:00
2021-09-13 01:00:47 +03:00
// 4. Set query’ s URL object’ s URL’ s query to serializedQuery.
2024-10-29 20:04:39 +01:00
m_url - > set_query ( { } , serialized_query ) ;
2021-09-13 01:00:47 +03:00
}
2024-08-10 23:35:14 +12:00
// https://url.spec.whatwg.org/#dom-urlsearchparams-delete
2024-08-11 00:24:54 +12:00
void URLSearchParams : : delete_ ( String const & name , Optional < String > const & value )
2021-09-13 01:00:47 +03:00
{
2024-08-10 23:35:14 +12:00
// 1. If value is given, then remove all tuples whose name is name and value is value from this’ s list.
if ( value . has_value ( ) ) {
m_list . remove_all_matching ( [ & name , & value ] ( auto & entry ) {
return entry . name = = name & & entry . value = = value . value ( ) ;
} ) ;
}
// 2. Otherwise, remove all tuples whose name is name from this’ s list.
else {
m_list . remove_all_matching ( [ & name ] ( auto & entry ) {
return entry . name = = name ;
} ) ;
}
2021-09-13 01:00:47 +03:00
// 2. Update this.
2024-08-11 00:24:54 +12:00
update ( ) ;
2021-09-13 01:00:47 +03:00
}
2023-03-01 20:10:01 +01:00
Optional < String > URLSearchParams : : get ( String const & name )
2021-09-13 01:00:47 +03:00
{
// return the value of the first name-value pair whose name is name in this’ s list, if there is such a pair, and null otherwise.
auto result = m_list . find_if ( [ & name ] ( auto & entry ) {
return entry . name = = name ;
} ) ;
if ( result . is_end ( ) )
return { } ;
return result - > value ;
}
2021-10-26 22:06:55 +01:00
// https://url.spec.whatwg.org/#dom-urlsearchparams-getall
2024-08-11 00:24:54 +12:00
Vector < String > URLSearchParams : : get_all ( String const & name )
2021-10-26 22:06:55 +01:00
{
// return the values of all name-value pairs whose name is name, in this’ s list, in list order, and the empty sequence otherwise.
2023-03-01 20:10:01 +01:00
Vector < String > values ;
2021-10-26 22:06:55 +01:00
for ( auto & entry : m_list ) {
if ( entry . name = = name )
2024-08-11 00:24:54 +12:00
values . append ( entry . value ) ;
2021-10-26 22:06:55 +01:00
}
return values ;
}
2024-08-10 23:52:54 +12:00
// https://url.spec.whatwg.org/#dom-urlsearchparams-has
bool URLSearchParams : : has ( String const & name , Optional < String > const & value )
2021-09-13 01:00:47 +03:00
{
2024-08-10 23:52:54 +12:00
// 1. If value is given and there is a tuple whose name is name and value is value in this’ s list, then return true.
if ( value . has_value ( ) ) {
if ( ! m_list . find_if ( [ & name , & value ] ( auto & entry ) {
return entry . name = = name & & entry . value = = value . value ( ) ;
} )
2024-12-27 18:47:33 -05:00
. is_end ( ) ) {
2024-08-10 23:52:54 +12:00
return true ;
}
}
// 2. If value is not given and there is a tuple whose name is name in this’ s list, then return true.
else {
if ( ! m_list . find_if ( [ & name ] ( auto & entry ) {
return entry . name = = name ;
} )
2024-12-27 18:47:33 -05:00
. is_end ( ) ) {
2024-08-10 23:52:54 +12:00
return true ;
}
}
// 3. Return false.
return false ;
2021-09-13 01:00:47 +03:00
}
2024-08-11 00:24:54 +12:00
void URLSearchParams : : set ( String const & name , String const & value )
2021-09-13 01:00:47 +03:00
{
// 1. If this’ s list contains any name-value pairs whose name is name, then set the value of the first such name-value pair to value and remove the others.
auto existing = m_list . find_if ( [ & name ] ( auto & entry ) {
return entry . name = = name ;
} ) ;
if ( ! existing . is_end ( ) ) {
existing - > value = value ;
m_list . remove_all_matching ( [ & name , & existing ] ( auto & entry ) {
2021-09-12 21:12:21 -07:00
return & entry ! = & * existing & & entry . name = = name ;
2021-09-13 01:00:47 +03:00
} ) ;
}
// 2. Otherwise, append a new name-value pair whose name is name and value is value, to this’ s list.
else {
2024-08-11 00:24:54 +12:00
m_list . empend ( name , value ) ;
2021-09-13 01:00:47 +03:00
}
2023-03-01 20:10:01 +01:00
2024-08-11 00:24:54 +12:00
// 3. Update this.
update ( ) ;
2021-09-13 01:00:47 +03:00
}
2024-08-15 20:40:10 +12:00
// https://url.spec.whatwg.org/#dom-urlsearchparams-sort
2024-08-11 00:24:54 +12:00
void URLSearchParams : : sort ( )
2021-09-13 01:00:47 +03:00
{
2025-05-12 20:01:56 +12:00
// 1. Set this’ s list to the result of sorting in ascending order this’ s list, with a being less than b if a’ s name is code unit less than b’ s name.
2024-08-11 11:27:18 +12:00
insertion_sort ( m_list , [ ] ( auto & a , auto & b ) {
2025-05-12 20:01:56 +12:00
return Infra : : code_unit_less_than ( a . name , b . name ) ;
2021-09-13 01:00:47 +03:00
} ) ;
2023-03-01 20:10:01 +01:00
2024-08-11 00:24:54 +12:00
// 2. Update this.
update ( ) ;
2021-09-13 01:00:47 +03:00
}
2024-08-11 00:24:54 +12:00
String URLSearchParams : : to_string ( ) const
2021-09-13 01:00:47 +03:00
{
// return the serialization of this’ s list.
2024-08-11 00:24:54 +12:00
return url_encode ( m_list ) ;
2021-09-13 01:00:47 +03:00
}
2021-10-31 10:03:29 -04:00
JS : : ThrowCompletionOr < void > URLSearchParams : : for_each ( ForEachCallback callback )
2021-09-28 02:11:55 +03:00
{
for ( auto i = 0u ; i < m_list . size ( ) ; + + i ) {
auto & query_param = m_list [ i ] ; // We are explicitly iterating over the indices here as the callback might delete items from the list
2021-10-31 10:03:29 -04:00
TRY ( callback ( query_param . name , query_param . value ) ) ;
2021-09-28 02:11:55 +03:00
}
2021-10-31 10:03:29 -04:00
return { } ;
2021-09-28 02:11:55 +03:00
}
2021-09-13 01:00:47 +03:00
}