Skip to content
This repository has been archived by the owner on May 28, 2024. It is now read-only.

Implement :matches() #48

Open
SimonSapin opened this issue Sep 5, 2015 · 6 comments
Open

Implement :matches() #48

SimonSapin opened this issue Sep 5, 2015 · 6 comments

Comments

@SimonSapin
Copy link
Member

Relevant spec: https://drafts.csswg.org/selectors-4/#matches

What makes this non-trivial is the handling of specificity: https://drafts.csswg.org/selectors-4/#specificity-rules

For example, div :matches(.foo, #bar) is equivalent div .foo, div #bar. The latter is a selector list that contains two selectors. (See terminology.) In Selectors level 3, selectors have an intrinsic specificity (here (0, 1, 1) and (1, 0, 1), respectively) but selector lists don’t. This is a consequence of the principle that these two bits of CSS are equivalent:

div .foo, div #bar { color: blue }
div .foo { color: blue }
div #bar { color: blue }

Going back to div :matches(.foo, #bar), preserving the equivalence means that, in Selectors level 4, the specificity is not intrinsic to (all) selectors anymore: the specificity of :matches() depends on which of its arguments actually matches for a given element.

I know of two ways to implement this:

  1. Expand at parse time a selector list that contains :matches() to the equivalent, longer selector list that doesn’t. This works without changing much code, but it cause a combinatorial explosion of the size of selectors with more than one :matches() pseudo-class. For example (from the user-agent stylesheet)

    :matches(dir, menu, ol, ul) :matches(dir, menu, ol, ul) :matches(dir, menu, ul) {
      list-style-type: square;
    }

    … this expands to 64 alternatives. The run-time cost of matching this may be prohibitive

  2. Change our matching code so that specificity is not determined at parse-time but is a result of matching. (A return value of Option<Specificity> instead of bool for a given (element, selector) set of arguments.) This might make more difficult plans to JIT-compile selectors or match them on the GPU

CC @pcwalton, @nox

@nox
Copy link

nox commented Sep 5, 2015

@SimonSapin First way is not possible, we have the same problem with selector lists, where only the greatest specificity of the selectors that matched count.

Second way is inefficient, we should actually have an Option<MaybePartiallyComputedSpecificity>, where:

pub enum MaybePartiallyComputedSpecificity {
    Exact { value: u32 }
    Partial { bounds: Range<Specificity>, token: ??? }
}

The token would be used to continue the computing of the partial specificity at a better place in the selector list than from the beginning again.

@nox
Copy link

nox commented Sep 5, 2015

Relevant quote from the spec about selector lists (emphasis mine):

If the selector is a selector list, this number is calculated for each selector in the list, and the specificity of the entire selector is the largest of any individual selector in the list that matches the element.

@SimonSapin
Copy link
Member Author

First way is not possible,

How so?

we have the same problem with selector lists, where only the greatest specificity of the selectors that matched count.

If the selector is a selector list, this number is calculated for each selector in the list, and the specificity of the entire selector is the largest of any individual selector in the list that matches the element.

We deal with this by not considering selector lists as units, but only the individual selectors they contain. This loop creates a Rule for every Selector in StyleRule::selectors (which is a Vec<Selector>). Roughly, it expands .foo, #bar { color: blue } to .foo { color: blue } #bar { color: blue }.

We can expand :matches(.foo, #bar) to the same thing. (But again, there’s combinatorial explosion with more than one :matches() in the same selector.)

Second way is inefficient,

How so? I don’t understand what token would be in your MaybePartiallyComputedSpecificity.

@nox
Copy link

nox commented Sep 6, 2015

Didn't I reply to you on IRC already yesterday? :)

For the first part of your reply, I didn't know that selector lists were expanded into individual selectors. Doesn't that mean you have to check whether all of them check, for the specificity? Why would you want to do that if your selector list is #some_id, something_very_complicated_of_higher_specificity?

The token would remember that #some_id matched, and that only something_very_complicated_of_higher_specificity needs to be checked to see if the specificity could be higher for that block.

@SimonSapin
Copy link
Member Author

That’s an interesting optimization we could do, but I think it’s orthogonal to how :matches() is implemented.

@nox
Copy link

nox commented Mar 2, 2016

I'm back on this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants