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.
// 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.
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②
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.
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.
if (query.has_value())
m_query->m_list = TRY_OR_THROW_OOM(vm, url_decode(*query));
m_query->m_list = url_decode(*query);
return {};
}
@ -411,10 +409,8 @@ WebIDL::ExceptionOr<String> DOMURL::search() const
}
// 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.
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);
// 4. Return.
return {};
return;
}
// 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);
// 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

View file

@ -68,7 +68,7 @@ public:
bool cannot_be_a_base_url() const { return m_url.cannot_be_a_base_url(); }
WebIDL::ExceptionOr<String> search() const;
WebIDL::ExceptionOr<void> set_search(String const&);
void set_search(String 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
// 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.
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.
if (!output.is_empty())
TRY(output.try_append('&'));
output.append('&');
// 5. Append name, followed by U+003D (=), followed by value, to output.
TRY(output.try_append(name));
TRY(output.try_append('='));
TRY(output.try_append(value));
output.append(name);
output.append('=');
output.append(value);
}
// 4. Return output.
return output.to_string();
return MUST(output.to_string());
}
// 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:
ErrorOr<Vector<QueryParam>> url_decode(StringView input)
Vector<QueryParam> url_decode(StringView input)
{
// 1. Let sequences be the result of splitting input on 0x26 (&).
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 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;
}
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));
}
// 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.
// 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
@ -203,36 +203,35 @@ size_t URLSearchParams::size() const
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.
TRY_OR_THROW_OOM(vm, m_list.try_empend(name, value));
// 2. Update this.
TRY(update());
m_list.empend(name, value);
return {};
// 2. Update this.
update();
}
WebIDL::ExceptionOr<void> URLSearchParams::update()
void URLSearchParams::update()
{
// 1. If querys URL object is null, then return.
if (!m_url)
return {};
return;
// 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.
if (serialized_query.is_empty())
serialized_query = {};
// 4. Set querys URL objects URLs query to serializedQuery.
m_url->set_query({}, move(serialized_query));
return {};
}
// 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.
if (value.has_value()) {
@ -248,9 +247,7 @@ WebIDL::ExceptionOr<void> URLSearchParams::delete_(String const& name, Optional<
}
// 2. Update this.
TRY(update());
return {};
update();
}
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
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.
Vector<String> values;
for (auto& entry : m_list) {
if (entry.name == name)
TRY_OR_THROW_OOM(vm, values.try_append(entry.value));
values.append(entry.value);
}
return values;
}
@ -304,10 +299,8 @@ bool URLSearchParams::has(String const& name, Optional<String> const& value)
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.
auto existing = m_list.find_if([&name](auto& entry) {
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.
else {
TRY_OR_THROW_OOM(vm, m_list.try_empend(name, value));
m_list.empend(name, value);
}
// 3. Update this.
TRY(update());
return {};
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.
quick_sort(m_list.begin(), m_list.end(), [](auto& a, auto& b) {
@ -349,18 +341,15 @@ WebIDL::ExceptionOr<void> URLSearchParams::sort()
}
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 TRY_OR_THROW_OOM(vm, url_encode(m_list));
return url_encode(m_list);
}
JS::ThrowCompletionOr<void> URLSearchParams::for_each(ForEachCallback callback)

View file

@ -16,31 +16,31 @@ struct QueryParam {
String name;
String value;
};
ErrorOr<String> url_encode(Vector<QueryParam> const&, StringView encoding = "UTF-8"sv);
ErrorOr<Vector<QueryParam>> url_decode(StringView);
String url_encode(Vector<QueryParam> const&, StringView encoding = "UTF-8"sv);
Vector<QueryParam> url_decode(StringView);
class URLSearchParams : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(URLSearchParams, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(URLSearchParams);
public:
static WebIDL::ExceptionOr<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&, StringView);
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);
virtual ~URLSearchParams() override;
size_t size() const;
WebIDL::ExceptionOr<void> append(String const& name, String const& value);
WebIDL::ExceptionOr<void> delete_(String const& name, Optional<String> const& value = {});
void append(String const& name, String const& value);
void delete_(String const& name, Optional<String> const& value = {});
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 = {});
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&)>;
JS::ThrowCompletionOr<void> for_each(ForEachCallback);
@ -54,7 +54,7 @@ private:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
WebIDL::ExceptionOr<void> update();
void update();
Vector<QueryParam> m_list;
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 });
// 2. If entries is failure, then throw a TypeError.
if (entries.is_error())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, entries.error().string_literal() };
// FIXME: Spec bug? It doesn't seem possible to throw an error here.
// 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.
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> {
// 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()));
// 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()));

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));
// 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.
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));
// 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.
// 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));
// 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".
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:
// -> Otherwise
// 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;
}