| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2024-10-04 13:19:50 +02:00
										 |  |  |  |  * Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org> | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |  * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org> | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-24 16:34:04 -06:00
										 |  |  |  | #include <LibWeb/Bindings/CSSStyleRulePrototype.h>
 | 
					
						
							|  |  |  |  | #include <LibWeb/Bindings/Intrinsics.h>
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  | #include <LibWeb/CSS/CSSRuleList.h>
 | 
					
						
							| 
									
										
										
										
											2021-03-07 15:00:02 +01:00
										 |  |  |  | #include <LibWeb/CSS/CSSStyleRule.h>
 | 
					
						
							| 
									
										
										
										
											2021-10-15 16:53:38 +01:00
										 |  |  |  | #include <LibWeb/CSS/Parser/Parser.h>
 | 
					
						
							| 
									
										
										
										
											2024-04-14 23:05:05 +01:00
										 |  |  |  | #include <LibWeb/CSS/StyleComputer.h>
 | 
					
						
							| 
									
										
										
										
											2019-06-21 19:19:49 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-26 20:01:35 +02:00
										 |  |  |  | namespace Web::CSS { | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-19 19:47:52 +01:00
										 |  |  |  | JS_DEFINE_ALLOCATOR(CSSStyleRule); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-17 12:01:13 +01:00
										 |  |  |  | JS::NonnullGCPtr<CSSStyleRule> CSSStyleRule::create(JS::Realm& realm, SelectorList&& selectors, PropertyOwningCSSStyleDeclaration& declaration, CSSRuleList& nested_rules) | 
					
						
							| 
									
										
										
										
											2022-08-07 15:46:44 +02:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-14 05:50:17 +13:00
										 |  |  |  |     return realm.create<CSSStyleRule>(realm, move(selectors), declaration, nested_rules); | 
					
						
							| 
									
										
										
										
											2022-08-07 15:46:44 +02:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-17 12:01:13 +01:00
										 |  |  |  | CSSStyleRule::CSSStyleRule(JS::Realm& realm, SelectorList&& selectors, PropertyOwningCSSStyleDeclaration& declaration, CSSRuleList& nested_rules) | 
					
						
							| 
									
										
										
										
											2024-10-28 20:16:28 +01:00
										 |  |  |  |     : CSSGroupingRule(realm, nested_rules, Type::Style) | 
					
						
							| 
									
										
										
										
											2022-08-07 15:46:44 +02:00
										 |  |  |  |     , m_selectors(move(selectors)) | 
					
						
							| 
									
										
										
										
											2022-08-07 16:21:26 +02:00
										 |  |  |  |     , m_declaration(declaration) | 
					
						
							| 
									
										
										
										
											2019-06-21 19:19:49 +02:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-14 16:38:23 +02:00
										 |  |  |  |     m_declaration->set_parent_rule(*this); | 
					
						
							| 
									
										
										
										
											2023-01-10 06:28:20 -05:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-07 08:41:28 +02:00
										 |  |  |  | void CSSStyleRule::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(CSSStyleRule); | 
					
						
							| 
									
										
										
										
											2019-06-21 20:54:13 +02:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-06-21 19:19:49 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-07 16:21:26 +02:00
										 |  |  |  | void CSSStyleRule::visit_edges(Cell::Visitor& visitor) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     Base::visit_edges(visitor); | 
					
						
							| 
									
										
										
										
											2023-02-26 16:09:02 -07:00
										 |  |  |  |     visitor.visit(m_declaration); | 
					
						
							| 
									
										
										
										
											2022-08-07 16:21:26 +02:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  | // https://drafts.csswg.org/cssom-1/#dom-cssstylerule-style
 | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  | CSSStyleDeclaration* CSSStyleRule::style() | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-02-26 16:09:02 -07:00
										 |  |  |  |     return m_declaration; | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  | // https://drafts.csswg.org/cssom-1/#serialize-a-css-rule
 | 
					
						
							| 
									
										
										
										
											2023-11-20 23:16:39 +13:00
										 |  |  |  | String CSSStyleRule::serialized() const | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     StringBuilder builder; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 1. Let s initially be the result of performing serialize a group of selectors on the rule’s associated selectors,
 | 
					
						
							|  |  |  |  |     //    followed by the string " {", i.e., a single SPACE (U+0020), followed by LEFT CURLY BRACKET (U+007B).
 | 
					
						
							| 
									
										
										
										
											2023-08-22 12:45:29 +01:00
										 |  |  |  |     builder.append(serialize_a_group_of_selectors(selectors())); | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  |     builder.append(" {"sv); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |     // 2. Let decls be the result of performing serialize a CSS declaration block on the rule’s associated declarations,
 | 
					
						
							|  |  |  |  |     //    or null if there are no such declarations.
 | 
					
						
							| 
									
										
										
										
											2023-11-21 10:39:54 +13:00
										 |  |  |  |     auto decls = declaration().length() > 0 ? declaration().serialized() : Optional<String>(); | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |     // 3. Let rules be the result of performing serialize a CSS rule on each rule in the rule’s cssRules list,
 | 
					
						
							|  |  |  |  |     //    or null if there are no such rules.
 | 
					
						
							|  |  |  |  |     Vector<String> rules; | 
					
						
							|  |  |  |  |     for (auto& rule : css_rules()) { | 
					
						
							|  |  |  |  |         rules.append(rule->serialized()); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 4. If decls and rules are both null, append " }" to s (i.e. a single SPACE (U+0020) followed by RIGHT CURLY BRACKET (U+007D)) and return s.
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |     if (!decls.has_value() && rules.is_empty()) { | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  |         builder.append(" }"sv); | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |         return builder.to_string_without_validation(); | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 5. If rules is null:
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |     if (rules.is_empty()) { | 
					
						
							|  |  |  |  |         // 1. Append a single SPACE (U+0020) to s
 | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  |         builder.append(' '); | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |         // 2. Append decls to s
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:00:58 +03:30
										 |  |  |  |         builder.append(*decls); | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |         // 3. Append " }" to s (i.e. a single SPACE (U+0020) followed by RIGHT CURLY BRACKET (U+007D)).
 | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  |         builder.append(" }"sv); | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |         // 4. Return s.
 | 
					
						
							|  |  |  |  |         return builder.to_string_without_validation(); | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |     // 6. Otherwise:
 | 
					
						
							|  |  |  |  |     else { | 
					
						
							|  |  |  |  |         // 1. If decls is not null, prepend it to rules.
 | 
					
						
							|  |  |  |  |         if (decls.has_value()) | 
					
						
							|  |  |  |  |             rules.prepend(decls.value()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 2. For each rule in rules:
 | 
					
						
							|  |  |  |  |         for (auto& rule : rules) { | 
					
						
							| 
									
										
										
										
											2024-10-14 09:43:13 +00:00
										 |  |  |  |             // * If rule is the empty string, do nothing.
 | 
					
						
							|  |  |  |  |             if (rule.is_empty()) | 
					
						
							|  |  |  |  |                 continue; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             // * Otherwise:
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  |             // 1. Append a newline followed by two spaces to s.
 | 
					
						
							|  |  |  |  |             // 2. Append rule to s.
 | 
					
						
							|  |  |  |  |             builder.appendff("\n  {}", rule); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 3. Append a newline followed by RIGHT CURLY BRACKET (U+007D) to s.
 | 
					
						
							|  |  |  |  |         builder.append("\n}"sv); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 4. Return s.
 | 
					
						
							|  |  |  |  |         return builder.to_string_without_validation(); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  | // https://drafts.csswg.org/cssom-1/#dom-cssstylerule-selectortext
 | 
					
						
							| 
									
										
										
										
											2023-12-01 16:55:52 +00:00
										 |  |  |  | String CSSStyleRule::selector_text() const | 
					
						
							| 
									
										
										
										
											2021-09-29 23:43:18 +02:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-10-01 19:57:45 +02:00
										 |  |  |  |     // The selectorText attribute, on getting, must return the result of serializing the associated group of selectors.
 | 
					
						
							| 
									
										
										
										
											2023-12-01 16:55:52 +00:00
										 |  |  |  |     return serialize_a_group_of_selectors(selectors()); | 
					
						
							| 
									
										
										
										
											2021-09-29 23:43:18 +02:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 12:06:53 +01:00
										 |  |  |  | // https://drafts.csswg.org/cssom-1/#dom-cssstylerule-selectortext
 | 
					
						
							| 
									
										
										
										
											2021-09-29 23:43:18 +02:00
										 |  |  |  | void CSSStyleRule::set_selector_text(StringView selector_text) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-10-17 12:26:37 +01:00
										 |  |  |  |     clear_caches(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 16:53:38 +01:00
										 |  |  |  |     // 1. Run the parse a group of selectors algorithm on the given value.
 | 
					
						
							| 
									
										
										
										
											2024-11-08 17:50:38 +00:00
										 |  |  |  |     Optional<SelectorList> parsed_selectors; | 
					
						
							|  |  |  |  |     if (parent_style_rule()) { | 
					
						
							|  |  |  |  |         // AD-HOC: If we're a nested style rule, then we need to parse the selector as relative and then adapt it with implicit &s.
 | 
					
						
							|  |  |  |  |         parsed_selectors = parse_selector_for_nested_style_rule(Parser::ParsingContext { realm() }, selector_text); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |         parsed_selectors = parse_selector(Parser::ParsingContext { realm() }, selector_text); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-29 23:43:18 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 16:53:38 +01:00
										 |  |  |  |     // 2. If the algorithm returns a non-null value replace the associated group of selectors with the returned value.
 | 
					
						
							| 
									
										
										
										
											2024-04-14 23:05:05 +01:00
										 |  |  |  |     if (parsed_selectors.has_value()) { | 
					
						
							| 
									
										
										
										
											2024-11-08 17:50:38 +00:00
										 |  |  |  |         // NOTE: If we have a parent style rule, we need to update the selectors to add any implicit `&`s
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 16:53:38 +01:00
										 |  |  |  |         m_selectors = parsed_selectors.release_value(); | 
					
						
							| 
									
										
										
										
											2024-04-14 23:05:05 +01:00
										 |  |  |  |         if (auto* sheet = parent_style_sheet()) { | 
					
						
							|  |  |  |  |             if (auto style_sheet_list = sheet->style_sheet_list()) { | 
					
						
							| 
									
										
										
										
											2024-08-20 14:55:28 +02:00
										 |  |  |  |                 style_sheet_list->document().style_computer().invalidate_rule_cache(); | 
					
						
							| 
									
										
										
										
											2024-09-04 10:01:08 +02:00
										 |  |  |  |                 style_sheet_list->document_or_shadow_root().invalidate_style(DOM::StyleInvalidationReason::SetSelectorText); | 
					
						
							| 
									
										
										
										
											2024-04-14 23:05:05 +01:00
										 |  |  |  |             } | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-29 23:43:18 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 16:53:38 +01:00
										 |  |  |  |     // 3. Otherwise, if the algorithm returns a null value, do nothing.
 | 
					
						
							| 
									
										
										
										
											2021-09-29 23:43:18 +02:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-17 12:26:37 +01:00
										 |  |  |  | SelectorList const& CSSStyleRule::absolutized_selectors() const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     if (m_cached_absolutized_selectors.has_value()) | 
					
						
							|  |  |  |  |         return m_cached_absolutized_selectors.value(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Replace all occurrences of `&` with the nearest ancestor style rule's selector list wrapped in `:is(...)`,
 | 
					
						
							|  |  |  |  |     // or if we have no such ancestor, with `:scope`.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // If we don't have any nesting selectors, we can just use our selectors as they are.
 | 
					
						
							|  |  |  |  |     bool has_any_nesting = false; | 
					
						
							|  |  |  |  |     for (auto const& selector : selectors()) { | 
					
						
							|  |  |  |  |         if (selector->contains_the_nesting_selector()) { | 
					
						
							|  |  |  |  |             has_any_nesting = true; | 
					
						
							|  |  |  |  |             break; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (!has_any_nesting) { | 
					
						
							|  |  |  |  |         m_cached_absolutized_selectors = m_selectors; | 
					
						
							|  |  |  |  |         return m_cached_absolutized_selectors.value(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Otherwise, build up a new list of selectors with the `&` replaced.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // First, figure out what we should replace `&` with.
 | 
					
						
							|  |  |  |  |     // "When used in the selector of a nested style rule, the nesting selector represents the elements matched by the parent rule.
 | 
					
						
							|  |  |  |  |     // When used in any other context, it represents the same elements as :scope in that context (unless otherwise defined)."
 | 
					
						
							|  |  |  |  |     // https://drafts.csswg.org/css-nesting-1/#nest-selector
 | 
					
						
							| 
									
										
										
										
											2024-11-08 17:50:38 +00:00
										 |  |  |  |     if (auto const* parent_style_rule = this->parent_style_rule()) { | 
					
						
							| 
									
										
										
										
											2024-10-17 12:26:37 +01:00
										 |  |  |  |         // TODO: If there's only 1, we don't have to use `:is()` for it
 | 
					
						
							| 
									
										
										
										
											2024-11-08 20:34:48 +00:00
										 |  |  |  |         Selector::SimpleSelector parent_selector = { | 
					
						
							| 
									
										
										
										
											2024-10-17 12:26:37 +01:00
										 |  |  |  |             .type = Selector::SimpleSelector::Type::PseudoClass, | 
					
						
							|  |  |  |  |             .value = Selector::SimpleSelector::PseudoClassSelector { | 
					
						
							|  |  |  |  |                 .type = PseudoClass::Is, | 
					
						
							|  |  |  |  |                 .argument_selector_list = parent_style_rule->absolutized_selectors(), | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2024-11-08 20:34:48 +00:00
										 |  |  |  |         SelectorList absolutized_selectors; | 
					
						
							|  |  |  |  |         for (auto const& selector : selectors()) | 
					
						
							|  |  |  |  |             absolutized_selectors.append(selector->absolutized(parent_selector)); | 
					
						
							|  |  |  |  |         m_cached_absolutized_selectors = move(absolutized_selectors); | 
					
						
							| 
									
										
										
										
											2024-10-17 12:26:37 +01:00
										 |  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2024-11-08 20:34:48 +00:00
										 |  |  |  |         // NOTE: We can't actually replace & with :scope, because & has to have 0 specificity.
 | 
					
						
							|  |  |  |  |         //       So we leave it, and treat & like :scope during matching.
 | 
					
						
							|  |  |  |  |         m_cached_absolutized_selectors = m_selectors; | 
					
						
							| 
									
										
										
										
											2024-10-17 12:26:37 +01:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return m_cached_absolutized_selectors.value(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | void CSSStyleRule::clear_caches() | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     Base::clear_caches(); | 
					
						
							|  |  |  |  |     m_cached_absolutized_selectors.clear(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-08 17:50:38 +00:00
										 |  |  |  | CSSStyleRule const* CSSStyleRule::parent_style_rule() const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     for (auto* parent = parent_rule(); parent; parent = parent->parent_rule()) { | 
					
						
							|  |  |  |  |         if (parent->type() == CSSStyleRule::Type::Style) | 
					
						
							|  |  |  |  |             return static_cast<CSSStyleRule const*>(parent); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     return nullptr; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  |  | } |