2020-01-18 09:38:21 +01:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
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>
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
2021-07-12 17:30:40 +01:00
CSSStyleRule : : CSSStyleRule ( NonnullRefPtrVector < Selector > & & selectors , NonnullRefPtr < CSSStyleDeclaration > & & declaration )
2019-06-21 19:19:49 +02:00
: m_selectors ( move ( selectors ) )
2019-09-30 20:06:17 +02:00
, m_declaration ( move ( declaration ) )
2019-06-21 19:19:49 +02:00
{
2019-06-21 20:54:13 +02:00
}
2019-06-21 19:19:49 +02:00
2021-03-07 15:00:02 +01:00
CSSStyleRule : : ~ CSSStyleRule ( )
2019-06-21 20:54:13 +02:00
{
2019-06-21 19:19:49 +02:00
}
2020-03-07 10:27:02 +01:00
2021-10-01 19:57:45 +02:00
// https://drafts.csswg.org/cssom/#dom-cssstylerule-style
CSSStyleDeclaration * CSSStyleRule : : style ( )
{
return m_declaration ;
}
static StringView to_string ( Selector : : SimpleSelector : : PseudoElement pseudo_element )
{
switch ( pseudo_element ) {
case Selector : : SimpleSelector : : PseudoElement : : Before :
return " before " sv ;
case Selector : : SimpleSelector : : PseudoElement : : After :
return " after " sv ;
case Selector : : SimpleSelector : : PseudoElement : : FirstLine :
return " first-line " sv ;
case Selector : : SimpleSelector : : PseudoElement : : FirstLetter :
return " first-letter " sv ;
case Selector : : SimpleSelector : : PseudoElement : : None :
break ;
}
VERIFY_NOT_REACHED ( ) ;
}
static StringView to_string ( Selector : : SimpleSelector : : PseudoClass : : Type pseudo_class )
{
switch ( pseudo_class ) {
case Selector : : SimpleSelector : : PseudoClass : : Type : : Link :
return " link " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Visited :
return " visited " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Hover :
return " hover " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Focus :
return " focus " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : FirstChild :
return " first-child " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : LastChild :
return " last-child " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : OnlyChild :
return " only-child " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Empty :
return " empty " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Root :
return " root " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : FirstOfType :
return " first-of-pseudo_class " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : LastOfType :
return " last-of-pseudo_class " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Disabled :
return " disabled " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Enabled :
return " enabled " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Checked :
return " checked " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Active :
return " active " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : NthChild :
return " nth-child " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : NthLastChild :
return " nth-last-child " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : Not :
return " not " sv ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : None :
break ;
}
VERIFY_NOT_REACHED ( ) ;
}
static String serialize_a_group_of_selectors ( NonnullRefPtrVector < Selector > const & ) ;
// https://drafts.csswg.org/cssom/#serialize-a-simple-selector
static String serialize_a_simple_selector ( Selector : : SimpleSelector const & simple_selector )
{
StringBuilder builder ;
switch ( simple_selector . type ) {
case Selector : : SimpleSelector : : Type : : TagName :
case Selector : : SimpleSelector : : Type : : Universal :
// FIXME: 1. If the namespace prefix maps to a namespace that is not the default namespace and is not the null namespace (not in a namespace) append the serialization of the namespace prefix as an identifier, followed by a "|" (U+007C) to s.
// FIXME: 2. If the namespace prefix maps to a namespace that is the null namespace (not in a namespace) append "|" (U+007C) to s.
// 3. If this is a type selector append the serialization of the element name as an identifier to s.
if ( simple_selector . type = = Selector : : SimpleSelector : : Type : : TagName ) {
// FIXME: Use the "serialize an identifier" algorithm.
builder . append ( simple_selector . value ) ;
}
// 4. If this is a universal selector append "*" (U+002A) to s.
if ( simple_selector . type = = Selector : : SimpleSelector : : Type : : Universal )
builder . append ( ' * ' ) ;
break ;
case Selector : : SimpleSelector : : Type : : Attribute :
// 1. Append "[" (U+005B) to s.
builder . append ( ' [ ' ) ;
// FIXME: 2. If the namespace prefix maps to a namespace that is not the null namespace (not in a namespace) append the serialization of the namespace prefix as an identifier, followed by a "|" (U+007C) to s.
// 3. Append the serialization of the attribute name as an identifier to s.
// FIXME: Use the "serialize an identifier" algorithm.
builder . append ( simple_selector . attribute . name ) ;
// 4. If there is an attribute value specified, append "=", "~=", "|=", "^=", "$=", or "*=" as appropriate (depending on the type of attribute selector),
// followed by the serialization of the attribute value as a string, to s.
if ( ! simple_selector . attribute . value . is_null ( ) ) {
switch ( simple_selector . attribute . match_type ) {
case Selector : : SimpleSelector : : Attribute : : MatchType : : ExactValueMatch :
builder . append ( " = " ) ;
break ;
case Selector : : SimpleSelector : : Attribute : : MatchType : : ContainsWord :
builder . append ( " ~= " ) ;
break ;
case Selector : : SimpleSelector : : Attribute : : MatchType : : ContainsString :
builder . append ( " *= " ) ;
break ;
case Selector : : SimpleSelector : : Attribute : : MatchType : : StartsWithSegment :
builder . append ( " |= " ) ;
break ;
case Selector : : SimpleSelector : : Attribute : : MatchType : : StartsWithString :
builder . append ( " ^= " ) ;
break ;
case Selector : : SimpleSelector : : Attribute : : MatchType : : EndsWithString :
builder . append ( " $= " ) ;
break ;
default :
break ;
}
}
// FIXME: 5. If the attribute selector has the case-sensitivity flag present, append " i" (U+0020 U+0069) to s.
// 6. Append "]" (U+005D) to s.
builder . append ( ' ] ' ) ;
break ;
case Selector : : SimpleSelector : : Type : : Class :
// Append a "." (U+002E), followed by the serialization of the class name as an identifier to s.
builder . append ( ' . ' ) ;
// FIXME: Use the "serialize an identifier" algorithm.
builder . append ( simple_selector . value ) ;
break ;
case Selector : : SimpleSelector : : Type : : Id :
// Append a "#" (U+0023), followed by the serialization of the ID as an identifier to s.
builder . append ( ' # ' ) ;
// FIXME: Use the "serialize an identifier" algorithm.
builder . append ( simple_selector . value ) ;
break ;
case Selector : : SimpleSelector : : Type : : PseudoClass :
switch ( simple_selector . pseudo_class . type ) {
case Selector : : SimpleSelector : : PseudoClass : : Type : : Link :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Visited :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Hover :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Focus :
case Selector : : SimpleSelector : : PseudoClass : : Type : : FirstChild :
case Selector : : SimpleSelector : : PseudoClass : : Type : : LastChild :
case Selector : : SimpleSelector : : PseudoClass : : Type : : OnlyChild :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Empty :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Root :
case Selector : : SimpleSelector : : PseudoClass : : Type : : FirstOfType :
case Selector : : SimpleSelector : : PseudoClass : : Type : : LastOfType :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Disabled :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Enabled :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Checked :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Active :
// If the pseudo-class does not accept arguments append ":" (U+003A), followed by the name of the pseudo-class, to s.
builder . append ( ' : ' ) ;
builder . append ( to_string ( simple_selector . pseudo_class . type ) ) ;
break ;
case Selector : : SimpleSelector : : PseudoClass : : Type : : NthChild :
case Selector : : SimpleSelector : : PseudoClass : : Type : : NthLastChild :
case Selector : : SimpleSelector : : PseudoClass : : Type : : Not :
// Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028),
// followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s.
builder . append ( ' : ' ) ;
builder . append ( to_string ( simple_selector . pseudo_class . type ) ) ;
builder . append ( ' ( ' ) ;
if ( simple_selector . pseudo_class . type = = Selector : : SimpleSelector : : PseudoClass : : Type : : NthChild
| | simple_selector . pseudo_class . type = = Selector : : SimpleSelector : : PseudoClass : : Type : : NthLastChild ) {
// FIXME: The result of serializing the value using the rules to serialize an <an+b> value.
TODO ( ) ;
} else if ( simple_selector . pseudo_class . type = = Selector : : SimpleSelector : : PseudoClass : : Type : : Not ) {
// The result of serializing the value using the rules for serializing a group of selectors.
builder . append ( serialize_a_group_of_selectors ( simple_selector . pseudo_class . not_selector ) ) ;
}
builder . append ( ' ) ' ) ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
break ;
default :
dbgln ( " FIXME: Unsupported simple selector serialization for type {} " , to_underlying ( simple_selector . type ) ) ;
break ;
}
return builder . to_string ( ) ;
}
// https://drafts.csswg.org/cssom/#serialize-a-selector
static String serialize_a_selector ( Selector const & selector )
{
StringBuilder builder ;
// To serialize a selector let s be the empty string, run the steps below for each part of the chain of the selector, and finally return s:
for ( size_t i = 0 ; i < selector . compound_selectors ( ) . size ( ) ; + + i ) {
auto const & compound_selector = selector . compound_selectors ( ) [ i ] ;
// 1. If there is only one simple selector in the compound selectors which is a universal selector, append the result of serializing the universal selector to s.
if ( compound_selector . simple_selectors . size ( ) = = 1
& & compound_selector . simple_selectors . first ( ) . type = = Selector : : SimpleSelector : : Type : : Universal ) {
builder . append ( serialize_a_simple_selector ( selector . compound_selectors ( ) . first ( ) . simple_selectors . first ( ) ) ) ;
}
// 2. Otherwise, for each simple selector in the compound selectors...
// FIXME: ...that is not a universal selector of which the namespace prefix maps to a namespace that is not the default namespace...
// ...serialize the simple selector and append the result to s.
else {
for ( auto & simple_selector : compound_selector . simple_selectors ) {
builder . append ( serialize_a_simple_selector ( simple_selector ) ) ;
}
}
// 3. If this is not the last part of the chain of the selector append a single SPACE (U+0020),
// followed by the combinator ">", "+", "~", ">>", "||", as appropriate, followed by another
// single SPACE (U+0020) if the combinator was not whitespace, to s.
if ( i ! = selector . compound_selectors ( ) . size ( ) - 1 ) {
builder . append ( ' ' ) ;
switch ( compound_selector . combinator ) {
case Selector : : Combinator : : ImmediateChild :
builder . append ( ' > ' ) ;
break ;
case Selector : : Combinator : : NextSibling :
builder . append ( ' + ' ) ;
break ;
case Selector : : Combinator : : SubsequentSibling :
builder . append ( ' ~ ' ) ;
break ;
case Selector : : Combinator : : Column :
builder . append ( " || " ) ;
break ;
default :
break ;
}
} else {
// 4. If this is the last part of the chain of the selector and there is a pseudo-element, append "::" followed by the name of the pseudo-element, to s.
// FIXME: This doesn't feel entirely correct. Our model of pseudo-elements seems off.
if ( ! compound_selector . simple_selectors . is_empty ( )
& & compound_selector . simple_selectors . first ( ) . type = = Selector : : SimpleSelector : : Type : : PseudoElement ) {
builder . append ( " :: " ) ;
builder . append ( to_string ( compound_selector . simple_selectors . first ( ) . pseudo_element ) ) ;
}
}
}
return builder . to_string ( ) ;
}
// https://drafts.csswg.org/cssom/#serialize-a-group-of-selectors
static String serialize_a_group_of_selectors ( NonnullRefPtrVector < Selector > const & selectors )
{
// To serialize a group of selectors serialize each selector in the group of selectors and then serialize a comma-separated list of these serializations.
StringBuilder builder ;
for ( auto & selector : selectors )
builder . append ( serialize_a_selector ( selector ) ) ;
return builder . to_string ( ) ;
}
// https://drafts.csswg.org/cssom/#serialize-a-css-rule
String CSSStyleRule : : serialized ( ) const
{
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).
builder . append ( serialize_a_group_of_selectors ( selectors ( ) ) ) ;
builder . append ( " { " sv ) ;
// 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.
auto decls = declaration ( ) . serialized ( ) ;
// FIXME: 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.
String rules ;
// 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.
if ( decls . is_null ( ) & & rules . is_null ( ) ) {
builder . append ( " } " sv ) ;
return builder . to_string ( ) ;
}
// 5. If rules is null:
if ( rules . is_null ( ) ) {
// 1. Append a single SPACE (U+0020) to s
builder . append ( ' ' ) ;
// 2. Append decls to s
builder . append ( decls ) ;
// 3. Append " }" to s (i.e. a single SPACE (U+0020) followed by RIGHT CURLY BRACKET (U+007D)).
builder . append ( " } " sv ) ;
// 4. Return s.
return builder . to_string ( ) ;
}
// FIXME: 6. Otherwise:
// FIXME: 1. If decls is not null, prepend it to rules.
// FIXME: 2. For each rule in rules:
// FIXME: 1. Append a newline followed by two spaces to s.
// FIXME: 2. Append rule to s.
// FIXME: 3. Append a newline followed by RIGHT CURLY BRACKET (U+007D) to s.
// FIXME: 4. Return s.
TODO ( ) ;
}
2021-09-29 23:43:18 +02:00
// https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext
String CSSStyleRule : : selector_text ( ) const
{
2021-10-01 19:57:45 +02:00
// The selectorText attribute, on getting, must return the result of serializing the associated group of selectors.
return serialized ( ) ;
2021-09-29 23:43:18 +02:00
}
// https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext
void CSSStyleRule : : set_selector_text ( StringView selector_text )
{
2021-10-15 16:53:38 +01:00
// 1. Run the parse a group of selectors algorithm on the given value.
auto parsed_selectors = parse_selector ( { } , 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.
if ( parsed_selectors . has_value ( ) )
m_selectors = parsed_selectors . release_value ( ) ;
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
}
2020-03-07 10:27:02 +01:00
}