LibURL+LibWeb: Port URL::complete_url to Optional

Removing one more source of the URL::is_valid API.
This commit is contained in:
Shannon Booth 2025-02-15 22:55:46 +13:00 committed by Tim Ledbetter
parent 356728b1e0
commit 53826995f6
Notes: github-actions[bot] 2025-02-15 17:07:12 +00:00
14 changed files with 57 additions and 52 deletions

View file

@ -33,16 +33,12 @@ URL::URL(StringView string)
}
}
URL URL::complete_url(StringView relative_url) const
Optional<URL> URL::complete_url(StringView relative_url) const
{
if (!is_valid())
return {};
auto result = Parser::basic_parse(relative_url, *this);
if (!result.has_value())
return {};
return result.release_value();
return Parser::basic_parse(relative_url, *this);
}
ByteString URL::path_segment_at_index(size_t index) const

View file

@ -135,7 +135,7 @@ public:
bool equals(URL const& other, ExcludeFragment = ExcludeFragment::No) const;
URL complete_url(StringView) const;
Optional<URL> complete_url(StringView) const;
[[nodiscard]] bool operator==(URL const& other) const
{

View file

@ -45,8 +45,8 @@ WebIDL::ExceptionOr<GC::Ref<CSSStyleSheet>> CSSStyleSheet::construct_impl(JS::Re
sheet_location_url = sheet->location().release_value();
// AD-HOC: This isn't explicitly mentioned in the specification, but multiple modern browsers do this.
URL::URL url = sheet->location().has_value() ? sheet_location_url->complete_url(options->base_url.value()) : options->base_url.value();
if (!url.is_valid())
Optional<URL::URL> url = sheet->location().has_value() ? sheet_location_url->complete_url(options->base_url.value()) : options->base_url.value();
if (!url.has_value())
return WebIDL::NotAllowedError::create(realm, "Constructed style sheets must have a valid base URL"_string);
sheet->set_base_url(url);

View file

@ -1761,7 +1761,7 @@ bool Parser::is_parsing_svg_presentation_attribute() const
// https://www.w3.org/TR/css-values-4/#relative-urls
// FIXME: URLs shouldn't be completed during parsing, but when used.
URL::URL Parser::complete_url(StringView relative_url) const
Optional<URL::URL> Parser::complete_url(StringView relative_url) const
{
return m_url.complete_url(relative_url);
}

View file

@ -460,7 +460,7 @@ private:
JS::Realm& realm() const;
bool in_quirks_mode() const;
bool is_parsing_svg_presentation_attribute() const;
URL::URL complete_url(StringView) const;
Optional<URL::URL> complete_url(StringView) const;
GC::Ptr<DOM::Document const> m_document;
GC::Ptr<JS::Realm> m_realm;

View file

@ -2470,7 +2470,7 @@ Optional<URL::URL> Parser::parse_url_function(TokenStream<ComponentValue>& token
auto convert_string_to_url = [&](StringView url_string) -> Optional<URL::URL> {
auto url = complete_url(url_string);
if (url.is_valid()) {
if (url.has_value()) {
transaction.commit();
return url;
}

View file

@ -194,7 +194,9 @@ String FormAssociatedElement::form_action() const
}
auto document_base_url = html_element.document().base_url();
return document_base_url.complete_url(form_action_attribute.value()).to_string();
if (auto maybe_url = document_base_url.complete_url(form_action_attribute.value()); maybe_url.has_value())
return maybe_url->to_string();
return {};
}
WebIDL::ExceptionOr<void> FormAssociatedElement::set_form_action(String const& value)

View file

@ -74,12 +74,12 @@ void HTMLBaseElement::set_the_frozen_base_url()
// 3. Set element's frozen base URL to document's fallback base URL, if urlRecord is failure or running Is base allowed for Document? on the resulting URL record and document returns "Blocked", and to urlRecord otherwise.
// FIXME: Apply "Is base allowed for Document?" CSP
if (!url_record.is_valid()) {
if (!url_record.has_value()) {
m_frozen_base_url = document.fallback_base_url();
return;
}
m_frozen_base_url = move(url_record);
m_frozen_base_url = url_record.release_value();
}
// https://html.spec.whatwg.org/multipage/semantics.html#dom-base-href
@ -96,11 +96,11 @@ String HTMLBaseElement::href() const
auto url_record = document.fallback_base_url().complete_url(url);
// 4. If urlRecord is failure, return url.
if (!url_record.is_valid())
if (!url_record.has_value())
return url;
// 5. Return the serialization of urlRecord.
return url_record.to_string();
return url_record->to_string();
}
// https://html.spec.whatwg.org/multipage/semantics.html#dom-base-href

View file

@ -619,7 +619,9 @@ String HTMLFormElement::action() const
return document().url_string();
}
return document().base_url().complete_url(form_action_attribute.value()).to_string();
if (auto maybe_url = document().base_url().complete_url(form_action_attribute.value()); maybe_url.has_value())
return maybe_url->to_string();
return {};
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-fs-action

View file

@ -25,7 +25,9 @@ GC::Ptr<SVGGeometryElement const> SVGTextPathElement::path_or_shape() const
if (!href.has_value())
return {};
auto url = document().url().complete_url(*href);
return try_resolve_url_to<SVGGeometryElement const>(url);
if (!url.has_value())
return {};
return try_resolve_url_to<SVGGeometryElement const>(*url);
}
void SVGTextPathElement::initialize(JS::Realm& realm)

View file

@ -79,19 +79,19 @@ void SVGUseElement::process_the_url(Optional<String> const& href)
// a same-document URL reference, and processing the URL must continue as indicated in Identifying
// the target element with the current document as the referenced document.
m_href = document().url().complete_url(href.value_or(String {}));
if (!m_href.is_valid())
if (!m_href.has_value())
return;
if (is_referrenced_element_same_document()) {
clone_element_tree_as_our_shadow_tree(referenced_element());
} else {
fetch_the_document(m_href);
fetch_the_document(*m_href);
}
}
bool SVGUseElement::is_referrenced_element_same_document() const
{
return m_href.equals(document().url(), URL::ExcludeFragment::Yes);
return m_href->equals(document().url(), URL::ExcludeFragment::Yes);
}
Gfx::AffineTransform SVGUseElement::element_transform() const
@ -121,11 +121,11 @@ void SVGUseElement::svg_element_changed(SVGElement& svg_element)
void SVGUseElement::svg_element_removed(SVGElement& svg_element)
{
if (!m_href.fragment().has_value() || !is_referrenced_element_same_document()) {
if (!m_href.has_value() || !m_href->fragment().has_value() || !is_referrenced_element_same_document()) {
return;
}
if (AK::StringUtils::matches(svg_element.get_attribute_value("id"_fly_string), m_href.fragment().value())) {
if (AK::StringUtils::matches(svg_element.get_attribute_value("id"_fly_string), m_href->fragment().value())) {
shadow_root()->remove_all_children();
}
}
@ -133,14 +133,17 @@ void SVGUseElement::svg_element_removed(SVGElement& svg_element)
// https://svgwg.org/svg2-draft/linking.html#processingURL-target
GC::Ptr<DOM::Element> SVGUseElement::referenced_element()
{
if (!m_href.is_valid())
if (!m_href.has_value())
return nullptr;
if (!m_href.fragment().has_value())
if (!m_href->is_valid())
return nullptr;
if (!m_href->fragment().has_value())
return nullptr;
if (is_referrenced_element_same_document())
return document().get_element_by_id(*m_href.fragment());
return document().get_element_by_id(*m_href->fragment());
if (!m_resource_request)
return nullptr;
@ -149,7 +152,7 @@ GC::Ptr<DOM::Element> SVGUseElement::referenced_element()
if (!data || !is<SVG::SVGDecodedImageData>(*data))
return nullptr;
return as<SVG::SVGDecodedImageData>(*data).svg_document().get_element_by_id(*m_href.fragment());
return as<SVG::SVGDecodedImageData>(*data).svg_document().get_element_by_id(*m_href->fragment());
}
// https://svgwg.org/svg2-draft/linking.html#processingURL-fetch

View file

@ -65,7 +65,7 @@ private:
Optional<float> m_x;
Optional<float> m_y;
URL::URL m_href;
Optional<URL::URL> m_href;
GC::Ptr<DOM::DocumentObserver> m_document_observer;
GC::Ptr<HTML::SharedResourceRequest> m_resource_request;

View file

@ -195,10 +195,10 @@ TEST_CASE(file_url_serialization)
TEST_CASE(file_url_relative)
{
EXPECT_EQ(URL::URL("https://vkoskiv.com/index.html"sv).complete_url("/static/foo.js"sv).serialize(), "https://vkoskiv.com/static/foo.js");
EXPECT_EQ(URL::URL("file:///home/vkoskiv/test/index.html"sv).complete_url("/static/foo.js"sv).serialize(), "file:///static/foo.js");
EXPECT_EQ(URL::URL("https://vkoskiv.com/index.html"sv).complete_url("static/foo.js"sv).serialize(), "https://vkoskiv.com/static/foo.js");
EXPECT_EQ(URL::URL("file:///home/vkoskiv/test/index.html"sv).complete_url("static/foo.js"sv).serialize(), "file:///home/vkoskiv/test/static/foo.js");
EXPECT_EQ(URL::URL("https://vkoskiv.com/index.html"sv).complete_url("/static/foo.js"sv)->serialize(), "https://vkoskiv.com/static/foo.js");
EXPECT_EQ(URL::URL("file:///home/vkoskiv/test/index.html"sv).complete_url("/static/foo.js"sv)->serialize(), "file:///static/foo.js");
EXPECT_EQ(URL::URL("https://vkoskiv.com/index.html"sv).complete_url("static/foo.js"sv)->serialize(), "https://vkoskiv.com/static/foo.js");
EXPECT_EQ(URL::URL("file:///home/vkoskiv/test/index.html"sv).complete_url("static/foo.js"sv)->serialize(), "file:///home/vkoskiv/test/static/foo.js");
}
TEST_CASE(about_url)
@ -243,10 +243,10 @@ TEST_CASE(mailto_url_with_subject)
TEST_CASE(trailing_slash_with_complete_url)
{
EXPECT_EQ(URL::URL("http://a/b/"sv).complete_url("c/"sv).serialize(), "http://a/b/c/");
EXPECT_EQ(URL::URL("http://a/b/"sv).complete_url("c"sv).serialize(), "http://a/b/c");
EXPECT_EQ(URL::URL("http://a/b"sv).complete_url("c/"sv).serialize(), "http://a/c/");
EXPECT_EQ(URL::URL("http://a/b"sv).complete_url("c"sv).serialize(), "http://a/c");
EXPECT_EQ(URL::URL("http://a/b/"sv).complete_url("c/"sv)->serialize(), "http://a/b/c/");
EXPECT_EQ(URL::URL("http://a/b/"sv).complete_url("c"sv)->serialize(), "http://a/b/c");
EXPECT_EQ(URL::URL("http://a/b"sv).complete_url("c/"sv)->serialize(), "http://a/c/");
EXPECT_EQ(URL::URL("http://a/b"sv).complete_url("c"sv)->serialize(), "http://a/c");
}
TEST_CASE(trailing_port)
@ -297,15 +297,15 @@ TEST_CASE(create_with_file_scheme)
TEST_CASE(complete_url)
{
URL::URL base_url("http://serenityos.org/index.html#fragment"sv);
URL::URL url = base_url.complete_url("test.html"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.serialized_host(), "serenityos.org");
EXPECT_EQ(url.serialize_path(), "/test.html");
EXPECT(!url.query().has_value());
EXPECT_EQ(url.cannot_be_a_base_url(), false);
auto url = base_url.complete_url("test.html"sv);
EXPECT(url.has_value());
EXPECT_EQ(url->scheme(), "http");
EXPECT_EQ(url->serialized_host(), "serenityos.org");
EXPECT_EQ(url->serialize_path(), "/test.html");
EXPECT(!url->query().has_value());
EXPECT_EQ(url->cannot_be_a_base_url(), false);
EXPECT(base_url.complete_url("../index.html#fragment"sv).equals(base_url));
EXPECT(base_url.complete_url("../index.html#fragment"sv)->equals(base_url));
}
TEST_CASE(leading_whitespace)
@ -385,8 +385,8 @@ TEST_CASE(complete_file_url_with_base)
EXPECT_EQ(url.path_segment_at_index(1), "index.html");
auto sub_url = url.complete_url("js/app.js"sv);
EXPECT(sub_url.is_valid());
EXPECT_EQ(sub_url.serialize_path(), "/home/js/app.js");
EXPECT(sub_url.has_value());
EXPECT_EQ(sub_url->serialize_path(), "/home/js/app.js");
}
TEST_CASE(empty_url_with_base_url)

View file

@ -102,12 +102,12 @@ TEST_CASE(data_url_base64_encoded_with_inline_whitespace)
TEST_CASE(data_url_completed_with_fragment)
{
auto url = URL::URL("data:text/plain,test"sv).complete_url("#a"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT_EQ(url.fragment(), "a");
EXPECT(!url.host().has_value());
EXPECT(url.has_value());
EXPECT_EQ(url->scheme(), "data");
EXPECT_EQ(url->fragment(), "a");
EXPECT(!url->host().has_value());
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(*url));
EXPECT_EQ(data_url.mime_type.serialized(), "text/plain");
EXPECT_EQ(StringView(data_url.body.bytes()), "test"sv);
}