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/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())