diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index 4e0f1cb356..a2b24ed93f 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -227,6 +227,8 @@ #include #include #include +#include +#include #include #include #include @@ -356,6 +358,7 @@ ADD_WINDOW_OBJECT_INTERFACE(SVGSVGElement) \ ADD_WINDOW_OBJECT_INTERFACE(Text) \ ADD_WINDOW_OBJECT_INTERFACE(UIEvent) \ + ADD_WINDOW_OBJECT_INTERFACE(URLSearchParams) \ ADD_WINDOW_OBJECT_INTERFACE(WebSocket) \ ADD_WINDOW_OBJECT_INTERFACE(XMLHttpRequest) \ ADD_WINDOW_OBJECT_INTERFACE(XMLHttpRequestEventTarget) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 117f81f447..49e2164104 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -231,6 +231,7 @@ set(SOURCES UIEvents/EventNames.cpp UIEvents/MouseEvent.cpp URL/URL.cpp + URL/URLSearchParams.cpp WebAssembly/WebAssemblyInstanceConstructor.cpp WebAssembly/WebAssemblyInstanceObject.cpp WebAssembly/WebAssemblyInstanceObjectPrototype.cpp @@ -421,6 +422,7 @@ libweb_js_wrapper(UIEvents/UIEvent) libweb_js_wrapper(XHR/ProgressEvent) libweb_js_wrapper(XHR/XMLHttpRequest) libweb_js_wrapper(XHR/XMLHttpRequestEventTarget) +libweb_js_wrapper(URL/URLSearchParams) add_custom_command( OUTPUT CSS/PropertyID.h diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 1cdd5734b9..8e115e93d6 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -206,6 +206,7 @@ class XMLHttpRequestEventTarget; } namespace Web::URL { +class URLSearchParams; } namespace Web::Bindings { @@ -333,5 +334,7 @@ class XMLHttpRequestEventTargetWrapper; class RangeConstructor; class RangePrototype; class RangeWrapper; - +class URLSearchParamsConstructor; +class URLSearchParamsPrototype; +class URLSearchParamsWrapper; } diff --git a/Userland/Libraries/LibWeb/URL/URLSearchParams.cpp b/Userland/Libraries/LibWeb/URL/URLSearchParams.cpp new file mode 100644 index 0000000000..2872471069 --- /dev/null +++ b/Userland/Libraries/LibWeb/URL/URLSearchParams.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Web::URL { + +DOM::ExceptionOr> URLSearchParams::create_with_global_object(Bindings::WindowObject&, String const& init) +{ + // 1. If init is a string and starts with U+003F (?), then remove the first code point from init. + StringView stripped_init = init.substring_view(init.starts_with('?')); + // 2. Initialize this with init. + + // URLSearchParams init from this point forward + + // TODO + // 1. If init is a sequence, then for each pair in init: + // a. If pair does not contain exactly two items, then throw a TypeError. + // 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. + + // TODO + // 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. + + // 3. Otherwise: + // a. Assert: init is a string. + // b. Set query’s list to the result of parsing init. + return URLSearchParams::create(url_decode(stripped_init)); +} + +void URLSearchParams::append(String const& name, String const& value) +{ + // 1. Append a new name-value pair whose name is name and value is value, to list. + m_list.empend(name, value); + // 2. Update this. + update(); +} + +void URLSearchParams::update() +{ + // TODO + // 1. If query’s URL object is null, then return. + // 2. Let serializedQuery be the serialization of query’s list. + // 3. If serializedQuery is the empty string, then set serializedQuery to null. + // 4. Set query’s URL object’s URL’s query to serializedQuery. +} + +void URLSearchParams::delete_(String const& name) +{ + // 1. Remove all name-value pairs whose name is name from list. + m_list.remove_all_matching([&name](auto& entry) { + return entry.name == name; + }); + // 2. Update this. + update(); +} + +String URLSearchParams::get(String const& name) +{ + // 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; +} + +bool URLSearchParams::has(String const& name) +{ + // return true if there is a name-value pair whose name is name in this’s list, and false otherwise. + return !m_list.find_if([&name](auto& entry) { + return entry.name == name; + }) + .is_end(); +} + +void URLSearchParams::set(const String& name, const String& value) +{ + // 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) { + return &entry != &*existing && name == name; + }); + } + // 2. Otherwise, append a new name-value pair whose name is name and value is value, to this’s list. + else { + m_list.empend(name, value); + } + // 3. Update this. + update(); +} + +void URLSearchParams::sort() +{ + // 1. Sort all name-value pairs, if any, by their names. Sorting must be done by comparison of code units. The relative order between name-value pairs with equal names must be preserved. + quick_sort(m_list.begin(), m_list.end(), [](auto& a, auto& b) { + Utf8View a_code_points { a.name }; + Utf8View b_code_points { b.name }; + + if (a_code_points.starts_with(b_code_points)) + return false; + if (b_code_points.starts_with(a_code_points)) + return true; + + for (auto k = a_code_points.begin(), l = b_code_points.begin(); + k != a_code_points.end() && l != b_code_points.end(); + ++k, ++l) { + if (*k != *l) { + if (*k < *l) { + return true; + } else { + return false; + } + } + } + VERIFY_NOT_REACHED(); + }); + // 2. Update this. + update(); +} + +String URLSearchParams::to_string() +{ + // return the serialization of this’s list. + return url_encode(m_list, AK::URL::PercentEncodeSet::ApplicationXWWWFormUrlencoded); +} + +} diff --git a/Userland/Libraries/LibWeb/URL/URLSearchParams.h b/Userland/Libraries/LibWeb/URL/URLSearchParams.h new file mode 100644 index 0000000000..7f2a3bad53 --- /dev/null +++ b/Userland/Libraries/LibWeb/URL/URLSearchParams.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::URL { + +class URLSearchParams : public Bindings::Wrappable + , public RefCounted { +public: + using WrapperType = Bindings::URLSearchParamsWrapper; + + static NonnullRefPtr create(Vector list) + { + return adopt_ref(*new URLSearchParams(move(list))); + } + + static DOM::ExceptionOr> create_with_global_object(Bindings::WindowObject&, const String& init); + + void append(String const& name, String const& value); + void delete_(String const& name); + String get(String const& name); + bool has(String const& name); + void set(String const& name, String const& value); + + void sort(); + + String to_string(); + +private: + explicit URLSearchParams(Vector list) + : m_list(move(list)) {}; + + void update(); + + Vector m_list; +}; + +} diff --git a/Userland/Libraries/LibWeb/URL/URLSearchParams.idl b/Userland/Libraries/LibWeb/URL/URLSearchParams.idl new file mode 100644 index 0000000000..874e457280 --- /dev/null +++ b/Userland/Libraries/LibWeb/URL/URLSearchParams.idl @@ -0,0 +1,17 @@ +interface URLSearchParams { + + // FIXME: the real type of init is (sequence> or record or USVString) + constructor(optional USVString init = ""); + + undefined append(USVString name, USVString value); + undefined delete(USVString name); + USVString? get(USVString name); + // TODO: sequence getAll(USVString name); + boolean has(USVString name); + undefined set(USVString name, USVString value); + + undefined sort(); + + // TODO: iterable; + stringifier; +};