2021-02-21 18:36:34 +02:00
/*
* Copyright ( c ) 2021 , the SerenityOS developers .
2024-12-20 20:25:40 +00:00
* Copyright ( c ) 2021 - 2024 , Sam Atkins < sam @ ladybird . org >
2024-10-12 16:52:36 +02:00
* Copyright ( c ) 2022 - 2024 , Andreas Kling < andreas @ ladybird . org >
2021-02-21 18:36:34 +02:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-02-21 18:36:34 +02:00
*/
2021-11-18 17:34:53 +00:00
# include <AK/Debug.h>
2024-12-20 20:25:40 +00:00
# include <AK/ScopeGuard.h>
2024-10-12 16:52:36 +02:00
# include <LibTextCodec/Decoder.h>
2022-09-24 16:34:04 -06:00
# include <LibWeb/Bindings/CSSImportRulePrototype.h>
# include <LibWeb/Bindings/Intrinsics.h>
2021-02-21 18:36:34 +02:00
# include <LibWeb/CSS/CSSImportRule.h>
2024-12-20 20:25:40 +00:00
# include <LibWeb/CSS/Fetch.h>
2021-11-18 17:34:53 +00:00
# include <LibWeb/CSS/Parser/Parser.h>
2023-05-08 06:37:18 +02:00
# include <LibWeb/CSS/StyleComputer.h>
2021-11-18 17:34:53 +00:00
# include <LibWeb/DOM/Document.h>
2025-04-08 18:16:38 +01:00
# include <LibWeb/DOMURL/DOMURL.h>
2022-08-28 13:42:07 +02:00
# include <LibWeb/HTML/Window.h>
2021-02-21 18:36:34 +02:00
namespace Web : : CSS {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( CSSImportRule ) ;
2023-11-19 19:47:52 +01:00
2025-04-08 18:16:38 +01:00
GC : : Ref < CSSImportRule > CSSImportRule : : create ( JS : : Realm & realm , URL url , GC : : Ptr < DOM : : Document > document , RefPtr < Supports > supports , Vector < NonnullRefPtr < MediaQuery > > media_query_list )
2022-08-07 15:46:44 +02:00
{
2025-04-08 16:50:51 +01:00
return realm . create < CSSImportRule > ( realm , move ( url ) , document , move ( supports ) , move ( media_query_list ) ) ;
2022-08-07 15:46:44 +02:00
}
2025-04-08 18:16:38 +01:00
CSSImportRule : : CSSImportRule ( JS : : Realm & realm , URL url , GC : : Ptr < DOM : : Document > document , RefPtr < Supports > supports , Vector < NonnullRefPtr < MediaQuery > > media_query_list )
2025-04-08 16:50:51 +01:00
: CSSRule ( realm , Type : : Import )
2022-08-07 15:46:44 +02:00
, m_url ( move ( url ) )
2021-11-18 17:34:53 +00:00
, m_document ( document )
2025-04-08 16:50:51 +01:00
, m_supports ( move ( supports ) )
2025-04-01 14:32:11 +01:00
, m_media_query_list ( move ( media_query_list ) )
2021-02-21 18:36:34 +02:00
{
}
2023-08-07 08:41:28 +02:00
void CSSImportRule : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( CSSImportRule ) ;
2023-01-10 06:28:20 -05:00
}
2022-08-07 15:46:44 +02:00
void CSSImportRule : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2022-09-03 15:44:06 +02:00
visitor . visit ( m_document ) ;
2022-08-07 15:46:44 +02:00
visitor . visit ( m_style_sheet ) ;
}
2024-12-20 20:25:40 +00:00
void CSSImportRule : : set_parent_style_sheet ( CSSStyleSheet * parent_style_sheet )
{
Base : : set_parent_style_sheet ( parent_style_sheet ) ;
// Crude detection of whether we're already fetching.
if ( m_style_sheet | | m_document_load_event_delayer . has_value ( ) )
return ;
2025-04-09 11:33:18 +01:00
// Only try to fetch if we now have a parent
if ( parent_style_sheet )
fetch ( ) ;
2024-12-20 20:25:40 +00:00
}
2021-10-15 19:38:39 +01:00
// https://www.w3.org/TR/cssom/#serialize-a-css-rule
2023-11-20 23:16:39 +13:00
String CSSImportRule : : serialized ( ) const
2021-10-01 19:57:45 +02:00
{
StringBuilder builder ;
// The result of concatenating the following:
// 1. The string "@import" followed by a single SPACE (U+0020).
builder . append ( " @import " sv ) ;
// 2. The result of performing serialize a URL on the rule’ s location.
2025-04-08 18:16:38 +01:00
builder . append ( m_url . to_string ( ) ) ;
2021-10-01 19:57:45 +02:00
2025-03-19 11:23:33 +00:00
// 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
if ( m_supports )
builder . appendff ( " supports({}) " , m_supports - > to_string ( ) ) ;
2025-04-01 14:32:11 +01:00
// 3. If the rule’ s associated media list is not empty, a single SPACE (U+0020) followed by the result of performing serialize a media query list on the media list.
if ( ! m_media_query_list . is_empty ( ) )
builder . appendff ( " {} " , serialize_a_media_query_list ( m_media_query_list ) ) ;
2021-10-01 19:57:45 +02:00
// 4. The string ";", i.e., SEMICOLON (U+003B).
builder . append ( ' ; ' ) ;
2023-11-20 23:16:39 +13:00
return MUST ( builder . to_string ( ) ) ;
2021-10-01 19:57:45 +02:00
}
2024-12-20 20:25:40 +00:00
// https://drafts.csswg.org/css-cascade-4/#fetch-an-import
void CSSImportRule : : fetch ( )
2021-11-18 17:34:53 +00:00
{
2024-12-20 20:25:40 +00:00
dbgln_if ( CSS_LOADER_DEBUG , " CSSImportRule: Loading import URL: {} " , m_url ) ;
// To fetch an @import, given an @import rule rule:
// 1. Let parentStylesheet be rule’ s parent CSS style sheet. [CSSOM]
VERIFY ( parent_style_sheet ( ) ) ;
auto & parent_style_sheet = * this - > parent_style_sheet ( ) ;
2025-03-19 11:23:33 +00:00
// 2. If rule has a <supports-condition>, and that condition is not true, return.
if ( m_supports & & ! m_supports - > matches ( ) ) {
return ;
}
2024-12-20 20:25:40 +00:00
// 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]
2025-04-08 18:16:38 +01:00
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 ;
}
2024-12-20 20:25:40 +00:00
// 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:
2025-04-08 18:16:38 +01:00
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 ) {
2024-12-20 20:25:40 +00:00
// AD-HOC: Stop delaying the load event.
ScopeGuard guard = [ strong_this ] {
strong_this - > m_document_load_event_delayer . clear ( ) ;
} ;
// 1. If byteStream is not a byte stream, return.
auto byte_stream = maybe_byte_stream . template get_pointer < ByteBuffer > ( ) ;
if ( ! byte_stream )
return ;
// FIXME: 2. If parentStylesheet is in quirks mode and response is CORS-same-origin, let content type be "text/css".
// Otherwise, let content type be the Content Type metadata of response.
auto content_type = " text/css " sv ;
// 3. If content type is not "text/css", return.
if ( content_type ! = " text/css " sv ) {
dbgln_if ( CSS_LOADER_DEBUG , " CSSImportRule: Rejecting loaded style sheet; content type isn't text/css; is: '{}' " , content_type ) ;
return ;
}
// 4. Let importedStylesheet be the result of parsing byteStream given parsedUrl.
// FIXME: Tidy up our parsing API. For now, do the decoding here.
// FIXME: Get the encoding from the response somehow.
auto encoding = " utf-8 " sv ;
auto maybe_decoder = TextCodec : : decoder_for ( encoding ) ;
if ( ! maybe_decoder . has_value ( ) ) {
2025-04-08 18:16:38 +01:00
dbgln_if ( CSS_LOADER_DEBUG , " CSSImportRule: Failed to decode CSS file: {} Unsupported encoding: {} " , parsed_url , encoding ) ;
2024-12-20 20:25:40 +00:00
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 ( ) ) {
2025-04-08 18:16:38 +01:00
dbgln_if ( CSS_LOADER_DEBUG , " CSSImportRule: Failed to decode CSS file: {} Encoding was: {} " , parsed_url , encoding ) ;
2024-12-20 20:25:40 +00:00
return ;
}
auto decoded = decoded_or_error . release_value ( ) ;
2025-04-14 14:04:36 +01:00
auto imported_style_sheet = parse_css_stylesheet ( Parser : : ParsingParams ( * strong_this - > m_document , parsed_url ) , decoded , parsed_url , strong_this - > m_media_query_list ) ;
2024-12-20 20:25:40 +00:00
// 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 ( ) ) ;
// 6. If response is not CORS-same-origin, unset importedStylesheet’ s origin-clean flag.
if ( ! response - > is_cors_cross_origin ( ) )
imported_style_sheet - > set_origin_clean ( false ) ;
// 7. Set rule’ s styleSheet to importedStylesheet.
2025-04-14 14:04:36 +01:00
strong_this - > set_style_sheet ( imported_style_sheet ) ;
2024-12-20 20:25:40 +00:00
} ) ;
2021-11-18 17:34:53 +00:00
}
2024-12-20 20:25:40 +00:00
void CSSImportRule : : set_style_sheet ( GC : : Ref < CSSStyleSheet > style_sheet )
2021-11-18 17:34:53 +00:00
{
2024-12-20 20:25:40 +00:00
m_style_sheet = style_sheet ;
2024-08-23 17:47:05 +01:00
m_style_sheet - > set_owner_css_rule ( this ) ;
2022-03-14 20:31:57 +01:00
m_document - > style_computer ( ) . invalidate_rule_cache ( ) ;
2023-08-12 17:28:40 +02:00
m_document - > style_computer ( ) . load_fonts_from_sheet ( * m_style_sheet ) ;
2024-09-04 10:01:08 +02:00
m_document - > invalidate_style ( DOM : : StyleInvalidationReason : : CSSImportRule ) ;
2021-11-18 17:34:53 +00:00
}
2025-04-02 09:39:51 +01:00
// https://drafts.csswg.org/cssom/#dom-cssimportrule-media
GC : : Ptr < MediaList > CSSImportRule : : media ( ) const
{
// The media attribute must return the value of the media attribute of the associated CSS style sheet.
if ( ! m_style_sheet )
return nullptr ;
return m_style_sheet - > media ( ) ;
}
2025-03-19 11:32:34 +00:00
Optional < String > CSSImportRule : : supports_text ( ) const
{
if ( ! m_supports )
return { } ;
return m_supports - > to_string ( ) ;
}
2021-02-21 18:36:34 +02:00
}