2021-03-07 16:14:04 +01:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2019 - 2022 , Andreas Kling < andreas @ ladybird . org >
2024-08-23 16:46:33 +01:00
* Copyright ( c ) 2022 - 2024 , Sam Atkins < sam @ ladybird . org >
2025-04-15 18:01:54 +01:00
* Copyright ( c ) 2024 - 2025 , Tim Ledbetter < tim . ledbetter @ ladybird . org >
2021-03-07 16:14:04 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-03-07 16:14:04 +01:00
*/
2025-02-16 14:45:52 +13:00
# include <LibURL/Parser.h>
2022-09-24 16:34:04 -06:00
# include <LibWeb/Bindings/CSSStyleSheetPrototype.h>
# include <LibWeb/Bindings/Intrinsics.h>
2024-08-23 16:46:33 +01:00
# include <LibWeb/CSS/CSSImportRule.h>
2021-03-07 16:14:04 +01:00
# include <LibWeb/CSS/CSSStyleSheet.h>
2021-09-29 21:12:39 +02:00
# include <LibWeb/CSS/Parser/Parser.h>
2023-05-08 06:37:18 +02:00
# include <LibWeb/CSS/StyleComputer.h>
2022-03-09 19:57:15 +01:00
# include <LibWeb/CSS/StyleSheetList.h>
# include <LibWeb/DOM/Document.h>
2024-10-25 12:38:19 -06:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2024-02-24 07:46:59 +00:00
# include <LibWeb/HTML/Window.h>
2024-02-24 07:46:59 +00:00
# include <LibWeb/Platform/EventLoopPlugin.h>
2022-09-25 17:03:42 +01:00
# include <LibWeb/WebIDL/ExceptionOr.h>
2021-03-07 16:14:04 +01:00
namespace Web : : CSS {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( CSSStyleSheet ) ;
2023-11-19 19:47:52 +01:00
2025-04-08 13:35:26 +01:00
GC : : Ref < CSSStyleSheet > CSSStyleSheet : : create ( JS : : Realm & realm , CSSRuleList & rules , MediaList & media , Optional < : : URL : : URL > location )
2021-03-07 16:14:04 +01:00
{
2024-11-14 05:50:17 +13:00
return realm . create < CSSStyleSheet > ( realm , rules , media , move ( location ) ) ;
2022-08-07 13:14:54 +02:00
}
2024-02-24 07:46:59 +00:00
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssstylesheet
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < CSSStyleSheet > > CSSStyleSheet : : construct_impl ( JS : : Realm & realm , Optional < CSSStyleSheetInit > const & options )
2024-02-24 07:46:59 +00:00
{
// 1. Construct a new CSSStyleSheet object sheet.
2025-04-14 16:02:29 +01:00
auto sheet = create ( realm , CSSRuleList : : create ( realm ) , CSS : : MediaList : : create ( realm , { } ) , { } ) ;
2024-02-24 07:46:59 +00:00
2024-10-21 13:48:44 +13:00
// 2. Set sheet’ s location to the base URL of the associated Document for the current principal global object.
2025-01-21 09:12:05 -05:00
auto associated_document = as < HTML : : Window > ( HTML : : current_principal_global_object ( ) ) . document ( ) ;
2025-04-08 13:18:56 +01:00
sheet - > set_location ( associated_document - > base_url ( ) ) ;
2024-02-24 07:46:59 +00:00
// 3. Set sheet’ s stylesheet base URL to the baseURL attribute value from options.
if ( options . has_value ( ) & & options - > base_url . has_value ( ) ) {
2025-04-08 13:35:26 +01:00
Optional < : : URL : : URL > sheet_location_url ;
2024-02-24 07:46:59 +00:00
if ( sheet - > location ( ) . has_value ( ) )
2025-04-08 13:18:56 +01:00
sheet_location_url = sheet - > location ( ) . release_value ( ) ;
2024-02-24 07:46:59 +00:00
// AD-HOC: This isn't explicitly mentioned in the specification, but multiple modern browsers do this.
2025-04-08 13:35:26 +01:00
Optional < : : URL : : URL > url = sheet - > location ( ) . has_value ( ) ? sheet_location_url - > complete_url ( options - > base_url . value ( ) ) : : : URL : : Parser : : basic_parse ( options - > base_url . value ( ) ) ;
2025-02-15 22:55:46 +13:00
if ( ! url . has_value ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : NotAllowedError : : create ( realm , " Constructed style sheets must have a valid base URL " _utf16 ) ;
2024-02-24 07:46:59 +00:00
sheet - > set_base_url ( url ) ;
}
// 4. Set sheet’ s parent CSS style sheet to null.
sheet - > set_parent_css_style_sheet ( nullptr ) ;
// 5. Set sheet’ s owner node to null.
sheet - > set_owner_node ( nullptr ) ;
// 6. Set sheet’ s owner CSS rule to null.
sheet - > set_owner_css_rule ( nullptr ) ;
2025-02-22 10:59:50 +08:00
// 7. Set sheet’ s title to the empty string.
2024-02-24 07:46:59 +00:00
sheet - > set_title ( String { } ) ;
// 8. Unset sheet’ s alternate flag.
sheet - > set_alternate ( false ) ;
// 9. Set sheet’ s origin-clean flag.
sheet - > set_origin_clean ( true ) ;
// 10. Set sheet’ s constructed flag.
sheet - > set_constructed ( true ) ;
// 11. Set sheet’ s Constructor document to the associated Document for the current global object.
sheet - > set_constructor_document ( associated_document ) ;
// 12. If the media attribute of options is a string, create a MediaList object from the string and assign it as sheet’ s media.
// Otherwise, serialize a media query list from the attribute and then create a MediaList object from the resulting string and set it as sheet’ s media.
if ( options . has_value ( ) ) {
if ( options - > media . has < String > ( ) ) {
sheet - > set_media ( options - > media . get < String > ( ) ) ;
} else {
2024-11-15 04:01:23 +13:00
sheet - > m_media = * options - > media . get < GC : : Root < MediaList > > ( ) ;
2024-02-24 07:46:59 +00:00
}
}
// 13. If the disabled attribute of options is true, set sheet’ s disabled flag.
if ( options . has_value ( ) & & options - > disabled )
sheet - > set_disabled ( true ) ;
// 14. Return sheet
return sheet ;
}
2025-04-08 13:35:26 +01:00
CSSStyleSheet : : CSSStyleSheet ( JS : : Realm & realm , CSSRuleList & rules , MediaList & media , Optional < : : URL : : URL > location )
2022-10-23 21:05:34 +03:00
: StyleSheet ( realm , media )
2022-08-07 15:46:44 +02:00
, m_rules ( & rules )
2022-08-07 13:14:54 +02:00
{
2022-03-29 16:51:31 +01:00
if ( location . has_value ( ) )
2025-04-08 13:18:56 +01:00
set_location ( move ( location ) ) ;
2022-04-22 19:56:22 +01:00
for ( auto & rule : * m_rules )
2023-02-26 16:09:02 -07:00
rule - > set_parent_style_sheet ( this ) ;
2023-07-31 17:46:57 +01:00
2024-08-23 16:46:33 +01:00
recalculate_rule_caches ( ) ;
2023-07-31 17:46:57 +01:00
m_rules - > on_change = [ this ] ( ) {
2024-08-23 16:46:33 +01:00
recalculate_rule_caches ( ) ;
2023-07-31 17:46:57 +01:00
} ;
2021-03-07 16:14:04 +01:00
}
2023-08-07 08:41:28 +02:00
void CSSStyleSheet : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( CSSStyleSheet ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2023-01-10 06:28:20 -05:00
}
2022-08-07 13:29:49 +02:00
void CSSStyleSheet : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2022-08-07 15:46:44 +02:00
visitor . visit ( m_rules ) ;
visitor . visit ( m_owner_css_rule ) ;
2023-08-03 13:25:56 +02:00
visitor . visit ( m_default_namespace_rule ) ;
2024-02-24 07:46:59 +00:00
visitor . visit ( m_constructor_document ) ;
2024-04-15 13:58:21 +02:00
visitor . visit ( m_namespace_rules ) ;
2024-08-23 16:46:33 +01:00
visitor . visit ( m_import_rules ) ;
2025-01-10 20:00:43 +03:00
visitor . visit ( m_owning_documents_or_shadow_roots ) ;
2025-07-30 14:38:41 +02:00
visitor . visit ( m_associated_font_loaders ) ;
2022-08-07 13:29:49 +02:00
}
2021-10-15 19:38:39 +01:00
// https://www.w3.org/TR/cssom/#dom-cssstylesheet-insertrule
2022-09-25 17:03:42 +01:00
WebIDL : : ExceptionOr < unsigned > CSSStyleSheet : : insert_rule ( StringView rule , unsigned index )
2021-09-29 20:28:32 +02:00
{
// FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception.
2024-02-24 07:46:59 +00:00
// If the disallow modification flag is set, throw a NotAllowedError DOMException.
if ( disallow_modification ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : NotAllowedError : : create ( realm ( ) , " Can't call insert_rule() on non-modifiable stylesheets. " _utf16 ) ;
2021-09-29 20:28:32 +02:00
2021-09-29 21:12:39 +02:00
// 3. Let parsed rule be the return value of invoking parse a rule with rule.
2025-04-08 18:10:38 +01:00
auto parsed_rule = parse_css_rule ( make_parsing_params ( ) , rule ) ;
2021-09-29 20:28:32 +02:00
2021-09-29 21:12:39 +02:00
// 4. If parsed rule is a syntax error, return parsed rule.
if ( ! parsed_rule )
2025-08-07 19:31:52 -04:00
return WebIDL : : SyntaxError : : create ( realm ( ) , " Unable to parse CSS rule. " _utf16 ) ;
2021-09-29 20:28:32 +02:00
2024-02-24 07:46:59 +00:00
// 5. If parsed rule is an @import rule, and the constructed flag is set, throw a SyntaxError DOMException.
if ( constructed ( ) & & parsed_rule - > type ( ) = = CSSRule : : Type : : Import )
2025-08-07 19:31:52 -04:00
return WebIDL : : SyntaxError : : create ( realm ( ) , " Can't insert @import rules into a constructed stylesheet. " _utf16 ) ;
2021-09-29 20:28:32 +02:00
2021-09-29 21:12:39 +02:00
// 6. Return the result of invoking insert a CSS rule rule in the CSS rules at index.
2025-06-23 22:40:37 +12:00
auto result = m_rules - > insert_a_css_rule ( parsed_rule , index , CSSRuleList : : Nested : : No , declared_namespaces ( ) ) ;
2022-03-09 19:57:15 +01:00
if ( ! result . is_exception ( ) ) {
2022-04-22 19:56:22 +01:00
// NOTE: The spec doesn't say where to set the parent style sheet, so we'll do it here.
2022-08-07 15:46:44 +02:00
parsed_rule - > set_parent_style_sheet ( this ) ;
2022-04-22 19:56:22 +01:00
2025-01-10 20:00:43 +03:00
invalidate_owners ( DOM : : StyleInvalidationReason : : StyleSheetInsertRule ) ;
2022-03-09 19:57:15 +01:00
}
return result ;
2021-09-29 20:28:32 +02:00
}
2021-10-15 19:38:39 +01:00
// https://www.w3.org/TR/cssom/#dom-cssstylesheet-deleterule
2022-09-25 17:03:42 +01:00
WebIDL : : ExceptionOr < void > CSSStyleSheet : : delete_rule ( unsigned index )
2021-09-29 20:28:32 +02:00
{
// FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception.
2024-02-24 07:46:59 +00:00
// 2. If the disallow modification flag is set, throw a NotAllowedError DOMException.
if ( disallow_modification ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : NotAllowedError : : create ( realm ( ) , " Can't call delete_rule() on non-modifiable stylesheets. " _utf16 ) ;
2021-09-29 20:28:32 +02:00
// 3. Remove a CSS rule in the CSS rules at index.
2022-03-09 19:57:15 +01:00
auto result = m_rules - > remove_a_css_rule ( index ) ;
if ( ! result . is_exception ( ) ) {
2025-01-10 20:00:43 +03:00
invalidate_owners ( DOM : : StyleInvalidationReason : : StyleSheetDeleteRule ) ;
2022-03-09 19:57:15 +01:00
}
return result ;
2021-09-29 20:28:32 +02:00
}
2024-02-24 07:46:59 +00:00
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-replace
2024-11-15 04:01:23 +13:00
GC : : Ref < WebIDL : : Promise > CSSStyleSheet : : replace ( String text )
2024-02-24 07:46:59 +00:00
{
2024-10-25 12:38:19 -06:00
auto & realm = this - > realm ( ) ;
2024-02-24 07:46:59 +00:00
// 1. Let promise be a promise
2024-10-25 12:38:19 -06:00
auto promise = WebIDL : : create_promise ( realm ) ;
2024-02-24 07:46:59 +00:00
// 2. If the constructed flag is not set, or the disallow modification flag is set, reject promise with a NotAllowedError DOMException and return promise.
if ( ! constructed ( ) ) {
2025-08-07 19:31:52 -04:00
WebIDL : : reject_promise ( realm , promise , WebIDL : : NotAllowedError : : create ( realm , " Can't call replace() on non-constructed stylesheets " _utf16 ) ) ;
2024-02-24 07:46:59 +00:00
return promise ;
}
if ( disallow_modification ( ) ) {
2025-08-07 19:31:52 -04:00
WebIDL : : reject_promise ( realm , promise , WebIDL : : NotAllowedError : : create ( realm , " Can't call replace() on non-modifiable stylesheets " _utf16 ) ) ;
2024-02-24 07:46:59 +00:00
return promise ;
}
// 3. Set the disallow modification flag.
set_disallow_modification ( true ) ;
// 4. In parallel, do these steps:
2024-11-15 04:01:23 +13:00
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( GC : : create_function ( realm . heap ( ) , [ & realm , this , text = move ( text ) , promise = GC : : Root ( promise ) ] {
2024-10-24 20:39:18 +13:00
HTML : : TemporaryExecutionContext execution_context { realm , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes } ;
2024-10-25 12:38:19 -06:00
2024-02-24 07:46:59 +00:00
// 1. Let rules be the result of running parse a stylesheet’ s contents from text.
2025-04-15 18:01:54 +01:00
auto rules = CSS : : Parser : : Parser : : create ( make_parsing_params ( ) , text ) . parse_as_stylesheet_contents ( ) ;
2024-02-24 07:46:59 +00:00
// 2. If rules contains one or more @import rules, remove those rules from rules.
2024-12-26 14:32:52 +01:00
GC : : RootVector < GC : : Ref < CSSRule > > rules_without_import ( realm . heap ( ) ) ;
2024-02-24 07:46:59 +00:00
for ( auto rule : rules ) {
if ( rule - > type ( ) ! = CSSRule : : Type : : Import )
rules_without_import . append ( rule ) ;
}
// 3. Set sheet’ s CSS rules to rules.
m_rules - > set_rules ( { } , rules_without_import ) ;
// 4. Unset sheet’ s disallow modification flag.
set_disallow_modification ( false ) ;
// 5. Resolve promise with sheet.
2024-10-25 12:38:19 -06:00
WebIDL : : resolve_promise ( realm , * promise , this ) ;
2024-10-31 02:39:29 +13:00
} ) ) ;
2024-02-24 07:46:59 +00:00
return promise ;
}
2024-02-24 07:46:59 +00:00
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-replacesync
WebIDL : : ExceptionOr < void > CSSStyleSheet : : replace_sync ( StringView text )
{
// 1. If the constructed flag is not set, or the disallow modification flag is set, throw a NotAllowedError DOMException.
if ( ! constructed ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : NotAllowedError : : create ( realm ( ) , " Can't call replaceSync() on non-constructed stylesheets " _utf16 ) ;
2024-02-24 07:46:59 +00:00
if ( disallow_modification ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : NotAllowedError : : create ( realm ( ) , " Can't call replaceSync() on non-modifiable stylesheets " _utf16 ) ;
2024-02-24 07:46:59 +00:00
// 2. Let rules be the result of running parse a stylesheet’ s contents from text.
2025-04-15 18:01:54 +01:00
auto rules = CSS : : Parser : : Parser : : create ( make_parsing_params ( ) , text ) . parse_as_stylesheet_contents ( ) ;
2024-02-24 07:46:59 +00:00
// 3. If rules contains one or more @import rules, remove those rules from rules.
2024-12-26 14:32:52 +01:00
GC : : RootVector < GC : : Ref < CSSRule > > rules_without_import ( realm ( ) . heap ( ) ) ;
2024-02-24 07:46:59 +00:00
for ( auto rule : rules ) {
if ( rule - > type ( ) ! = CSSRule : : Type : : Import )
rules_without_import . append ( rule ) ;
}
2025-04-24 14:25:50 +02:00
// NOTE: The spec doesn't say where to set the parent style sheet, so we'll do it here.
for ( auto & rule : rules_without_import ) {
rule - > set_parent_style_sheet ( this ) ;
}
2025-04-15 18:01:54 +01:00
// 4. Set sheet’ s CSS rules to rules.
2024-02-24 07:46:59 +00:00
m_rules - > set_rules ( { } , rules_without_import ) ;
return { } ;
}
2024-02-24 07:46:59 +00:00
// https://drafts.csswg.org/cssom/#dom-cssstylesheet-addrule
WebIDL : : ExceptionOr < WebIDL : : Long > CSSStyleSheet : : add_rule ( Optional < String > selector , Optional < String > style , Optional < WebIDL : : UnsignedLong > index )
{
// 1. Let rule be an empty string.
StringBuilder rule ;
// 2. Append selector to rule.
if ( selector . has_value ( ) )
rule . append ( selector . release_value ( ) ) ;
// 3. Append " { " to rule.
rule . append ( ' { ' ) ;
// 4. If block is not empty, append block, followed by a space, to rule.
if ( style . has_value ( ) & & ! style - > is_empty ( ) )
rule . appendff ( " {} " , style . release_value ( ) ) ;
// 5. Append "}" to rule.
rule . append ( ' } ' ) ;
// 6. Let index be optionalIndex if provided, or the number of CSS rules in the stylesheet otherwise.
// 7. Call insertRule(), with rule and index as arguments.
TRY ( insert_rule ( rule . string_view ( ) , index . value_or ( rules ( ) . length ( ) ) ) ) ;
// 8. Return -1.
return - 1 ;
}
2021-10-15 19:38:39 +01:00
// https://www.w3.org/TR/cssom/#dom-cssstylesheet-removerule
2024-02-24 07:46:59 +00:00
WebIDL : : ExceptionOr < void > CSSStyleSheet : : remove_rule ( Optional < WebIDL : : UnsignedLong > index )
2021-09-29 20:28:32 +02:00
{
// The removeRule(index) method must run the same steps as deleteRule().
2024-02-24 07:46:59 +00:00
return delete_rule ( index . value_or ( 0 ) ) ;
2021-09-29 20:28:32 +02:00
}
2024-09-03 11:43:20 +01:00
void CSSStyleSheet : : for_each_effective_rule ( TraversalOrder order , Function < void ( Web : : CSS : : CSSRule const & ) > const & callback ) const
{
if ( m_media - > matches ( ) )
m_rules - > for_each_effective_rule ( order , callback ) ;
}
2024-10-17 13:48:00 +01:00
void CSSStyleSheet : : for_each_effective_style_producing_rule ( Function < void ( CSSRule const & ) > const & callback ) const
2021-09-30 22:57:35 +02:00
{
2024-09-03 11:43:20 +01:00
for_each_effective_rule ( TraversalOrder : : Preorder , [ & ] ( CSSRule const & rule ) {
2024-10-17 13:48:00 +01:00
if ( rule . type ( ) = = CSSRule : : Type : : Style | | rule . type ( ) = = CSSRule : : Type : : NestedDeclarations )
callback ( rule ) ;
2024-09-03 11:43:20 +01:00
} ) ;
2021-09-30 22:57:35 +02:00
}
2023-05-26 23:30:54 +03:30
void CSSStyleSheet : : for_each_effective_keyframes_at_rule ( Function < void ( CSSKeyframesRule const & ) > const & callback ) const
{
2024-09-03 11:43:20 +01:00
for_each_effective_rule ( TraversalOrder : : Preorder , [ & ] ( CSSRule const & rule ) {
if ( rule . type ( ) = = CSSRule : : Type : : Keyframes )
callback ( static_cast < CSSKeyframesRule const & > ( rule ) ) ;
} ) ;
2023-05-26 23:30:54 +03:30
}
2025-01-10 20:00:43 +03:00
void CSSStyleSheet : : add_owning_document_or_shadow_root ( DOM : : Node & document_or_shadow_root )
{
VERIFY ( document_or_shadow_root . is_document ( ) | | document_or_shadow_root . is_shadow_root ( ) ) ;
m_owning_documents_or_shadow_roots . set ( document_or_shadow_root ) ;
2025-10-15 23:45:38 +13:00
2025-10-16 01:30:31 +13:00
// All owning documents or shadow roots must be part of the same document so we only need to load this style
// sheet's fonts against the document of the first
if ( this - > owning_documents_or_shadow_roots ( ) . size ( ) = = 1 )
document_or_shadow_root . document ( ) . style_computer ( ) . load_fonts_from_sheet ( * this ) ;
2025-10-15 23:45:38 +13:00
for ( auto const & import_rule : m_import_rules ) {
if ( import_rule - > loaded_style_sheet ( ) )
import_rule - > loaded_style_sheet ( ) - > add_owning_document_or_shadow_root ( document_or_shadow_root ) ;
}
2025-01-10 20:00:43 +03:00
}
void CSSStyleSheet : : remove_owning_document_or_shadow_root ( DOM : : Node & document_or_shadow_root )
{
m_owning_documents_or_shadow_roots . remove ( document_or_shadow_root ) ;
2025-10-15 23:45:38 +13:00
2025-10-16 01:30:31 +13:00
// All owning documents or shadow roots must be part of the same document so we only need to unload this style
// sheet's fonts once we have none remaining.
if ( this - > owning_documents_or_shadow_roots ( ) . size ( ) = = 0 )
document_or_shadow_root . document ( ) . style_computer ( ) . unload_fonts_from_sheet ( * this ) ;
2025-10-15 23:45:38 +13:00
for ( auto const & import_rule : m_import_rules ) {
if ( import_rule - > loaded_style_sheet ( ) )
import_rule - > loaded_style_sheet ( ) - > remove_owning_document_or_shadow_root ( document_or_shadow_root ) ;
}
2025-01-10 20:00:43 +03:00
}
void CSSStyleSheet : : invalidate_owners ( DOM : : StyleInvalidationReason reason )
{
2025-04-03 17:20:13 +01:00
m_did_match = { } ;
2025-01-10 20:00:43 +03:00
for ( auto & document_or_shadow_root : m_owning_documents_or_shadow_roots ) {
document_or_shadow_root - > invalidate_style ( reason ) ;
document_or_shadow_root - > document ( ) . style_computer ( ) . invalidate_rule_cache ( ) ;
}
}
2025-04-10 16:01:17 +01:00
GC : : Ptr < DOM : : Document > CSSStyleSheet : : owning_document ( ) const
{
if ( ! m_owning_documents_or_shadow_roots . is_empty ( ) )
return ( * m_owning_documents_or_shadow_roots . begin ( ) ) - > document ( ) ;
if ( auto * element = const_cast < CSSStyleSheet * > ( this ) - > owner_node ( ) )
return element - > document ( ) ;
return nullptr ;
}
2025-10-07 00:54:19 +13:00
bool CSSStyleSheet : : evaluate_media_queries ( DOM : : Document const & document )
2021-10-08 20:21:46 +01:00
{
2022-10-23 21:05:34 +03:00
bool any_media_queries_changed_match_state = false ;
2025-10-07 00:54:19 +13:00
bool now_matches = m_media - > evaluate ( document ) ;
2024-08-11 09:34:37 -06:00
if ( ! m_did_match . has_value ( ) | | m_did_match . value ( ) ! = now_matches )
2022-10-23 21:05:34 +03:00
any_media_queries_changed_match_state = true ;
2025-10-07 00:54:19 +13:00
if ( now_matches & & m_rules - > evaluate_media_queries ( document ) )
2022-10-23 21:05:34 +03:00
any_media_queries_changed_match_state = true ;
2024-08-11 09:34:37 -06:00
m_did_match = now_matches ;
2022-10-23 21:05:34 +03:00
return any_media_queries_changed_match_state ;
2021-10-08 20:21:46 +01:00
}
2023-11-24 19:14:24 +13:00
Optional < FlyString > CSSStyleSheet : : default_namespace ( ) const
2023-07-31 17:46:57 +01:00
{
if ( m_default_namespace_rule )
2023-12-01 13:36:40 +01:00
return m_default_namespace_rule - > namespace_uri ( ) ;
2023-07-31 17:46:57 +01:00
return { } ;
}
2025-06-23 22:40:37 +12:00
HashTable < FlyString > CSSStyleSheet : : declared_namespaces ( ) const
{
HashTable < FlyString > declared_namespaces ;
for ( auto namespace_ : m_namespace_rules . keys ( ) ) {
declared_namespaces . set ( namespace_ ) ;
}
return declared_namespaces ;
}
2023-11-24 19:14:24 +13:00
Optional < FlyString > CSSStyleSheet : : namespace_uri ( StringView namespace_prefix ) const
2023-08-04 17:02:29 +01:00
{
return m_namespace_rules . get ( namespace_prefix )
2024-11-15 04:01:23 +13:00
. map ( [ ] ( GC : : Ptr < CSSNamespaceRule > namespace_ ) {
2023-12-01 13:36:40 +01:00
return namespace_ - > namespace_uri ( ) ;
2023-08-04 17:02:29 +01:00
} ) ;
}
2024-08-23 16:46:33 +01:00
void CSSStyleSheet : : recalculate_rule_caches ( )
2023-07-29 11:51:15 -05:00
{
2023-08-04 17:02:29 +01:00
m_default_namespace_rule = nullptr ;
m_namespace_rules . clear ( ) ;
2024-08-23 16:46:33 +01:00
m_import_rules . clear ( ) ;
for ( auto const & rule : * m_rules ) {
// "Any @import rules must precede all other valid at-rules and style rules in a style sheet
// (ignoring @charset and @layer statement rules) and must not have any other valid at-rules
// or style rules between it and previous @import rules, or else the @import rule is invalid."
// https://drafts.csswg.org/css-cascade-5/#at-import
//
2023-07-31 17:46:57 +01:00
// "Any @namespace rules must follow all @charset and @import rules and precede all other
// non-ignored at-rules and style rules in a style sheet.
// ...
// A syntactically invalid @namespace rule (whether malformed or misplaced) must be ignored."
// https://drafts.csswg.org/css-namespaces/#syntax
switch ( rule - > type ( ) ) {
2024-08-23 16:46:33 +01:00
case CSSRule : : Type : : Import : {
// @import rules must appear before @namespace rules, so skip this if we've seen @namespace.
if ( ! m_namespace_rules . is_empty ( ) )
continue ;
2025-01-21 09:12:05 -05:00
m_import_rules . append ( as < CSSImportRule > ( * rule ) ) ;
2023-07-31 17:46:57 +01:00
break ;
2024-08-23 16:46:33 +01:00
}
case CSSRule : : Type : : Namespace : {
2025-01-21 09:12:05 -05:00
auto & namespace_rule = as < CSSNamespaceRule > ( * rule ) ;
2024-08-23 16:46:33 +01:00
if ( ! namespace_rule . namespace_uri ( ) . is_empty ( ) & & namespace_rule . prefix ( ) . is_empty ( ) )
m_default_namespace_rule = namespace_rule ;
2023-07-31 17:46:57 +01:00
2024-08-23 16:46:33 +01:00
m_namespace_rules . set ( namespace_rule . prefix ( ) , namespace_rule ) ;
break ;
}
2023-07-31 17:46:57 +01:00
default :
// Any other types mean that further @namespace rules are invalid, so we can stop here.
return ;
2023-07-29 11:51:15 -05:00
}
2023-07-31 17:46:57 +01:00
}
2023-07-29 11:51:15 -05:00
}
2024-08-22 17:23:54 +01:00
void CSSStyleSheet : : set_source_text ( String source )
{
m_source_text = move ( source ) ;
}
Optional < String > CSSStyleSheet : : source_text ( Badge < DOM : : Document > ) const
{
return m_source_text ;
}
2024-09-22 18:10:46 +02:00
bool CSSStyleSheet : : has_associated_font_loader ( FontLoader & font_loader ) const
{
for ( auto & loader : m_associated_font_loaders ) {
if ( loader . ptr ( ) = = & font_loader )
return true ;
}
return false ;
}
2025-04-08 18:10:38 +01:00
Parser : : ParsingParams CSSStyleSheet : : make_parsing_params ( ) const
{
2025-06-23 22:40:37 +12:00
Parser : : ParsingParams parsing_params ;
2025-04-10 16:01:17 +01:00
if ( auto document = owning_document ( ) )
2025-06-23 22:40:37 +12:00
parsing_params = Parser : : ParsingParams { * document } ;
else
parsing_params = Parser : : ParsingParams { realm ( ) } ;
parsing_params . declared_namespaces = declared_namespaces ( ) ;
return parsing_params ;
2025-04-08 18:10:38 +01:00
}
2021-03-07 16:14:04 +01:00
}