| <!DOCTYPE html> |
| <meta charset="utf-8" /> |
| <title>CSS Selectors Invalidation: user-action pseudo classes in :has() argument</title> |
| <link rel="author" title="Byungwoo Lee" href="blee@igalia.com"> |
| <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> |
| .ancestor:has(.descendant1:hover) { color: blue } |
| .ancestor:has(.descendant1:hover) .other-descendant { color: navy } |
| .ancestor:has(.descendant1:hover:active) { color: skyblue } |
| .ancestor:has(.descendant1:hover:active) .other-descendant { color: lightblue } |
| .ancestor:has(:focus) { color: green } |
| .ancestor:has(:focus) .other-descendant { color: darkgreen } |
| .ancestor:has(.descendant2:focus-visible) { color: yellowgreen } |
| .ancestor:has(.descendant2:focus-visible) .other-descendant { color: greenyellow } |
| .ancestor:has(.descendant3:focus-within) { color: lightgreen } |
| .ancestor:has(.descendant3:focus-within) .other-descendant { color: violet } |
| </style> |
| <div id=subject1 class=ancestor> |
| <div> |
| <div id=hoverme class=descendant1>Hover and click me</div> |
| <div id=focusme1 tabindex=1>Focus me</div> |
| <div id=focusme2 class=descendant2 tabindex=2>Focus me</div> |
| <div class=descendant3> |
| <div><div id=focusme3 tabindex=3>Focus me</div></div> |
| </div> |
| </div> |
| <div><div id=subject3 class=other-descendant>subject</div></div> |
| </div> |
| <div id=subject2 class=ancestor> |
| <div id=focusme4 tabindex=4>Focus me</div> |
| <div><div id=subject4 class=other-descendant>subject</div></div> |
| </div> |
| <script> |
| const tab_key = '\ue004'; |
| |
| async_test(function(t) { |
| hoverme.addEventListener("mouseover", t.step_func(event => { |
| assert_equals(getComputedStyle(subject1).color, "rgb(0, 0, 255)", |
| "subject1 should be blue"); |
| assert_equals(getComputedStyle(subject3).color, "rgb(0, 0, 128)", |
| "subject3 should be navy"); |
| })); |
| hoverme.addEventListener("mousedown", t.step_func(event => { |
| assert_equals(getComputedStyle(subject1).color, "rgb(135, 206, 235)", |
| "subject1 should be skyblue"); |
| assert_equals(getComputedStyle(subject3).color, "rgb(173, 216, 230)", |
| "subject3 should be lightblue"); |
| })); |
| hoverme.addEventListener("mouseup", t.step_func(event => { |
| assert_equals(getComputedStyle(subject1).color, "rgb(0, 0, 255)", |
| "subject1 should be blue again"); |
| assert_equals(getComputedStyle(subject3).color, "rgb(0, 0, 128)", |
| "subject3 should be navy again"); |
| })); |
| focusme1.addEventListener("focus", t.step_func(function() { |
| assert_equals(getComputedStyle(subject1).color, "rgb(0, 128, 0)", |
| "subject1 should be green"); |
| assert_equals(getComputedStyle(subject3).color, "rgb(0, 100, 0)", |
| "subject3 should be darkgreen"); |
| test_driver.send_keys(document.body, tab_key); |
| })); |
| focusme2.addEventListener("focus", t.step_func(function() { |
| assert_equals(getComputedStyle(subject1).color, "rgb(154, 205, 50)", |
| "subject1 should be yellowgreen"); |
| assert_equals(getComputedStyle(subject3).color, "rgb(173, 255, 47)", |
| "subject3 should be greenyellow"); |
| test_driver.send_keys(document.body, tab_key); |
| })); |
| focusme3.addEventListener("focus", t.step_func(function() { |
| assert_equals(getComputedStyle(subject1).color, "rgb(144, 238, 144)", |
| "subject1 should be lightgreen"); |
| assert_equals(getComputedStyle(subject3).color, "rgb(238, 130, 238)", |
| "subject3 should be violet"); |
| |
| focusme3.remove(); |
| assert_equals(getComputedStyle(subject1).color, "rgb(0, 0, 0)", |
| "subject1 should be black"); |
| assert_equals(getComputedStyle(subject3).color, "rgb(0, 0, 0)", |
| "subject3 should be black"); |
| |
| test_driver.send_keys(document.body, tab_key); |
| })); |
| focusme4.addEventListener("focus", t.step_func_done(function() { |
| assert_equals(getComputedStyle(subject2).color, "rgb(0, 128, 0)", |
| "subject2 should be green"); |
| assert_equals(getComputedStyle(subject4).color, "rgb(0, 100, 0)", |
| "subject4 should be darkgreen"); |
| |
| focusme4.remove(); |
| assert_equals(getComputedStyle(subject2).color, "rgb(0, 0, 0)", |
| "subject2 should be black"); |
| assert_equals(getComputedStyle(subject4).color, "rgb(0, 0, 0)", |
| "subject4 should be black"); |
| })); |
| }, "Invalidation with :focus, :focus-within, :focus-visible in :has()"); |
| |
| test(() => { |
| assert_equals(getComputedStyle(subject1).color, "rgb(0, 0, 0)"); |
| assert_equals(getComputedStyle(subject2).color, "rgb(0, 0, 0)"); |
| }, "ancestor should be black"); |
| |
| let hovermeRect = hoverme.getBoundingClientRect(); |
| let focusme1Rect = focusme1.getBoundingClientRect(); |
| new test_driver.Actions() |
| .pointerMove(hovermeRect.x + 1, hovermeRect.y + 1, {origin: "viewport"}) |
| .pointerDown() |
| .pointerUp() |
| .pointerMove(focusme1Rect.x + 1, focusme1Rect.y + 1, {origin: "viewport"}) |
| .pointerDown() |
| .pointerUp() |
| .send(); |
| </script> |