LibWeb: Don't propogate small OOMs from URLSearchParams

Made easier now that URL percent encode after encoding is also not
throwing any errors. This simplfies a bunch of error handling.
This commit is contained in:
Shannon Booth 2024-08-11 00:24:54 +12:00 committed by Tim Ledbetter
commit df4739d7ce
Notes: github-actions[bot] 2024-08-12 22:02:35 +00:00
7 changed files with 59 additions and 77 deletions

View file

@ -59,7 +59,7 @@ JS::NonnullGCPtr<DOMURL> DOMURL::initialize_a_url(JS::Realm& realm, URL::URL con
// 2. Set urls URL to urlRecord. // 2. Set urls URL to urlRecord.
// 3. Set urls query object to a new URLSearchParams object. // 3. Set urls query object to a new URLSearchParams object.
auto query_object = MUST(URLSearchParams::create(realm, query)); auto query_object = URLSearchParams::create(realm, query);
// 4. Initialize urls query object with query. // 4. Initialize urls query object with query.
auto result_url = DOMURL::create(realm, url_record, move(query_object)); auto result_url = DOMURL::create(realm, url_record, move(query_object));
@ -190,8 +190,6 @@ WebIDL::ExceptionOr<String> DOMURL::to_json() const
// https://url.spec.whatwg.org/#ref-for-dom-url-href② // https://url.spec.whatwg.org/#ref-for-dom-url-href②
WebIDL::ExceptionOr<void> DOMURL::set_href(String const& href) WebIDL::ExceptionOr<void> DOMURL::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. // 1. Let parsedURL be the result of running the basic URL parser on the given value.
URL::URL parsed_url = href; URL::URL parsed_url = href;
@ -210,7 +208,7 @@ WebIDL::ExceptionOr<void> DOMURL::set_href(String const& href)
// 6. If query is non-null, then set thiss query objects list to the result of parsing query. // 6. If query is non-null, then set thiss query objects list to the result of parsing query.
if (query.has_value()) if (query.has_value())
m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(*query)); m_query->m_list = url_decode(*query);
return {}; return {};
} }
@ -411,10 +409,8 @@ WebIDL::ExceptionOr<String> DOMURL::search() const
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-search%E2%91%A0 // https://url.spec.whatwg.org/#ref-for-dom-url-search%E2%91%A0
WebIDL::ExceptionOr<void> DOMURL::set_search(String const& search) void DOMURL::set_search(String const& search)
{ {
auto& vm = realm().vm();
// 1. Let url be thiss URL. // 1. Let url be thiss URL.
auto& url = m_url; auto& url = m_url;
@ -430,7 +426,7 @@ WebIDL::ExceptionOr<void> DOMURL::set_search(String const& search)
strip_trailing_spaces_from_an_opaque_path(*this); strip_trailing_spaces_from_an_opaque_path(*this);
// 4. Return. // 4. Return.
return {}; return;
} }
// 3. Let input be the given value with a single leading U+003F (?) removed, if any. // 3. Let input be the given value with a single leading U+003F (?) removed, if any.
@ -447,10 +443,8 @@ WebIDL::ExceptionOr<void> DOMURL::set_search(String const& search)
m_url = move(result_url); m_url = move(result_url);
// 6. Set thiss query objects list to the result of parsing input. // 6. Set thiss query objects list to the result of parsing input.
m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(input)); m_query->m_list = url_decode(input);
} }
return {};
} }
// https://url.spec.whatwg.org/#dom-url-searchparams // https://url.spec.whatwg.org/#dom-url-searchparams

View file

@ -68,7 +68,7 @@ public:
bool cannot_be_a_base_url() const { return m_url.cannot_be_a_base_url(); } bool cannot_be_a_base_url() const { return m_url.cannot_be_a_base_url(); }
WebIDL::ExceptionOr<String> search() const; WebIDL::ExceptionOr<String> search() const;
WebIDL::ExceptionOr<void> set_search(String const&); void set_search(String const&);
JS::NonnullGCPtr<URLSearchParams const> search_params() const; JS::NonnullGCPtr<URLSearchParams const> search_params() const;

View file

@ -43,7 +43,7 @@ void URLSearchParams::visit_edges(Cell::Visitor& visitor)
// https://url.spec.whatwg.org/#concept-urlencoded-serializer // 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. // 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.
ErrorOr<String> url_encode(Vector<QueryParam> const& tuples, StringView encoding) String url_encode(Vector<QueryParam> const& tuples, StringView encoding)
{ {
// 1. Set encoding to the result of getting an output encoding from encoding. // 1. Set encoding to the result of getting an output encoding from encoding.
encoding = TextCodec::get_output_encoding(encoding); encoding = TextCodec::get_output_encoding(encoding);
@ -69,21 +69,21 @@ ErrorOr<String> url_encode(Vector<QueryParam> const& tuples, StringView encoding
// 4. If output is not the empty string, then append U+0026 (&) to output. // 4. If output is not the empty string, then append U+0026 (&) to output.
if (!output.is_empty()) if (!output.is_empty())
TRY(output.try_append('&')); output.append('&');
// 5. Append name, followed by U+003D (=), followed by value, to output. // 5. Append name, followed by U+003D (=), followed by value, to output.
TRY(output.try_append(name)); output.append(name);
TRY(output.try_append('=')); output.append('=');
TRY(output.try_append(value)); output.append(value);
} }
// 4. Return output. // 4. Return output.
return output.to_string(); return MUST(output.to_string());
} }
// https://url.spec.whatwg.org/#concept-urlencoded-parser // 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: // The application/x-www-form-urlencoded parser takes a byte sequence input, and then runs these steps:
ErrorOr<Vector<QueryParam>> url_decode(StringView input) Vector<QueryParam> url_decode(StringView input)
{ {
// 1. Let sequences be the result of splitting input on 0x26 (&). // 1. Let sequences be the result of splitting input on 0x26 (&).
auto sequences = input.split_view('&'); auto sequences = input.split_view('&');
@ -119,23 +119,23 @@ ErrorOr<Vector<QueryParam>> url_decode(StringView input)
auto name_string = String::from_utf8_with_replacement_character(URL::percent_decode(space_decoded_name), String::WithBOMHandling::No); 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); auto value_string = String::from_utf8_with_replacement_character(URL::percent_decode(space_decoded_value), String::WithBOMHandling::No);
TRY(output.try_empend(move(name_string), move(value_string))); output.empend(move(name_string), move(value_string));
} }
return output; return output;
} }
WebIDL::ExceptionOr<JS::NonnullGCPtr<URLSearchParams>> URLSearchParams::create(JS::Realm& realm, Vector<QueryParam> list) JS::NonnullGCPtr<URLSearchParams> URLSearchParams::create(JS::Realm& realm, Vector<QueryParam> list)
{ {
return realm.heap().allocate<URLSearchParams>(realm, realm, move(list)); return realm.heap().allocate<URLSearchParams>(realm, realm, move(list));
} }
// https://url.spec.whatwg.org/#urlsearchparams-initialize // https://url.spec.whatwg.org/#urlsearchparams-initialize
WebIDL::ExceptionOr<JS::NonnullGCPtr<URLSearchParams>> URLSearchParams::create(JS::Realm& realm, StringView init) JS::NonnullGCPtr<URLSearchParams> URLSearchParams::create(JS::Realm& realm, StringView init)
{ {
// NOTE: We skip the other steps since we know it is a string at this point. // NOTE: We skip the other steps since we know it is a string at this point.
// b. Set querys list to the result of parsing init. // b. Set querys list to the result of parsing init.
return URLSearchParams::create(realm, MUST(url_decode(init))); return URLSearchParams::create(realm, url_decode(init));
} }
// https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams // https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams
@ -203,36 +203,35 @@ size_t URLSearchParams::size() const
return m_list.size(); return m_list.size();
} }
WebIDL::ExceptionOr<void> URLSearchParams::append(String const& name, String const& value) // https://url.spec.whatwg.org/#dom-urlsearchparams-append
void 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. // 1. Append a new name-value pair whose name is name and value is value, to list.
TRY_OR_THROW_OOM(vm, m_list.try_empend(name, value)); m_list.empend(name, value);
// 2. Update this.
TRY(update());
return {}; // 2. Update this.
update();
} }
WebIDL::ExceptionOr<void> URLSearchParams::update() void URLSearchParams::update()
{ {
// 1. If querys URL object is null, then return. // 1. If querys URL object is null, then return.
if (!m_url) if (!m_url)
return {}; return;
// 2. Let serializedQuery be the serialization of querys list. // 2. Let serializedQuery be the serialization of querys list.
auto serialized_query = TRY(to_string()); auto serialized_query = to_string();
// 3. If serializedQuery is the empty string, then set serializedQuery to null. // 3. If serializedQuery is the empty string, then set serializedQuery to null.
if (serialized_query.is_empty()) if (serialized_query.is_empty())
serialized_query = {}; serialized_query = {};
// 4. Set querys URL objects URLs query to serializedQuery. // 4. Set querys URL objects URLs query to serializedQuery.
m_url->set_query({}, move(serialized_query)); m_url->set_query({}, move(serialized_query));
return {};
} }
// https://url.spec.whatwg.org/#dom-urlsearchparams-delete // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
WebIDL::ExceptionOr<void> URLSearchParams::delete_(String const& name, Optional<String> const& value) void URLSearchParams::delete_(String const& name, Optional<String> const& value)
{ {
// 1. If value is given, then remove all tuples whose name is name and value is value from thiss list. // 1. If value is given, then remove all tuples whose name is name and value is value from thiss list.
if (value.has_value()) { if (value.has_value()) {
@ -248,9 +247,7 @@ WebIDL::ExceptionOr<void> URLSearchParams::delete_(String const& name, Optional<
} }
// 2. Update this. // 2. Update this.
TRY(update()); update();
return {};
} }
Optional<String> URLSearchParams::get(String const& name) Optional<String> URLSearchParams::get(String const& name)
@ -265,15 +262,13 @@ Optional<String> URLSearchParams::get(String const& name)
} }
// https://url.spec.whatwg.org/#dom-urlsearchparams-getall // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
WebIDL::ExceptionOr<Vector<String>> URLSearchParams::get_all(String const& name) Vector<String> URLSearchParams::get_all(String const& name)
{ {
auto& vm = realm().vm();
// return the values of all name-value pairs whose name is name, in thiss list, in list order, and the empty sequence otherwise. // return the values of all name-value pairs whose name is name, in thiss list, in list order, and the empty sequence otherwise.
Vector<String> values; Vector<String> values;
for (auto& entry : m_list) { for (auto& entry : m_list) {
if (entry.name == name) if (entry.name == name)
TRY_OR_THROW_OOM(vm, values.try_append(entry.value)); values.append(entry.value);
} }
return values; return values;
} }
@ -304,10 +299,8 @@ bool URLSearchParams::has(String const& name, Optional<String> const& value)
return false; return false;
} }
WebIDL::ExceptionOr<void> URLSearchParams::set(String const& name, String const& value) void URLSearchParams::set(String const& name, String const& value)
{ {
auto& vm = realm().vm();
// 1. If thiss 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. // 1. If thiss 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) { auto existing = m_list.find_if([&name](auto& entry) {
return entry.name == name; return entry.name == name;
@ -320,15 +313,14 @@ WebIDL::ExceptionOr<void> URLSearchParams::set(String const& name, String const&
} }
// 2. Otherwise, append a new name-value pair whose name is name and value is value, to thiss list. // 2. Otherwise, append a new name-value pair whose name is name and value is value, to thiss list.
else { else {
TRY_OR_THROW_OOM(vm, m_list.try_empend(name, value)); m_list.empend(name, value);
} }
// 3. Update this.
TRY(update());
return {}; // 3. Update this.
update();
} }
WebIDL::ExceptionOr<void> URLSearchParams::sort() 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. // 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) { quick_sort(m_list.begin(), m_list.end(), [](auto& a, auto& b) {
@ -349,18 +341,15 @@ WebIDL::ExceptionOr<void> URLSearchParams::sort()
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
}); });
// 2. Update this.
TRY(update());
return {}; // 2. Update this.
update();
} }
WebIDL::ExceptionOr<String> URLSearchParams::to_string() const String URLSearchParams::to_string() const
{ {
auto& vm = realm().vm();
// return the serialization of thiss list. // return the serialization of thiss list.
return TRY_OR_THROW_OOM(vm, url_encode(m_list)); return url_encode(m_list);
} }
JS::ThrowCompletionOr<void> URLSearchParams::for_each(ForEachCallback callback) JS::ThrowCompletionOr<void> URLSearchParams::for_each(ForEachCallback callback)

View file

@ -16,31 +16,31 @@ struct QueryParam {
String name; String name;
String value; String value;
}; };
ErrorOr<String> url_encode(Vector<QueryParam> const&, StringView encoding = "UTF-8"sv); String url_encode(Vector<QueryParam> const&, StringView encoding = "UTF-8"sv);
ErrorOr<Vector<QueryParam>> url_decode(StringView); Vector<QueryParam> url_decode(StringView);
class URLSearchParams : public Bindings::PlatformObject { class URLSearchParams : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(URLSearchParams, Bindings::PlatformObject); WEB_PLATFORM_OBJECT(URLSearchParams, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(URLSearchParams); JS_DECLARE_ALLOCATOR(URLSearchParams);
public: public:
static WebIDL::ExceptionOr<JS::NonnullGCPtr<URLSearchParams>> create(JS::Realm&, StringView); static JS::NonnullGCPtr<URLSearchParams> create(JS::Realm&, StringView);
static WebIDL::ExceptionOr<JS::NonnullGCPtr<URLSearchParams>> create(JS::Realm&, Vector<QueryParam> list); static JS::NonnullGCPtr<URLSearchParams> create(JS::Realm&, Vector<QueryParam> list);
static WebIDL::ExceptionOr<JS::NonnullGCPtr<URLSearchParams>> construct_impl(JS::Realm&, Variant<Vector<Vector<String>>, OrderedHashMap<String, String>, String> const& init); static WebIDL::ExceptionOr<JS::NonnullGCPtr<URLSearchParams>> construct_impl(JS::Realm&, Variant<Vector<Vector<String>>, OrderedHashMap<String, String>, String> const& init);
virtual ~URLSearchParams() override; virtual ~URLSearchParams() override;
size_t size() const; size_t size() const;
WebIDL::ExceptionOr<void> append(String const& name, String const& value); void append(String const& name, String const& value);
WebIDL::ExceptionOr<void> delete_(String const& name, Optional<String> const& value = {}); void delete_(String const& name, Optional<String> const& value = {});
Optional<String> get(String const& name); Optional<String> get(String const& name);
WebIDL::ExceptionOr<Vector<String>> get_all(String const& name); Vector<String> get_all(String const& name);
bool has(String const& name, Optional<String> const& value = {}); bool has(String const& name, Optional<String> const& value = {});
WebIDL::ExceptionOr<void> set(String const& name, String const& value); void set(String const& name, String const& value);
WebIDL::ExceptionOr<void> sort(); void sort();
WebIDL::ExceptionOr<String> to_string() const; String to_string() const;
using ForEachCallback = Function<JS::ThrowCompletionOr<void>(String const&, String const&)>; using ForEachCallback = Function<JS::ThrowCompletionOr<void>(String const&, String const&)>;
JS::ThrowCompletionOr<void> for_each(ForEachCallback); JS::ThrowCompletionOr<void> for_each(ForEachCallback);
@ -54,7 +54,7 @@ private:
virtual void initialize(JS::Realm&) override; virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
WebIDL::ExceptionOr<void> update(); void update();
Vector<QueryParam> m_list; Vector<QueryParam> m_list;
JS::GCPtr<DOMURL> m_url; JS::GCPtr<DOMURL> m_url;

View file

@ -149,11 +149,10 @@ WebIDL::ExceptionOr<JS::Value> package_data(JS::Realm& realm, ByteBuffer bytes,
auto entries = DOMURL::url_decode(StringView { bytes }); auto entries = DOMURL::url_decode(StringView { bytes });
// 2. If entries is failure, then throw a TypeError. // 2. If entries is failure, then throw a TypeError.
if (entries.is_error()) // FIXME: Spec bug? It doesn't seem possible to throw an error here.
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, entries.error().string_literal() };
// 3. Return a new FormData object whose entry list is entries. // 3. Return a new FormData object whose entry list is entries.
return TRY(XHR::FormData::create(realm, entries.release_value())); return TRY(XHR::FormData::create(realm, entries));
} }
// Otherwise, throw a TypeError. // Otherwise, throw a TypeError.
else { else {

View file

@ -108,7 +108,7 @@ WebIDL::ExceptionOr<Infrastructure::BodyWithType> extract_body(JS::Realm& realm,
}, },
[&](JS::Handle<DOMURL::URLSearchParams> const& url_search_params) -> WebIDL::ExceptionOr<void> { [&](JS::Handle<DOMURL::URLSearchParams> const& url_search_params) -> WebIDL::ExceptionOr<void> {
// Set source to the result of running the application/x-www-form-urlencoded serializer with objects list. // Set source to the result of running the application/x-www-form-urlencoded serializer with objects list.
auto search_params_string = TRY(url_search_params->to_string()); auto search_params_string = url_search_params->to_string();
source = MUST(ByteBuffer::copy(search_params_string.bytes())); source = MUST(ByteBuffer::copy(search_params_string.bytes()));
// Set type to `application/x-www-form-urlencoded;charset=UTF-8`. // Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
type = MUST(ByteBuffer::copy("application/x-www-form-urlencoded;charset=UTF-8"sv.bytes())); type = MUST(ByteBuffer::copy("application/x-www-form-urlencoded;charset=UTF-8"sv.bytes()));

View file

@ -731,7 +731,7 @@ ErrorOr<void> HTMLFormElement::mutate_action_url(URL::URL parsed_action, Vector<
auto pairs = TRY(convert_to_list_of_name_value_pairs(entry_list)); auto pairs = TRY(convert_to_list_of_name_value_pairs(entry_list));
// 2. Let query be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding. // 2. Let query be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
auto query = TRY(url_encode(pairs, encoding)); auto query = url_encode(pairs, encoding);
// 3. Set parsed action's query component to query. // 3. Set parsed action's query component to query.
parsed_action.set_query(query); parsed_action.set_query(query);
@ -757,7 +757,7 @@ ErrorOr<void> HTMLFormElement::submit_as_entity_body(URL::URL parsed_action, Vec
auto pairs = TRY(convert_to_list_of_name_value_pairs(entry_list)); auto pairs = TRY(convert_to_list_of_name_value_pairs(entry_list));
// 2. Let body be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding. // 2. Let body be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
body = TRY(ByteBuffer::copy(TRY(url_encode(pairs, encoding)).bytes())); body = TRY(ByteBuffer::copy(url_encode(pairs, encoding).bytes()));
// 3. Set body to the result of encoding body. // 3. Set body to the result of encoding body.
// NOTE: `encoding` refers to `UTF-8 encode`, which body already is encoded as because it uses AK::String. // NOTE: `encoding` refers to `UTF-8 encode`, which body already is encoded as because it uses AK::String.
@ -815,7 +815,7 @@ ErrorOr<void> HTMLFormElement::mail_with_headers(URL::URL parsed_action, Vector<
auto pairs = TRY(convert_to_list_of_name_value_pairs(entry_list)); auto pairs = TRY(convert_to_list_of_name_value_pairs(entry_list));
// 2. Let headers be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding. // 2. Let headers be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
auto headers = TRY(url_encode(pairs, encoding)); auto headers = url_encode(pairs, encoding);
// 3. Replace occurrences of U+002B PLUS SIGN characters (+) in headers with the string "%20". // 3. Replace occurrences of U+002B PLUS SIGN characters (+) in headers with the string "%20".
TRY(headers.replace("+"sv, "%20"sv, ReplaceMode::All)); TRY(headers.replace("+"sv, "%20"sv, ReplaceMode::All));
@ -851,7 +851,7 @@ ErrorOr<void> HTMLFormElement::mail_as_body(URL::URL parsed_action, Vector<XHR::
default: default:
// -> Otherwise // -> Otherwise
// Let body be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding. // Let body be the result of running the application/x-www-form-urlencoded serializer with pairs and encoding.
body = TRY(url_encode(pairs, encoding)); body = url_encode(pairs, encoding);
break; break;
} }