Title: [285054] trunk
Revision
285054
Author
[email protected]
Date
2021-10-29 14:42:50 -0700 (Fri, 29 Oct 2021)

Log Message

Allow :is/:where after all pseudo elements
https://bugs.webkit.org/show_bug.cgi?id=232434

Reviewed by Dean Jackson.

LayoutTests/imported/w3c:

* web-platform-tests/css/css-scoping/slotted-parsing-expected.txt:

Source/WebCore:

Any subselectors that are not legal in the context will be removed by the forgiving parsing.
This matches the behavior in other engines and the discussion in https://github.com/w3c/csswg-drafts/issues/5093.

The validity check is moved from the compound selector level to the simple selector level. This way if we are
inside forgiving selector list parsing, it can recover and continue.

* css/parser/CSSSelectorParser.cpp:
(WebCore::isLogicalCombinationPseudoClass):
(WebCore::isPseudoClassValidAfterPseudoElement):
(WebCore::isSimpleSelectorValidAfterPseudoElement):

:is(), :where(), and so on are always legal but their content is necessarily not.

(WebCore::CSSSelectorParser::consumeCompoundSelector):

Set the preceding pseudo-element as parser state.

(WebCore::CSSSelectorParser::consumeSimpleSelector):

Check if the pseudo-class or pseudo-element is valid immediately after consuming it.

(WebCore::CSSSelectorParser::consumePseudo):

Disallow nesting in all cases.

(WebCore::CSSSelectorParser::DisallowPseudoElementsScope::DisallowPseudoElementsScope): Deleted.
(WebCore::CSSSelectorParser::DisallowPseudoElementsScope::~DisallowPseudoElementsScope): Deleted.

Just use SetForScope.

* css/parser/CSSSelectorParser.h:

Modified Paths

Diff

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (285053 => 285054)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2021-10-29 21:31:58 UTC (rev 285053)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2021-10-29 21:42:50 UTC (rev 285054)
@@ -1,3 +1,12 @@
+2021-10-29  Antti Koivisto  <[email protected]>
+
+        Allow :is/:where after all pseudo elements
+        https://bugs.webkit.org/show_bug.cgi?id=232434
+
+        Reviewed by Dean Jackson.
+
+        * web-platform-tests/css/css-scoping/slotted-parsing-expected.txt:
+
 2021-10-29  Yusuke Suzuki  <[email protected]>
 
         Unreviewed, revert r284440, r284397, r284359

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/slotted-parsing-expected.txt (285053 => 285054)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/slotted-parsing-expected.txt	2021-10-29 21:31:58 UTC (rev 285053)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/slotted-parsing-expected.txt	2021-10-29 21:42:50 UTC (rev 285054)
@@ -15,12 +15,12 @@
 PASS "::slotted(div)" should be a valid selector
 PASS "::slotted([attr]:hover)" should be a valid selector
 PASS "::slotted(:not(.a))" should be a valid selector
-FAIL "::slotted(*):is()" should be a valid selector The string did not match the expected pattern.
-FAIL "::slotted(*):is(:hover)" should be a valid selector The string did not match the expected pattern.
-FAIL "::slotted(*):is(#id)" should be a valid selector The string did not match the expected pattern.
-FAIL "::slotted(*):where()" should be a valid selector The string did not match the expected pattern.
-FAIL "::slotted(*):where(:hover)" should be a valid selector The string did not match the expected pattern.
-FAIL "::slotted(*):where(#id)" should be a valid selector The string did not match the expected pattern.
+PASS "::slotted(*):is()" should be a valid selector
+PASS "::slotted(*):is(:hover)" should be a valid selector
+PASS "::slotted(*):is(#id)" should be a valid selector
+PASS "::slotted(*):where()" should be a valid selector
+PASS "::slotted(*):where(:hover)" should be a valid selector
+PASS "::slotted(*):where(#id)" should be a valid selector
 PASS "::slotted(*)::before" should be a valid selector
 PASS "::slotted(*)::after" should be a valid selector
 FAIL "::slotted(*)::placeholder" should be a valid selector The string did not match the expected pattern.

Modified: trunk/Source/WebCore/ChangeLog (285053 => 285054)


--- trunk/Source/WebCore/ChangeLog	2021-10-29 21:31:58 UTC (rev 285053)
+++ trunk/Source/WebCore/ChangeLog	2021-10-29 21:42:50 UTC (rev 285054)
@@ -1,3 +1,42 @@
+2021-10-29  Antti Koivisto  <[email protected]>
+
+        Allow :is/:where after all pseudo elements
+        https://bugs.webkit.org/show_bug.cgi?id=232434
+
+        Reviewed by Dean Jackson.
+
+        Any subselectors that are not legal in the context will be removed by the forgiving parsing.
+        This matches the behavior in other engines and the discussion in https://github.com/w3c/csswg-drafts/issues/5093.
+
+        The validity check is moved from the compound selector level to the simple selector level. This way if we are
+        inside forgiving selector list parsing, it can recover and continue.
+
+        * css/parser/CSSSelectorParser.cpp:
+        (WebCore::isLogicalCombinationPseudoClass):
+        (WebCore::isPseudoClassValidAfterPseudoElement):
+        (WebCore::isSimpleSelectorValidAfterPseudoElement):
+
+        :is(), :where(), and so on are always legal but their content is necessarily not.
+
+        (WebCore::CSSSelectorParser::consumeCompoundSelector):
+
+        Set the preceding pseudo-element as parser state.
+
+        (WebCore::CSSSelectorParser::consumeSimpleSelector):
+
+        Check if the pseudo-class or pseudo-element is valid immediately after consuming it.
+
+        (WebCore::CSSSelectorParser::consumePseudo):
+
+        Disallow nesting in all cases.
+
+        (WebCore::CSSSelectorParser::DisallowPseudoElementsScope::DisallowPseudoElementsScope): Deleted.
+        (WebCore::CSSSelectorParser::DisallowPseudoElementsScope::~DisallowPseudoElementsScope): Deleted.
+
+        Just use SetForScope.
+
+        * css/parser/CSSSelectorParser.h:
+
 2021-10-29  Alan Bujtas  <[email protected]>
 
         [LFC][IFC] Introduce InlineItemsBuilder

Modified: trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp (285053 => 285054)


--- trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp	2021-10-29 21:31:58 UTC (rev 285053)
+++ trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp	2021-10-29 21:42:50 UTC (rev 285054)
@@ -39,24 +39,6 @@
 
 namespace WebCore {
 
-class CSSSelectorParser::DisallowPseudoElementsScope {
-public:
-    explicit DisallowPseudoElementsScope(CSSSelectorParser& parser)
-        : m_parser(parser)
-        , m_wasDisallowed(std::exchange(m_parser.m_disallowPseudoElements, true))
-    {
-    }
-
-    ~DisallowPseudoElementsScope()
-    {
-        m_parser.m_disallowPseudoElements = m_wasDisallowed;
-    }
-
-private:
-    CSSSelectorParser& m_parser;
-    bool m_wasDisallowed;
-};
-
 static AtomString serializeANPlusB(const std::pair<int, int>&);
 static bool consumeANPlusB(CSSParserTokenRange&, std::pair<int, int>&);
 
@@ -338,13 +320,31 @@
     }
 }
 
+static bool isLogicalCombinationPseudoClass(CSSSelector::PseudoClassType pseudo)
+{
+    switch (pseudo) {
+    case CSSSelector::PseudoClassIs:
+    case CSSSelector::PseudoClassWhere:
+    case CSSSelector::PseudoClassNot:
+    case CSSSelector::PseudoClassAny:
+    case CSSSelector::PseudoClassMatches:
+    case CSSSelector::PseudoClassHas:
+        return true;
+    default:
+        return false;
+    }
+}
+
 static bool isPseudoClassValidAfterPseudoElement(CSSSelector::PseudoClassType pseudoClass, CSSSelector::PseudoElementType compoundPseudoElement)
 {
+    // Validity of these is determined by their content.
+    if (isLogicalCombinationPseudoClass(pseudoClass))
+        return true;
+
     switch (compoundPseudoElement) {
     case CSSSelector::PseudoElementPart:
         return !isTreeStructuralPseudoClass(pseudoClass);
     case CSSSelector::PseudoElementSlotted:
-        // FIXME: A WPT indicates :is/:where should be parsed but reduce to nothing as their content is not legal in the context.
         return false;
     case CSSSelector::PseudoElementResizer:
     case CSSSelector::PseudoElementScrollbar:
@@ -379,8 +379,8 @@
 
 static bool isSimpleSelectorValidAfterPseudoElement(const CSSParserSelector& simpleSelector, CSSSelector::PseudoElementType compoundPseudoElement)
 {
-    if (compoundPseudoElement == CSSSelector::PseudoElementUnknown)
-        return true;
+    ASSERT(compoundPseudoElement != CSSSelector::PseudoElementUnknown);
+    
     if (compoundPseudoElement == CSSSelector::PseudoElementPart) {
         if (simpleSelector.match() == CSSSelector::PseudoElement && simpleSelector.pseudoElementType() != CSSSelector::PseudoElementPart)
             return true;
@@ -391,13 +391,8 @@
     }
     if (simpleSelector.match() != CSSSelector::PseudoClass)
         return false;
-    CSSSelector::PseudoClassType pseudo = simpleSelector.pseudoClassType();
-    if (pseudo == CSSSelector::PseudoClassNot) {
-        ASSERT(simpleSelector.selectorList());
-        ASSERT(simpleSelector.selectorList()->first());
-        pseudo = simpleSelector.selectorList()->first()->pseudoClassType();
-    }
-    return isPseudoClassValidAfterPseudoElement(pseudo, compoundPseudoElement);
+
+    return isPseudoClassValidAfterPseudoElement(simpleSelector.pseudoClassType(), compoundPseudoElement);
 }
 
 static bool atEndIgnoringWhitespace(CSSParserTokenRange range)
@@ -408,11 +403,12 @@
 
 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSParserTokenRange& range)
 {
+    ASSERT(!m_precedingPseudoElement || m_disallowPseudoElements);
+
     std::unique_ptr<CSSParserSelector> compoundSelector;
 
     AtomString namespacePrefix;
     AtomString elementName;
-    CSSSelector::PseudoElementType compoundPseudoElement = CSSSelector::PseudoElementUnknown;
     const bool hasName = consumeName(range, elementName, namespacePrefix);
     if (!hasName) {
         compoundSelector = consumeSimpleSelector(range);
@@ -419,19 +415,12 @@
         if (!compoundSelector)
             return nullptr;
         if (compoundSelector->match() == CSSSelector::PseudoElement)
-            compoundPseudoElement = compoundSelector->pseudoElementType();
+            m_precedingPseudoElement = compoundSelector->pseudoElementType();
     }
 
     while (auto simpleSelector = consumeSimpleSelector(range)) {
-        // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
-        // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
-        // video::-webkit-media-text-track-region-container.scrolling
-        if (m_context.mode != UASheetMode && !isSimpleSelectorValidAfterPseudoElement(*simpleSelector.get(), compoundPseudoElement)) {
-            m_failedParsing = true;
-            return nullptr;
-        }
         if (simpleSelector->match() == CSSSelector::PseudoElement)
-            compoundPseudoElement = simpleSelector->pseudoElementType();
+            m_precedingPseudoElement = simpleSelector->pseudoElementType();
 
         if (compoundSelector)
             compoundSelector->appendTagHistory(CSSSelector::Subselector, WTFMove(simpleSelector));
@@ -439,6 +428,8 @@
             compoundSelector = WTFMove(simpleSelector);
     }
 
+    if (!m_disallowPseudoElements)
+        m_precedingPseudoElement = { };
 
     // While inside a nested selector like :is(), the default namespace shall be ignored when [1]:
     // * The compound selector represents the subject [2], and
@@ -476,8 +467,20 @@
         selector = consumePseudo(range);
     else
         return nullptr;
-    if (!selector)
+
+    if (!selector) {
         m_failedParsing = true;
+        return nullptr;
+    }
+
+    if (m_precedingPseudoElement) {
+        // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
+        // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
+        // video::-webkit-media-text-track-region-container.scrolling
+        if (m_context.mode != UASheetMode && !isSimpleSelectorValidAfterPseudoElement(*selector, *m_precedingPseudoElement))
+            m_failedParsing = true;
+    }
+
     return selector;
 }
 
@@ -676,9 +679,14 @@
 #endif
     }
 
-    if (!selector || (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements))
+    if (!selector)
         return nullptr;
 
+    // Pseudo-elements are not allowed inside pseudo-classes or pseudo-elements.
+    if (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements)
+        return nullptr;
+    SetForScope disallowPseudoElementsScope(m_disallowPseudoElements, true);
+
     if (token.type() == IdentToken) {
         range.consume();
         if ((selector->match() == CSSSelector::PseudoElement && (selector->pseudoElementType() == CSSSelector::PseudoElementUnknown || isOnlyPseudoElementFunction(selector->pseudoElementType())))
@@ -691,12 +699,11 @@
     block.consumeWhitespace();
     if (token.type() != FunctionToken)
         return nullptr;
-    
+
     if (selector->match() == CSSSelector::PseudoClass) {
         switch (selector->pseudoClassType()) {
         case CSSSelector::PseudoClassNot: {
             SetForScope<bool> resistDefaultNamespace(m_resistDefaultNamespace, true);
-            DisallowPseudoElementsScope scope(*this);
             auto selectorList = makeUnique<CSSSelectorList>();
             *selectorList = consumeComplexSelectorList(block);
             if (!selectorList->first() || !block.atEnd())
@@ -722,7 +729,6 @@
                     return nullptr;
                 if (block.peek().type() != WhitespaceToken)
                     return nullptr;
-                DisallowPseudoElementsScope scope(*this);
                 block.consumeWhitespace();
                 auto selectorList = makeUnique<CSSSelectorList>();
                 *selectorList = consumeComplexSelectorList(block);
@@ -746,7 +752,6 @@
         case CSSSelector::PseudoClassMatches:
         case CSSSelector::PseudoClassAny: {
             SetForScope<bool> resistDefaultNamespace(m_resistDefaultNamespace, true);
-            DisallowPseudoElementsScope scope(*this);
             auto selectorList = makeUnique<CSSSelectorList>();
             *selectorList = consumeForgivingComplexSelectorList(block);
             if (!block.atEnd())
@@ -763,7 +768,6 @@
             return selector;
         }
         case CSSSelector::PseudoClassHas: {
-            DisallowPseudoElementsScope scope(*this);
             auto selectorList = makeUnique<CSSSelectorList>();
             *selectorList = consumeForgivingRelativeSelectorList(block);
             if (selectorList->isEmpty() || !block.atEnd())
@@ -790,7 +794,6 @@
         switch (selector->pseudoElementType()) {
 #if ENABLE(VIDEO)
         case CSSSelector::PseudoElementCue: {
-            DisallowPseudoElementsScope scope(*this);
             auto selectorList = makeUnique<CSSSelectorList>();
             *selectorList = consumeCompoundSelectorList(block);
             if (selectorList->isEmpty() || !block.atEnd())
@@ -800,8 +803,6 @@
         }
 #endif
         case CSSSelector::PseudoElementHighlight: {
-            DisallowPseudoElementsScope scope(*this);
-
             auto& ident = block.consumeIncludingWhitespace();
             if (ident.type() != IdentToken || !block.atEnd())
                 return nullptr;
@@ -825,8 +826,6 @@
             return selector;
         }
         case CSSSelector::PseudoElementSlotted: {
-            DisallowPseudoElementsScope scope(*this);
-
             auto innerSelector = consumeCompoundSelector(block);
             block.consumeWhitespace();
             if (!innerSelector || !block.atEnd())

Modified: trunk/Source/WebCore/css/parser/CSSSelectorParser.h (285053 => 285054)


--- trunk/Source/WebCore/css/parser/CSSSelectorParser.h	2021-10-29 21:31:58 UTC (rev 285053)
+++ trunk/Source/WebCore/css/parser/CSSSelectorParser.h	2021-10-29 21:42:50 UTC (rev 285054)
@@ -91,6 +91,7 @@
     bool m_disallowPseudoElements { false };
     bool m_resistDefaultNamespace { false };
     bool m_ignoreDefaultNamespace { false };
+    std::optional<CSSSelector::PseudoElementType> m_precedingPseudoElement;
 };
 
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to