Accurate style invalidation for user action pseudo classes
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Mar 2020 13:30:10 +0000 (13:30 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Mar 2020 13:30:10 +0000 (13:30 +0000)
https://bugs.webkit.org/show_bug.cgi?id=208859
<rdar://problem/55196888>

Reviewed by Zalan Bujtas.

Source/WebCore:

Currently :hover, :focus, :focus-within and :active lack fine grained invalidation using
rule sets like we do with class and attribute selectors.

This can be added easily following the same pattern.

Tests: fast/selectors/style-invalidation-hover-change-descendants.html
       fast/selectors/style-invalidation-hover-change-siblings.html
       fast/selectors/style-invalidation-focus-change-descendants.html
       fast/selectors/style-invalidation-focus-change-siblings.html
       fast/selectors/style-invalidation-focus-within-change-descendants.html
       fast/selectors/style-invalidation-focus-within-change-siblings.html

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* dom/Element.cpp:
(WebCore::Element::setActive):
(WebCore::Element::setFocus):
(WebCore::Element::setHasFocusWithin):
(WebCore::Element::setHovered):

Use PseudoClassChangeInvalidation.

* dom/Element.h:
(WebCore::Element::setHasFocusWithin): Deleted.
* page/FrameViewLayoutContext.cpp:
(WebCore::RenderTreeNeedsLayoutChecker::~RenderTreeNeedsLayoutChecker):
* style/PseudoClassChangeInvalidation.cpp: Added.
(WebCore::Style::PseudoClassChangeInvalidation::computeInvalidation):

Compute invalidation rule set for a pseudo class change.

(WebCore::Style::PseudoClassChangeInvalidation::invalidateStyleWithRuleSets):
* style/PseudoClassChangeInvalidation.h: Added.
(WebCore::Style::PseudoClassChangeInvalidation::PseudoClassChangeInvalidation):
(WebCore::Style::PseudoClassChangeInvalidation::~PseudoClassChangeInvalidation):
* style/RuleFeature.cpp:
(WebCore::Style::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):
(WebCore::Style::RuleFeatureSet::collectFeatures):

Collect pseudo class features, similar to classes/attributes.

(WebCore::Style::RuleFeatureSet::add):
(WebCore::Style::RuleFeatureSet::clear):
(WebCore::Style::RuleFeatureSet::shrinkToFit):
* style/RuleFeature.h:
* style/StyleScopeRuleSets.cpp:
(WebCore::Style::ScopeRuleSets::collectFeatures const):
(WebCore::Style::ensureInvalidationRuleSets):

Make more generic to allow enum key.

(WebCore::Style::ScopeRuleSets::pseudoClassInvalidationRuleSets const):

Create pseudo class invalidation ruleset.

* style/StyleScopeRuleSets.h:

LayoutTests:

* fast/selectors/style-invalidation-focus-change-descendants-expected.txt: Added.
* fast/selectors/style-invalidation-focus-change-descendants.html: Added.
* fast/selectors/style-invalidation-focus-change-siblings-expected.txt: Added.
* fast/selectors/style-invalidation-focus-change-siblings.html: Added.
* fast/selectors/style-invalidation-focus-within-change-descendants-expected.txt: Added.
* fast/selectors/style-invalidation-focus-within-change-descendants.html: Added.
* fast/selectors/style-invalidation-focus-within-change-siblings-expected.txt: Added.
* fast/selectors/style-invalidation-focus-within-change-siblings.html: Added.
* fast/selectors/style-invalidation-hover-change-descendants-expected.txt: Added.
* fast/selectors/style-invalidation-hover-change-descendants.html: Added.
* fast/selectors/style-invalidation-hover-change-siblings-expected.txt: Added.
* fast/selectors/style-invalidation-hover-change-siblings.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@258321 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/selectors/style-invalidation-focus-change-descendants-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-focus-change-descendants.html [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-focus-change-siblings-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-focus-change-siblings.html [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-focus-within-change-descendants-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-focus-within-change-descendants.html [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-focus-within-change-siblings-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-focus-within-change-siblings.html [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-hover-change-descendants-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-hover-change-descendants.html [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-hover-change-siblings-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/style-invalidation-hover-change-siblings.html [new file with mode: 0644]
LayoutTests/platform/ios/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/style/PseudoClassChangeInvalidation.cpp [new file with mode: 0644]
Source/WebCore/style/PseudoClassChangeInvalidation.h [new file with mode: 0644]
Source/WebCore/style/RuleFeature.cpp
Source/WebCore/style/RuleFeature.h
Source/WebCore/style/StyleScopeRuleSets.cpp
Source/WebCore/style/StyleScopeRuleSets.h

index e4e6987..817e791 100644 (file)
@@ -1,3 +1,24 @@
+2020-03-12  Antti Koivisto  <antti@apple.com>
+
+        Accurate style invalidation for user action pseudo classes
+        https://bugs.webkit.org/show_bug.cgi?id=208859
+        <rdar://problem/55196888>
+
+        Reviewed by Zalan Bujtas.
+
+        * fast/selectors/style-invalidation-focus-change-descendants-expected.txt: Added.
+        * fast/selectors/style-invalidation-focus-change-descendants.html: Added.
+        * fast/selectors/style-invalidation-focus-change-siblings-expected.txt: Added.
+        * fast/selectors/style-invalidation-focus-change-siblings.html: Added.
+        * fast/selectors/style-invalidation-focus-within-change-descendants-expected.txt: Added.
+        * fast/selectors/style-invalidation-focus-within-change-descendants.html: Added.
+        * fast/selectors/style-invalidation-focus-within-change-siblings-expected.txt: Added.
+        * fast/selectors/style-invalidation-focus-within-change-siblings.html: Added.
+        * fast/selectors/style-invalidation-hover-change-descendants-expected.txt: Added.
+        * fast/selectors/style-invalidation-hover-change-descendants.html: Added.
+        * fast/selectors/style-invalidation-hover-change-siblings-expected.txt: Added.
+        * fast/selectors/style-invalidation-hover-change-siblings.html: Added.
+
 2020-03-12  Diego Pino Garcia  <dpino@igalia.com>
 
         [WPE] Gardening, expected to fail but passed
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-change-descendants-expected.txt b/LayoutTests/fast/selectors/style-invalidation-focus-change-descendants-expected.txt
new file mode 100644 (file)
index 0000000..0335bbe
--- /dev/null
@@ -0,0 +1,7 @@
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-change-descendants.html b/LayoutTests/fast/selectors/style-invalidation-focus-change-descendants.html
new file mode 100644 (file)
index 0000000..09eff0d
--- /dev/null
@@ -0,0 +1,82 @@
+<html>
+<style>
+.container {
+    background-color: blue;
+    width: 200px;
+    height: 600px;
+    position: absolute;
+}
+.inert {
+    background-color: yellow;
+        width: 100px;
+    height: 100px;
+    position: relative;
+}
+.target {
+    background-color: red;
+    width: 100px;
+    height: 100px;
+    position: relative;
+}
+.container:focus .target {
+    background-color: green;
+}
+.child {
+    width: 50px;
+    height: 50px;
+}
+</style>
+<div class=container tabindex=1 id=focusTarget>
+    <div class=inert>
+    </div>
+    <div class=target>
+    </div>
+    <div class=inert>
+    </div>
+    <div class=target>
+        <div class="inert child">
+        </div>
+    </div>
+    <div class=inert>
+        <div class="target child">
+        </div>
+    </div>
+</div>
+<pre id=log></pre>
+
+<script>
+function testStyleChangeType(selector, expectedType)
+{
+    let pass = true;
+    const elements = document.querySelectorAll(selector);
+    for (var i = 0; i < elements.length; ++i) {
+        const type = window.internals.styleChangeType(elements[i]);
+        if (type != expectedType) {
+            log.textContent += `FAIL ${selector} styleChangeType was ${type} expected ${expectedType}\n`;
+            pass = false;
+        }
+    }
+    if (pass)
+        log.textContent += "PASS\n";
+}
+
+window.onload = function () {
+    if (!window.testRunner)
+        return;
+    testRunner.dumpAsText();
+
+    document.body.offsetLeft;
+    focusTarget.focus();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "InlineStyleChange"); // Style change due to to UA style sheet.
+    testStyleChangeType(".inert", "NoStyleChange");
+
+    document.body.offsetLeft;
+    focusTarget.blur();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "InlineStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+};
+</script>
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-change-siblings-expected.txt b/LayoutTests/fast/selectors/style-invalidation-focus-change-siblings-expected.txt
new file mode 100644 (file)
index 0000000..0335bbe
--- /dev/null
@@ -0,0 +1,7 @@
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-change-siblings.html b/LayoutTests/fast/selectors/style-invalidation-focus-change-siblings.html
new file mode 100644 (file)
index 0000000..83d0d55
--- /dev/null
@@ -0,0 +1,86 @@
+<html>
+<style>
+.container {
+    background-color: blue;
+    width: 200px;
+    height: 600px;
+    position: absolute;
+}
+.inert {
+    background-color: yellow;
+        width: 100px;
+    height: 100px;
+    position: relative;
+}
+.target {
+    background-color: red;
+    width: 100px;
+    height: 100px;
+    position: relative;
+}
+:focus ~ .target {
+    background-color: green;
+}
+:focus ~ .inert > .target {
+    background-color: green;
+}
+.child {
+    width: 50px;
+    height: 50px;
+}
+</style>
+<div class=container>
+    <!--Style change due to to UA style sheet.-->
+    <div class=target tabindex=1 id=focusTarget>
+    </div>
+    <div class=target>
+    </div>
+    <div class=inert>
+    </div>
+    <div class=target>
+        <div class="inert child">
+        </div>
+    </div>
+    <div class=inert>
+        <div class="target child">
+        </div>
+    </div>
+</div>
+<pre id=log></pre>
+
+<script>
+function testStyleChangeType(selector, expectedType)
+{
+    let pass = true;
+    const elements = document.querySelectorAll(selector);
+    for (var i = 0; i < elements.length; ++i) {
+        const type = window.internals.styleChangeType(elements[i]);
+        if (type != expectedType) {
+            log.textContent += `FAIL ${selector} styleChangeType was ${type} expected ${expectedType}\n`;
+            pass = false;
+        }
+    }
+    if (pass)
+        log.textContent += "PASS\n";
+}
+
+window.onload = function () {
+    if (!window.testRunner)
+        return;
+    testRunner.dumpAsText();
+
+    document.body.offsetLeft;
+    focusTarget.focus();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+
+    document.body.offsetLeft;
+    focusTarget.blur();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+};
+</script>
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-within-change-descendants-expected.txt b/LayoutTests/fast/selectors/style-invalidation-focus-within-change-descendants-expected.txt
new file mode 100644 (file)
index 0000000..0335bbe
--- /dev/null
@@ -0,0 +1,7 @@
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-within-change-descendants.html b/LayoutTests/fast/selectors/style-invalidation-focus-within-change-descendants.html
new file mode 100644 (file)
index 0000000..c23ac83
--- /dev/null
@@ -0,0 +1,82 @@
+<html>
+<style>
+.container {
+    background-color: blue;
+    width: 200px;
+    height: 600px;
+    position: absolute;
+}
+.inert {
+    background-color: yellow;
+        width: 100px;
+    height: 100px;
+    position: relative;
+}
+.target {
+    background-color: red;
+    width: 100px;
+    height: 100px;
+    position: relative;
+}
+.container:focus-within .target {
+    background-color: green;
+}
+.child {
+    width: 50px;
+    height: 50px;
+}
+</style>
+<div class=container>
+    <div class=target tabindex=1 id=focusTarget>
+    </div>
+    <div class=target>
+    </div>
+    <div class=inert>
+    </div>
+    <div class=target>
+        <div class="inert child">
+        </div>
+    </div>
+    <div class=inert>
+        <div class="target child">
+        </div>
+    </div>
+</div>
+<pre id=log></pre>
+
+<script>
+function testStyleChangeType(selector, expectedType)
+{
+    let pass = true;
+    const elements = document.querySelectorAll(selector);
+    for (var i = 0; i < elements.length; ++i) {
+        const type = window.internals.styleChangeType(elements[i]);
+        if (type != expectedType) {
+            log.textContent += `FAIL ${selector} styleChangeType was ${type} expected ${expectedType}\n`;
+            pass = false;
+        }
+    }
+    if (pass)
+        log.textContent += "PASS\n";
+}
+
+window.onload = function () {
+    if (!window.testRunner)
+        return;
+    testRunner.dumpAsText();
+
+    document.body.offsetLeft;
+    focusTarget.focus();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+
+    document.body.offsetLeft;
+    focusTarget.blur();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+};
+</script>
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-within-change-siblings-expected.txt b/LayoutTests/fast/selectors/style-invalidation-focus-within-change-siblings-expected.txt
new file mode 100644 (file)
index 0000000..0335bbe
--- /dev/null
@@ -0,0 +1,7 @@
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+
diff --git a/LayoutTests/fast/selectors/style-invalidation-focus-within-change-siblings.html b/LayoutTests/fast/selectors/style-invalidation-focus-within-change-siblings.html
new file mode 100644 (file)
index 0000000..292c887
--- /dev/null
@@ -0,0 +1,88 @@
+<html>
+<style>
+.container {
+    background-color: blue;
+    width: 200px;
+    height: 600px;
+    position: absolute;
+}
+.inert {
+    background-color: yellow;
+        width: 100px;
+    height: 100px;
+    position: relative;
+}
+.target {
+    background-color: red;
+    width: 100px;
+    height: 100px;
+    position: relative;
+}
+.inert:focus-within ~ .target {
+    background-color: green;
+}
+.inert:focus-within ~ .inert > .target {
+    background-color: green;
+}
+.child {
+    width: 50px;
+    height: 50px;
+}
+</style>
+<div class=container>
+    <div class=inert>
+        <!--Style change due to to UA style sheet.-->
+        <div class="target child" tabindex=1 id=focusTarget>
+        </div>
+    </div>
+    <div class=target>
+    </div>
+    <div class=inert>
+    </div>
+    <div class=target>
+        <div class="inert child">
+        </div>
+    </div>
+    <div class=inert>
+        <div class="target child">
+        </div>
+    </div>
+</div>
+<pre id=log></pre>
+
+<script>
+function testStyleChangeType(selector, expectedType)
+{
+    let pass = true;
+    const elements = document.querySelectorAll(selector);
+    for (var i = 0; i < elements.length; ++i) {
+        const type = window.internals.styleChangeType(elements[i]);
+        if (type != expectedType) {
+            log.textContent += `FAIL ${selector} styleChangeType was ${type} expected ${expectedType}\n`;
+            pass = false;
+        }
+    }
+    if (pass)
+        log.textContent += "PASS\n";
+}
+
+window.onload = function () {
+    if (!window.testRunner)
+        return;
+    testRunner.dumpAsText();
+
+    document.body.offsetLeft;
+    focusTarget.focus();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+
+    document.body.offsetLeft;
+    focusTarget.blur();
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+};
+</script>
diff --git a/LayoutTests/fast/selectors/style-invalidation-hover-change-descendants-expected.txt b/LayoutTests/fast/selectors/style-invalidation-hover-change-descendants-expected.txt
new file mode 100644 (file)
index 0000000..0335bbe
--- /dev/null
@@ -0,0 +1,7 @@
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+
diff --git a/LayoutTests/fast/selectors/style-invalidation-hover-change-descendants.html b/LayoutTests/fast/selectors/style-invalidation-hover-change-descendants.html
new file mode 100644 (file)
index 0000000..a0747f5
--- /dev/null
@@ -0,0 +1,82 @@
+<html>
+<style>
+.container {
+    background-color: blue;
+    width: 200px;
+    height: 600px;
+    position: absolute;
+}
+.inert {
+    background-color: yellow;
+        width: 100px;
+    height: 100px;
+    position: relative;
+}
+.target {
+    background-color: red;
+    width: 100px;
+    height: 100px;
+    position: relative;
+}
+.container:hover .target {
+    background-color: green;
+}
+.child {
+    width: 50px;
+    height: 50px;
+}
+</style>
+<div class=container>
+    <div class=inert>
+    </div>
+    <div class=target>
+    </div>
+    <div class=inert>
+    </div>
+    <div class=target>
+        <div class="inert child">
+        </div>
+    </div>
+    <div class=inert>
+        <div class="target child">
+        </div>
+    </div>
+</div>
+<pre id=log></pre>
+
+<script>
+function testStyleChangeType(selector, expectedType)
+{
+    let pass = true;
+    const elements = document.querySelectorAll(selector);
+    for (var i = 0; i < elements.length; ++i) {
+        const type = window.internals.styleChangeType(elements[i]);
+        if (type != expectedType) {
+            log.textContent += `FAIL ${selector} styleChangeType was ${type} expected ${expectedType}\n`;
+            pass = false;
+        }
+    }
+    if (pass)
+        log.textContent += "PASS\n";
+}
+
+window.onload = function () {
+    if (!window.testRunner)
+        return;
+    testRunner.dumpAsText();
+
+    document.body.offsetLeft;
+    eventSender.mouseMoveTo(50,50);
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+
+    document.body.offsetLeft;
+    eventSender.mouseMoveTo(300,50);
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+};
+</script>
diff --git a/LayoutTests/fast/selectors/style-invalidation-hover-change-siblings-expected.txt b/LayoutTests/fast/selectors/style-invalidation-hover-change-siblings-expected.txt
new file mode 100644 (file)
index 0000000..0335bbe
--- /dev/null
@@ -0,0 +1,7 @@
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+
diff --git a/LayoutTests/fast/selectors/style-invalidation-hover-change-siblings.html b/LayoutTests/fast/selectors/style-invalidation-hover-change-siblings.html
new file mode 100644 (file)
index 0000000..6d2ffac
--- /dev/null
@@ -0,0 +1,85 @@
+<html>
+<style>
+.container {
+    background-color: blue;
+    width: 200px;
+    height: 600px;
+    position: absolute;
+}
+.inert {
+    background-color: yellow;
+        width: 100px;
+    height: 100px;
+    position: relative;
+}
+.target {
+    background-color: red;
+    width: 100px;
+    height: 100px;
+    position: relative;
+}
+.inert:hover ~ .target {
+    background-color: green;
+}
+.inert:hover ~ .inert > .target {
+    background-color: green;
+}
+.child {
+    width: 50px;
+    height: 50px;
+}
+</style>
+<div class=container>
+    <div class=inert>
+    </div>
+    <div class=target>
+    </div>
+    <div class=inert>
+    </div>
+    <div class=target>
+        <div class="inert child">
+        </div>
+    </div>
+    <div class=inert>
+        <div class="target child">
+        </div>
+    </div>
+</div>
+<pre id=log></pre>
+
+<script>
+function testStyleChangeType(selector, expectedType)
+{
+    let pass = true;
+    const elements = document.querySelectorAll(selector);
+    for (var i = 0; i < elements.length; ++i) {
+        const type = window.internals.styleChangeType(elements[i]);
+        if (type != expectedType) {
+            log.textContent += `FAIL ${selector} styleChangeType was ${type} expected ${expectedType}\n`;
+            pass = false;
+        }
+    }
+    if (pass)
+        log.textContent += "PASS\n";
+}
+
+window.onload = function () {
+    if (!window.testRunner)
+        return;
+    testRunner.dumpAsText();
+
+    document.body.offsetLeft;
+    eventSender.mouseMoveTo(50,50);
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+
+    document.body.offsetLeft;
+    eventSender.mouseMoveTo(300,50);
+
+    testStyleChangeType(".target", "InlineStyleChange");
+    testStyleChangeType(".container", "NoStyleChange");
+    testStyleChangeType(".inert", "NoStyleChange");
+};
+</script>
index f96c48b..ba4edf9 100644 (file)
@@ -693,6 +693,8 @@ fast/selectors/hover-quirks.html [ Skip ]
 fast/selectors/hover-strict.html [ Skip ]
 fast/selectors/not-active-hover-quirks.html [ Skip ]
 fast/selectors/not-active-hover-strict.html [ Skip ]
+fast/selectors/style-invalidation-hover-change-descendants.html [ Skip ]
+fast/selectors/style-invalidation-hover-change-siblings.html [ Skip ]
 fast/shapes/shape-outside-floats/shape-outside-clip-path-selection.html [ Skip ]
 fast/shadow-dom/mouseenter-mouseleave-across-shadow-boundary.html [ Skip ]
 fast/shadow-dom/mouseenter-mouseleave-inside-shadow-tree.html [ Skip ]
index 208260f..73a23fd 100644 (file)
@@ -2863,6 +2863,14 @@ webkit.org/b/140519 [ Debug ] fast/selectors/querySelector-id-with-multiple-elem
 fast/selectors/querySelector-window-inactive.html [ Failure ]
 fast/selectors/read-only-read-write-input-basics.html [ Failure ]
 
+# Win port sometimes forces synchronous style recalc on focus/hover making these unreliable.
+fast/selectors/style-invalidation-focus-change-descendants.html [ Pass Failure ]
+fast/selectors/style-invalidation-focus-change-siblings.html [ Pass Failure ]
+fast/selectors/style-invalidation-focus-within-change-descendants.html [ Pass Failure ]
+fast/selectors/style-invalidation-focus-within-change-siblings.html [ Pass Failure ]
+fast/selectors/style-invalidation-hover-change-descendants.html [ Pass Failure ]
+fast/selectors/style-invalidation-hover-change-siblings.html [ Pass Failure ]
+
 fast/shapes/shape-outside-floats/shape-outside-image-fit-002.html [ Pass ImageOnlyFailure ]
 
 fast/table/border-collapsing/cached-change-tbody-border-width.html [ Pass Failure ]
index 0c91bf2..cfe97ed 100644 (file)
@@ -1,3 +1,68 @@
+2020-03-12  Antti Koivisto  <antti@apple.com>
+
+        Accurate style invalidation for user action pseudo classes
+        https://bugs.webkit.org/show_bug.cgi?id=208859
+        <rdar://problem/55196888>
+
+        Reviewed by Zalan Bujtas.
+
+        Currently :hover, :focus, :focus-within and :active lack fine grained invalidation using
+        rule sets like we do with class and attribute selectors.
+
+        This can be added easily following the same pattern.
+
+        Tests: fast/selectors/style-invalidation-hover-change-descendants.html
+               fast/selectors/style-invalidation-hover-change-siblings.html
+               fast/selectors/style-invalidation-focus-change-descendants.html
+               fast/selectors/style-invalidation-focus-change-siblings.html
+               fast/selectors/style-invalidation-focus-within-change-descendants.html
+               fast/selectors/style-invalidation-focus-within-change-siblings.html
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/Element.cpp:
+        (WebCore::Element::setActive):
+        (WebCore::Element::setFocus):
+        (WebCore::Element::setHasFocusWithin):
+        (WebCore::Element::setHovered):
+
+        Use PseudoClassChangeInvalidation.
+
+        * dom/Element.h:
+        (WebCore::Element::setHasFocusWithin): Deleted.
+        * page/FrameViewLayoutContext.cpp:
+        (WebCore::RenderTreeNeedsLayoutChecker::~RenderTreeNeedsLayoutChecker):
+        * style/PseudoClassChangeInvalidation.cpp: Added.
+        (WebCore::Style::PseudoClassChangeInvalidation::computeInvalidation):
+
+        Compute invalidation rule set for a pseudo class change.
+
+        (WebCore::Style::PseudoClassChangeInvalidation::invalidateStyleWithRuleSets):
+        * style/PseudoClassChangeInvalidation.h: Added.
+        (WebCore::Style::PseudoClassChangeInvalidation::PseudoClassChangeInvalidation):
+        (WebCore::Style::PseudoClassChangeInvalidation::~PseudoClassChangeInvalidation):
+        * style/RuleFeature.cpp:
+        (WebCore::Style::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):
+        (WebCore::Style::RuleFeatureSet::collectFeatures):
+
+        Collect pseudo class features, similar to classes/attributes.
+
+        (WebCore::Style::RuleFeatureSet::add):
+        (WebCore::Style::RuleFeatureSet::clear):
+        (WebCore::Style::RuleFeatureSet::shrinkToFit):
+        * style/RuleFeature.h:
+        * style/StyleScopeRuleSets.cpp:
+        (WebCore::Style::ScopeRuleSets::collectFeatures const):
+        (WebCore::Style::ensureInvalidationRuleSets):
+
+        Make more generic to allow enum key.
+
+        (WebCore::Style::ScopeRuleSets::pseudoClassInvalidationRuleSets const):
+
+        Create pseudo class invalidation ruleset.
+
+        * style/StyleScopeRuleSets.h:
+
 2020-03-12  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         [GPU Process] GraphicsContextStateChange must accumulate fill and stroke fields as single properties
index 0d4532f..b076c23 100644 (file)
@@ -2374,6 +2374,7 @@ style/InspectorCSSOMWrappers.cpp
 style/MatchedDeclarationsCache.cpp
 style/PageRuleCollector.cpp
 style/PropertyCascade.cpp
+style/PseudoClassChangeInvalidation.cpp
 style/RuleData.cpp
 style/RuleFeature.cpp
 style/RuleSet.cpp
index b6b3cd8..6e1971b 100644 (file)
                E451C6322394031A00993190 /* MarginTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FE7AA2621C37B6300296DCD /* MarginTypes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E451C6342394058F00993190 /* DisplayInlineContent.h in Headers */ = {isa = PBXBuildFile; fileRef = E451C6332394058E00993190 /* DisplayInlineContent.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E45322AC140CE267005A0F92 /* SelectorQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = E45322AA140CE267005A0F92 /* SelectorQuery.h */; };
+               E45A6C772417BA59006E4CD5 /* PseudoClassChangeInvalidation.h in Headers */ = {isa = PBXBuildFile; fileRef = E45A6C762417BA59006E4CD5 /* PseudoClassChangeInvalidation.h */; };
                E45BA6AA2374926C004DFC07 /* MatchedDeclarationsCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E45BA6A82374926B004DFC07 /* MatchedDeclarationsCache.h */; };
                E45BA6B6237622A3004DFC07 /* StyleAdjuster.h in Headers */ = {isa = PBXBuildFile; fileRef = E45BA6B52376229F004DFC07 /* StyleAdjuster.h */; };
                E4605FEC2166480900E53046 /* PrewarmInformation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4605FEA2166480800E53046 /* PrewarmInformation.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E45390340EAFD637003695C8 /* ScrollViewIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollViewIOS.mm; sourceTree = "<group>"; };
                E453903C0EAFD637003695C8 /* WidgetIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WidgetIOS.mm; sourceTree = "<group>"; };
                E45390AD0EAFF4B5003695C8 /* SystemMemoryIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemMemoryIOS.cpp; sourceTree = "<group>"; };
+               E45A6C732417BA4C006E4CD5 /* PseudoClassChangeInvalidation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PseudoClassChangeInvalidation.cpp; sourceTree = "<group>"; };
+               E45A6C762417BA59006E4CD5 /* PseudoClassChangeInvalidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PseudoClassChangeInvalidation.h; sourceTree = "<group>"; };
                E45BA6A82374926B004DFC07 /* MatchedDeclarationsCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatchedDeclarationsCache.h; sourceTree = "<group>"; };
                E45BA6AB2374927B004DFC07 /* MatchedDeclarationsCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MatchedDeclarationsCache.cpp; sourceTree = "<group>"; };
                E45BA6B22376227E004DFC07 /* StyleAdjuster.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StyleAdjuster.cpp; sourceTree = "<group>"; };
                                FBDB61A016D6037E00BB3394 /* PageRuleCollector.h */,
                                E4ABABE52361A34200FA4345 /* PropertyCascade.cpp */,
                                E4ABABE22361A32900FA4345 /* PropertyCascade.h */,
+                               E45A6C732417BA4C006E4CD5 /* PseudoClassChangeInvalidation.cpp */,
+                               E45A6C762417BA59006E4CD5 /* PseudoClassChangeInvalidation.h */,
                                E4863CFA23842E8700972158 /* RuleData.cpp */,
                                E4863CFD23842E9E00972158 /* RuleData.h */,
                                A79BAD9D161E7F3F00C2E652 /* RuleFeature.cpp */,
                                B2FA3D570AB75A6F000E5AC4 /* JSSVGAnimationElement.h in Headers */,
                                B2FA3D590AB75A6F000E5AC4 /* JSSVGCircleElement.h in Headers */,
                                B2FA3D5B0AB75A6F000E5AC4 /* JSSVGClipPathElement.h in Headers */,
+                               E45A6C772417BA59006E4CD5 /* PseudoClassChangeInvalidation.h in Headers */,
                                B2FA3D5F0AB75A6F000E5AC4 /* JSSVGComponentTransferFunctionElement.h in Headers */,
                                B2FA3D610AB75A6F000E5AC4 /* JSSVGCursorElement.h in Headers */,
                                B2FA3D630AB75A6F000E5AC4 /* JSSVGDefsElement.h in Headers */,
index a0c82c2..b670bfb 100644 (file)
@@ -86,6 +86,7 @@
 #include "PointerCaptureController.h"
 #include "PointerEvent.h"
 #include "PointerLockController.h"
+#include "PseudoClassChangeInvalidation.h"
 #include "RenderFragmentedFlow.h"
 #include "RenderLayer.h"
 #include "RenderLayerBacking.h"
@@ -632,17 +633,15 @@ void Element::setActive(bool flag, bool pause)
 {
     if (flag == active())
         return;
-
-    document().userActionElements().setActive(*this, flag);
-
-    auto* renderStyle = renderOrDisplayContentsStyle();
-    bool reactsToPress = (renderStyle && renderStyle->affectedByActive()) || styleAffectedByActive();
-    if (reactsToPress)
-        invalidateStyleForSubtree();
+    {
+        Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassActive);
+        document().userActionElements().setActive(*this, flag);
+    }
 
     if (!renderer())
         return;
 
+    bool reactsToPress = false;
     if (renderer()->style().hasAppearance() && renderer()->theme().stateChanged(*renderer(), ControlStates::PressedState))
         reactsToPress = true;
 
@@ -681,9 +680,11 @@ void Element::setFocus(bool flag)
 {
     if (flag == focused())
         return;
-
-    document().userActionElements().setFocused(*this, flag);
-    invalidateStyleForSubtree();
+    {
+        Style::PseudoClassChangeInvalidation focusStyleInvalidation(*this, CSSSelector::PseudoClassFocus);
+        Style::PseudoClassChangeInvalidation directFocusStyleInvalidation(*this, CSSSelector::PseudoClassDirectFocus);
+        document().userActionElements().setFocused(*this, flag);
+    }
 
     // Shadow host with a slot that contain focused element is not considered focused.
     for (auto* root = containingShadowRoot(); root; root = root->host()->containingShadowRoot()) {
@@ -691,34 +692,30 @@ void Element::setFocus(bool flag)
         root->host()->invalidateStyle();
     }
 
-    for (Element* element = this; element; element = element->parentElementInComposedTree())
+    for (auto* element = this; element; element = element->parentElementInComposedTree())
         element->setHasFocusWithin(flag);
 }
 
-void Element::setHovered(bool flag)
+void Element::setHasFocusWithin(bool flag)
 {
-    if (flag == hovered())
+    if (hasFocusWithin() == flag)
         return;
+    {
+        Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassFocusWithin);
+        setFlag(flag, HasFocusWithin);
+    }
+}
 
-    document().userActionElements().setHovered(*this, flag);
-
-    auto* style = renderOrDisplayContentsStyle();
-    if (style && (style->affectedByHover() || childrenAffectedByHover()))
-        invalidateStyleForSubtree();
-
-    if (!renderer()) {
-        // When setting hover to false, the style needs to be recalc'd even when
-        // there's no renderer (imagine setting display:none in the :hover class,
-        // if a nil renderer would prevent this element from recalculating its
-        // style, it would never go back to its normal style and remain
-        // stuck in its hovered style).
-        if (!flag && !style)
-            invalidateStyleForSubtree();
-
+void Element::setHovered(bool flag)
+{
+    if (flag == hovered())
         return;
+    {
+        Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassHover);
+        document().userActionElements().setHovered(*this, flag);
     }
 
-    if (style->hasAppearance())
+    if (auto* style = renderStyle(); style && style->hasAppearance())
         renderer()->theme().stateChanged(*renderer(), ControlStates::HoverState);
 }
 
index 1c6bb12..ba49f09 100644 (file)
@@ -859,15 +859,6 @@ inline bool shouldIgnoreAttributeCase(const Element& element)
     return element.isHTMLElement() && element.document().isHTMLDocument();
 }
 
-inline void Element::setHasFocusWithin(bool flag)
-{
-    if (hasFocusWithin() == flag)
-        return;
-    setFlag(flag, HasFocusWithin);
-    if (styleAffectedByFocusWithin())
-        invalidateStyleForSubtree();
-}
-
 template<typename... QualifiedNames>
 inline const AtomString& Element::getAttribute(const QualifiedName& name, const QualifiedNames&... names) const
 {
diff --git a/Source/WebCore/style/PseudoClassChangeInvalidation.cpp b/Source/WebCore/style/PseudoClassChangeInvalidation.cpp
new file mode 100644 (file)
index 0000000..b715bc9
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PseudoClassChangeInvalidation.h"
+
+#include "ElementChildIterator.h"
+#include "StyleInvalidationFunctions.h"
+
+namespace WebCore {
+namespace Style {
+
+void PseudoClassChangeInvalidation::computeInvalidation(CSSSelector::PseudoClassType pseudoClass)
+{
+    bool shouldInvalidateCurrent = false;
+    bool mayAffectStyleInShadowTree = false;
+
+    traverseRuleFeatures(m_element, [&] (const RuleFeatureSet& features, bool mayAffectShadowTree) {
+        if (mayAffectShadowTree && features.pseudoClassRules.contains(pseudoClass))
+            mayAffectStyleInShadowTree = true;
+        if (m_element.shadowRoot() && features.pseudoClassesAffectingHost.contains(pseudoClass))
+            shouldInvalidateCurrent = true;
+    });
+
+    if (mayAffectStyleInShadowTree) {
+        // FIXME: We should do fine-grained invalidation for shadow tree.
+        m_element.invalidateStyleForSubtree();
+    }
+
+    if (shouldInvalidateCurrent)
+        m_element.invalidateStyle();
+
+    auto& ruleSets = m_element.styleResolver().ruleSets();
+    if (auto* invalidationRuleSets = ruleSets.pseudoClassInvalidationRuleSets(pseudoClass)) {
+        for (auto& invalidationRuleSet : *invalidationRuleSets)
+            Invalidator::addToMatchElementRuleSets(m_matchElementRuleSets, invalidationRuleSet);
+    }
+}
+
+void PseudoClassChangeInvalidation::invalidateStyleWithRuleSets()
+{
+    Invalidator::invalidateWithMatchElementRuleSets(m_element, m_matchElementRuleSets);
+}
+
+}
+}
diff --git a/Source/WebCore/style/PseudoClassChangeInvalidation.h b/Source/WebCore/style/PseudoClassChangeInvalidation.h
new file mode 100644 (file)
index 0000000..a4c580c
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CSSSelector.h"
+#include "Element.h"
+#include "StyleInvalidator.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+namespace Style {
+
+class PseudoClassChangeInvalidation {
+public:
+    PseudoClassChangeInvalidation(Element&, CSSSelector::PseudoClassType);
+    ~PseudoClassChangeInvalidation();
+
+private:
+    void computeInvalidation(CSSSelector::PseudoClassType);
+    void invalidateStyleWithRuleSets();
+
+    const bool m_isEnabled;
+    Element& m_element;
+
+    Invalidator::MatchElementRuleSets m_matchElementRuleSets;
+};
+
+inline PseudoClassChangeInvalidation::PseudoClassChangeInvalidation(Element& element, CSSSelector::PseudoClassType pseudoClassType)
+    : m_isEnabled(element.needsStyleInvalidation())
+    , m_element(element)
+
+{
+    if (!m_isEnabled)
+        return;
+    computeInvalidation(pseudoClassType);
+    invalidateStyleWithRuleSets();
+}
+
+inline PseudoClassChangeInvalidation::~PseudoClassChangeInvalidation()
+{
+    if (!m_isEnabled)
+        return;
+    invalidateStyleWithRuleSets();
+}
+
+}
+}
index 7dffdae..e3fec5c 100644 (file)
@@ -153,7 +153,8 @@ void RuleFeatureSet::recursivelyCollectFeaturesFromSelector(SelectorFeatures& se
             default:
                 break;
             }
-        }
+        } else if (selector->match() == CSSSelector::PseudoClass)
+            selectorFeatures.pseudoClasses.append(std::make_pair(selector->pseudoClassType(), matchElement));
 
         if (!selectorFeatures.hasSiblingSelector && selector->isSiblingSelector())
             selectorFeatures.hasSiblingSelector = true;
@@ -199,6 +200,13 @@ void RuleFeatureSet::collectFeatures(const RuleData& ruleData)
         if (matchElement == MatchElement::Host)
             attributesAffectingHost.add(selector->attribute().localName().convertToASCIILowercase());
     }
+    for (auto& keyAndMatch : selectorFeatures.pseudoClasses) {
+        pseudoClassRules.ensure(keyAndMatch.first, [] {
+            return makeUnique<Vector<RuleFeature>>();
+        }).iterator->value->append({ ruleData, keyAndMatch.second });
+        if (keyAndMatch.second == MatchElement::Host)
+            pseudoClassesAffectingHost.add(keyAndMatch.first);
+    }
 }
 
 void RuleFeatureSet::add(const RuleFeatureSet& other)
@@ -224,6 +232,13 @@ void RuleFeatureSet::add(const RuleFeatureSet& other)
     }
     attributesAffectingHost.add(other.attributesAffectingHost.begin(), other.attributesAffectingHost.end());
 
+    for (auto& keyValuePair : other.pseudoClassRules) {
+        pseudoClassRules.ensure(keyValuePair.key, [] {
+            return makeUnique<Vector<RuleFeature>>();
+        }).iterator->value->appendVector(*keyValuePair.value);
+    }
+    pseudoClassesAffectingHost.add(other.pseudoClassesAffectingHost.begin(), other.pseudoClassesAffectingHost.end());
+
     usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
     usesFirstLetterRules = usesFirstLetterRules || other.usesFirstLetterRules;
 }
@@ -248,6 +263,8 @@ void RuleFeatureSet::clear()
     classesAffectingHost.clear();
     attributeRules.clear();
     attributesAffectingHost.clear();
+    pseudoClassRules.clear();
+    pseudoClassesAffectingHost.clear();
     usesFirstLineRules = false;
     usesFirstLetterRules = false;
 }
@@ -260,6 +277,8 @@ void RuleFeatureSet::shrinkToFit()
         rules->shrinkToFit();
     for (auto& rules : attributeRules.values())
         rules->shrinkToFit();
+    for (auto& rules : pseudoClassRules.values())
+        rules->shrinkToFit();
 }
 
 } // namespace Style
index cc67f3f..4418ff6 100644 (file)
@@ -75,8 +75,10 @@ struct RuleFeatureSet {
     
     HashMap<AtomString, std::unique_ptr<Vector<RuleFeature>>> classRules;
     HashMap<AtomString, std::unique_ptr<Vector<RuleFeatureWithInvalidationSelector>>> attributeRules;
+    HashMap<CSSSelector::PseudoClassType, std::unique_ptr<Vector<RuleFeature>>, WTF::IntHash<CSSSelector::PseudoClassType>, WTF::StrongEnumHashTraits<CSSSelector::PseudoClassType>> pseudoClassRules;
     HashSet<AtomString> classesAffectingHost;
     HashSet<AtomString> attributesAffectingHost;
+    HashSet<CSSSelector::PseudoClassType, WTF::IntHash<CSSSelector::PseudoClassType>, WTF::StrongEnumHashTraits<CSSSelector::PseudoClassType>> pseudoClassesAffectingHost;
 
     bool usesFirstLineRules { false };
     bool usesFirstLetterRules { false };
@@ -90,6 +92,7 @@ private:
 
         Vector<std::pair<AtomString, MatchElement>, 32> classes;
         Vector<std::pair<const CSSSelector*, MatchElement>, 32> attributes;
+        Vector<std::pair<CSSSelector::PseudoClassType, MatchElement>, 32> pseudoClasses;
     };
     void recursivelyCollectFeaturesFromSelector(SelectorFeatures&, const CSSSelector&, MatchElement = MatchElement::Subject);
 };
index e313d02..db3b2ba 100644 (file)
@@ -196,13 +196,15 @@ void ScopeRuleSets::collectFeatures() const
 
     m_classInvalidationRuleSets.clear();
     m_attributeInvalidationRuleSets.clear();
+    m_pseudoClassInvalidationRuleSets.clear();
+
     m_cachedHasComplexSelectorsForStyleAttribute = WTF::nullopt;
 
     m_features.shrinkToFit();
 }
 
-template<typename RuleFeatureType>
-static Vector<InvalidationRuleSet>* ensureInvalidationRuleSets(const AtomString& key, HashMap<AtomString, std::unique_ptr<Vector<InvalidationRuleSet>>>& ruleSetMap, const HashMap<AtomString, std::unique_ptr<Vector<RuleFeatureType>>>& ruleFeatures)
+template<typename KeyType, typename RuleFeatureType, typename Hash, typename HashTraits>
+static Vector<InvalidationRuleSet>* ensureInvalidationRuleSets(const KeyType& key, HashMap<KeyType, std::unique_ptr<Vector<InvalidationRuleSet>>, Hash, HashTraits>& ruleSetMap, const HashMap<KeyType, std::unique_ptr<Vector<RuleFeatureType>>, Hash, HashTraits>& ruleFeatures)
 {
     return ruleSetMap.ensure(key, [&] () -> std::unique_ptr<Vector<InvalidationRuleSet>> {
         auto* features = ruleFeatures.get(key);
@@ -241,6 +243,11 @@ const Vector<InvalidationRuleSet>* ScopeRuleSets::attributeInvalidationRuleSets(
     return ensureInvalidationRuleSets(attributeName, m_attributeInvalidationRuleSets, m_features.attributeRules);
 }
 
+const Vector<InvalidationRuleSet>* ScopeRuleSets::pseudoClassInvalidationRuleSets(CSSSelector::PseudoClassType pseudoClass) const
+{
+    return ensureInvalidationRuleSets(pseudoClass, m_pseudoClassInvalidationRuleSets, m_features.pseudoClassRules);
+}
+
 bool ScopeRuleSets::hasComplexSelectorsForStyleAttribute() const
 {
     auto compute = [&] {
index 8cccfaa..e4e3f5c 100644 (file)
@@ -65,6 +65,7 @@ public:
 
     const Vector<InvalidationRuleSet>* classInvalidationRuleSets(const AtomString& className) const;
     const Vector<InvalidationRuleSet>* attributeInvalidationRuleSets(const AtomString& attributeName) const;
+    const Vector<InvalidationRuleSet>* pseudoClassInvalidationRuleSets(CSSSelector::PseudoClassType) const;
 
     bool hasComplexSelectorsForStyleAttribute() const;
 
@@ -101,6 +102,7 @@ private:
     mutable RefPtr<RuleSet> m_uncommonAttributeRuleSet;
     mutable HashMap<AtomString, std::unique_ptr<Vector<InvalidationRuleSet>>> m_classInvalidationRuleSets;
     mutable HashMap<AtomString, std::unique_ptr<Vector<InvalidationRuleSet>>> m_attributeInvalidationRuleSets;
+    mutable HashMap<CSSSelector::PseudoClassType, std::unique_ptr<Vector<InvalidationRuleSet>>, WTF::IntHash<CSSSelector::PseudoClassType>, WTF::StrongEnumHashTraits<CSSSelector::PseudoClassType>> m_pseudoClassInvalidationRuleSets;
 
     mutable Optional<bool> m_cachedHasComplexSelectorsForStyleAttribute;