diff --git a/Libraries/LibWeb/CSS/CSSImportRule.cpp b/Libraries/LibWeb/CSS/CSSImportRule.cpp index e2610237cb3..ea5773c50bb 100644 --- a/Libraries/LibWeb/CSS/CSSImportRule.cpp +++ b/Libraries/LibWeb/CSS/CSSImportRule.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -17,18 +16,19 @@ #include #include #include +#include #include namespace Web::CSS { GC_DEFINE_ALLOCATOR(CSSImportRule); -GC::Ref CSSImportRule::create(JS::Realm& realm, ::URL::URL url, GC::Ptr document, RefPtr supports, Vector> media_query_list) +GC::Ref CSSImportRule::create(JS::Realm& realm, URL url, GC::Ptr document, RefPtr supports, Vector> media_query_list) { return realm.create(realm, move(url), document, move(supports), move(media_query_list)); } -CSSImportRule::CSSImportRule(JS::Realm& realm, ::URL::URL url, GC::Ptr document, RefPtr supports, Vector> media_query_list) +CSSImportRule::CSSImportRule(JS::Realm& realm, URL url, GC::Ptr document, RefPtr supports, Vector> media_query_list) : CSSRule(realm, Type::Import) , m_url(move(url)) , m_document(document) @@ -72,7 +72,7 @@ String CSSImportRule::serialized() const builder.append("@import "sv); // 2. The result of performing serialize a URL on the rule’s location. - serialize_a_url(builder, m_url.to_string()); + builder.append(m_url.to_string()); // AD-HOC: Serialize the rule's supports condition if it exists. // This isn't currently specified, but major browsers include this in their serialization of import rules @@ -106,15 +106,18 @@ void CSSImportRule::fetch() // 3. Let parsedUrl be the result of the URL parser steps with rule’s URL and parentStylesheet’s location. // If the algorithm returns an error, return. [CSSOM] - // FIXME: Stop producing a URL::URL when parsing the @import - auto parsed_url = url().to_string(); + auto parsed_url = DOMURL::parse(href(), parent_style_sheet.location()); + if (!parsed_url.has_value()) { + dbgln("Unable to parse @import url `{}` parent location `{}` as a URL.", href(), parent_style_sheet.location()); + return; + } // FIXME: Figure out the "correct" way to delay the load event. m_document_load_event_delayer.emplace(*m_document); // 4. Fetch a style resource from parsedUrl, with stylesheet parentStylesheet, destination "style", CORS mode "no-cors", and processResponse being the following steps given response response and byte stream, null or failure byteStream: - fetch_a_style_resource(parsed_url, parent_style_sheet, Fetch::Infrastructure::Request::Destination::Style, CorsMode::NoCors, - [strong_this = GC::Ref { *this }, parent_style_sheet = GC::Ref { parent_style_sheet }](auto response, auto maybe_byte_stream) { + fetch_a_style_resource(parsed_url->to_string(), parent_style_sheet, Fetch::Infrastructure::Request::Destination::Style, CorsMode::NoCors, + [strong_this = GC::Ref { *this }, parent_style_sheet = GC::Ref { parent_style_sheet }, parsed_url = parsed_url.value()](auto response, auto maybe_byte_stream) { // AD-HOC: Stop delaying the load event. ScopeGuard guard = [strong_this] { strong_this->m_document_load_event_delayer.clear(); @@ -141,19 +144,19 @@ void CSSImportRule::fetch() auto encoding = "utf-8"sv; auto maybe_decoder = TextCodec::decoder_for(encoding); if (!maybe_decoder.has_value()) { - dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Unsupported encoding: {}", strong_this->url(), encoding); + dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Unsupported encoding: {}", parsed_url, encoding); return; } auto& decoder = maybe_decoder.release_value(); auto decoded_or_error = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(decoder, *byte_stream); if (decoded_or_error.is_error()) { - dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Encoding was: {}", strong_this->url(), encoding); + dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Encoding was: {}", parsed_url, encoding); return; } auto decoded = decoded_or_error.release_value(); - auto* imported_style_sheet = parse_css_stylesheet(Parser::ParsingParams(*strong_this->m_document, strong_this->url()), decoded, strong_this->url(), strong_this->m_media_query_list); + auto* imported_style_sheet = parse_css_stylesheet(Parser::ParsingParams(*strong_this->m_document, parsed_url), decoded, parsed_url, strong_this->m_media_query_list); // 5. Set importedStylesheet’s origin-clean flag to parentStylesheet’s origin-clean flag. imported_style_sheet->set_origin_clean(parent_style_sheet->is_origin_clean()); diff --git a/Libraries/LibWeb/CSS/CSSImportRule.h b/Libraries/LibWeb/CSS/CSSImportRule.h index 72df3a1d2cb..a9f3abb3480 100644 --- a/Libraries/LibWeb/CSS/CSSImportRule.h +++ b/Libraries/LibWeb/CSS/CSSImportRule.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, the SerenityOS developers. - * Copyright (c) 2021-2024, Sam Atkins + * Copyright (c) 2021-2025, Sam Atkins * Copyright (c) 2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause @@ -8,9 +8,9 @@ #pragma once -#include #include #include +#include #include namespace Web::CSS { @@ -21,13 +21,12 @@ class CSSImportRule final GC_DECLARE_ALLOCATOR(CSSImportRule); public: - [[nodiscard]] static GC::Ref create(JS::Realm&, ::URL::URL, GC::Ptr, RefPtr, Vector>); + [[nodiscard]] static GC::Ref create(JS::Realm&, URL, GC::Ptr, RefPtr, Vector>); virtual ~CSSImportRule() = default; - ::URL::URL const& url() const { return m_url; } - // FIXME: This should return only the specified part of the url. eg, "stuff/foo.css", not "https://example.com/stuff/foo.css". - String href() const { return m_url.to_string(); } + URL const& url() const { return m_url; } + String href() const { return m_url.url(); } CSSStyleSheet* loaded_style_sheet() { return m_style_sheet; } CSSStyleSheet const* loaded_style_sheet() const { return m_style_sheet; } @@ -37,7 +36,7 @@ public: Optional supports_text() const; private: - CSSImportRule(JS::Realm&, ::URL::URL, GC::Ptr, RefPtr, Vector>); + CSSImportRule(JS::Realm&, URL, GC::Ptr, RefPtr, Vector>); virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; @@ -49,7 +48,7 @@ private: void fetch(); void set_style_sheet(GC::Ref); - ::URL::URL m_url; + URL m_url; GC::Ptr m_document; RefPtr m_supports; Vector> m_media_query_list; diff --git a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp index aed7a88923b..de02d4ba10f 100644 --- a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp @@ -162,13 +162,6 @@ GC::Ptr Parser::convert_to_import_rule(AtRule const& rule) return {}; } - // FIXME: Stop completing the URL here - auto resolved_url = complete_url(url->url()); - if (!resolved_url.has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @import rule: Unable to complete `{}` as URL.", url->url()); - return {}; - } - tokens.discard_whitespace(); // FIXME: Implement layer support. RefPtr supports {}; @@ -198,7 +191,7 @@ GC::Ptr Parser::convert_to_import_rule(AtRule const& rule) return {}; } - return CSSImportRule::create(realm(), resolved_url.release_value(), const_cast(m_document.ptr()), supports, move(media_query_list)); + return CSSImportRule::create(realm(), url.release_value(), const_cast(m_document.ptr()), supports, move(media_query_list)); } Optional Parser::parse_layer_name(TokenStream& tokens, AllowBlankLayerName allow_blank_layer_name) diff --git a/Libraries/LibWeb/Dump.cpp b/Libraries/LibWeb/Dump.cpp index 2803277488f..a8fe444eb28 100644 --- a/Libraries/LibWeb/Dump.cpp +++ b/Libraries/LibWeb/Dump.cpp @@ -790,7 +790,7 @@ void dump_font_face_rule(StringBuilder& builder, CSS::CSSFontFaceRule const& rul void dump_import_rule(StringBuilder& builder, CSS::CSSImportRule const& rule, int indent_levels) { indent(builder, indent_levels); - builder.appendff(" Document URL: {}\n", rule.url()); + builder.appendff(" Document URL: {}\n", rule.url().to_string()); } void dump_layer_block_rule(StringBuilder& builder, CSS::CSSLayerBlockRule const& layer_block, int indent_levels) diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index 1749b33db44..86c36aad0bb 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -848,7 +848,7 @@ static void gather_style_sheets(Vector& results, // We can gather this anyway, and hope it loads later results.append({ .type = Web::CSS::StyleSheetIdentifier::Type::ImportRule, - .url = import_rule->url().to_string(), + .url = import_rule->href(), }); } } diff --git a/Tests/LibWeb/Text/expected/css/CSSImportRule-supportsText.txt b/Tests/LibWeb/Text/expected/css/CSSImportRule-supportsText.txt index 52162806d0d..d9ef6f2cb1e 100644 --- a/Tests/LibWeb/Text/expected/css/CSSImportRule-supportsText.txt +++ b/Tests/LibWeb/Text/expected/css/CSSImportRule-supportsText.txt @@ -1,4 +1,4 @@ -cssText: @import url("https://something.invalid/") supports(foo: bar); +cssText: @import url("https://something.invalid") supports(foo: bar); supportsText: foo: bar -cssText: @import url("https://something.invalid/") supports((display: block) and (foo: bar)); +cssText: @import url("https://something.invalid") supports((display: block) and (foo: bar)); supportsText: (display: block) and (foo: bar) diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/parsing/supports-import-parsing.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/parsing/supports-import-parsing.txt index 13f9fa737db..b58337dfe22 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/parsing/supports-import-parsing.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/parsing/supports-import-parsing.txt @@ -2,17 +2,17 @@ Harness status: OK Found 22 tests -5 Pass -17 Fail +9 Pass +13 Fail Pass @import url("nonexist.css") supports(); should be an invalid import rule due to an invalid supports() declaration Pass @import url("nonexist.css") supports(foo: bar); should be an invalid import rule due to an invalid supports() declaration Fail @import url("nonexist.css") supports(display:block); should be a valid supports() import rule Fail @import url("nonexist.css") supports((display:flex)); should be a valid supports() import rule Fail @import url("nonexist.css") supports(not (display: flex)); should be a valid supports() import rule -Fail @import url("nonexist.css") supports((display: flex) and (display: block)); should be a valid supports() import rule -Fail @import url("nonexist.css") supports((display: flex) or (display: block)); should be a valid supports() import rule -Fail @import url("nonexist.css") supports((display: flex) or (foo: bar)); should be a valid supports() import rule -Fail @import url("nonexist.css") supports(display: block !important); should be a valid supports() import rule +Pass @import url("nonexist.css") supports((display: flex) and (display: block)); should be a valid supports() import rule +Pass @import url("nonexist.css") supports((display: flex) or (display: block)); should be a valid supports() import rule +Pass @import url("nonexist.css") supports((display: flex) or (foo: bar)); should be a valid supports() import rule +Pass @import url("nonexist.css") supports(display: block !important); should be a valid supports() import rule Pass @import url("nonexist.css") layer supports(); should be an invalid import rule due to an invalid supports() declaration Pass @import url("nonexist.css") layer supports(foo: bar); should be an invalid import rule due to an invalid supports() declaration Fail @import url("nonexist.css") layer(A) supports((display: flex) or (foo: bar)); should be a valid supports() import rule diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-conditional/js/conditional-CSSGroupingRule.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-conditional/js/conditional-CSSGroupingRule.txt index e45e3813268..ea806d930f2 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-conditional/js/conditional-CSSGroupingRule.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-conditional/js/conditional-CSSGroupingRule.txt @@ -2,14 +2,14 @@ Harness status: OK Found 34 tests -30 Pass -4 Fail +26 Pass +8 Fail Pass @media is CSSGroupingRule Pass @media rule type Pass Empty @media rule length Fail insertRule of @import into @media -Pass insertRule into empty @media at bad index -Pass insertRule into @media updates length +Fail insertRule into empty @media at bad index +Fail insertRule into @media updates length Pass insertRule of valid @media into @media Pass insertRule of valid style rule into @media Fail insertRule of invalid @media into @media @@ -25,8 +25,8 @@ Pass @supports is CSSGroupingRule Pass @supports rule type Pass Empty @supports rule length Fail insertRule of @import into @supports -Pass insertRule into empty @supports at bad index -Pass insertRule into @supports updates length +Fail insertRule into empty @supports at bad index +Fail insertRule into @supports updates length Pass insertRule of valid @media into @supports Pass insertRule of valid style rule into @supports Fail insertRule of invalid @media into @supports diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssimportrule.txt b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssimportrule.txt index 17f7264c257..7bc6e91ba48 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssimportrule.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssimportrule.txt @@ -2,12 +2,12 @@ Harness status: OK Found 11 tests -7 Pass -4 Fail +8 Pass +3 Fail Pass CSSRule and CSSImportRule types Pass Type of CSSRule#type and constant values Pass Existence and writability of CSSRule attributes -Fail Values of CSSRule attributes +Pass Values of CSSRule attributes Pass Existence and writability of CSSImportRule attributes Fail Values of CSSImportRule attributes Fail CSSImportRule : MediaList mediaText attribute should be updated due to [PutForwards]