<!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>
