Skip to content
Permalink
Browse files
[:has() pseudo-class] Support invalidation for :target pseudo-class
https://bugs.webkit.org/show_bug.cgi?id=240329

Reviewed by Antti Koivisto.

LayoutTests/imported/w3c:

Extend current test to cover more cases, and mark it as passing.

* web-platform-tests/css/selectors/invalidation/target-pseudo-in-has-expected.txt:
* web-platform-tests/css/selectors/invalidation/target-pseudo-in-has.html:

Source/WebCore:

Test: imported/w3c/web-platform-tests/css/selectors/invalidation/target-pseudo-in-has.html

* dom/Document.cpp:
(WebCore::Document::setCSSTarget):

Canonical link: https://commits.webkit.org/250483@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@294098 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
nt1m committed May 12, 2022
1 parent a8766e9 commit 9ea10e6701ff5576719bcea89389b491c0cdfa18
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 22 deletions.
@@ -1,3 +1,15 @@
2022-05-12 Tim Nguyen <ntim@apple.com>

[:has() pseudo-class] Support invalidation for :target pseudo-class
https://bugs.webkit.org/show_bug.cgi?id=240329

Reviewed by Antti Koivisto.

Extend current test to cover more cases, and mark it as passing.

* web-platform-tests/css/selectors/invalidation/target-pseudo-in-has-expected.txt:
* web-platform-tests/css/selectors/invalidation/target-pseudo-in-has.html:

2022-05-12 Tim Nguyen <ntim@apple.com>

Re-import css/selectors WPT from revision 4653e9128742e2c2609e76f04f4084cdc10ffead
@@ -1,5 +1,7 @@
parent color must be yellow green when containing :target
link to #fragment link to #
link to #fragment link to #fragment2 link to #fragment3 link to #
1: Must be green when containing :target
2: Must be green when containing :target
3: Must be green when containing :target

FAIL CSS Selectors Invalidation: target pseudo in :has() argument assert_equals: parent should be yellowgreen on fragment click expected "rgb(154, 205, 50)" but got "rgb(0, 128, 0)"
PASS CSS Selectors Invalidation: :target pseudo-class in :has() argument

@@ -1,36 +1,93 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>CSS Selectors Invalidation: target pseudo in :has() argument</title>
<title>CSS Selectors Invalidation: :target pseudo-class in :has() argument</title>
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#parent1 { color: green; }
#parent1:has(:target) { color: yellowgreen; }
#parent1 { color: grey; }
#parent1:has(:target) { color: green; }

#parent2 { color: blue; }
#parent2:has(:not(:target)) { color: grey; }
#parent2:has(:target) { color: green; }

#parent3 { color: green; }
#parent3:not(:has(:target)) { color: grey; }
</style>
<a href="#fragment">link to #fragment</a>
<a href="#fragment2">link to #fragment2</a>
<a href="#fragment3">link to #fragment3</a>
<a href="#">link to #</a>
<div id="parent1">
<div id="fragment">parent color must be yellow green when containing :target</div>
<a href="#fragment">link to #fragment</a>
<a href="#">link to #</a>
1:
<span id="fragment">Must be green when containing :target</span>
</div>
<div id="parent2">
2:
<span id="fragment2">Must be green when containing :target</span>
</div>
<div id="parent3">
3:
<span id="fragment3">Must be green when containing :target</span>
</div>
<script>
test((t) => {
const GREEN = "rgb(0, 128, 0)";
const GREY = "rgb(128, 128, 128)";
const BLUE = "rgb(0, 0, 255)";

function fragmentLink(fragment) {
return document.querySelector(`a[href="#${fragment}"]`);
}

promise_test(async () => {
const fragment = document.querySelector("#fragment");
const toFragment = document.querySelector(`a[href="#fragment"]`);
const toTop = document.querySelector(`a[href="#"]`);
const fragment2 = document.querySelector("#fragment2");
const fragment3 = document.querySelector("#fragment3");

location.hash = "";

assert_equals(getComputedStyle(parent1).color, "rgb(0, 128, 0)", "parent should be green");
assert_equals(getComputedStyle(parent1).color, GREY, "parent1 should be grey without :target");
assert_equals(getComputedStyle(parent2).color, GREY, "parent2 should be grey without :target");
assert_equals(getComputedStyle(parent3).color, GREY, "parent3 should be grey without :target");

toFragment.click();
fragmentLink("fragment").click();

assert_true(fragment.matches(":target"));
assert_equals(getComputedStyle(parent1).color, "rgb(154, 205, 50)", "parent should be yellowgreen on fragment click");
assert_equals(getComputedStyle(parent1).color, GREEN, "parent1 should be green on fragment click");
assert_equals(getComputedStyle(parent2).color, GREY, "parent2 should be grey without :target");
assert_equals(getComputedStyle(parent3).color, GREY, "parent3 should be grey without :target");

fragmentLink("fragment2").click();

assert_true(fragment2.matches(":target"));
assert_equals(getComputedStyle(parent1).color, GREY, "parent1 should be grey without :target");
assert_equals(getComputedStyle(parent2).color, GREEN, "parent2 should be green on fragment click");
assert_equals(getComputedStyle(parent3).color, GREY, "parent3 should be grey without :target");

fragment2.remove();
assert_equals(getComputedStyle(parent2).color, BLUE, "parent2 should be blue after removing only child");

parent2.append(fragment2);

// Wait for :target to be detected.
await new Promise(r => requestAnimationFrame(r));

assert_true(fragment2.matches(":target"));
assert_equals(getComputedStyle(parent2).color, GREEN, "parent2 should be green after re-appending :target child");

fragmentLink("fragment3").click();

assert_true(fragment3.matches(":target"));
assert_equals(getComputedStyle(parent1).color, GREY, "parent1 should be grey without :target");
assert_equals(getComputedStyle(parent2).color, GREY, "parent2 should be grey without :target");
assert_equals(getComputedStyle(parent3).color, GREEN, "parent3 should be green on fragment click");

toTop.click();
fragmentLink("").click();

assert_equals(location.hash, "");
assert_equals(getComputedStyle(parent1).color, "rgb(0, 128, 0)", "parent should be green without :target");
assert_equals(getComputedStyle(parent1).color, GREY, "parent1 should be grey without :target");
assert_equals(getComputedStyle(parent2).color, GREY, "parent2 should be grey without :target");
assert_equals(getComputedStyle(parent3).color, GREY, "parent3 should be grey without :target");
});
</script>
@@ -1,3 +1,15 @@
2022-05-12 Tim Nguyen <ntim@apple.com>

[:has() pseudo-class] Support invalidation for :target pseudo-class
https://bugs.webkit.org/show_bug.cgi?id=240329

Reviewed by Antti Koivisto.

Test: imported/w3c/web-platform-tests/css/selectors/invalidation/target-pseudo-in-has.html

* dom/Document.cpp:
(WebCore::Document::setCSSTarget):

2022-05-11 Diego Pino Garcia <dpino@igalia.com>

[WPE] Unreviewed, build fix after r293816
@@ -4870,13 +4870,19 @@ Element* Document::focusNavigationStartingNode(FocusDirection direction) const
return node->parentOrShadowHostElement();
}

void Document::setCSSTarget(Element* targetNode)
void Document::setCSSTarget(Element* newTarget)
{
if (m_cssTarget == newTarget)
return;

std::optional<Style::PseudoClassChangeInvalidation> oldInvalidation;
if (m_cssTarget)
m_cssTarget->invalidateStyleForSubtree();
m_cssTarget = targetNode;
if (targetNode)
targetNode->invalidateStyleForSubtree();
emplace(oldInvalidation, *m_cssTarget, { { CSSSelector::PseudoClassTarget, false } });

std::optional<Style::PseudoClassChangeInvalidation> newInvalidation;
if (newTarget)
emplace(newInvalidation, *newTarget, { { CSSSelector::PseudoClassTarget, true } });
m_cssTarget = newTarget;
}

void Document::registerNodeListForInvalidation(LiveNodeList& list)

0 comments on commit 9ea10e6

Please sign in to comment.