Skip to content

Commit

Permalink
Fix ':has()' matching and invalidation error with nesting parent
Browse files Browse the repository at this point in the history
1. Fix incorrect cache hit for ':has()' selector matching:

In case of having multiple style rules with same ':has()' selector
expression but different nesting parent, the ':has()' selector
matching cache (CheckPseudoCacheScope) returned incorrect cache hit
result because it used ':has()' argument selector text as the cache
key for the ':has()' match result.
To fix this bug, this CL added a 'CSSSelector' method that returns
selector text with replacing pseudo parent expressions ('&') with
equivalent pseudo ':is' expression (':is(<nesting parent>)'), and let
the cache uses the replaced selector string as its cache key.

2. Fix ':has()' invalidation error with complex nesting parent:

To build invalidation set for a ':has()' that contains a logical
combinations containing a complex selector, 'RuleFeatureSet' checks
a flag in the ':has()' selector:
- ContainsComplexLogicalCombinationsInsideHasPseudoClass
'CSSSelectorParser' need to set the flag while parsing ':has()'
argument selector, but the flag was not set in case that the ':has()'
contains complex nesting parent.
This CL sets the flag when a ':has()' contains nesting parent selector
so that 'RuleFeatureSet' successfully build invalidation sets for the
complex nesting parent inside ':has()'.

Low-Coverage-Reason: COVERAGE_UNDERREPORTED - The unit tests and wpt tests in this CL cover the changes, and most of the lines detected in css_selector.cc are existing code that are not directly related to this change and should have been tested previously.

Bug: 350946979
Change-Id: Ie3b457df2ac66e0e5884beb0e29f3877b436e2a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5680906
Reviewed-by: Anders Hartvoll Ruud <[email protected]>
Commit-Queue: Byungwoo Lee <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1335644}
  • Loading branch information
byung-woo authored and chromium-wpt-export-bot committed Jul 31, 2024
1 parent 2fea553 commit 42cfd14
Showing 1 changed file with 158 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Selector Invalidation: :has() with nesting parent containing complex selector</title>
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
.anchor { background-color: white; }

.ancestor .descendant {
.anchor:has(&) { background-color: blue; }
}

.ancestor .child {
.anchor:has(> &) { background-color: lightblue; }
}

.ancestor_prev ~ div .descendant {
.anchor:has(&) { background-color: yellow; }
}

.ancestor_prev ~ div.ancestor .descendant {
.anchor:has(&) { background-color: yellowgreen; }
}

.prev ~ .indirect_next {
.anchor:has(~ &) { background-color: green; }
}

.prev ~ .direct_next {
.anchor:has(+ &) { background-color: lightgreen; }
}
</style>
<div><div id="grand_parent1">
<div id="parent1">
<div id="anchor1" class="anchor">
<div><div class="descendant"></div></div>
</div>
</div>
</div></div>
<div><div id="grand_parent2">
<div id="parent2">
<div id="anchor2" class="anchor">
<div class="child"></div>
</div>
</div>
</div></div>
<div><div id="grand_parent_indirect_prev3"></div>
<div id="grand_parent_direct_prev3"></div>
<div id="grand_parent3">
<div id="parent_indirect_prev3"></div>
<div id="parent_direct_prev3"></div>
<div id="parent3">
<div id="anchor_indirect_prev3"></div>
<div id="anchor_direct_prev3"></div>
<div id="anchor3" class="anchor">
<div><div class="descendant"></div></div>
</div>
</div>
</div></div>
<div><div id="indirect_prev4"></div>
<div id="direct_prev4"></div>
<div id="anchor4" class="anchor"></div>
<div></div><div class="indirect_next">
</div></div>
<div><div id="indirect_prev5"></div>
<div id="direct_prev5"></div>
<div id="anchor5" class="anchor"></div>
<div class="direct_next">
</div></div>
<script>
const white = "rgb(255, 255, 255)";
const blue = "rgb(0, 0, 255)";
const lightblue = "rgb(173, 216, 230)";
const yellow = "rgb(255, 255, 0)";
const yellowgreen = "rgb(154, 205, 50)";
const green = "rgb(0, 128, 0)";
const lightgreen = "rgb(144, 238, 144)";

function bg_color(element, color, message) {
promise_test(async () => {
assert_equals(getComputedStyle(element)['background-color'], color);
}, message);
}

function add_class_and_check_bg_color(
element_to_add, class_name, has_anchor, color) {
promise_test(async () => {
element_to_add.classList.add(class_name);
assert_equals(getComputedStyle(has_anchor)['background-color'], color);
}, `#${has_anchor.id} becomes ${color} after adding .${class_name} to #${element_to_add.id}`);
}

function remove_class_and_check_bg_color(
element_to_remove, class_name, has_anchor, color) {
promise_test(async () => {
element_to_remove.classList.remove(class_name);
assert_equals(getComputedStyle(has_anchor)['background-color'], color);
}, `#${has_anchor.id} becomes ${color} after removing .${class_name} from #${element_to_remove.id}`);
}

bg_color(anchor1, white, "#anchor1 initially white");
add_class_and_check_bg_color(grand_parent1, "ancestor", anchor1, blue);
remove_class_and_check_bg_color(grand_parent1, "ancestor", anchor1, white);
add_class_and_check_bg_color(parent1, "ancestor", anchor1, blue);
remove_class_and_check_bg_color(parent1, "ancestor", anchor1, white);

bg_color(anchor2, white, "#anchor2 initially white");
add_class_and_check_bg_color(grand_parent2, "ancestor", anchor2, lightblue);
remove_class_and_check_bg_color(grand_parent2, "ancestor", anchor2, white);
add_class_and_check_bg_color(parent2, "ancestor", anchor2, lightblue);
remove_class_and_check_bg_color(parent2, "ancestor", anchor2, white);

bg_color(anchor3, white, "#anchor3 initially white");
add_class_and_check_bg_color(grand_parent_indirect_prev3, "ancestor_prev",
anchor3, yellow);
add_class_and_check_bg_color(grand_parent3, "ancestor", anchor3, yellowgreen);
remove_class_and_check_bg_color(grand_parent3, "ancestor", anchor3, yellow);
remove_class_and_check_bg_color(grand_parent_indirect_prev3, "ancestor_prev",
anchor3, white);
add_class_and_check_bg_color(grand_parent_direct_prev3, "ancestor_prev",
anchor3, yellow);
remove_class_and_check_bg_color(grand_parent_direct_prev3, "ancestor_prev",
anchor3, white);
add_class_and_check_bg_color(parent_indirect_prev3, "ancestor_prev",
anchor3, yellow);
add_class_and_check_bg_color(parent3, "ancestor", anchor3, yellowgreen);
remove_class_and_check_bg_color(parent3, "ancestor", anchor3, yellow);
remove_class_and_check_bg_color(parent_indirect_prev3, "ancestor_prev",
anchor3, white);
add_class_and_check_bg_color(parent_direct_prev3, "ancestor_prev",
anchor3, yellow);
remove_class_and_check_bg_color(parent_direct_prev3, "ancestor_prev",
anchor3, white);
add_class_and_check_bg_color(anchor_indirect_prev3, "ancestor_prev",
anchor3, yellow);
remove_class_and_check_bg_color(anchor_indirect_prev3, "ancestor_prev",
anchor3, white);
add_class_and_check_bg_color(anchor_direct_prev3, "ancestor_prev",
anchor3, yellow);
remove_class_and_check_bg_color(anchor_direct_prev3, "ancestor_prev",
anchor3, white);

bg_color(anchor4, white, "#anchor4 initially white");
add_class_and_check_bg_color(indirect_prev4, "prev", anchor4, green);
remove_class_and_check_bg_color(indirect_prev4, "prev", anchor4, white);
add_class_and_check_bg_color(direct_prev4, "prev", anchor4, green);
remove_class_and_check_bg_color(direct_prev4, "prev", anchor4, white);

bg_color(anchor5, white, "#anchor5 initially white");
add_class_and_check_bg_color(indirect_prev5, "prev", anchor5, lightgreen);
remove_class_and_check_bg_color(indirect_prev5, "prev", anchor5, white);
add_class_and_check_bg_color(direct_prev5, "prev", anchor5, lightgreen);
remove_class_and_check_bg_color(direct_prev5, "prev", anchor5, white);
</script>

0 comments on commit 42cfd14

Please sign in to comment.