Changeset 285054 in webkit


Ignore:
Timestamp:
Oct 29, 2021 2:42:50 PM (9 months ago)
Author:
Antti Koivisto
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:
Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r285045 r285054  
     12021-10-29  Antti Koivisto  <antti@apple.com>
     2
     3        Allow :is/:where after all pseudo elements
     4        https://bugs.webkit.org/show_bug.cgi?id=232434
     5
     6        Reviewed by Dean Jackson.
     7
     8        * web-platform-tests/css/css-scoping/slotted-parsing-expected.txt:
     9
    1102021-10-29  Yusuke Suzuki  <ysuzuki@apple.com>
    211
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/slotted-parsing-expected.txt

    r284973 r285054  
    1616PASS "::slotted([attr]:hover)" should be a valid selector
    1717PASS "::slotted(:not(.a))" should be a valid selector
    18 FAIL "::slotted(*):is()" should be a valid selector The string did not match the expected pattern.
    19 FAIL "::slotted(*):is(:hover)" should be a valid selector The string did not match the expected pattern.
    20 FAIL "::slotted(*):is(#id)" should be a valid selector The string did not match the expected pattern.
    21 FAIL "::slotted(*):where()" should be a valid selector The string did not match the expected pattern.
    22 FAIL "::slotted(*):where(:hover)" should be a valid selector The string did not match the expected pattern.
    23 FAIL "::slotted(*):where(#id)" should be a valid selector The string did not match the expected pattern.
     18PASS "::slotted(*):is()" should be a valid selector
     19PASS "::slotted(*):is(:hover)" should be a valid selector
     20PASS "::slotted(*):is(#id)" should be a valid selector
     21PASS "::slotted(*):where()" should be a valid selector
     22PASS "::slotted(*):where(:hover)" should be a valid selector
     23PASS "::slotted(*):where(#id)" should be a valid selector
    2424PASS "::slotted(*)::before" should be a valid selector
    2525PASS "::slotted(*)::after" should be a valid selector
  • trunk/Source/WebCore/ChangeLog

    r285050 r285054  
     12021-10-29  Antti Koivisto  <antti@apple.com>
     2
     3        Allow :is/:where after all pseudo elements
     4        https://bugs.webkit.org/show_bug.cgi?id=232434
     5
     6        Reviewed by Dean Jackson.
     7
     8        Any subselectors that are not legal in the context will be removed by the forgiving parsing.
     9        This matches the behavior in other engines and the discussion in https://github.com/w3c/csswg-drafts/issues/5093.
     10
     11        The validity check is moved from the compound selector level to the simple selector level. This way if we are
     12        inside forgiving selector list parsing, it can recover and continue.
     13
     14        * css/parser/CSSSelectorParser.cpp:
     15        (WebCore::isLogicalCombinationPseudoClass):
     16        (WebCore::isPseudoClassValidAfterPseudoElement):
     17        (WebCore::isSimpleSelectorValidAfterPseudoElement):
     18
     19        :is(), :where(), and so on are always legal but their content is necessarily not.
     20
     21        (WebCore::CSSSelectorParser::consumeCompoundSelector):
     22
     23        Set the preceding pseudo-element as parser state.
     24
     25        (WebCore::CSSSelectorParser::consumeSimpleSelector):
     26
     27        Check if the pseudo-class or pseudo-element is valid immediately after consuming it.
     28
     29        (WebCore::CSSSelectorParser::consumePseudo):
     30
     31        Disallow nesting in all cases.
     32
     33        (WebCore::CSSSelectorParser::DisallowPseudoElementsScope::DisallowPseudoElementsScope): Deleted.
     34        (WebCore::CSSSelectorParser::DisallowPseudoElementsScope::~DisallowPseudoElementsScope): Deleted.
     35
     36        Just use SetForScope.
     37
     38        * css/parser/CSSSelectorParser.h:
     39
    1402021-10-29  Alan Bujtas  <zalan@apple.com>
    241
  • trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp

    r285032 r285054  
    4040namespace WebCore {
    4141
    42 class CSSSelectorParser::DisallowPseudoElementsScope {
    43 public:
    44     explicit DisallowPseudoElementsScope(CSSSelectorParser& parser)
    45         : m_parser(parser)
    46         , m_wasDisallowed(std::exchange(m_parser.m_disallowPseudoElements, true))
    47     {
    48     }
    49 
    50     ~DisallowPseudoElementsScope()
    51     {
    52         m_parser.m_disallowPseudoElements = m_wasDisallowed;
    53     }
    54 
    55 private:
    56     CSSSelectorParser& m_parser;
    57     bool m_wasDisallowed;
    58 };
    59 
    6042static AtomString serializeANPlusB(const std::pair<int, int>&);
    6143static bool consumeANPlusB(CSSParserTokenRange&, std::pair<int, int>&);
     
    339321}
    340322
     323static bool isLogicalCombinationPseudoClass(CSSSelector::PseudoClassType pseudo)
     324{
     325    switch (pseudo) {
     326    case CSSSelector::PseudoClassIs:
     327    case CSSSelector::PseudoClassWhere:
     328    case CSSSelector::PseudoClassNot:
     329    case CSSSelector::PseudoClassAny:
     330    case CSSSelector::PseudoClassMatches:
     331    case CSSSelector::PseudoClassHas:
     332        return true;
     333    default:
     334        return false;
     335    }
     336}
     337
    341338static bool isPseudoClassValidAfterPseudoElement(CSSSelector::PseudoClassType pseudoClass, CSSSelector::PseudoElementType compoundPseudoElement)
    342339{
     340    // Validity of these is determined by their content.
     341    if (isLogicalCombinationPseudoClass(pseudoClass))
     342        return true;
     343
    343344    switch (compoundPseudoElement) {
    344345    case CSSSelector::PseudoElementPart:
    345346        return !isTreeStructuralPseudoClass(pseudoClass);
    346347    case CSSSelector::PseudoElementSlotted:
    347         // FIXME: A WPT indicates :is/:where should be parsed but reduce to nothing as their content is not legal in the context.
    348348        return false;
    349349    case CSSSelector::PseudoElementResizer:
     
    380380static bool isSimpleSelectorValidAfterPseudoElement(const CSSParserSelector& simpleSelector, CSSSelector::PseudoElementType compoundPseudoElement)
    381381{
    382     if (compoundPseudoElement == CSSSelector::PseudoElementUnknown)
    383         return true;
     382    ASSERT(compoundPseudoElement != CSSSelector::PseudoElementUnknown);
     383   
    384384    if (compoundPseudoElement == CSSSelector::PseudoElementPart) {
    385385        if (simpleSelector.match() == CSSSelector::PseudoElement && simpleSelector.pseudoElementType() != CSSSelector::PseudoElementPart)
     
    392392    if (simpleSelector.match() != CSSSelector::PseudoClass)
    393393        return false;
    394     CSSSelector::PseudoClassType pseudo = simpleSelector.pseudoClassType();
    395     if (pseudo == CSSSelector::PseudoClassNot) {
    396         ASSERT(simpleSelector.selectorList());
    397         ASSERT(simpleSelector.selectorList()->first());
    398         pseudo = simpleSelector.selectorList()->first()->pseudoClassType();
    399     }
    400     return isPseudoClassValidAfterPseudoElement(pseudo, compoundPseudoElement);
     394
     395    return isPseudoClassValidAfterPseudoElement(simpleSelector.pseudoClassType(), compoundPseudoElement);
    401396}
    402397
     
    409404std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSParserTokenRange& range)
    410405{
     406    ASSERT(!m_precedingPseudoElement || m_disallowPseudoElements);
     407
    411408    std::unique_ptr<CSSParserSelector> compoundSelector;
    412409
    413410    AtomString namespacePrefix;
    414411    AtomString elementName;
    415     CSSSelector::PseudoElementType compoundPseudoElement = CSSSelector::PseudoElementUnknown;
    416412    const bool hasName = consumeName(range, elementName, namespacePrefix);
    417413    if (!hasName) {
     
    420416            return nullptr;
    421417        if (compoundSelector->match() == CSSSelector::PseudoElement)
    422             compoundPseudoElement = compoundSelector->pseudoElementType();
     418            m_precedingPseudoElement = compoundSelector->pseudoElementType();
    423419    }
    424420
    425421    while (auto simpleSelector = consumeSimpleSelector(range)) {
    426         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
    427         // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
    428         // video::-webkit-media-text-track-region-container.scrolling
    429         if (m_context.mode != UASheetMode && !isSimpleSelectorValidAfterPseudoElement(*simpleSelector.get(), compoundPseudoElement)) {
    430             m_failedParsing = true;
    431             return nullptr;
    432         }
    433422        if (simpleSelector->match() == CSSSelector::PseudoElement)
    434             compoundPseudoElement = simpleSelector->pseudoElementType();
     423            m_precedingPseudoElement = simpleSelector->pseudoElementType();
    435424
    436425        if (compoundSelector)
     
    440429    }
    441430
     431    if (!m_disallowPseudoElements)
     432        m_precedingPseudoElement = { };
    442433
    443434    // While inside a nested selector like :is(), the default namespace shall be ignored when [1]:
     
    477468    else
    478469        return nullptr;
    479     if (!selector)
     470
     471    if (!selector) {
    480472        m_failedParsing = true;
     473        return nullptr;
     474    }
     475
     476    if (m_precedingPseudoElement) {
     477        // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
     478        // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
     479        // video::-webkit-media-text-track-region-container.scrolling
     480        if (m_context.mode != UASheetMode && !isSimpleSelectorValidAfterPseudoElement(*selector, *m_precedingPseudoElement))
     481            m_failedParsing = true;
     482    }
     483
    481484    return selector;
    482485}
     
    677680    }
    678681
    679     if (!selector || (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements))
    680         return nullptr;
     682    if (!selector)
     683        return nullptr;
     684
     685    // Pseudo-elements are not allowed inside pseudo-classes or pseudo-elements.
     686    if (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements)
     687        return nullptr;
     688    SetForScope disallowPseudoElementsScope(m_disallowPseudoElements, true);
    681689
    682690    if (token.type() == IdentToken) {
     
    692700    if (token.type() != FunctionToken)
    693701        return nullptr;
    694    
     702
    695703    if (selector->match() == CSSSelector::PseudoClass) {
    696704        switch (selector->pseudoClassType()) {
    697705        case CSSSelector::PseudoClassNot: {
    698706            SetForScope<bool> resistDefaultNamespace(m_resistDefaultNamespace, true);
    699             DisallowPseudoElementsScope scope(*this);
    700707            auto selectorList = makeUnique<CSSSelectorList>();
    701708            *selectorList = consumeComplexSelectorList(block);
     
    723730                if (block.peek().type() != WhitespaceToken)
    724731                    return nullptr;
    725                 DisallowPseudoElementsScope scope(*this);
    726732                block.consumeWhitespace();
    727733                auto selectorList = makeUnique<CSSSelectorList>();
     
    747753        case CSSSelector::PseudoClassAny: {
    748754            SetForScope<bool> resistDefaultNamespace(m_resistDefaultNamespace, true);
    749             DisallowPseudoElementsScope scope(*this);
    750755            auto selectorList = makeUnique<CSSSelectorList>();
    751756            *selectorList = consumeForgivingComplexSelectorList(block);
     
    764769        }
    765770        case CSSSelector::PseudoClassHas: {
    766             DisallowPseudoElementsScope scope(*this);
    767771            auto selectorList = makeUnique<CSSSelectorList>();
    768772            *selectorList = consumeForgivingRelativeSelectorList(block);
     
    791795#if ENABLE(VIDEO)
    792796        case CSSSelector::PseudoElementCue: {
    793             DisallowPseudoElementsScope scope(*this);
    794797            auto selectorList = makeUnique<CSSSelectorList>();
    795798            *selectorList = consumeCompoundSelectorList(block);
     
    801804#endif
    802805        case CSSSelector::PseudoElementHighlight: {
    803             DisallowPseudoElementsScope scope(*this);
    804 
    805806            auto& ident = block.consumeIncludingWhitespace();
    806807            if (ident.type() != IdentToken || !block.atEnd())
     
    826827        }
    827828        case CSSSelector::PseudoElementSlotted: {
    828             DisallowPseudoElementsScope scope(*this);
    829 
    830829            auto innerSelector = consumeCompoundSelector(block);
    831830            block.consumeWhitespace();
  • trunk/Source/WebCore/css/parser/CSSSelectorParser.h

    r281295 r285054  
    9292    bool m_resistDefaultNamespace { false };
    9393    bool m_ignoreDefaultNamespace { false };
     94    std::optional<CSSSelector::PseudoElementType> m_precedingPseudoElement;
    9495};
    9596
Note: See TracChangeset for help on using the changeset viewer.