The :has() pseudo class is a selector that specifies an element which has at least one element that matches the relative selector passed as an argument.
Unlike other selectors, :has pseudo class gives a way to apply a style rule to preceding elements (preceding siblings / ancestors / preceding siblings of ancestors) of a certain elements. This difference is attractive to web developers, but also it generates lots of concerns mainly about performance and complexity. So there have been discussion about these over the years, but it was difficult to get those discussion moving forward. It is true that it can generate performance issues and complex cases. But it is also true that there have been clear demands for the usage. We thought that, clarifying those concerns would be helpful for the discussion. So we started to check feasibility on blink, and were able to get some meaningful and reasonable results from this step. Based on the results, we are going to move forward by prototyping. With the prototyping, we want to break down the large and complex problems into smaller ones, create solutions to the smaller problems, then create the feasible ways to extend it into large and complex problems again, and then see the results. And we hope that the results will give us a clear understanding of what affects how much performance, and what makes how complex problems.
The overall concept and design of the approach are based on the blink style engine and invalidation approach. Gecko and Webkit have it's own style engine and invalidation approach, so there may be Gecko or WebKit specific issues or risks when applying the approach, and it may not be possible to apply it.
Hello Artur,
I haven't considered or checked that deeply yet. I thought that this will not make that kind of issues because it only adds logic to match ':has' pseudo class and it doesn't change other matching operations or style computation sequences.
But it is an important issue and I think I should check it
explicitly. I'll check it and try to add some tests about that.
Thanks to point this!
Regards,
Byungwoo.
--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/cc304139-1a71-4523-8c62-dadb1a4cb68fn%40chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/6a770fec-1402-a0cb-19d1-fe0ed49efaec%40igalia.com.
Hello Anders and Artur,
I tried some :visited cases. Most cases looks as expected, but I
got confused by the :has(:is) case.
<style> a:visited { color: red; } div:is(a:visited #grandchild) { color: blue; } div:has(:is(a:visited #grandchild)) { color: green; } </style> <main id=main> <div id=grandparent> This is black. <div id=parent> This is black. <a id=link href=""> This is red. <div id=child> This is red. Is it correct? or should be green? <div id=grandchild> This is blue. </div> </div> </a> </div> </div> </main>
The subject element of 'div:has(:is(a:visited #grandchild)) {...}' can be an ancestor of 'a:visited' and also can be descendant of it.
What will be the correct behavior for this? Is it OK to ignore
the style for all the subject elements? Or should apply the style
for some subject elements those are descendant of 'a:visited'
element?
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAPYVjqpFh33obWTyLUqUnQ0C2bZKHxHBHqRJ2UPtNH__3rBOVA%40mail.gmail.com.
> what I was more worried about how this features interacts with the protections described in https://dbaron.org/mozilla/visited-privacy#algorithm (or, more likely, the Chromium-specific implementation of this approach). Specifically, we should make sure that :has() doesn't make it possible to use properties that affect layout (e.g. display, margin, etc.) based on a link's visitedness. I'm hoping this is already safe, it's just something we shouldn't regress on.
Ok, I got your point, Thanks!
It works well with the current prototyping. I also checked about
using the properties, and it works well(doesn't leak visitedness).
Currently the prototype is acting like - ':visited' will return always false for the ':has()' argument matching (because it doesn't set 'context.is_inside_visited_link' flag for :has argument matching). So, all the style rule that has ':visited' in :has() argument selector will ignored if the rule needs the ':visited' as true.
This behavior doesn't make regression on the perspective of
leaking visitedness, but I think that the behavior can create this
question.
"Why the ':is(:visited .b .c) {...}' style works,
but the '.b:has(:is(:visited .c)) {...}' style doesn't work?"
Currently, I think that, if we can answer the quest as below, then the issue will be simply handled without any issue.
"'With :has' , you can apply the style to ancestors of ':visited', and this can leak the visitedness. So, in case of ':visited' in the ':has()' argument, placed a restriction so that it always returns false."
Is it acceptable to place this kind of limitation?
Hello Artur,
I checked again about the behavior and logic, and I think I can
say that
the current :has prototyping approach follows the blink approach
of :visited
privacy issue and it doesn't have regression
I think I can explain with this sample.
<style>
:is(:visited .b .c) { color: red }
:is(:visited .b ~ .c) { color: red }
.a:has(:visited) { color: red }
.b:has(:is(:visited .c) { color: red }
</style>
<main>
<div class="a">a
<div class="b">b
<a href="">visited
<div class="b">b
<div class="c">descendant of .b</div>
</div>
<div class="c">sibling of .b</div>
</a>
</div>
</div>
</main>
In this case, the 'descendant of .b' is red but the 'sibling of
.b' is not red
even though the sibling of .b is actually a descendant of
:visited.
This is because that the SelectorChecker strictly checks the
combinator.
It only allows descendant/child combinator for :visited. If the
SelectorChecker
reaches sibling/direct sibling combinator, it will clear the flag
'is_inside_visited_link'
as false to make the :visited always return false.
We can think about :has as a 'ancestor or preceding sibling
relational selector'.
It always selects ancestors or preceding siblings of an element.
So, in the above
example, the :has() is technically an ancestor combinator, and the
SelectorChecker
should not allow those for :visited.
Currently, SelectorChecker set the flag 'is_inside_visited_link'
as false for the :has
argument matching, so the :visited in the argument will always
return false.
And as a result, the 3rd and 4th rule will not be applied, so the
'a' and 'b' will not be red.
This follows current solution of 'only allow the descendant/child
combinator' for :visited.
And it doesn't have regression.
How about this approach? Is this acceptable?
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/d7d5d721-f998-6f5e-f2a8-8fbaa672e4a1%40igalia.com.
Hello Artur,
I checked again about the behavior and logic, and I think I can say that
> what I was more worried about how this features interacts with the protections described in https://dbaron.org/mozilla/visited-privacy#algorithm (or, more likely, the Chromium-specific implementation of this approach). Specifically, we should make sure that :has() doesn't make it possible to use properties that affect layout (e.g. display, margin, etc.) based on a link's visitedness. I'm hoping this is already safe, it's just something we shouldn't regress on.
Ok, I got your point, Thanks!
It works well with the current prototyping. I also checked about using the properties, and it works well(doesn't leak visitedness).Currently the prototype is acting like - ':visited' will return always false for the ':has()' argument matching (because it doesn't set 'context.is_inside_visited_link' flag for :has argument matching). So, all the style rule that has ':visited' in :has() argument selector will ignored if the rule needs the ':visited' as true.
Thank you David for checking the approach and sharing the link to
the discussion on the :visited :)
I'll add a comment to the issue as you suggested, and will keep following the related discussions.
Best regards,
Byungwoo.