diff --git a/Libraries/LibURL/URL.cpp b/Libraries/LibURL/URL.cpp index 308cd344487..f36693ae715 100644 --- a/Libraries/LibURL/URL.cpp +++ b/Libraries/LibURL/URL.cpp @@ -33,16 +33,12 @@ URL::URL(StringView string) } } -URL URL::complete_url(StringView relative_url) const +Optional 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 diff --git a/Libraries/LibURL/URL.h b/Libraries/LibURL/URL.h index c699162a4f6..86782ebd99b 100644 --- a/Libraries/LibURL/URL.h +++ b/Libraries/LibURL/URL.h @@ -135,7 +135,7 @@ public: bool equals(URL const& other, ExcludeFragment = ExcludeFragment::No) const; - URL complete_url(StringView) const; + Optional complete_url(StringView) const; [[nodiscard]] bool operator==(URL const& other) const { diff --git a/Libraries/LibWeb/CSS/CSSStyleSheet.cpp b/Libraries/LibWeb/CSS/CSSStyleSheet.cpp index a91b9b66759..25fdb128ce5 100644 --- a/Libraries/LibWeb/CSS/CSSStyleSheet.cpp +++ b/Libraries/LibWeb/CSS/CSSStyleSheet.cpp @@ -45,8 +45,8 @@ WebIDL::ExceptionOr> 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 = 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); diff --git a/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Libraries/LibWeb/CSS/Parser/Parser.cpp index accd1ffbd21..c606f8aefbc 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -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 Parser::complete_url(StringView relative_url) const { return m_url.complete_url(relative_url); } diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 1863ecb682e..b06e67fc7e7 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -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 complete_url(StringView) const; GC::Ptr m_document; GC::Ptr m_realm; diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index 2e24636aa33..bc4819bc42c 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -2470,7 +2470,7 @@ Optional Parser::parse_url_function(TokenStream& token auto convert_string_to_url = [&](StringView url_string) -> Optional { auto url = complete_url(url_string); - if (url.is_valid()) { + if (url.has_value()) { transaction.commit(); return url; } diff --git a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp index 37e34eb07be..effb79e8488 100644 --- a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp +++ b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp @@ -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 FormAssociatedElement::set_form_action(String const& value) diff --git a/Libraries/LibWeb/HTML/HTMLBaseElement.cpp b/Libraries/LibWeb/HTML/HTMLBaseElement.cpp index d484629f4ce..a7456805650 100644 --- a/Libraries/LibWeb/HTML/HTMLBaseElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLBaseElement.cpp @@ -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 diff --git a/Libraries/LibWeb/HTML/HTMLFormElement.cpp b/Libraries/LibWeb/HTML/HTMLFormElement.cpp index 8b6db2f250a..7d17382c896 100644 --- a/Libraries/LibWeb/HTML/HTMLFormElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLFormElement.cpp @@ -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 diff --git a/Libraries/LibWeb/SVG/SVGTextPathElement.cpp b/Libraries/LibWeb/SVG/SVGTextPathElement.cpp index 044cab2b026..f583c568129 100644 --- a/Libraries/LibWeb/SVG/SVGTextPathElement.cpp +++ b/Libraries/LibWeb/SVG/SVGTextPathElement.cpp @@ -25,7 +25,9 @@ GC::Ptr SVGTextPathElement::path_or_shape() const if (!href.has_value()) return {}; auto url = document().url().complete_url(*href); - return try_resolve_url_to(url); + if (!url.has_value()) + return {}; + return try_resolve_url_to(*url); } void SVGTextPathElement::initialize(JS::Realm& realm) diff --git a/Libraries/LibWeb/SVG/SVGUseElement.cpp b/Libraries/LibWeb/SVG/SVGUseElement.cpp index 2cb4344529b..ac8f2744103 100644 --- a/Libraries/LibWeb/SVG/SVGUseElement.cpp +++ b/Libraries/LibWeb/SVG/SVGUseElement.cpp @@ -79,19 +79,19 @@ void SVGUseElement::process_the_url(Optional 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 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 SVGUseElement::referenced_element() if (!data || !is(*data)) return nullptr; - return as(*data).svg_document().get_element_by_id(*m_href.fragment()); + return as(*data).svg_document().get_element_by_id(*m_href->fragment()); } // https://svgwg.org/svg2-draft/linking.html#processingURL-fetch diff --git a/Libraries/LibWeb/SVG/SVGUseElement.h b/Libraries/LibWeb/SVG/SVGUseElement.h index 5e839c05fae..7a1dea33859 100644 --- a/Libraries/LibWeb/SVG/SVGUseElement.h +++ b/Libraries/LibWeb/SVG/SVGUseElement.h @@ -65,7 +65,7 @@ private: Optional m_x; Optional m_y; - URL::URL m_href; + Optional m_href; GC::Ptr m_document_observer; GC::Ptr m_resource_request; diff --git a/Tests/LibURL/TestURL.cpp b/Tests/LibURL/TestURL.cpp index 18858d53a42..ba4f13b7600 100644 --- a/Tests/LibURL/TestURL.cpp +++ b/Tests/LibURL/TestURL.cpp @@ -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) diff --git a/Tests/LibWeb/TestFetchURL.cpp b/Tests/LibWeb/TestFetchURL.cpp index 031ab0ed1ac..265fb889366 100644 --- a/Tests/LibWeb/TestFetchURL.cpp +++ b/Tests/LibWeb/TestFetchURL.cpp @@ -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); }