@@ -22,7 +22,9 @@ import { getElementLanguageTag, matchesLanguageRange, parseLanguageTag } from '.
2222import { isDelimiter , isOmitted } from '../utils/value.js'
2323import { isHTMLDocument , isShadowRoot , traverse } from '../utils/dom/tree.js'
2424import { serializeComponentValue , serializeIdentifier } from '../serialize.js'
25+ import CSSPseudoElement from '../cssom/CSSPseudoElement.js'
2526import getDirectionality from '../utils/dom/directionality.js'
27+ import { list } from '../values/value.js'
2628import { parseGrammar } from '../parse/parser.js'
2729import { toLowerCase } from '../utils/string.js'
2830
@@ -1186,19 +1188,90 @@ function matchPseudoClassSelector(element, { name, value }, context, options) {
11861188}
11871189
11881190/**
1189- * @param {ElementImpl } element
1191+ * @param {CSSPseudoElementImpl } element
11901192 * @param {*[] } selector
11911193 * @param {object } context
11921194 * @param {object } options
11931195 * @returns {boolean }
11941196 */
1195- function matchPseudoCompoundSelectors ( element , selector , context , { pseudos } ) {
1196- return selector . every ( selector => {
1197- const [ pseudoElement , pseudoClasses ] = selector
1198- const type = `::${ pseudoElement . types [ 0 ] === '<function>' ? `${ pseudoElement . name } ()` : pseudoElement . value } `
1199- const pseudo = pseudos . includes ( type ) && element . pseudo ( type )
1200- return pseudo && pseudoClasses . length === 0
1201- } )
1197+ function matchPseudoCompoundSelector ( element , [ pseudoElement , pseudoClasses ] , context , options ) {
1198+ return element . selectorText === serializeComponentValue ( pseudoElement )
1199+ && pseudoClasses . every ( selector => matchPseudoClassSelector ( element , selector , context , options ) )
1200+ }
1201+
1202+ /**
1203+ * @param {CSSPseudoElementImpl|ElementImpl } element
1204+ * @param {*[] } selector
1205+ * @param {object } context
1206+ * @param {object } options
1207+ * @returns {boolean }
1208+ */
1209+ function matchPseudoCompoundSelectors ( element , selector , context , options ) {
1210+ for ( let index = selector . length - 1 ; - 1 < index ; index -- ) {
1211+ if ( ! CSSPseudoElement . isImpl ( element ) ) {
1212+ return false
1213+ }
1214+ if ( ! matchPseudoCompoundSelector ( element , selector [ index ] , context , options ) ) {
1215+ return false
1216+ }
1217+ element = element . parent
1218+ }
1219+ return true
1220+ }
1221+
1222+ /**
1223+ * @param {CSSPseudoElementImpl } element
1224+ * @param {*[] } selector
1225+ * @param {object } context
1226+ * @param {object } options
1227+ * @returns {boolean }
1228+ */
1229+ function matchPseudoElementAgainstComplexSelector ( element , selector , context , options ) {
1230+
1231+ let [ head , tail ] = selector
1232+ let compound
1233+ let pseudoCompounds
1234+
1235+ // Resolve and replace <pseudo-compound-selector>* in the subject with empty list
1236+ if ( tail . length === 0 ) {
1237+ [ compound , pseudoCompounds ] = head
1238+ const unit = list ( [ compound , list ( [ ] , '' ) ] , '' , [ '<complex-selector-unit>' ] )
1239+ selector = [ unit , tail ]
1240+ } else {
1241+ let combinator
1242+ tail = [ ...tail ] ;
1243+ [ combinator , [ compound , pseudoCompounds ] ] = tail . pop ( )
1244+ const unit = list ( [ compound , list ( [ ] , '' ) ] , '' , [ '<complex-selector-unit>' ] )
1245+ tail . push ( list ( [ combinator , unit ] , '' ) )
1246+ selector = [ head , list ( tail , '' ) ]
1247+ }
1248+ selector = list ( selector , '' , [ '<complex-selector>' ] )
1249+
1250+ return matchPseudoCompoundSelectors ( element , pseudoCompounds , context , options )
1251+ && matchComplexSelector ( element . element , selector , context , options )
1252+ }
1253+
1254+ /**
1255+ * @param {CSSPseudoElementImpl } element
1256+ * @param {*[] } selectors
1257+ * @param {object } [context]
1258+ * @param {object } [options]
1259+ * @returns {*[]|null }
1260+ * @see {@link https://drafts.csswg.org/selectors-4/#match-against-pseudo-element }
1261+ */
1262+ function matchPseudoElementAgainstSelectors ( element , selectors , context , options ) {
1263+
1264+ if ( selectors . types . includes ( '<complex-real-selector-list>' ) ) {
1265+ return false
1266+ }
1267+
1268+ context = createContext ( element . element , context )
1269+ options = createOptions ( context , options )
1270+
1271+ if ( options . all ) {
1272+ return selectors . every ( selector => matchPseudoElementAgainstComplexSelector ( element , selector , context , options ) )
1273+ }
1274+ return selectors . find ( selector => matchPseudoElementAgainstComplexSelector ( element , selector , context , options ) ) ?? null
12021275}
12031276
12041277/**
@@ -1355,6 +1428,10 @@ function matchCompoundSelector(element, selector, context, options) {
13551428 return ( isOmitted ( compound ) || matchCompoundSelector ( element , compound , context , options ) )
13561429 && matchPseudoCompoundSelectors ( element , pseudos , context , options )
13571430 }
1431+ // It can be omitted only when matching a pseudo-element
1432+ if ( isOmitted ( selector ) ) {
1433+ return true
1434+ }
13581435 const [ type , subclasses ] = selector
13591436 return matchTypeSelector ( element , type , context )
13601437 && matchSubclassSelector ( element , subclasses , context , options )
@@ -1675,5 +1752,6 @@ function matchTreesAgainstSelectors(trees, selectors, context, options) {
16751752
16761753export {
16771754 matchElementAgainstSelectors ,
1755+ matchPseudoElementAgainstSelectors ,
16781756 matchTreesAgainstSelectors ,
16791757}
0 commit comments