LibWeb: Port Document encoding_parse_url and parse_url to Optional<URL>

This ports two more APIs away from URL::is_valid.
This commit is contained in:
Shannon Booth 2025-01-23 19:40:57 +13:00 committed by Tim Ledbetter
commit 22a7cd9700
Notes: github-actions[bot] 2025-01-27 00:04:07 +00:00
26 changed files with 135 additions and 107 deletions

View file

@ -466,10 +466,15 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
if (!matches_link_pseudo_class(element)) if (!matches_link_pseudo_class(element))
return false; return false;
auto document_url = element.document().url(); auto document_url = element.document().url();
URL::URL target_url = element.document().encoding_parse_url(element.attribute(HTML::AttributeNames::href).value_or({})); auto maybe_href = element.attribute(HTML::AttributeNames::href);
if (target_url.fragment().has_value()) if (!maybe_href.has_value())
return document_url.equals(target_url, URL::ExcludeFragment::No); return false;
return document_url.equals(target_url, URL::ExcludeFragment::Yes); auto target_url = element.document().encoding_parse_url(*maybe_href);
if (!target_url.has_value())
return false;
if (target_url->fragment().has_value())
return document_url.equals(*target_url, URL::ExcludeFragment::No);
return document_url.equals(*target_url, URL::ExcludeFragment::Yes);
} }
case CSS::PseudoClass::Visited: case CSS::PseudoClass::Visited:
// FIXME: Maybe match this selector sometimes? // FIXME: Maybe match this selector sometimes?

View file

@ -2923,7 +2923,7 @@ Optional<FontLoader&> StyleComputer::load_font_face(ParsedFontFace const& font_f
for (auto const& source : font_face.sources()) { for (auto const& source : font_face.sources()) {
// FIXME: These should be loaded relative to the stylesheet URL instead of the document URL. // FIXME: These should be loaded relative to the stylesheet URL instead of the document URL.
if (source.local_or_url.has<URL::URL>()) if (source.local_or_url.has<URL::URL>())
urls.append(m_document->encoding_parse_url(source.local_or_url.get<URL::URL>().to_string())); urls.append(*m_document->encoding_parse_url(source.local_or_url.get<URL::URL>().to_string()));
// FIXME: Handle local() // FIXME: Handle local()
} }

View file

@ -1122,17 +1122,17 @@ URL::URL Document::base_url() const
} }
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#parse-a-url // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#parse-a-url
URL::URL Document::parse_url(StringView url) const Optional<URL::URL> Document::parse_url(StringView url) const
{ {
// 1. Let baseURL be environment's base URL, if environment is a Document object; otherwise environment's API base URL. // 1. Let baseURL be environment's base URL, if environment is a Document object; otherwise environment's API base URL.
auto base_url = this->base_url(); auto base_url = this->base_url();
// 2. Return the result of applying the URL parser to url, with baseURL. // 2. Return the result of applying the URL parser to url, with baseURL.
return DOMURL::parse(url, base_url).value_or(URL::URL {}); return DOMURL::parse(url, base_url);
} }
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#encoding-parsing-a-url // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#encoding-parsing-a-url
URL::URL Document::encoding_parse_url(StringView url) const Optional<URL::URL> Document::encoding_parse_url(StringView url) const
{ {
// 1. Let encoding be UTF-8. // 1. Let encoding be UTF-8.
// 2. If environment is a Document object, then set encoding to environment's character encoding. // 2. If environment is a Document object, then set encoding to environment's character encoding.
@ -1145,7 +1145,7 @@ URL::URL Document::encoding_parse_url(StringView url) const
auto base_url = this->base_url(); auto base_url = this->base_url();
// 5. Return the result of applying the URL parser to url, with baseURL and encoding. // 5. Return the result of applying the URL parser to url, with baseURL and encoding.
return DOMURL::parse(url, base_url, encoding).value_or(URL::URL {}); return DOMURL::parse(url, base_url, encoding);
} }
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#encoding-parsing-and-serializing-a-url // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#encoding-parsing-and-serializing-a-url
@ -1155,11 +1155,11 @@ Optional<String> Document::encoding_parse_and_serialize_url(StringView url) cons
auto parsed_url = encoding_parse_url(url); auto parsed_url = encoding_parse_url(url);
// 2. If url is failure, then return failure. // 2. If url is failure, then return failure.
if (!parsed_url.is_valid()) if (!parsed_url.has_value())
return {}; return {};
// 3. Return the result of applying the URL serializer to url. // 3. Return the result of applying the URL serializer to url.
return parsed_url.serialize(); return parsed_url->serialize();
} }
void Document::set_needs_layout() void Document::set_needs_layout()
@ -4752,10 +4752,10 @@ void Document::shared_declarative_refresh_steps(StringView input, GC::Ptr<HTML::
// 11. Parse: Parse urlString relative to document. If that fails, return. Otherwise, set urlRecord to the // 11. Parse: Parse urlString relative to document. If that fails, return. Otherwise, set urlRecord to the
// resulting URL record. // resulting URL record.
auto maybe_url_record = parse_url(url_string); auto maybe_url_record = parse_url(url_string);
if (!maybe_url_record.is_valid()) if (!maybe_url_record.has_value())
return; return;
url_record = maybe_url_record; url_record = maybe_url_record.release_value();
} }
// 12. Set document's will declaratively refresh to true. // 12. Set document's will declaratively refresh to true.

View file

@ -162,8 +162,8 @@ public:
HTML::OpenerPolicy const& opener_policy() const { return m_opener_policy; } HTML::OpenerPolicy const& opener_policy() const { return m_opener_policy; }
void set_opener_policy(HTML::OpenerPolicy policy) { m_opener_policy = move(policy); } void set_opener_policy(HTML::OpenerPolicy policy) { m_opener_policy = move(policy); }
URL::URL parse_url(StringView) const; Optional<URL::URL> parse_url(StringView) const;
URL::URL encoding_parse_url(StringView) const; Optional<URL::URL> encoding_parse_url(StringView) const;
Optional<String> encoding_parse_and_serialize_url(StringView) const; Optional<String> encoding_parse_and_serialize_url(StringView) const;
CSS::StyleComputer& style_computer() { return *m_style_computer; } CSS::StyleComputer& style_computer() { return *m_style_computer; }

View file

@ -125,12 +125,13 @@ void HTMLBodyElement::attribute_changed(FlyString const& name, Optional<String>
document().set_visited_link_color(color.value()); document().set_visited_link_color(color.value());
} else if (name.equals_ignoring_ascii_case("background"sv)) { } else if (name.equals_ignoring_ascii_case("background"sv)) {
// https://html.spec.whatwg.org/multipage/rendering.html#the-page:attr-background // https://html.spec.whatwg.org/multipage/rendering.html#the-page:attr-background
m_background_style_value = CSS::ImageStyleValue::create(document().encoding_parse_url(value.value_or(String {}))); if (auto maybe_background_url = document().encoding_parse_url(value.value_or(String {})); maybe_background_url.has_value()) {
m_background_style_value->on_animate = [this] { m_background_style_value = CSS::ImageStyleValue::create(maybe_background_url.value());
if (paintable()) { m_background_style_value->on_animate = [this] {
paintable()->set_needs_display(); if (paintable())
} paintable()->set_needs_display();
}; };
}
} }
#undef __ENUMERATE #undef __ENUMERATE

View file

@ -223,13 +223,13 @@ WebIDL::ExceptionOr<void> HTMLFormElement::submit_form(GC::Ref<HTMLElement> subm
// 14. Parse a URL given action, relative to the submitter element's node document. If this fails, return. // 14. Parse a URL given action, relative to the submitter element's node document. If this fails, return.
// 15. Let parsed action be the resulting URL record. // 15. Let parsed action be the resulting URL record.
auto parsed_action = submitter->document().parse_url(action); auto parsed_action = submitter->document().parse_url(action);
if (!parsed_action.is_valid()) { if (!parsed_action.has_value()) {
dbgln("Failed to submit form: Invalid URL: {}", action); dbgln("Failed to submit form: Invalid URL: {}", action);
return {}; return {};
} }
// 16. Let scheme be the scheme of parsed action. // 16. Let scheme be the scheme of parsed action.
auto const& scheme = parsed_action.scheme(); auto const& scheme = parsed_action->scheme();
// 17. Let enctype be the submitter element's enctype. // 17. Let enctype be the submitter element's enctype.
auto encoding_type = encoding_type_state_from_form_element(submitter); auto encoding_type = encoding_type_state_from_form_element(submitter);
@ -282,21 +282,21 @@ WebIDL::ExceptionOr<void> HTMLFormElement::submit_form(GC::Ref<HTMLElement> subm
if (scheme.is_one_of("http"sv, "https"sv)) { if (scheme.is_one_of("http"sv, "https"sv)) {
if (method == MethodAttributeState::GET) if (method == MethodAttributeState::GET)
TRY_OR_THROW_OOM(vm, mutate_action_url(move(parsed_action), move(entry_list), move(encoding), *target_navigable, history_handling, options.user_involvement)); TRY_OR_THROW_OOM(vm, mutate_action_url(parsed_action.release_value(), move(entry_list), move(encoding), *target_navigable, history_handling, options.user_involvement));
else else
TRY_OR_THROW_OOM(vm, submit_as_entity_body(move(parsed_action), move(entry_list), encoding_type, move(encoding), *target_navigable, history_handling, options.user_involvement)); TRY_OR_THROW_OOM(vm, submit_as_entity_body(parsed_action.release_value(), move(entry_list), encoding_type, move(encoding), *target_navigable, history_handling, options.user_involvement));
} else if (scheme.is_one_of("ftp"sv, "javascript"sv)) { } else if (scheme.is_one_of("ftp"sv, "javascript"sv)) {
get_action_url(move(parsed_action), *target_navigable, history_handling, options.user_involvement); get_action_url(parsed_action.release_value(), *target_navigable, history_handling, options.user_involvement);
} else if (scheme.is_one_of("data"sv, "file"sv)) { } else if (scheme.is_one_of("data"sv, "file"sv)) {
if (method == MethodAttributeState::GET) if (method == MethodAttributeState::GET)
TRY_OR_THROW_OOM(vm, mutate_action_url(move(parsed_action), move(entry_list), move(encoding), *target_navigable, history_handling, options.user_involvement)); TRY_OR_THROW_OOM(vm, mutate_action_url(parsed_action.release_value(), move(entry_list), move(encoding), *target_navigable, history_handling, options.user_involvement));
else else
get_action_url(move(parsed_action), *target_navigable, history_handling, options.user_involvement); get_action_url(parsed_action.release_value(), *target_navigable, history_handling, options.user_involvement);
} else if (scheme == "mailto"sv) { } else if (scheme == "mailto"sv) {
if (method == MethodAttributeState::GET) if (method == MethodAttributeState::GET)
TRY_OR_THROW_OOM(vm, mail_with_headers(move(parsed_action), move(entry_list), move(encoding), *target_navigable, history_handling, options.user_involvement)); TRY_OR_THROW_OOM(vm, mail_with_headers(parsed_action.release_value(), move(entry_list), move(encoding), *target_navigable, history_handling, options.user_involvement));
else else
TRY_OR_THROW_OOM(vm, mail_as_body(move(parsed_action), move(entry_list), encoding_type, move(encoding), *target_navigable, history_handling, options.user_involvement)); TRY_OR_THROW_OOM(vm, mail_as_body(parsed_action.release_value(), move(entry_list), encoding_type, move(encoding), *target_navigable, history_handling, options.user_involvement));
} else { } else {
dbgln("Failed to submit form: Unknown scheme: {}", scheme); dbgln("Failed to submit form: Unknown scheme: {}", scheme);
return {}; return {};

View file

@ -47,8 +47,8 @@ void HTMLHyperlinkElementUtils::set_the_url()
auto url = hyperlink_element_utils_document().encoding_parse_url(*href_content_attribute); auto url = hyperlink_element_utils_document().encoding_parse_url(*href_content_attribute);
// 4. If url is not failure, then set this element's url to url. // 4. If url is not failure, then set this element's url to url.
if (url.is_valid()) if (url.has_value())
m_url = move(url); m_url = url.release_value();
} }
// https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-origin // https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-origin

View file

@ -522,13 +522,13 @@ ErrorOr<void> HTMLImageElement::update_the_image_data(bool restart_animations, b
// If that is not successful, then abort this inner set of steps. // If that is not successful, then abort this inner set of steps.
// Otherwise, let urlString be the resulting URL string. // Otherwise, let urlString be the resulting URL string.
auto url_string = document().parse_url(selected_source.value()); auto url_string = document().parse_url(selected_source.value());
if (!url_string.is_valid()) if (!url_string.has_value())
goto after_step_7; goto after_step_7;
// 2. Let key be a tuple consisting of urlString, the img element's crossorigin attribute's mode, // 2. Let key be a tuple consisting of urlString, the img element's crossorigin attribute's mode,
// and, if that mode is not No CORS, the node document's origin. // and, if that mode is not No CORS, the node document's origin.
ListOfAvailableImages::Key key; ListOfAvailableImages::Key key;
key.url = url_string; key.url = url_string.value();
key.mode = m_cors_setting; key.mode = m_cors_setting;
key.origin = document().origin(); key.origin = document().origin();
@ -564,7 +564,7 @@ ErrorOr<void> HTMLImageElement::update_the_image_data(bool restart_animations, b
restart_the_animation(); restart_the_animation();
// 2. Set current request's current URL to urlString. // 2. Set current request's current URL to urlString.
m_current_request->set_current_url(realm(), url_string); m_current_request->set_current_url(realm(), *url_string);
// 3. If maybe omit events is not set or previousURL is not equal to urlString, then fire an event named load at the img element. // 3. If maybe omit events is not set or previousURL is not equal to urlString, then fire an event named load at the img element.
if (!maybe_omit_events || previous_url != url_string) if (!maybe_omit_events || previous_url != url_string)
@ -622,7 +622,7 @@ after_step_7:
// 12. Parse selected source, relative to the element's node document, and let urlString be the resulting URL string. // 12. Parse selected source, relative to the element's node document, and let urlString be the resulting URL string.
auto url_string = document().parse_url(selected_source.value().url.to_byte_string()); auto url_string = document().parse_url(selected_source.value().url.to_byte_string());
// If that is not successful, then: // If that is not successful, then:
if (!url_string.is_valid()) { if (!url_string.has_value()) {
// 1. Abort the image request for the current request and the pending request. // 1. Abort the image request for the current request and the pending request.
abort_the_image_request(realm(), m_current_request); abort_the_image_request(realm(), m_current_request);
abort_the_image_request(realm(), m_pending_request); abort_the_image_request(realm(), m_pending_request);
@ -673,7 +673,7 @@ after_step_7:
// 16. Set image request to a new image request whose current URL is urlString. // 16. Set image request to a new image request whose current URL is urlString.
auto image_request = ImageRequest::create(realm(), document().page()); auto image_request = ImageRequest::create(realm(), document().page());
image_request->set_current_url(realm(), url_string); image_request->set_current_url(realm(), *url_string);
// 17. If current request's state is unavailable or broken, then set the current request to image request. // 17. If current request's state is unavailable or broken, then set the current request to image request.
// Otherwise, set the pending request to image request. // Otherwise, set the pending request to image request.
@ -690,7 +690,7 @@ after_step_7:
if (delay_load_event) if (delay_load_event)
m_load_event_delayer.emplace(document()); m_load_event_delayer.emplace(document());
add_callbacks_to_image_request(*image_request, maybe_omit_events, url_string, previous_url); add_callbacks_to_image_request(*image_request, maybe_omit_events, *url_string, previous_url);
// AD-HOC: If the image request is already available or fetching, no need to start another fetch. // AD-HOC: If the image request is already available or fetching, no need to start another fetch.
if (image_request->is_available() || image_request->is_fetching()) if (image_request->is_available() || image_request->is_fetching())
@ -698,7 +698,7 @@ after_step_7:
// 18. Let request be the result of creating a potential-CORS request given urlString, "image", // 18. Let request be the result of creating a potential-CORS request given urlString, "image",
// and the current state of the element's crossorigin content attribute. // and the current state of the element's crossorigin content attribute.
auto request = create_potential_CORS_request(vm(), url_string, Fetch::Infrastructure::Request::Destination::Image, m_cors_setting); auto request = create_potential_CORS_request(vm(), *url_string, Fetch::Infrastructure::Request::Destination::Image, m_cors_setting);
// 19. Set request's client to the element's node document's relevant settings object. // 19. Set request's client to the element's node document's relevant settings object.
request->set_client(&document().relevant_settings_object()); request->set_client(&document().relevant_settings_object());
@ -848,7 +848,7 @@ void HTMLImageElement::react_to_changes_in_the_environment()
// 6. ⌛ Parse selected source, relative to the element's node document, // 6. ⌛ Parse selected source, relative to the element's node document,
// and let urlString be the resulting URL string. If that is not successful, then return. // and let urlString be the resulting URL string. If that is not successful, then return.
auto url_string = document().parse_url(selected_source.value()); auto url_string = document().parse_url(selected_source.value());
if (!url_string.is_valid()) if (!url_string.has_value())
return; return;
// 7. ⌛ Let corsAttributeState be the state of the element's crossorigin content attribute. // 7. ⌛ Let corsAttributeState be the state of the element's crossorigin content attribute.
@ -862,14 +862,14 @@ void HTMLImageElement::react_to_changes_in_the_environment()
// 10. ⌛ Let key be a tuple consisting of urlString, corsAttributeState, and, if corsAttributeState is not No CORS, origin. // 10. ⌛ Let key be a tuple consisting of urlString, corsAttributeState, and, if corsAttributeState is not No CORS, origin.
ListOfAvailableImages::Key key; ListOfAvailableImages::Key key;
key.url = url_string; key.url = *url_string;
key.mode = m_cors_setting; key.mode = m_cors_setting;
if (cors_attribute_state != CORSSettingAttribute::NoCORS) if (cors_attribute_state != CORSSettingAttribute::NoCORS)
key.origin = document().origin(); key.origin = document().origin();
// 11. ⌛ Let image request be a new image request whose current URL is urlString // 11. ⌛ Let image request be a new image request whose current URL is urlString
auto image_request = ImageRequest::create(realm(), document().page()); auto image_request = ImageRequest::create(realm(), document().page());
image_request->set_current_url(realm(), url_string); image_request->set_current_url(realm(), *url_string);
// 12. ⌛ Let the element's pending request be image request. // 12. ⌛ Let the element's pending request be image request.
m_pending_request = image_request; m_pending_request = image_request;
@ -917,7 +917,7 @@ void HTMLImageElement::react_to_changes_in_the_environment()
// Otherwise: // Otherwise:
else { else {
// 1. Let request be the result of creating a potential-CORS request given urlString, "image", and corsAttributeState. // 1. Let request be the result of creating a potential-CORS request given urlString, "image", and corsAttributeState.
auto request = create_potential_CORS_request(vm(), url_string, Fetch::Infrastructure::Request::Destination::Image, m_cors_setting); auto request = create_potential_CORS_request(vm(), *url_string, Fetch::Infrastructure::Request::Destination::Image, m_cors_setting);
// 2. Set request's client to client, initiator to "imageset", and set request's synchronous flag. // 2. Set request's client to client, initiator to "imageset", and set request's synchronous flag.
request->set_client(&client); request->set_client(&client);

View file

@ -1385,14 +1385,14 @@ WebIDL::ExceptionOr<void> HTMLInputElement::handle_src_attribute(String const& v
auto url = document().encoding_parse_url(value); auto url = document().encoding_parse_url(value);
// 2. If url is failure, then return. // 2. If url is failure, then return.
if (!url.is_valid()) if (!url.has_value())
return {}; return {};
// 3. Let request be a new request whose URL is url, client is the element's node document's relevant settings // 3. Let request be a new request whose URL is url, client is the element's node document's relevant settings
// object, destination is "image", initiator type is "input", credentials mode is "include", and whose // object, destination is "image", initiator type is "input", credentials mode is "include", and whose
// use-URL-credentials flag is set. // use-URL-credentials flag is set.
auto request = Fetch::Infrastructure::Request::create(vm); auto request = Fetch::Infrastructure::Request::create(vm);
request->set_url(move(url)); request->set_url(url.release_value());
request->set_client(&document().relevant_settings_object()); request->set_client(&document().relevant_settings_object());
request->set_destination(Fetch::Infrastructure::Request::Destination::Image); request->set_destination(Fetch::Infrastructure::Request::Destination::Image);
request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Input); request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Input);

View file

@ -77,18 +77,25 @@ void HTMLLinkElement::inserted()
// FIXME: Follow spec for fetching and processing these attributes as well // FIXME: Follow spec for fetching and processing these attributes as well
if (m_relationship & Relationship::Preload) { if (m_relationship & Relationship::Preload) {
// FIXME: Respect the "as" attribute. if (auto maybe_href = document().encoding_parse_url(get_attribute_value(HTML::AttributeNames::href)); maybe_href.has_value()) {
LoadRequest request; // FIXME: Respect the "as" attribute.
request.set_url(document().encoding_parse_url(get_attribute_value(HTML::AttributeNames::href))); LoadRequest request;
set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request)); request.set_url(maybe_href.value());
set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
}
} else if (m_relationship & Relationship::DNSPrefetch) { } else if (m_relationship & Relationship::DNSPrefetch) {
ResourceLoader::the().prefetch_dns(document().encoding_parse_url(get_attribute_value(HTML::AttributeNames::href))); if (auto dns_prefetch_url = document().encoding_parse_url(get_attribute_value(HTML::AttributeNames::href)); dns_prefetch_url.has_value()) {
ResourceLoader::the().prefetch_dns(dns_prefetch_url.value());
}
} else if (m_relationship & Relationship::Preconnect) { } else if (m_relationship & Relationship::Preconnect) {
ResourceLoader::the().preconnect(document().encoding_parse_url(get_attribute_value(HTML::AttributeNames::href))); if (auto maybe_href = document().encoding_parse_url(get_attribute_value(HTML::AttributeNames::href)); maybe_href.has_value()) {
ResourceLoader::the().preconnect(maybe_href.value());
}
} else if (m_relationship & Relationship::Icon) { } else if (m_relationship & Relationship::Icon) {
auto favicon_url = document().encoding_parse_url(href()); if (auto favicon_url = document().encoding_parse_url(href()); favicon_url.has_value()) {
auto favicon_request = LoadRequest::create_for_url_on_page(favicon_url, &document().page()); auto favicon_request = LoadRequest::create_for_url_on_page(favicon_url.value(), &document().page());
set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, favicon_request)); set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, favicon_request));
}
} }
} }
@ -572,7 +579,7 @@ WebIDL::ExceptionOr<void> HTMLLinkElement::load_fallback_favicon_if_needed(GC::R
// synchronous flag is set, credentials mode is "include", and whose use-URL-credentials flag is set. // synchronous flag is set, credentials mode is "include", and whose use-URL-credentials flag is set.
// NOTE: Fetch requests no longer have a synchronous flag, see https://github.com/whatwg/fetch/pull/1165 // NOTE: Fetch requests no longer have a synchronous flag, see https://github.com/whatwg/fetch/pull/1165
auto request = Fetch::Infrastructure::Request::create(vm); auto request = Fetch::Infrastructure::Request::create(vm);
request->set_url(document->parse_url("/favicon.ico"sv)); request->set_url(*document->parse_url("/favicon.ico"sv));
request->set_client(&document->relevant_settings_object()); request->set_client(&document->relevant_settings_object());
request->set_destination(Fetch::Infrastructure::Request::Destination::Image); request->set_destination(Fetch::Infrastructure::Request::Destination::Image);
request->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include); request->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include);

View file

@ -625,14 +625,14 @@ public:
// would have resulted from parsing the URL specified by candidate's src attribute's value relative to the // would have resulted from parsing the URL specified by candidate's src attribute's value relative to the
// candidate's node document when the src attribute was last changed. // candidate's node document when the src attribute was last changed.
auto url_record = m_candidate->document().parse_url(candiate_src); auto url_record = m_candidate->document().parse_url(candiate_src);
auto url_string = url_record.to_string();
// 4. ⌛ If urlString was not obtained successfully, then end the synchronous section, and jump down to the failed // 4. ⌛ If urlString was not obtained successfully, then end the synchronous section, and jump down to the failed
// with elements step below. // with elements step below.
if (!url_record.is_valid()) { if (!url_record.has_value()) {
TRY(failed_with_elements()); TRY(failed_with_elements());
return {}; return {};
} }
auto url_string = url_record->to_string();
// FIXME: 5. ⌛ If candidate has a type attribute whose value, when parsed as a MIME type (including any codecs described // FIXME: 5. ⌛ If candidate has a type attribute whose value, when parsed as a MIME type (including any codecs described
// by the codecs parameter, for types that define that parameter), represents a type that the user agent knows // by the codecs parameter, for types that define that parameter), represents a type that the user agent knows
@ -645,7 +645,7 @@ public:
// 8. Run the resource fetch algorithm with urlRecord. If that algorithm returns without aborting this one, then // 8. Run the resource fetch algorithm with urlRecord. If that algorithm returns without aborting this one, then
// the load failed. // the load failed.
TRY(m_media_element->fetch_resource(url_record, [this](auto) { TRY(m_media_element->fetch_resource(*url_record, [this](auto) {
failed_with_elements().release_value_but_fixme_should_propagate_errors(); failed_with_elements().release_value_but_fixme_should_propagate_errors();
})); }));
@ -871,15 +871,15 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
auto url_record = document().parse_url(source); auto url_record = document().parse_url(source);
// 3. ⌛ If urlString was obtained successfully, set the currentSrc attribute to urlString. // 3. ⌛ If urlString was obtained successfully, set the currentSrc attribute to urlString.
if (url_record.is_valid()) if (url_record.has_value())
m_current_src = url_record.to_string(); m_current_src = url_record->to_string();
// 4. End the synchronous section, continuing the remaining steps in parallel. // 4. End the synchronous section, continuing the remaining steps in parallel.
// 5. If urlRecord was obtained successfully, run the resource fetch algorithm with urlRecord. If that algorithm returns without aborting this one, // 5. If urlRecord was obtained successfully, run the resource fetch algorithm with urlRecord. If that algorithm returns without aborting this one,
// then the load failed. // then the load failed.
if (url_record.is_valid()) { if (url_record.has_value()) {
TRY(fetch_resource(url_record, move(failed_with_attribute))); TRY(fetch_resource(*url_record, move(failed_with_attribute)));
return {}; return {};
} }

View file

@ -169,7 +169,11 @@ String HTMLObjectElement::data() const
if (!data.has_value()) if (!data.has_value())
return {}; return {};
return document().encoding_parse_url(*data).to_string(); auto maybe_url = document().encoding_parse_url(*data);
if (!maybe_url.has_value())
return {};
return maybe_url->to_string();
} }
GC::Ptr<Layout::Node> HTMLObjectElement::create_layout_node(GC::Ref<CSS::ComputedProperties> style) GC::Ptr<Layout::Node> HTMLObjectElement::create_layout_node(GC::Ref<CSS::ComputedProperties> style)
@ -257,7 +261,7 @@ void HTMLObjectElement::queue_element_task_to_run_object_representation_steps()
auto url = document().encoding_parse_url(*data); auto url = document().encoding_parse_url(*data);
// 3. If url is failure, then fire an event named error at the element and jump to the step below labeled fallback. // 3. If url is failure, then fire an event named error at the element and jump to the step below labeled fallback.
if (!url.is_valid()) { if (!url.has_value()) {
dispatch_event(DOM::Event::create(realm, HTML::EventNames::error)); dispatch_event(DOM::Event::create(realm, HTML::EventNames::error));
run_object_representation_fallback_steps(); run_object_representation_fallback_steps();
return; return;
@ -267,7 +271,7 @@ void HTMLObjectElement::queue_element_task_to_run_object_representation_steps()
// object, destination is "object", credentials mode is "include", mode is "navigate", initiator type is // object, destination is "object", credentials mode is "include", mode is "navigate", initiator type is
// "object", and whose use-URL-credentials flag is set. // "object", and whose use-URL-credentials flag is set.
auto request = Fetch::Infrastructure::Request::create(vm); auto request = Fetch::Infrastructure::Request::create(vm);
request->set_url(move(url)); request->set_url(url.release_value());
request->set_client(&document().relevant_settings_object()); request->set_client(&document().relevant_settings_object());
request->set_destination(Fetch::Infrastructure::Request::Destination::Object); request->set_destination(Fetch::Infrastructure::Request::Destination::Object);
request->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include); request->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include);
@ -512,7 +516,13 @@ void HTMLObjectElement::load_image()
// FIXME: This currently reloads the image instead of reusing the resource we've already downloaded. // FIXME: This currently reloads the image instead of reusing the resource we've already downloaded.
auto data = get_attribute_value(HTML::AttributeNames::data); auto data = get_attribute_value(HTML::AttributeNames::data);
auto url = document().encoding_parse_url(data); auto url = document().encoding_parse_url(data);
m_resource_request = HTML::SharedResourceRequest::get_or_create(realm(), document().page(), url);
if (!url.has_value()) {
run_object_representation_fallback_steps();
return;
}
m_resource_request = HTML::SharedResourceRequest::get_or_create(realm(), document().page(), *url);
m_resource_request->add_callbacks( m_resource_request->add_callbacks(
[this] { [this] {
run_object_representation_completed_steps(Representation::Image); run_object_representation_completed_steps(Representation::Image);
@ -522,7 +532,7 @@ void HTMLObjectElement::load_image()
}); });
if (m_resource_request->needs_fetching()) { if (m_resource_request->needs_fetching()) {
auto request = HTML::create_potential_CORS_request(vm(), url, Fetch::Infrastructure::Request::Destination::Image, HTML::CORSSettingAttribute::NoCORS); auto request = HTML::create_potential_CORS_request(vm(), *url, Fetch::Infrastructure::Request::Destination::Image, HTML::CORSSettingAttribute::NoCORS);
request->set_client(&document().relevant_settings_object()); request->set_client(&document().relevant_settings_object());
m_resource_request->fetch_resource(realm(), request); m_resource_request->fetch_resource(realm(), request);
} }

View file

@ -385,7 +385,7 @@ void HTMLScriptElement::prepare_script()
auto url = document().parse_url(src); auto url = document().parse_url(src);
// 6. If the previous step failed, then queue an element task on the DOM manipulation task source given el to fire an event named error at el, and return. Otherwise, let url be the resulting URL record. // 6. If the previous step failed, then queue an element task on the DOM manipulation task source given el to fire an event named error at el, and return. Otherwise, let url be the resulting URL record.
if (!url.is_valid()) { if (!url.has_value()) {
dbgln("HTMLScriptElement: Refusing to run script because the src URL '{}' is invalid.", url); dbgln("HTMLScriptElement: Refusing to run script because the src URL '{}' is invalid.", url);
queue_an_element_task(HTML::Task::Source::DOMManipulation, [this] { queue_an_element_task(HTML::Task::Source::DOMManipulation, [this] {
dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error)); dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error));
@ -413,16 +413,16 @@ void HTMLScriptElement::prepare_script()
// -> "classic" // -> "classic"
if (m_script_type == ScriptType::Classic) { if (m_script_type == ScriptType::Classic) {
// Fetch a classic script given url, settings object, options, classic script CORS setting, encoding, and onComplete. // Fetch a classic script given url, settings object, options, classic script CORS setting, encoding, and onComplete.
fetch_classic_script(*this, url, settings_object, move(options), classic_script_cors_setting, encoding.release_value(), on_complete).release_value_but_fixme_should_propagate_errors(); fetch_classic_script(*this, *url, settings_object, move(options), classic_script_cors_setting, encoding.release_value(), on_complete).release_value_but_fixme_should_propagate_errors();
} }
// -> "module" // -> "module"
else if (m_script_type == ScriptType::Module) { else if (m_script_type == ScriptType::Module) {
// If el does not have an integrity attribute, then set options's integrity metadata to the result of resolving a module integrity metadata with url and settings object. // If el does not have an integrity attribute, then set options's integrity metadata to the result of resolving a module integrity metadata with url and settings object.
if (!has_attribute(HTML::AttributeNames::integrity)) if (!has_attribute(HTML::AttributeNames::integrity))
options.integrity_metadata = resolve_a_module_integrity_metadata(url, settings_object); options.integrity_metadata = resolve_a_module_integrity_metadata(*url, settings_object);
// Fetch an external module script graph given url, settings object, options, and onComplete. // Fetch an external module script graph given url, settings object, options, and onComplete.
fetch_external_module_script_graph(realm(), url, settings_object, options, on_complete); fetch_external_module_script_graph(realm(), *url, settings_object, options, on_complete);
} }
} }

View file

@ -89,8 +89,8 @@ void HTMLTableCellElement::apply_presentational_hints(GC::Ref<CSS::CascadedPrope
return; return;
} else if (name == HTML::AttributeNames::background) { } else if (name == HTML::AttributeNames::background) {
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url
if (auto parsed_value = document().encoding_parse_url(value); parsed_value.is_valid()) if (auto parsed_value = document().encoding_parse_url(value); parsed_value.has_value())
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(parsed_value)); cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(*parsed_value));
return; return;
} }
}); });

View file

@ -91,8 +91,8 @@ void HTMLTableElement::apply_presentational_hints(GC::Ref<CSS::CascadedPropertie
} }
if (name == HTML::AttributeNames::background) { if (name == HTML::AttributeNames::background) {
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url
if (auto parsed_value = document().encoding_parse_url(value); parsed_value.is_valid()) if (auto parsed_value = document().encoding_parse_url(value); parsed_value.has_value())
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(parsed_value)); cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(*parsed_value));
return; return;
} }
if (name == HTML::AttributeNames::bgcolor) { if (name == HTML::AttributeNames::bgcolor) {

View file

@ -62,8 +62,8 @@ void HTMLTableRowElement::apply_presentational_hints(GC::Ref<CSS::CascadedProper
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundColor, CSS::CSSColorValue::create_from_color(color.value())); cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundColor, CSS::CSSColorValue::create_from_color(color.value()));
} else if (name == HTML::AttributeNames::background) { } else if (name == HTML::AttributeNames::background) {
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url
if (auto parsed_value = document().encoding_parse_url(value); parsed_value.is_valid()) if (auto parsed_value = document().encoding_parse_url(value); parsed_value.has_value())
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(parsed_value)); cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(*parsed_value));
} else if (name == HTML::AttributeNames::height) { } else if (name == HTML::AttributeNames::height) {
if (auto parsed_value = parse_dimension_value(value)) if (auto parsed_value = parse_dimension_value(value))
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::Height, *parsed_value); cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::Height, *parsed_value);

View file

@ -115,8 +115,8 @@ void HTMLTableSectionElement::apply_presentational_hints(GC::Ref<CSS::CascadedPr
for_each_attribute([&](auto& name, auto& value) { for_each_attribute([&](auto& name, auto& value) {
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:encoding-parsing-and-serializing-a-url
if (name == HTML::AttributeNames::background) { if (name == HTML::AttributeNames::background) {
if (auto parsed_value = document().encoding_parse_url(value); parsed_value.is_valid()) if (auto parsed_value = document().encoding_parse_url(value); parsed_value.has_value())
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(parsed_value)); cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(*parsed_value));
} }
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2:rules-for-parsing-a-legacy-colour-value // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:rules-for-parsing-a-legacy-colour-value
else if (name == HTML::AttributeNames::bgcolor) { else if (name == HTML::AttributeNames::bgcolor) {

View file

@ -164,14 +164,14 @@ WebIDL::ExceptionOr<void> HTMLVideoElement::determine_element_poster_frame(Optio
// 3. Parse the poster attribute's value relative to the element's node document. If this fails, then there is no // 3. Parse the poster attribute's value relative to the element's node document. If this fails, then there is no
// poster frame; return. // poster frame; return.
auto url_record = document().parse_url(*poster); auto url_record = document().parse_url(*poster);
if (!url_record.is_valid()) if (!url_record.has_value())
return {}; return {};
// 4. Let request be a new request whose URL is the resulting URL record, client is the element's node document's // 4. Let request be a new request whose URL is the resulting URL record, client is the element's node document's
// relevant settings object, destination is "image", initiator type is "video", credentials mode is "include", // relevant settings object, destination is "image", initiator type is "video", credentials mode is "include",
// and whose use-URL-credentials flag is set. // and whose use-URL-credentials flag is set.
auto request = Fetch::Infrastructure::Request::create(vm); auto request = Fetch::Infrastructure::Request::create(vm);
request->set_url(move(url_record)); request->set_url(url_record.release_value());
request->set_client(&document().relevant_settings_object()); request->set_client(&document().relevant_settings_object());
request->set_destination(Fetch::Infrastructure::Request::Destination::Image); request->set_destination(Fetch::Infrastructure::Request::Destination::Image);
request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Video); request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Video);

View file

@ -208,8 +208,8 @@ Optional<URL::URL> NavigableContainer::shared_attribute_processing_steps_for_ifr
auto src_attribute_value = get_attribute_value(HTML::AttributeNames::src); auto src_attribute_value = get_attribute_value(HTML::AttributeNames::src);
if (!src_attribute_value.is_empty()) { if (!src_attribute_value.is_empty()) {
auto parsed_src = document().parse_url(src_attribute_value); auto parsed_src = document().parse_url(src_attribute_value);
if (parsed_src.is_valid()) if (parsed_src.has_value())
url = parsed_src; url = parsed_src.release_value();
} }
// 3. If the inclusive ancestor navigables of element's node navigable contains a navigable // 3. If the inclusive ancestor navigables of element's node navigable contains a navigable

View file

@ -202,7 +202,7 @@ WebIDL::ExceptionOr<Window::OpenedWindow> Window::window_open_steps_internal(Str
url_record = source_document.encoding_parse_url(url); url_record = source_document.encoding_parse_url(url);
// 2. If urlRecord is failure, then throw a "SyntaxError" DOMException. // 2. If urlRecord is failure, then throw a "SyntaxError" DOMException.
if (!url_record->is_valid()) if (!url_record.has_value())
return WebIDL::SyntaxError::create(realm(), MUST(String::formatted("Invalid URL '{}'", url))); return WebIDL::SyntaxError::create(realm(), MUST(String::formatted("Invalid URL '{}'", url)));
} }

View file

@ -497,28 +497,31 @@ EventResult EventHandler::handle_mouseup(CSSPixelPoint viewport_position, CSSPix
GC::Ref<DOM::Document> document = *m_navigable->active_document(); GC::Ref<DOM::Document> document = *m_navigable->active_document();
auto href = link->href(); auto href = link->href();
auto url = document->encoding_parse_url(href); auto url = document->encoding_parse_url(href);
if (url.has_value()) {
if (button == UIEvents::MouseButton::Primary && (modifiers & UIEvents::Mod_PlatformCtrl) != 0) { if (button == UIEvents::MouseButton::Primary && (modifiers & UIEvents::Mod_PlatformCtrl) != 0) {
m_navigable->page().client().page_did_click_link(url, link->target().to_byte_string(), modifiers); m_navigable->page().client().page_did_click_link(*url, link->target().to_byte_string(), modifiers);
} else if (button == UIEvents::MouseButton::Middle) { } else if (button == UIEvents::MouseButton::Middle) {
m_navigable->page().client().page_did_middle_click_link(url, link->target().to_byte_string(), modifiers); m_navigable->page().client().page_did_middle_click_link(*url, link->target().to_byte_string(), modifiers);
} else if (button == UIEvents::MouseButton::Secondary) { } else if (button == UIEvents::MouseButton::Secondary) {
m_navigable->page().client().page_did_request_link_context_menu(top_level_viewport_position, url, link->target().to_byte_string(), modifiers); m_navigable->page().client().page_did_request_link_context_menu(top_level_viewport_position, *url, link->target().to_byte_string(), modifiers);
}
} }
} else if (button == UIEvents::MouseButton::Secondary) { } else if (button == UIEvents::MouseButton::Secondary) {
if (is<HTML::HTMLImageElement>(*node)) { if (is<HTML::HTMLImageElement>(*node)) {
auto& image_element = as<HTML::HTMLImageElement>(*node); auto& image_element = as<HTML::HTMLImageElement>(*node);
auto image_url = image_element.document().encoding_parse_url(image_element.src()); auto image_url = image_element.document().encoding_parse_url(image_element.src());
Optional<Gfx::Bitmap const*> bitmap; if (image_url.has_value()) {
if (image_element.immutable_bitmap()) Optional<Gfx::Bitmap const*> bitmap;
bitmap = image_element.immutable_bitmap()->bitmap(); if (image_element.immutable_bitmap())
bitmap = image_element.immutable_bitmap()->bitmap();
m_navigable->page().client().page_did_request_image_context_menu(top_level_viewport_position, image_url, "", modifiers, bitmap); m_navigable->page().client().page_did_request_image_context_menu(top_level_viewport_position, *image_url, "", modifiers, bitmap);
}
} else if (is<HTML::HTMLMediaElement>(*node)) { } else if (is<HTML::HTMLMediaElement>(*node)) {
auto& media_element = as<HTML::HTMLMediaElement>(*node); auto& media_element = as<HTML::HTMLMediaElement>(*node);
Page::MediaContextMenu menu { Page::MediaContextMenu menu {
.media_url = media_element.document().encoding_parse_url(media_element.current_src()), .media_url = *media_element.document().encoding_parse_url(media_element.current_src()),
.is_video = is<HTML::HTMLVideoElement>(*node), .is_video = is<HTML::HTMLVideoElement>(*node),
.is_playing = media_element.potentially_playing(), .is_playing = media_element.potentially_playing(),
.is_muted = media_element.muted(), .is_muted = media_element.muted(),
@ -811,7 +814,7 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSP
if (is_hovering_link) { if (is_hovering_link) {
page.set_is_hovering_link(true); page.set_is_hovering_link(true);
page.client().page_did_hover_link(document.encoding_parse_url(hovered_link_element->href())); page.client().page_did_hover_link(*document.encoding_parse_url(hovered_link_element->href()));
} else if (page.is_hovering_link()) { } else if (page.is_hovering_link()) {
page.set_is_hovering_link(false); page.set_is_hovering_link(false);
page.client().page_did_unhover_link(); page.client().page_did_unhover_link();

View file

@ -129,7 +129,9 @@ GC::Ptr<SVGGradientElement const> SVGGradientElement::linked_gradient(HashTable<
auto link = has_attribute(AttributeNames::href) ? get_attribute(AttributeNames::href) : get_attribute("xlink:href"_fly_string); auto link = has_attribute(AttributeNames::href) ? get_attribute(AttributeNames::href) : get_attribute("xlink:href"_fly_string);
if (auto href = link; href.has_value() && !link->is_empty()) { if (auto href = link; href.has_value() && !link->is_empty()) {
auto url = document().encoding_parse_url(*href); auto url = document().encoding_parse_url(*href);
auto id = url.fragment(); if (!url.has_value())
return {};
auto id = url->fragment();
if (!id.has_value() || id->is_empty()) if (!id.has_value() || id->is_empty())
return {}; return {};
auto element = document().get_element_by_id(id.value()); auto element = document().get_element_by_id(id.value());

View file

@ -146,11 +146,10 @@ void SVGImageElement::process_the_url(Optional<String> const& href)
} }
m_href = document().parse_url(*href); m_href = document().parse_url(*href);
if (!m_href.has_value())
if (!m_href.is_valid())
return; return;
fetch_the_document(m_href); fetch_the_document(*m_href);
} }
// https://svgwg.org/svg2-draft/linking.html#processingURL-fetch // https://svgwg.org/svg2-draft/linking.html#processingURL-fetch

View file

@ -60,7 +60,7 @@ private:
size_t m_current_frame_index { 0 }; size_t m_current_frame_index { 0 };
size_t m_loops_completed { 0 }; size_t m_loops_completed { 0 };
URL::URL m_href; Optional<URL::URL> m_href;
GC::Ptr<HTML::SharedResourceRequest> m_resource_request; GC::Ptr<HTML::SharedResourceRequest> m_resource_request;
Optional<DOM::DocumentLoadEventDelayer> m_load_event_delayer; Optional<DOM::DocumentLoadEventDelayer> m_load_event_delayer;

View file

@ -53,11 +53,12 @@ void SVGScriptElement::process_the_script_element()
if (has_attribute(SVG::AttributeNames::href) || has_attribute_ns(Namespace::XLink.to_string(), SVG::AttributeNames::href)) { if (has_attribute(SVG::AttributeNames::href) || has_attribute_ns(Namespace::XLink.to_string(), SVG::AttributeNames::href)) {
auto href_value = href()->base_val(); auto href_value = href()->base_val();
script_url = document().parse_url(href_value); auto maybe_script_url = document().parse_url(href_value);
if (!script_url.is_valid()) { if (!maybe_script_url.has_value()) {
dbgln("Invalid script URL: {}", href_value); dbgln("Invalid script URL: {}", href_value);
return; return;
} }
script_url = maybe_script_url.release_value();
auto& vm = realm().vm(); auto& vm = realm().vm();
auto request = Fetch::Infrastructure::Request::create(vm); auto request = Fetch::Infrastructure::Request::create(vm);

View file

@ -433,14 +433,14 @@ void ConnectionFromClient::debug_request(u64 page_id, ByteString const& request,
} else { } else {
auto link = maybe_link.release_value(); auto link = maybe_link.release_value();
auto url = document->encoding_parse_url(link->get_attribute_value(Web::HTML::AttributeNames::href)); auto url = document->encoding_parse_url(link->get_attribute_value(Web::HTML::AttributeNames::href));
if (url.query().has_value() && !url.query()->is_empty()) { if (url->query().has_value() && !url->query()->is_empty()) {
load_html(page_id, "<h1>Invalid ref test link - query string must be empty</h1>"); load_html(page_id, "<h1>Invalid ref test link - query string must be empty</h1>");
return; return;
} }
if (has_mismatch_selector) if (has_mismatch_selector)
url.set_query("mismatch"_string); url->set_query("mismatch"_string);
load_url(page_id, url); load_url(page_id, *url);
} }
} }
return; return;