From 9da09e4fd30a223103d45b040719eff171dea272 Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Wed, 1 Mar 2023 20:10:01 +0100 Subject: [PATCH] LibWeb: Port URL and URLSearchParams to new String --- Userland/Libraries/LibWeb/Fetch/BodyInit.cpp | 3 +- .../LibWeb/HTML/HTMLButtonElement.cpp | 2 +- .../Libraries/LibWeb/HTML/HTMLFormElement.cpp | 44 +++--- .../Libraries/LibWeb/HTML/HTMLFormElement.h | 4 +- .../LibWeb/HTML/HTMLInputElement.cpp | 14 +- .../Libraries/LibWeb/HTML/HTMLInputElement.h | 2 +- Userland/Libraries/LibWeb/URL/URL.cpp | 137 +++++++++++------- Userland/Libraries/LibWeb/URL/URL.h | 48 +++--- Userland/Libraries/LibWeb/URL/URL.idl | 2 +- .../Libraries/LibWeb/URL/URLSearchParams.cpp | 98 ++++++++----- .../Libraries/LibWeb/URL/URLSearchParams.h | 30 ++-- .../Libraries/LibWeb/URL/URLSearchParams.idl | 2 +- 12 files changed, 229 insertions(+), 157 deletions(-) diff --git a/Userland/Libraries/LibWeb/Fetch/BodyInit.cpp b/Userland/Libraries/LibWeb/Fetch/BodyInit.cpp index f70ad7df9c..fa60c917bb 100644 --- a/Userland/Libraries/LibWeb/Fetch/BodyInit.cpp +++ b/Userland/Libraries/LibWeb/Fetch/BodyInit.cpp @@ -90,7 +90,8 @@ WebIDL::ExceptionOr extract_body(JS::Realm& realm, }, [&](JS::Handle const& url_search_params) -> WebIDL::ExceptionOr { // Set source to the result of running the application/x-www-form-urlencoded serializer with object’s list. - source = url_search_params->to_deprecated_string().to_byte_buffer(); + 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 {}; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp index 6dacc24ac6..0847d8e7da 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp @@ -32,7 +32,7 @@ HTMLButtonElement::HTMLButtonElement(DOM::Document& document, DOM::QualifiedName case TypeAttributeState::Submit: // Submit Button // Submit element's form owner from element. - form()->submit_form(this); + form()->submit_form(this).release_value_but_fixme_should_propagate_errors(); break; case TypeAttributeState::Reset: // Reset Button diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp index a5015c7d1c..3314dc6fc6 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp @@ -47,21 +47,21 @@ void HTMLFormElement::visit_edges(Cell::Visitor& visitor) visitor.visit(element.ptr()); } -void HTMLFormElement::submit_form(JS::GCPtr submitter, bool from_submit_binding) +ErrorOr HTMLFormElement::submit_form(JS::GCPtr submitter, bool from_submit_binding) { if (cannot_navigate()) - return; + return {}; if (action().is_null()) { dbgln("Unsupported form action ''"); - return; + return {}; } auto effective_method = method().to_lowercase(); if (effective_method == "dialog") { dbgln("Failed to submit form: Unsupported form method '{}'", method()); - return; + return {}; } if (effective_method != "get" && effective_method != "post") { @@ -70,7 +70,7 @@ void HTMLFormElement::submit_form(JS::GCPtr submitter, bool from_su if (!from_submit_binding) { if (m_firing_submission_events) - return; + return {}; m_firing_submission_events = true; @@ -91,51 +91,56 @@ void HTMLFormElement::submit_form(JS::GCPtr submitter, bool from_su m_firing_submission_events = false; if (!continue_) - return; + return {}; // This is checked again because arbitrary JS may have run when handling submit, // which may have changed the result. if (cannot_navigate()) - return; + return {}; } AK::URL url(document().parse_url(action())); if (!url.is_valid()) { dbgln("Failed to submit form: Invalid URL: {}", action()); - return; + return {}; } if (url.scheme() == "file") { if (document().url().scheme() != "file") { dbgln("Failed to submit form: Security violation: {} may not submit to {}", document().url(), url); - return; + return {}; } if (effective_method != "get") { dbgln("Failed to submit form: Unsupported form method '{}' for URL: {}", method(), url); - return; + return {}; } } else if (url.scheme() != "http" && url.scheme() != "https") { dbgln("Failed to submit form: Unsupported protocol for URL: {}", url); - return; + return {}; } Vector parameters; for_each_in_inclusive_subtree_of_type([&](auto& input) { - if (!input.name().is_null() && (input.type() != "submit" || &input == submitter)) - parameters.append({ input.name(), input.value() }); + if (!input.name().is_null() && (input.type() != "submit" || &input == submitter)) { + auto name = String::from_deprecated_string(input.name()).release_value_but_fixme_should_propagate_errors(); + auto value = String::from_deprecated_string(input.value()).release_value_but_fixme_should_propagate_errors(); + parameters.append({ move(name), move(value) }); + } return IterationDecision::Continue; }); if (effective_method == "get") { - url.set_query(url_encode(parameters, AK::URL::PercentEncodeSet::ApplicationXWWWFormUrlencoded)); + auto url_encoded_parameters = TRY(url_encode(parameters, AK::URL::PercentEncodeSet::ApplicationXWWWFormUrlencoded)).to_deprecated_string(); + url.set_query(move(url_encoded_parameters)); } LoadRequest request = LoadRequest::create_for_url_on_page(url, document().page()); if (effective_method == "post") { - auto body = url_encode(parameters, AK::URL::PercentEncodeSet::ApplicationXWWWFormUrlencoded).to_byte_buffer(); + auto url_encoded_parameters_as_bytes = TRY(url_encode(parameters, AK::URL::PercentEncodeSet::ApplicationXWWWFormUrlencoded)).bytes(); + auto body = TRY(ByteBuffer::copy(url_encoded_parameters_as_bytes)); request.set_method("POST"); request.set_header("Content-Type", "application/x-www-form-urlencoded"); request.set_body(move(body)); @@ -143,6 +148,8 @@ void HTMLFormElement::submit_form(JS::GCPtr submitter, bool from_su if (auto* page = document().page()) page->load(request); + + return {}; } // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#resetting-a-form @@ -166,9 +173,12 @@ void HTMLFormElement::reset_form() } } -void HTMLFormElement::submit() +WebIDL::ExceptionOr HTMLFormElement::submit() { - submit_form(this, true); + auto& vm = realm().vm(); + + TRY_OR_THROW_OOM(vm, submit_form(this, true)); + return {}; } // https://html.spec.whatwg.org/multipage/forms.html#dom-form-reset diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h index 21d828dea5..fec8369c0e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h @@ -22,12 +22,12 @@ public: DeprecatedString action() const; DeprecatedString method() const { return attribute(HTML::AttributeNames::method); } - void submit_form(JS::GCPtr submitter, bool from_submit_binding = false); + ErrorOr submit_form(JS::GCPtr submitter, bool from_submit_binding = false); void reset_form(); // NOTE: This is for the JS bindings. Use submit_form instead. - void submit(); + WebIDL::ExceptionOr submit(); // NOTE: This is for the JS bindings. Use submit_form instead. void reset(); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 450862f317..7c8fbaff92 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -35,7 +35,7 @@ HTMLInputElement::HTMLInputElement(DOM::Document& document, DOM::QualifiedName q // FIXME: 1. If this element is not mutable and is not in the Checkbox state and is not in the Radio state, then return. // 2. Run this element's input activation behavior, if any, and do nothing otherwise. - run_input_activation_behavior(); + run_input_activation_behavior().release_value_but_fixme_should_propagate_errors(); }; } @@ -217,12 +217,12 @@ WebIDL::ExceptionOr HTMLInputElement::show_picker() } // https://html.spec.whatwg.org/multipage/input.html#input-activation-behavior -void HTMLInputElement::run_input_activation_behavior() +ErrorOr HTMLInputElement::run_input_activation_behavior() { if (type_state() == TypeAttributeState::Checkbox || type_state() == TypeAttributeState::RadioButton) { // 1. If the element is not connected, then return. if (!is_connected()) - return; + return {}; // 2. Fire an event named input at the element with the bubbles and composed attributes initialized to true. auto input_event = DOM::Event::create(realm(), HTML::EventNames::input).release_value_but_fixme_should_propagate_errors(); @@ -238,19 +238,21 @@ void HTMLInputElement::run_input_activation_behavior() JS::GCPtr form; // 1. If the element does not have a form owner, then return. if (!(form = this->form())) - return; + return {}; // 2. If the element's node document is not fully active, then return. if (!document().is_fully_active()) - return; + return {}; // 3. Submit the form owner from the element. - form->submit_form(this); + TRY(form->submit_form(this)); } else if (type_state() == TypeAttributeState::FileUpload) { show_the_picker_if_applicable(*this); } else { dispatch_event(DOM::Event::create(realm(), EventNames::change).release_value_but_fixme_should_propagate_errors()); } + + return {}; } void HTMLInputElement::did_edit_text_node(Badge) diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index 41a82ce5ce..ee916ff5c5 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -139,7 +139,7 @@ private: static TypeAttributeState parse_type_attribute(StringView); void create_shadow_tree_if_needed(); - void run_input_activation_behavior(); + ErrorOr run_input_activation_behavior(); void set_checked_within_group(); // https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm diff --git a/Userland/Libraries/LibWeb/URL/URL.cpp b/Userland/Libraries/LibWeb/URL/URL.cpp index 334d9698ea..e575888c42 100644 --- a/Userland/Libraries/LibWeb/URL/URL.cpp +++ b/Userland/Libraries/LibWeb/URL/URL.cpp @@ -18,14 +18,16 @@ WebIDL::ExceptionOr> URL::create(JS::Realm& realm, AK::URL return MUST_OR_THROW_OOM(realm.heap().allocate(realm, realm, move(url), move(query))); } -WebIDL::ExceptionOr> URL::construct_impl(JS::Realm& realm, DeprecatedString const& url, DeprecatedString const& base) +WebIDL::ExceptionOr> URL::construct_impl(JS::Realm& realm, String const& url, Optional const& base) { + auto& vm = realm.vm(); + // 1. Let parsedBase be null. Optional parsed_base; // 2. If base is given, then: - if (!base.is_null()) { + if (base.has_value()) { // 1. Let parsedBase be the result of running the basic URL parser on base. - parsed_base = base; + parsed_base = base.value(); // 2. If parsedBase is failure, then throw a TypeError. if (!parsed_base->is_valid()) return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid base URL"sv }; @@ -40,7 +42,7 @@ WebIDL::ExceptionOr> URL::construct_impl(JS::Realm& realm, if (!parsed_url.is_valid()) return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid URL"sv }; // 5. Let query be parsedURL’s query, if that is non-null, and the empty string otherwise. - auto& query = parsed_url.query().is_null() ? DeprecatedString::empty() : parsed_url.query(); + auto query = parsed_url.query().is_null() ? String {} : TRY_OR_THROW_OOM(vm, String::from_deprecated_string(parsed_url.query())); // 6. Set this’s URL to parsedURL. // 7. Set this’s query object to a new URLSearchParams object. auto query_object = MUST(URLSearchParams::construct_impl(realm, query)); @@ -75,20 +77,26 @@ void URL::visit_edges(Cell::Visitor& visitor) visitor.visit(m_query.ptr()); } -DeprecatedString URL::href() const +WebIDL::ExceptionOr URL::href() const { + auto& vm = realm().vm(); + // return the serialization of this’s URL. - return m_url.serialize(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize())); } -DeprecatedString URL::to_json() const +WebIDL::ExceptionOr URL::to_json() const { + auto& vm = realm().vm(); + // return the serialization of this’s URL. - return m_url.serialize(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize())); } -WebIDL::ExceptionOr URL::set_href(DeprecatedString const& href) +WebIDL::ExceptionOr URL::set_href(String const& href) { + auto& vm = realm().vm(); + // 1. Let parsedURL be the result of running the basic URL parser on the given value. AK::URL parsed_url = href; // 2. If parsedURL is failure, then throw a TypeError. @@ -102,37 +110,46 @@ WebIDL::ExceptionOr URL::set_href(DeprecatedString const& href) auto& query = m_url.query(); // 6. If query is non-null, then set this’s query object’s list to the result of parsing query. if (!query.is_null()) - m_query->m_list = url_decode(query); + m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(query)); return {}; } -DeprecatedString URL::origin() const +WebIDL::ExceptionOr URL::origin() const { + auto& vm = realm().vm(); + // return the serialization of this’s URL’s origin. - return m_url.serialize_origin(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize_origin())); } -DeprecatedString URL::protocol() const +WebIDL::ExceptionOr URL::protocol() const { + auto& vm = realm().vm(); + // return this’s URL’s scheme, followed by U+003A (:). - return DeprecatedString::formatted("{}:", m_url.scheme()); + return TRY_OR_THROW_OOM(vm, String::formatted("{}:", m_url.scheme())); } -void URL::set_protocol(DeprecatedString const& protocol) +WebIDL::ExceptionOr URL::set_protocol(String const& protocol) { + auto& vm = realm().vm(); + // basic URL parse the given value, followed by U+003A (:), with this’s URL as url and scheme start state as state override. - auto result_url = URLParser::parse(DeprecatedString::formatted("{}:", protocol), nullptr, m_url, URLParser::State::SchemeStart); + auto result_url = URLParser::parse(TRY_OR_THROW_OOM(vm, String::formatted("{}:", protocol)), nullptr, m_url, URLParser::State::SchemeStart); if (result_url.is_valid()) m_url = move(result_url); + return {}; } -DeprecatedString URL::username() const +WebIDL::ExceptionOr URL::username() const { + auto& vm = realm().vm(); + // return this’s URL’s username. - return m_url.username(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.username())); } -void URL::set_username(DeprecatedString const& username) +void URL::set_username(String const& username) { // 1. If this’s URL cannot have a username/password/port, then return. if (m_url.cannot_have_a_username_or_password_or_port()) @@ -141,13 +158,15 @@ void URL::set_username(DeprecatedString const& username) m_url.set_username(AK::URL::percent_encode(username, AK::URL::PercentEncodeSet::Userinfo)); } -DeprecatedString URL::password() const +WebIDL::ExceptionOr URL::password() const { + auto& vm = realm().vm(); + // return this’s URL’s password. - return m_url.password(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.password())); } -void URL::set_password(DeprecatedString const& password) +void URL::set_password(String const& password) { // 1. If this’s URL cannot have a username/password/port, then return. if (m_url.cannot_have_a_username_or_password_or_port()) @@ -156,21 +175,23 @@ void URL::set_password(DeprecatedString const& password) m_url.set_password(AK::URL::percent_encode(password, AK::URL::PercentEncodeSet::Userinfo)); } -DeprecatedString URL::host() const +WebIDL::ExceptionOr URL::host() const { + auto& vm = realm().vm(); + // 1. Let url be this’s URL. auto& url = m_url; // 2. If url’s host is null, then return the empty string. if (url.host().is_null()) - return DeprecatedString::empty(); + return String {}; // 3. If url’s port is null, return url’s host, serialized. if (!url.port().has_value()) - return url.host(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host())); // 4. Return url’s host, serialized, followed by U+003A (:) and url’s port, serialized. - return DeprecatedString::formatted("{}:{}", url.host(), *url.port()); + return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host(), *url.port())); } -void URL::set_host(DeprecatedString const& host) +void URL::set_host(String const& host) { // 1. If this’s URL’s cannot-be-a-base-URL is true, then return. if (m_url.cannot_be_a_base_url()) @@ -181,16 +202,18 @@ void URL::set_host(DeprecatedString const& host) m_url = move(result_url); } -DeprecatedString URL::hostname() const +WebIDL::ExceptionOr URL::hostname() const { + auto& vm = realm().vm(); + // 1. If this’s URL’s host is null, then return the empty string. if (m_url.host().is_null()) - return DeprecatedString::empty(); + return String {}; // 2. Return this’s URL’s host, serialized. - return m_url.host(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.host())); } -void URL::set_hostname(DeprecatedString const& hostname) +void URL::set_hostname(String const& hostname) { // 1. If this’s URL’s cannot-be-a-base-URL is true, then return. if (m_url.cannot_be_a_base_url()) @@ -201,17 +224,19 @@ void URL::set_hostname(DeprecatedString const& hostname) m_url = move(result_url); } -DeprecatedString URL::port() const +WebIDL::ExceptionOr URL::port() const { + auto& vm = realm().vm(); + // 1. If this’s URL’s port is null, then return the empty string. if (!m_url.port().has_value()) - return {}; + return String {}; // 2. Return this’s URL’s port, serialized. - return DeprecatedString::formatted("{}", *m_url.port()); + return TRY_OR_THROW_OOM(vm, String::formatted("{}", *m_url.port())); } -void URL::set_port(DeprecatedString const& port) +void URL::set_port(String const& port) { // 1. If this’s URL cannot have a username/password/port, then return. if (m_url.cannot_have_a_username_or_password_or_port()) @@ -229,15 +254,17 @@ void URL::set_port(DeprecatedString const& port) m_url = move(result_url); } -DeprecatedString URL::pathname() const +WebIDL::ExceptionOr URL::pathname() const { + auto& vm = realm().vm(); + // 1. If this’s URL’s cannot-be-a-base-URL is true, then return this’s URL’s path[0]. // 2. If this’s URL’s path is empty, then return the empty string. // 3. Return U+002F (/), followed by the strings in this’s URL’s path (including empty strings), if any, separated from each other by U+002F (/). - return m_url.path(); + return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.path())); } -void URL::set_pathname(DeprecatedString const& pathname) +void URL::set_pathname(String const& pathname) { // 1. If this’s URL’s cannot-be-a-base-URL is true, then return. if (m_url.cannot_be_a_base_url()) @@ -251,27 +278,32 @@ void URL::set_pathname(DeprecatedString const& pathname) m_url = move(result_url); } -DeprecatedString URL::search() const +WebIDL::ExceptionOr URL::search() const { + auto& vm = realm().vm(); + // 1. If this’s URL’s query is either null or the empty string, then return the empty string. if (m_url.query().is_null() || m_url.query().is_empty()) - return DeprecatedString::empty(); + return String {}; // 2. Return U+003F (?), followed by this’s URL’s query. - return DeprecatedString::formatted("?{}", m_url.query()); + return TRY_OR_THROW_OOM(vm, String::formatted("?{}", m_url.query())); } -void URL::set_search(DeprecatedString const& search) +WebIDL::ExceptionOr URL::set_search(String const& search) { + auto& vm = realm().vm(); + // 1. Let url be this’s URL. auto& url = m_url; // If the given value is the empty string, set url’s query to null, empty this’s query object’s list, and then return. if (search.is_empty()) { url.set_query({}); m_query->m_list.clear(); - return; + return {}; } // 2. Let input be the given value with a single leading U+003F (?) removed, if any. - auto input = search.substring_view(search.starts_with('?')); + auto search_as_string_view = search.bytes_as_string_view(); + auto input = search_as_string_view.substring_view(search_as_string_view.starts_with('?')); // 3. Set url’s query to the empty string. auto url_copy = url; // We copy the URL here to follow other browser's behaviour of reverting the search change if the parse failed. url_copy.set_query(DeprecatedString::empty()); @@ -280,8 +312,10 @@ void URL::set_search(DeprecatedString const& search) if (result_url.is_valid()) { m_url = move(result_url); // 5. Set this’s query object’s list to the result of parsing input. - m_query->m_list = url_decode(input); + m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(input)); } + + return {}; } URLSearchParams const* URL::search_params() const @@ -289,16 +323,18 @@ URLSearchParams const* URL::search_params() const return m_query; } -DeprecatedString URL::hash() const +WebIDL::ExceptionOr URL::hash() const { + auto& vm = realm().vm(); + // 1. If this’s URL’s fragment is either null or the empty string, then return the empty string. if (m_url.fragment().is_null() || m_url.fragment().is_empty()) - return DeprecatedString::empty(); + return String {}; // 2. Return U+0023 (#), followed by this’s URL’s fragment. - return DeprecatedString::formatted("#{}", m_url.fragment()); + return TRY_OR_THROW_OOM(vm, String::formatted("#{}", m_url.fragment())); } -void URL::set_hash(DeprecatedString const& hash) +void URL::set_hash(String const& hash) { // 1. If the given value is the empty string, then set this’s URL’s fragment to null and return. if (hash.is_empty()) { @@ -306,7 +342,8 @@ void URL::set_hash(DeprecatedString const& hash) return; } // 2. Let input be the given value with a single leading U+0023 (#) removed, if any. - auto input = hash.substring_view(hash.starts_with('#')); + auto hash_as_string_view = hash.bytes_as_string_view(); + auto input = hash_as_string_view.substring_view(hash_as_string_view.starts_with('#')); // 3. Set this’s URL’s fragment to the empty string. auto url = m_url; // We copy the URL here to follow other browser's behaviour of reverting the hash change if the parse failed. url.set_fragment(DeprecatedString::empty()); diff --git a/Userland/Libraries/LibWeb/URL/URL.h b/Userland/Libraries/LibWeb/URL/URL.h index 4ed9d98df2..e290e32990 100644 --- a/Userland/Libraries/LibWeb/URL/URL.h +++ b/Userland/Libraries/LibWeb/URL/URL.h @@ -20,47 +20,47 @@ class URL : public Bindings::PlatformObject { public: static WebIDL::ExceptionOr> create(JS::Realm&, AK::URL url, JS::NonnullGCPtr query); - static WebIDL::ExceptionOr> construct_impl(JS::Realm&, DeprecatedString const& url, DeprecatedString const& base); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, String const& url, Optional const& base = {}); virtual ~URL() override; - DeprecatedString href() const; - WebIDL::ExceptionOr set_href(DeprecatedString const&); + WebIDL::ExceptionOr href() const; + WebIDL::ExceptionOr set_href(String const&); - DeprecatedString origin() const; + WebIDL::ExceptionOr origin() const; - DeprecatedString protocol() const; - void set_protocol(DeprecatedString const&); + WebIDL::ExceptionOr protocol() const; + WebIDL::ExceptionOr set_protocol(String const&); - DeprecatedString username() const; - void set_username(DeprecatedString const&); + WebIDL::ExceptionOr username() const; + void set_username(String const&); - DeprecatedString password() const; - void set_password(DeprecatedString const&); + WebIDL::ExceptionOr password() const; + void set_password(String const&); - DeprecatedString host() const; - void set_host(DeprecatedString const&); + WebIDL::ExceptionOr host() const; + void set_host(String const&); - DeprecatedString hostname() const; - void set_hostname(DeprecatedString const&); + WebIDL::ExceptionOr hostname() const; + void set_hostname(String const&); - DeprecatedString port() const; - void set_port(DeprecatedString const&); + WebIDL::ExceptionOr port() const; + void set_port(String const&); - DeprecatedString pathname() const; - void set_pathname(DeprecatedString const&); + WebIDL::ExceptionOr pathname() const; + void set_pathname(String const&); - DeprecatedString search() const; - void set_search(DeprecatedString const&); + WebIDL::ExceptionOr search() const; + WebIDL::ExceptionOr set_search(String const&); URLSearchParams const* search_params() const; - DeprecatedString hash() const; - void set_hash(DeprecatedString const&); + WebIDL::ExceptionOr hash() const; + void set_hash(String const&); - DeprecatedString to_json() const; + WebIDL::ExceptionOr to_json() const; - void set_query(Badge, DeprecatedString query) { m_url.set_query(move(query)); } + void set_query(Badge, String query) { m_url.set_query(query.to_deprecated_string()); } private: URL(JS::Realm&, AK::URL, JS::NonnullGCPtr query); diff --git a/Userland/Libraries/LibWeb/URL/URL.idl b/Userland/Libraries/LibWeb/URL/URL.idl index 47b64bee7c..d149a998e9 100644 --- a/Userland/Libraries/LibWeb/URL/URL.idl +++ b/Userland/Libraries/LibWeb/URL/URL.idl @@ -1,7 +1,7 @@ #import // https://url.spec.whatwg.org/#url -[Exposed=*, LegacyWindowAlias=webkitURL] +[Exposed=*, LegacyWindowAlias=webkitURL, UseNewAKString] interface URL { constructor(USVString url, optional USVString base); diff --git a/Userland/Libraries/LibWeb/URL/URLSearchParams.cpp b/Userland/Libraries/LibWeb/URL/URLSearchParams.cpp index 73ab3dc379..38b3b1294c 100644 --- a/Userland/Libraries/LibWeb/URL/URLSearchParams.cpp +++ b/Userland/Libraries/LibWeb/URL/URLSearchParams.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -35,20 +36,20 @@ void URLSearchParams::visit_edges(Cell::Visitor& visitor) visitor.visit(m_url); } -DeprecatedString url_encode(Vector const& pairs, AK::URL::PercentEncodeSet percent_encode_set) +ErrorOr url_encode(Vector const& pairs, AK::URL::PercentEncodeSet percent_encode_set) { StringBuilder builder; for (size_t i = 0; i < pairs.size(); ++i) { - builder.append(AK::URL::percent_encode(pairs[i].name, percent_encode_set, AK::URL::SpaceAsPlus::Yes)); - builder.append('='); - builder.append(AK::URL::percent_encode(pairs[i].value, percent_encode_set, AK::URL::SpaceAsPlus::Yes)); + TRY(builder.try_append(AK::URL::percent_encode(pairs[i].name, percent_encode_set, AK::URL::SpaceAsPlus::Yes))); + TRY(builder.try_append('=')); + TRY(builder.try_append(AK::URL::percent_encode(pairs[i].value, percent_encode_set, AK::URL::SpaceAsPlus::Yes))); if (i != pairs.size() - 1) - builder.append('&'); + TRY(builder.try_append('&')); } - return builder.to_deprecated_string(); + return builder.to_string(); } -Vector url_decode(StringView input) +ErrorOr> url_decode(StringView input) { // 1. Let sequences be the result of splitting input on 0x26 (&). auto sequences = input.split_view('&'); @@ -80,10 +81,10 @@ Vector url_decode(StringView input) auto space_decoded_name = name.replace("+"sv, " "sv, ReplaceMode::All); // 5. Let nameString and valueString be the result of running UTF-8 decode without BOM on the percent-decoding of name and value, respectively. - auto name_string = AK::URL::percent_decode(space_decoded_name); - auto value_string = AK::URL::percent_decode(value); + auto name_string = TRY(String::from_deprecated_string(AK::URL::percent_decode(space_decoded_name))); + auto value_string = TRY(String::from_deprecated_string(AK::URL::percent_decode(value))); - output.empend(move(name_string), move(value_string)); + TRY(output.try_empend(move(name_string), move(value_string))); } return output; @@ -96,8 +97,10 @@ WebIDL::ExceptionOr> URLSearchParams::create(J // https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams // https://url.spec.whatwg.org/#urlsearchparams-initialize -WebIDL::ExceptionOr> URLSearchParams::construct_impl(JS::Realm& realm, Variant>, OrderedHashMap, DeprecatedString> const& init) +WebIDL::ExceptionOr> URLSearchParams::construct_impl(JS::Realm& realm, Variant>, OrderedHashMap, String> const& init) { + auto& vm = realm.vm(); + // 1. If init is a string and starts with U+003F (?), then remove the first code point from init. // NOTE: We do this when we know that it's a string on step 3 of initialization. @@ -106,8 +109,8 @@ WebIDL::ExceptionOr> URLSearchParams::construc // URLSearchParams init from this point forward // 1. If init is a sequence, then for each pair in init: - if (init.has>>()) { - auto const& init_sequence = init.get>>(); + if (init.has>>()) { + auto const& init_sequence = init.get>>(); Vector list; list.ensure_capacity(init_sequence.size()); @@ -125,8 +128,8 @@ WebIDL::ExceptionOr> URLSearchParams::construc } // 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. - if (init.has>()) { - auto const& init_record = init.get>(); + if (init.has>()) { + auto const& init_record = init.get>(); Vector list; list.ensure_capacity(init_record.size()); @@ -140,13 +143,14 @@ WebIDL::ExceptionOr> URLSearchParams::construc // 3. Otherwise: // a. Assert: init is a string. // NOTE: `get` performs `VERIFY(has())` - auto const& init_string = init.get(); + auto const& init_string = init.get(); // See NOTE at the start of this function. - StringView stripped_init = init_string.substring_view(init_string.starts_with('?')); + auto init_string_view = init_string.bytes_as_string_view(); + auto stripped_init = init_string_view.substring_view(init_string_view.starts_with('?')); // b. Set query’s list to the result of parsing init. - return URLSearchParams::create(realm, url_decode(stripped_init)); + return URLSearchParams::create(realm, TRY_OR_THROW_OOM(vm, url_decode(stripped_init))); } // https://url.spec.whatwg.org/#dom-urlsearchparams-size @@ -156,39 +160,47 @@ size_t URLSearchParams::size() const return m_list.size(); } -void URLSearchParams::append(DeprecatedString const& name, DeprecatedString const& value) +WebIDL::ExceptionOr URLSearchParams::append(String const& name, String const& value) { + auto& vm = realm().vm(); + // 1. Append a new name-value pair whose name is name and value is value, to list. - m_list.empend(name, value); + TRY_OR_THROW_OOM(vm, m_list.try_empend(name, value)); // 2. Update this. - update(); + TRY(update()); + + return {}; } -void URLSearchParams::update() +WebIDL::ExceptionOr URLSearchParams::update() { // 1. If query’s URL object is null, then return. if (!m_url) - return; + return {}; // 2. Let serializedQuery be the serialization of query’s list. - auto serialized_query = to_deprecated_string(); + auto serialized_query = TRY(to_string()); // 3. If serializedQuery is the empty string, then set serializedQuery to null. if (serialized_query.is_empty()) serialized_query = {}; // 4. Set query’s URL object’s URL’s query to serializedQuery. m_url->set_query({}, move(serialized_query)); + + return {}; } -void URLSearchParams::delete_(DeprecatedString const& name) +WebIDL::ExceptionOr 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(); + TRY(update()); + + return {}; } -DeprecatedString URLSearchParams::get(DeprecatedString const& name) +Optional 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) { @@ -200,18 +212,20 @@ DeprecatedString URLSearchParams::get(DeprecatedString const& name) } // https://url.spec.whatwg.org/#dom-urlsearchparams-getall -Vector URLSearchParams::get_all(DeprecatedString const& name) +WebIDL::ExceptionOr> URLSearchParams::get_all(String const& name) { + auto& vm = realm().vm(); + // return the values of all name-value pairs whose name is name, in this’s list, in list order, and the empty sequence otherwise. - Vector values; + Vector values; for (auto& entry : m_list) { if (entry.name == name) - values.append(entry.value); + TRY_OR_THROW_OOM(vm, values.try_append(entry.value)); } return values; } -bool URLSearchParams::has(DeprecatedString const& name) +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) { @@ -220,8 +234,10 @@ bool URLSearchParams::has(DeprecatedString const& name) .is_end(); } -void URLSearchParams::set(DeprecatedString const& name, DeprecatedString const& value) +WebIDL::ExceptionOr URLSearchParams::set(String const& name, String const& value) { + auto& vm = realm().vm(); + // 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; @@ -234,13 +250,15 @@ void URLSearchParams::set(DeprecatedString const& name, DeprecatedString const& } // 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); + TRY_OR_THROW_OOM(vm, m_list.try_empend(name, value)); } // 3. Update this. - update(); + TRY(update()); + + return {}; } -void URLSearchParams::sort() +WebIDL::ExceptionOr 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) { @@ -266,13 +284,17 @@ void URLSearchParams::sort() VERIFY_NOT_REACHED(); }); // 2. Update this. - update(); + TRY(update()); + + return {}; } -DeprecatedString URLSearchParams::to_deprecated_string() const +WebIDL::ExceptionOr URLSearchParams::to_string() const { + auto& vm = realm().vm(); + // return the serialization of this’s list. - return url_encode(m_list, AK::URL::PercentEncodeSet::ApplicationXWWWFormUrlencoded); + return TRY_OR_THROW_OOM(vm, url_encode(m_list, AK::URL::PercentEncodeSet::ApplicationXWWWFormUrlencoded)); } JS::ThrowCompletionOr URLSearchParams::for_each(ForEachCallback callback) diff --git a/Userland/Libraries/LibWeb/URL/URLSearchParams.h b/Userland/Libraries/LibWeb/URL/URLSearchParams.h index bdc7755413..5d0d45c402 100644 --- a/Userland/Libraries/LibWeb/URL/URLSearchParams.h +++ b/Userland/Libraries/LibWeb/URL/URLSearchParams.h @@ -13,34 +13,34 @@ namespace Web::URL { struct QueryParam { - DeprecatedString name; - DeprecatedString value; + String name; + String value; }; -DeprecatedString url_encode(Vector const&, AK::URL::PercentEncodeSet); -Vector url_decode(StringView); +ErrorOr url_encode(Vector const&, AK::URL::PercentEncodeSet); +ErrorOr> url_decode(StringView); class URLSearchParams : public Bindings::PlatformObject { WEB_PLATFORM_OBJECT(URLSearchParams, Bindings::PlatformObject); public: static WebIDL::ExceptionOr> create(JS::Realm&, Vector list); - static WebIDL::ExceptionOr> construct_impl(JS::Realm&, Variant>, OrderedHashMap, DeprecatedString> const& init); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, Variant>, OrderedHashMap, String> const& init); virtual ~URLSearchParams() override; size_t size() const; - void append(DeprecatedString const& name, DeprecatedString const& value); - void delete_(DeprecatedString const& name); - DeprecatedString get(DeprecatedString const& name); - Vector get_all(DeprecatedString const& name); - bool has(DeprecatedString const& name); - void set(DeprecatedString const& name, DeprecatedString const& value); + WebIDL::ExceptionOr append(String const& name, String const& value); + WebIDL::ExceptionOr delete_(String const& name); + Optional get(String const& name); + WebIDL::ExceptionOr> get_all(String const& name); + bool has(String const& name); + WebIDL::ExceptionOr set(String const& name, String const& value); - void sort(); + WebIDL::ExceptionOr sort(); - DeprecatedString to_deprecated_string() const; + WebIDL::ExceptionOr to_string() const; - using ForEachCallback = Function(DeprecatedString const&, DeprecatedString const&)>; + using ForEachCallback = Function(String const&, String const&)>; JS::ThrowCompletionOr for_each(ForEachCallback); private: @@ -52,7 +52,7 @@ private: virtual JS::ThrowCompletionOr initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; - void update(); + WebIDL::ExceptionOr update(); Vector m_list; JS::GCPtr m_url; diff --git a/Userland/Libraries/LibWeb/URL/URLSearchParams.idl b/Userland/Libraries/LibWeb/URL/URLSearchParams.idl index 6269743ad8..f90559a954 100644 --- a/Userland/Libraries/LibWeb/URL/URLSearchParams.idl +++ b/Userland/Libraries/LibWeb/URL/URLSearchParams.idl @@ -1,5 +1,5 @@ // https://url.spec.whatwg.org/#urlsearchparams -[Exposed=*] +[Exposed=*, UseNewAKString] interface URLSearchParams { constructor(optional (sequence> or record or USVString) init = "");