Title: [250788] trunk
Revision
250788
Author
rn...@webkit.org
Date
2019-10-07 13:58:04 -0700 (Mon, 07 Oct 2019)

Log Message

focus pseudo class should match a shadow host whose shadow tree contains the focused element
https://bugs.webkit.org/show_bug.cgi?id=202432

Reviewed by Antti Koivisto.

Source/WebCore:

Note that focus pseudo class does not match a shadow host when its shadow tree contains a slot element
with a focused element or its ancestor assigned since such a shadow host has the actual focused element
in the same tree as the shadow host. (e.g. the focused element can be a direct child of the host).

In order to preserve the behavior of focus ring, which should be only drawn on the currently focused
element and not any shadow host which contains such an element, this patch introduces a new pseudo class,
-webkit-direct-focus, which is only available in the user agent stylesheet. Putting :host(:focus) rule
isn't sufficient because style rules inside shadow trees always has a lower precendence than rules
outside the shadow tree (the tree of its shadow host).

[1] Also see https://github.com/whatwg/html/pull/4731

Tests: fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet.html
       fast/shadow-dom/focus-pseudo-matches-on-shadow-host.html
       fast/shadow-dom/focus-pseudo-on-shadow-host-1.html
       fast/shadow-dom/focus-pseudo-on-shadow-host-2.html
       fast/shadow-dom/focus-pseudo-on-shadow-host-3.html

* css/CSSSelector.cpp:
(WebCore::CSSSelector::selectorText const): Added the support for -webkit-direct-focus.
* css/CSSSelector.h:
* css/RuleSet.cpp:
(WebCore::RuleSet::addRule): Ditto.
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne const):: Ditto.
(WebCore::doesShadowTreeContainFocusedElement):: Ditto.
(WebCore::SelectorChecker::matchesFocusPseudoClass): Implemented the new behavior.
(WebCore::SelectorChecker::matchesDirectFocusPseudoClass): Added. Implements the old behavior for
the focus ring via -webkit-direct-focus pseudo class.
* css/SelectorChecker.h:
* css/SelectorPseudoClassAndCompatibilityElementMap.in: Added -webkit-direct-focus.
* css/html.css: Use -webkit-direct-focus pseudo class to preserve the existing behavior of focus ring.
* css/parser/CSSSelectorParser.cpp:
(WebCore::CSSSelectorParser::consumePseudo): Ignore -webkit-direct-focus in author and user stylesheets.
* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::addPseudoClassType): Added the support for -webkit-direct-focus.
* dom/Element.cpp:
(WebCore::Element::setFocus): Invoke setContainsFocusedElement on each shadow ancestor root of
the newly focused element. Note that we can't use :focus-within pseudo class since that would also match
the host of a shadow root which contains a slotted focused element, causing both the shadow host and
the slotted element to match :focus pseudo class in the host's tree.
* dom/ShadowRoot.h:

LayoutTests:

Added W3C style testharness tests and ref tests.

* fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet-expected.txt: Added.
* fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet.html: Added.
* fast/shadow-dom/focus-pseudo-matches-on-shadow-host-expected.txt: Added.
* fast/shadow-dom/focus-pseudo-matches-on-shadow-host.html: Added.
* fast/shadow-dom/focus-pseudo-on-shadow-host-1-expected.html: Added.
* fast/shadow-dom/focus-pseudo-on-shadow-host-1.html: Added.
* fast/shadow-dom/focus-pseudo-on-shadow-host-2-expected.html: Added.
* fast/shadow-dom/focus-pseudo-on-shadow-host-2.html: Added.
* fast/shadow-dom/focus-pseudo-on-shadow-host-3-expected.html: Added.
* fast/shadow-dom/focus-pseudo-on-shadow-host-3.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (250787 => 250788)


--- trunk/LayoutTests/ChangeLog	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/LayoutTests/ChangeLog	2019-10-07 20:58:04 UTC (rev 250788)
@@ -1,3 +1,23 @@
+2019-10-07  Ryosuke Niwa  <rn...@webkit.org>
+
+        focus pseudo class should match a shadow host whose shadow tree contains the focused element
+        https://bugs.webkit.org/show_bug.cgi?id=202432
+
+        Reviewed by Antti Koivisto.
+
+        Added W3C style testharness tests and ref tests.
+
+        * fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet-expected.txt: Added.
+        * fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet.html: Added.
+        * fast/shadow-dom/focus-pseudo-matches-on-shadow-host-expected.txt: Added.
+        * fast/shadow-dom/focus-pseudo-matches-on-shadow-host.html: Added.
+        * fast/shadow-dom/focus-pseudo-on-shadow-host-1-expected.html: Added.
+        * fast/shadow-dom/focus-pseudo-on-shadow-host-1.html: Added.
+        * fast/shadow-dom/focus-pseudo-on-shadow-host-2-expected.html: Added.
+        * fast/shadow-dom/focus-pseudo-on-shadow-host-2.html: Added.
+        * fast/shadow-dom/focus-pseudo-on-shadow-host-3-expected.html: Added.
+        * fast/shadow-dom/focus-pseudo-on-shadow-host-3.html: Added.
+
 2019-10-07  Chris Dumez  <cdu...@apple.com>
 
         DOMCache should not prevent pages from entering the back/forward cache

Added: trunk/LayoutTests/fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet-expected.txt (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet-expected.txt	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,14 @@
+This tests that -webkit-direct-focus is not recognized in author stylesheets
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS defaultFocus.matches(":focus") is true
+PASS defaultFocus.matches(":-webkit-direct-focus") threw exception SyntaxError: The string did not match the expected pattern..
+PASS inputInShadowRoot.matches(":focus") is true
+PASS inputInShadowRoot.matches(":-webkit-direct-focus") threw exception SyntaxError: The string did not match the expected pattern..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+

Added: trunk/LayoutTests/fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<input id="defaultFocus" autofocus>
+<script>
+
+jsTestIsAsync = true;
+
+description('This tests that -webkit-direct-focus is not recognized in author stylesheets');
+
+let focusedDefault = false;
+function didFocusDefault() { }
+function checkFocusMatch() {
+    if (defaultFocus.matches(':focus')) {
+        focusedDefault = true;
+        didFocusDefault();
+    } else
+        setTimeout(checkFocusMatch, 100);
+}
+defaultFocus.addEventListener('focus', checkFocusMatch);
+
+function waitForFrameToBeFocused(test)
+{
+    return new Promise((resolve) => {
+        if (focusedDefault)
+            resolve();
+        else
+            didFocusDefault = resolve;
+    });
+}
+
+async function runTest() {
+    await waitForFrameToBeFocused();
+
+    shouldBeTrue('defaultFocus.matches(":focus")');
+    shouldThrowErrorName('defaultFocus.matches(":-webkit-direct-focus")', 'SyntaxError');
+
+    const host = document.body.appendChild(document.createElement('div'));
+    const shadowRoot = host.attachShadow({mode: 'closed'});
+    shadowRoot.innerHTML = '<input>';
+    window.inputInShadowRoot = shadowRoot.firstChild;
+    inputInShadowRoot.focus();
+
+    shouldBeTrue('inputInShadowRoot.matches(":focus")');
+    shouldThrowErrorName('inputInShadowRoot.matches(":-webkit-direct-focus")', 'SyntaxError');
+
+    finishJSTest();
+}
+
+window._onload_ = runTest;
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-matches-on-shadow-host-expected.txt (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-matches-on-shadow-host-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-matches-on-shadow-host-expected.txt	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,9 @@
+
+
+PASS :focus must not match a shadow host with open mode shadow root that does not contain the focused element 
+PASS :focus must match a shadow host with open mode shadow root that contains the focused element 
+PASS :focus must not match a shadow host with open mode shadow root contains the focused element assigned to a slot 
+PASS :focus must not match a shadow host with closed mode shadow root that does not contain the focused element 
+PASS :focus must match a shadow host with closed mode shadow root that contains the focused element 
+PASS :focus must not match a shadow host with closed mode shadow root contains the focused element assigned to a slot 
+

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-matches-on-shadow-host.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-matches-on-shadow-host.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-matches-on-shadow-host.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content=":focus should match a shadow host which contains the focused element">
+<link rel="help" href=""
+<script src=""
+<script src=""
+</head>
+<body>
+<input id="defaultFocus" autofocus>
+<div id="log"></div>
+<div id="container"></div>
+<script>
+
+let focusedDefault = false;
+function didFocusDefault() { }
+function checkFocusMatch() {
+    if (defaultFocus.matches(':focus')) {
+        focusedDefault = true;
+        didFocusDefault();
+    } else
+        setTimeout(checkFocusMatch, 100);
+}
+defaultFocus.addEventListener('focus', checkFocusMatch);
+
+function prepare(test)
+{
+    test.add_cleanup(() => {
+        defaultFocus.focus();
+        container.textContent = '';
+    });
+    return new Promise((resolve) => {
+        if (focusedDefault)
+            resolve();
+        else
+            didFocusDefault = resolve;
+    });
+}
+
+function testInMode(mode) {
+    promise_test(async function () {
+        await prepare(this);
+        const host = document.createElement('div');
+        container.appendChild(host);
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<input>';
+        assert_equals(document.activeElement, defaultFocus);
+        assert_equals(shadowRoot.activeElement, null);
+        assert_false(host.matches(':focus'));
+    }, `:focus must not match a shadow host with ${mode} mode shadow root that does not contain the focused element`);
+
+    promise_test(async function () {
+        await prepare(this);
+        const host = document.createElement('div');
+        document.body.appendChild(host);
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<input>';
+        shadowRoot.firstChild.focus();
+        assert_equals(document.activeElement, host);
+        assert_equals(shadowRoot.activeElement, shadowRoot.firstChild);
+        assert_true(host.matches(':focus'));
+    }, `:focus must match a shadow host with ${mode} mode shadow root that contains the focused element`);
+
+    promise_test(async function () {
+        await prepare(this);
+        const host = document.createElement('div');
+        container.appendChild(host);
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<slot>';
+        host.innerHTML = '<input>';
+        host.firstChild.focus();
+        assert_equals(document.activeElement, host.firstChild);
+        assert_equals(shadowRoot.activeElement, null);
+        assert_false(host.matches(':focus'));
+    }, `:focus must not match a shadow host with ${mode} mode shadow root contains the focused element assigned to a slot`);
+
+}
+
+testInMode('open');
+testInMode('closed');
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-1-expected.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-1-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-1-expected.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+    <body>
+        <p>Test passes if you see a single 100px by 100px green box below.</p>
+        <div style="width: 100px; height: 100px; background: green;"></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-1.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-1.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-1.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content=":focus should match a shadow host which contains the focused element">
+<link rel="help" href=""
+</head>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div id="host"></div>
+<style>
+#host { background: red; width: 100px; height: 100px; }
+#host:focus { background: green; }
+</style>
+<script>
+
+const shadowRoot = host.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<div tabindex="0" style="outline: none;"></div>';
+shadowRoot.firstChild.focus();
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-2-expected.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-2-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-2-expected.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+    <body>
+        <p>Test passes if you see a single 100px by 100px green box below.</p>
+        <div style="width: 100px; height: 100px; background: green;"></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-2.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-2.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-2.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content=":focus should match a shadow host which contains the focused element">
+<link rel="help" href=""
+</head>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div id="host"><span>FAIL</span></div>
+<style>
+#host { background: green; width: 100px; height: 100px; }
+#host span { background: red; }
+#host:focus span { background: green; color: green; }
+</style>
+<script>
+
+const shadowRoot = host.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<div tabindex="0" style="outline: none;"><slot></slot></div>';
+shadowRoot.firstChild.focus();
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-3-expected.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-3-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-3-expected.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+    <body>
+        <p>Test passes if you see a single 100px by 100px green box below.</p>
+        <div style="width: 100px; height: 100px; background: green;"></div>
+    </body>
+</html>

Added: trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-3.html (0 => 250788)


--- trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-3.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/focus-pseudo-on-shadow-host-3.html	2019-10-07 20:58:04 UTC (rev 250788)
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href=""
+<meta name="assert" content=":focus should not match a shadow host if the focused element is a slotted content">
+<link rel="help" href=""
+</head>
+<body>
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div id="host"><div id="target" tabindex="0"></div></div>
+<style>
+#host { background: green; width: 100px; height: 100px; }
+#host:focus #target { background: red; width: 100px; height: 100px; }
+#target { outline: none; }
+</style>
+<script>
+
+const shadowRoot = host.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<slot></slot>';
+target.focus();
+
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt (250787 => 250788)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt	2019-10-07 20:58:04 UTC (rev 250788)
@@ -1,12 +1,12 @@
 foo
 foo
 
-FAIL :focus applies to host with delegatesFocus=true when the shadow root's descendant has focus assert_true: host matches :focus expected true got false
+PASS :focus applies to host with delegatesFocus=true when the shadow root's descendant has focus 
 FAIL :focus applies to host with delegatesFocus=true when slotted element has focus assert_true: host matches :focus expected true got false
-FAIL :focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=true is focused assert_true: host of nested shadow tree matches focus expected true got false
-FAIL :focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=false is focused assert_true: host of nested shadow tree matches focus expected true got false
-FAIL :focus applies to host with delegatesFocus=false when the shadow root's descendant has focus assert_true: host matches :focus expected true got false
+PASS :focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=true is focused 
+PASS :focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=false is focused 
+PASS :focus applies to host with delegatesFocus=false when the shadow root's descendant has focus 
 FAIL :focus applies to host with delegatesFocus=false when slotted element has focus assert_true: host matches :focus expected true got false
-FAIL :focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=true is focused assert_true: host of nested shadow tree matches focus expected true got false
-FAIL :focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=false is focused assert_true: host of nested shadow tree matches focus expected true got false
+PASS :focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=true is focused 
+PASS :focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=false is focused 
 

Modified: trunk/Source/WebCore/ChangeLog (250787 => 250788)


--- trunk/Source/WebCore/ChangeLog	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/ChangeLog	2019-10-07 20:58:04 UTC (rev 250788)
@@ -1,3 +1,53 @@
+2019-10-07  Ryosuke Niwa  <rn...@webkit.org>
+
+        focus pseudo class should match a shadow host whose shadow tree contains the focused element
+        https://bugs.webkit.org/show_bug.cgi?id=202432
+
+        Reviewed by Antti Koivisto.
+
+        Note that focus pseudo class does not match a shadow host when its shadow tree contains a slot element
+        with a focused element or its ancestor assigned since such a shadow host has the actual focused element
+        in the same tree as the shadow host. (e.g. the focused element can be a direct child of the host).
+
+        In order to preserve the behavior of focus ring, which should be only drawn on the currently focused
+        element and not any shadow host which contains such an element, this patch introduces a new pseudo class,
+        -webkit-direct-focus, which is only available in the user agent stylesheet. Putting :host(:focus) rule
+        isn't sufficient because style rules inside shadow trees always has a lower precendence than rules
+        outside the shadow tree (the tree of its shadow host).
+
+        [1] Also see https://github.com/whatwg/html/pull/4731
+
+        Tests: fast/shadow-dom/direct-focus-pseudo-does-not-match-in-author-stylesheet.html
+               fast/shadow-dom/focus-pseudo-matches-on-shadow-host.html
+               fast/shadow-dom/focus-pseudo-on-shadow-host-1.html
+               fast/shadow-dom/focus-pseudo-on-shadow-host-2.html
+               fast/shadow-dom/focus-pseudo-on-shadow-host-3.html
+
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::selectorText const): Added the support for -webkit-direct-focus.
+        * css/CSSSelector.h:
+        * css/RuleSet.cpp:
+        (WebCore::RuleSet::addRule): Ditto.
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::checkOne const):: Ditto.
+        (WebCore::doesShadowTreeContainFocusedElement):: Ditto.
+        (WebCore::SelectorChecker::matchesFocusPseudoClass): Implemented the new behavior.
+        (WebCore::SelectorChecker::matchesDirectFocusPseudoClass): Added. Implements the old behavior for
+        the focus ring via -webkit-direct-focus pseudo class.
+        * css/SelectorChecker.h:
+        * css/SelectorPseudoClassAndCompatibilityElementMap.in: Added -webkit-direct-focus.
+        * css/html.css: Use -webkit-direct-focus pseudo class to preserve the existing behavior of focus ring.
+        * css/parser/CSSSelectorParser.cpp:
+        (WebCore::CSSSelectorParser::consumePseudo): Ignore -webkit-direct-focus in author and user stylesheets.
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::addPseudoClassType): Added the support for -webkit-direct-focus.
+        * dom/Element.cpp:
+        (WebCore::Element::setFocus): Invoke setContainsFocusedElement on each shadow ancestor root of
+        the newly focused element. Note that we can't use :focus-within pseudo class since that would also match
+        the host of a shadow root which contains a slotted focused element, causing both the shadow host and
+        the slotted element to match :focus pseudo class in the host's tree.
+        * dom/ShadowRoot.h:
+
 2019-10-07  Rob Buis  <rb...@igalia.com>
 
         Change Response's statusText's default

Modified: trunk/Source/WebCore/css/CSSSelector.cpp (250787 => 250788)


--- trunk/Source/WebCore/css/CSSSelector.cpp	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/CSSSelector.cpp	2019-10-07 20:58:04 UTC (rev 250788)
@@ -438,6 +438,9 @@
             case CSSSelector::PseudoClassAutofillStrongPasswordViewable:
                 str.appendLiteral(":-webkit-autofill-strong-password-viewable");
                 break;
+            case CSSSelector::PseudoClassDirectFocus:
+                str.appendLiteral(":-webkit-direct-focus");
+                break;
             case CSSSelector::PseudoClassDrag:
                 str.appendLiteral(":-webkit-drag");
                 break;

Modified: trunk/Source/WebCore/css/CSSSelector.h (250787 => 250788)


--- trunk/Source/WebCore/css/CSSSelector.h	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/CSSSelector.h	2019-10-07 20:58:04 UTC (rev 250788)
@@ -110,6 +110,7 @@
             PseudoClassAutofillStrongPassword,
             PseudoClassAutofillStrongPasswordViewable,
             PseudoClassHover,
+            PseudoClassDirectFocus,
             PseudoClassDrag,
             PseudoClassFocus,
             PseudoClassFocusWithin,

Modified: trunk/Source/WebCore/css/RuleSet.cpp (250787 => 250788)


--- trunk/Source/WebCore/css/RuleSet.cpp	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/RuleSet.cpp	2019-10-07 20:58:04 UTC (rev 250788)
@@ -278,6 +278,7 @@
             case CSSSelector::PseudoClassAnyLinkDeprecated:
                 linkSelector = selector;
                 break;
+            case CSSSelector::PseudoClassDirectFocus:
             case CSSSelector::PseudoClassFocus:
                 focusSelector = selector;
                 break;

Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (250787 => 250788)


--- trunk/Source/WebCore/css/SelectorChecker.cpp	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp	2019-10-07 20:58:04 UTC (rev 250788)
@@ -985,6 +985,8 @@
             if (context.inFunctionalPseudoClass)
                 return false;
             return element.isLink() && context.visitedMatchType == VisitedMatchType::Enabled;
+        case CSSSelector::PseudoClassDirectFocus:
+            return matchesDirectFocusPseudoClass(element);
         case CSSSelector::PseudoClassDrag:
             addStyleRelation(checkingContext, element, Style::Relation::AffectedByDrag);
 
@@ -1282,10 +1284,27 @@
     return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
 }
 
+static bool doesShadowTreeContainFocusedElement(const Element& element)
+{
+    auto* shadowRoot = element.shadowRoot();
+    return shadowRoot && shadowRoot->containsFocusedElement();
+}
+
 bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
 {
     if (InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassFocus))
         return true;
+
+    return (element.focused() || doesShadowTreeContainFocusedElement(element)) && isFrameFocused(element);
+}
+
+// This needs to match a subset of elements matchesFocusPseudoClass match since direct focus is treated
+// as a part of focus pseudo class selectors in ElementRuleCollector::collectMatchingRules.
+bool SelectorChecker::matchesDirectFocusPseudoClass(const Element& element)
+{
+    if (InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassFocus))
+        return true;
+
     return element.focused() && isFrameFocused(element);
 }
 

Modified: trunk/Source/WebCore/css/SelectorChecker.h (250787 => 250788)


--- trunk/Source/WebCore/css/SelectorChecker.h	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/SelectorChecker.h	2019-10-07 20:58:04 UTC (rev 250788)
@@ -97,6 +97,7 @@
 
     static bool isCommonPseudoClassSelector(const CSSSelector*);
     static bool matchesFocusPseudoClass(const Element&);
+    static bool matchesDirectFocusPseudoClass(const Element&);
     static bool attributeSelectorMatches(const Element&, const QualifiedName&, const AtomString& attributeValue, const CSSSelector&);
 
     enum LinkMatchMask { MatchDefault = 0, MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };

Modified: trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in (250787 => 250788)


--- trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in	2019-10-07 20:58:04 UTC (rev 250788)
@@ -4,6 +4,7 @@
 -webkit-autofill
 -webkit-autofill-strong-password
 -webkit-autofill-strong-password-viewable
+-webkit-direct-focus
 -webkit-drag
 -webkit-full-page-media
 active

Modified: trunk/Source/WebCore/css/html.css (250787 => 250788)


--- trunk/Source/WebCore/css/html.css	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/html.css	2019-10-07 20:58:04 UTC (rev 250788)
@@ -1165,7 +1165,7 @@
 
 /* states */
 
-:focus {
+:-webkit-direct-focus {
 #if defined(WTF_PLATFORM_IOS_FAMILY) && WTF_PLATFORM_IOS_FAMILY
     outline: auto 3px -webkit-focus-ring-color;
 #else

Modified: trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp (250787 => 250788)


--- trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/css/parser/CSSSelectorParser.cpp	2019-10-07 20:58:04 UTC (rev 250788)
@@ -494,10 +494,16 @@
     
     if (colons == 1) {
         selector = CSSParserSelector::parsePseudoClassSelector(token.value());
+        if (!selector)
+            return nullptr;
+        if (selector->match() == CSSSelector::PseudoClass) {
+            if (m_context.mode != UASheetMode && selector->pseudoClassType() == CSSSelector::PseudoClassDirectFocus)
+                return nullptr;
 #if ENABLE(ATTACHMENT_ELEMENT)
-        if (!m_context.attachmentEnabled && selector && selector->match() == CSSSelector::PseudoClass && selector->pseudoClassType() == CSSSelector::PseudoClassHasAttachment)
-            return nullptr;
+            if (!m_context.attachmentEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassHasAttachment)
+                return nullptr;
 #endif
+        }
     } else {
         selector = CSSParserSelector::parsePseudoElementSelector(token.value());
 #if ENABLE(VIDEO_TRACK)

Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (250787 => 250788)


--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp	2019-10-07 20:58:04 UTC (rev 250788)
@@ -558,6 +558,9 @@
     case CSSSelector::PseudoClassDefined:
         fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<CSSOperationPtrTag>(isDefinedElement));
         return FunctionType::SimpleSelectorChecker;
+    case CSSSelector::PseudoClassDirectFocus:
+        fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<CSSOperationPtrTag>(SelectorChecker::matchesDirectFocusPseudoClass));
+        return FunctionType::SimpleSelectorChecker;
     case CSSSelector::PseudoClassFocus:
         fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<CSSOperationPtrTag>(SelectorChecker::matchesFocusPseudoClass));
         return FunctionType::SimpleSelectorChecker;

Modified: trunk/Source/WebCore/dom/Element.cpp (250787 => 250788)


--- trunk/Source/WebCore/dom/Element.cpp	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/dom/Element.cpp	2019-10-07 20:58:04 UTC (rev 250788)
@@ -677,6 +677,12 @@
     document().userActionElements().setFocused(*this, flag);
     invalidateStyleForSubtree();
 
+    // Shadow host with a slot that contain focused element is not considered focused.
+    for (auto* root = containingShadowRoot(); root; root = root->host()->containingShadowRoot()) {
+        root->setContainsFocusedElement(flag);
+        root->host()->invalidateStyle();
+    }
+
     for (Element* element = this; element; element = element->parentElementInComposedTree())
         element->setHasFocusWithin(flag);
 }

Modified: trunk/Source/WebCore/dom/ShadowRoot.h (250787 => 250788)


--- trunk/Source/WebCore/dom/ShadowRoot.h	2019-10-07 20:41:59 UTC (rev 250787)
+++ trunk/Source/WebCore/dom/ShadowRoot.h	2019-10-07 20:58:04 UTC (rev 250788)
@@ -61,6 +61,9 @@
     bool resetStyleInheritance() const { return m_resetStyleInheritance; }
     void setResetStyleInheritance(bool);
 
+    bool containsFocusedElement() const { return m_containsFocusedElement; }
+    void setContainsFocusedElement(bool flag) { m_containsFocusedElement = flag; }
+
     Element* host() const { return m_host; }
     void setHost(Element* host) { m_host = host; }
 
@@ -113,6 +116,7 @@
 
     bool m_resetStyleInheritance { false };
     bool m_hasBegunDeletingDetachedChildren { false };
+    bool m_containsFocusedElement { false };
     ShadowRootMode m_type { ShadowRootMode::UserAgent };
 
     Element* m_host { nullptr };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to