diff --git a/Libraries/LibURL/URL.cpp b/Libraries/LibURL/URL.cpp index 308cd34448..f36693ae71 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 c699162a4f..86782ebd99 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 a91b9b6675..25fdb128ce 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 accd1ffbd2..c606f8aefb 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 1863ecb682..b06e67fc7e 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 2e24636aa3..bc4819bc42 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 37e34eb07b..effb79e848 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 d484629f4c..a745680565 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 8b6db2f250..7d17382c89 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 044cab2b02..f583c56812 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 2cb4344529..ac8f274410 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 5e839c05fa..7a1dea3385 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 18858d53a4..ba4f13b760 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 031ab0ed1a..265fb88936 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); }