LibWeb/CSS: Don't resolve @import URLs until they are used

The regression in the "conditional-CSSGroupingRule" test is we now fail
the "inserting an `@import`" subtests differently and the subtests
aren't independent. Specifically, we don't yet implement the checks in
`CSSRuleList::insert_a_css_rule()` that reject certain rules from being
inserted. Previously we didn't insert the `@import` rule because we
failed to parse its relative URL. Now we parse it correctly, we end up
inserting it.
This commit is contained in:
Sam Atkins 2025-04-08 18:16:38 +01:00 committed by Tim Ledbetter
parent ca0890ce16
commit 0f42d5ec3e
Notes: github-actions[bot] 2025-04-09 17:47:10 +00:00
9 changed files with 41 additions and 46 deletions

View file

@ -9,7 +9,6 @@
#include <AK/Debug.h>
#include <AK/ScopeGuard.h>
#include <LibTextCodec/Decoder.h>
#include <LibURL/URL.h>
#include <LibWeb/Bindings/CSSImportRulePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSImportRule.h>
@ -17,18 +16,19 @@
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleComputer.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOMURL/DOMURL.h>
#include <LibWeb/HTML/Window.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSImportRule);
GC::Ref<CSSImportRule> CSSImportRule::create(JS::Realm& realm, ::URL::URL url, GC::Ptr<DOM::Document> document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
GC::Ref<CSSImportRule> CSSImportRule::create(JS::Realm& realm, URL url, GC::Ptr<DOM::Document> document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
{
return realm.create<CSSImportRule>(realm, move(url), document, move(supports), move(media_query_list));
}
CSSImportRule::CSSImportRule(JS::Realm& realm, ::URL::URL url, GC::Ptr<DOM::Document> document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
CSSImportRule::CSSImportRule(JS::Realm& realm, URL url, GC::Ptr<DOM::Document> document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> 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 rules 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 rules URL and parentStylesheets 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 importedStylesheets origin-clean flag to parentStylesheets origin-clean flag.
imported_style_sheet->set_origin_clean(parent_style_sheet->is_origin_clean());