Diff
Modified: trunk/LayoutTests/ChangeLog (202469 => 202470)
--- trunk/LayoutTests/ChangeLog 2016-06-25 19:07:15 UTC (rev 202469)
+++ trunk/LayoutTests/ChangeLog 2016-06-26 04:34:23 UTC (rev 202470)
@@ -1,3 +1,15 @@
+2016-06-25 Benjamin Poulain <[email protected]>
+
+ The active state of elements can break when focus changes
+ https://bugs.webkit.org/show_bug.cgi?id=159112
+
+ Reviewed by Antti Koivisto.
+
+ * fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus-expected.txt: Added.
+ * fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus.html: Added.
+ * fast/css/pseudo-active-with-programmatic-focus-expected.txt: Added.
+ * fast/css/pseudo-active-with-programmatic-focus.html: Added.
+
2016-06-24 Jer Noble <[email protected]>
Consider exposing or hiding knowledge of a redirect from clients of WebCoreNSURLSession
Added: trunk/LayoutTests/fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus-expected.txt (0 => 202470)
--- trunk/LayoutTests/fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus-expected.txt 2016-06-26 04:34:23 UTC (rev 202470)
@@ -0,0 +1,49 @@
+Verify that indirect :active on a labeled element is not affected when the element gets focus from the click.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+ Initial state
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingActiveSelector() is []
+On Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable", "sibling5"]
+PASS elementsMatchingActiveSelector() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable"]
+After Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable", "sibling5"]
+PASS elementsMatchingActiveSelector() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable"]
+On Mouse Up
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingActiveSelector() is []
+After Mouse Up
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingActiveSelector() is []
+On Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable", "sibling5"]
+PASS elementsMatchingActiveSelector() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable"]
+After Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable", "sibling5"]
+PASS elementsMatchingActiveSelector() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable"]
+On Mouse Up
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingActiveSelector() is []
+After Mouse Up
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingActiveSelector() is []
+On Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable", "sibling5"]
+PASS elementsMatchingActiveSelector() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable"]
+After Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable", "sibling5"]
+PASS elementsMatchingActiveSelector() is ["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable"]
+On Mouse Up
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingActiveSelector() is []
+After Mouse Up
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingActiveSelector() is []
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus.html (0 => 202470)
--- trunk/LayoutTests/fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus.html (rev 0)
+++ trunk/LayoutTests/fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus.html 2016-06-26 04:34:23 UTC (rev 202470)
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html id="html">
+<head>
+<style id="custom-style">
+ * {
+ background-color: white;
+ margin: 0;
+ padding: 0;
+ }
+ :active, input:active + * + * + * {
+ background-color: rgb(50, 100, 150) !important;
+ }
+ #target {
+ display: block;
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ }
+</style>
+</head>
+<script src=""
+<body id="body">
+ <div id="prime-ancestor">
+ <div id="labelable-ancestor">
+ <div id="labelable-parent">
+ <label for="" id="target">Label</label>
+ </div>
+ </div>
+ <div id="next-group" style="display:block">
+ <div id="sibling1">Sibling1</div>
+ <div id="sibling2">Sibling2</div>
+ <input id="labelable" value="Labelable Input">
+ <div id="sibling3">Sibling3</div>
+ <div id="sibling4">Sibling4</div>
+ <div id="sibling5">Sibling5</div>
+ </div>
+ </div>
+ <div id="console">
+ </div>
+ <script>
+ description("Verify that indirect :active on a labeled element is not affected when the element gets focus from the click.");
+ window.jsTestIsAsync = true;
+
+ function elementsWithActiveStyle() {
+ let elements = [];
+ for (let element of document.querySelectorAll("*")) {
+ if (getComputedStyle(element).backgroundColor === "rgb(50, 100, 150)")
+ elements.push(element.id);
+ }
+ return elements;
+ }
+ function elementsMatchingActiveSelector() {
+ let elements = [];
+ for (let element of document.querySelectorAll(":active")) {
+ elements.push(element.id);
+ }
+ return elements;
+ }
+
+ if (window.eventSender) {
+ eventSender.mouseMoveTo(50, 50);
+ } else {
+ debug("");
+ debug("To run Manually, click-hold-release 3 times on the green rect. The rect and the input element should change color. All the results below should say PASS.")
+ debug("");
+ }
+
+ function sendMouseDown() {
+ if (window.eventSender) {
+ eventSender.mouseDown();
+ }
+ }
+
+ function sendMouseUp() {
+ if (window.eventSender) {
+ eventSender.mouseUp();
+ }
+ }
+
+ function mouseDownHandler(event, delayed = false) {
+ debug(delayed ? "After Mouse Down" : "On Mouse Down");
+ shouldBe('elementsWithActiveStyle()', '["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable", "sibling5"]');
+ shouldBe('elementsMatchingActiveSelector()', '["html", "body", "prime-ancestor", "labelable-ancestor", "labelable-parent", "target", "labelable"]');
+
+ // The current spec does not defined the order in which elements are activated.
+ // It is reasonable to have the activation after the mouseDown dispatch. That's what Firefox does at this time.
+ // This delayed handler ensure we cover both possibilities. The first handler fails on Firefox but that's not
+ // necessarily wrong, just undefined. The result is fine as long as one of the two handler succeed.
+ if (!delayed) {
+ setTimeout(function() { mouseDownHandler(event, true); }, 0);
+ } else {
+ sendMouseUp();
+ }
+ }
+ var target = document.getElementById('target');
+ target.addEventListener('mousedown', mouseDownHandler);
+
+ let mouseUpCount = 0;
+ function mouseUpHandler(event, delayed = false) {
+ debug(delayed ? "After Mouse Up" : "On Mouse Up");
+ shouldBe('elementsWithActiveStyle()', '[]');
+ shouldBe('elementsMatchingActiveSelector()', '[]');
+ if (++mouseUpCount === 6) {
+ document.getElementById("prime-ancestor").style.display = "none";
+ document.getElementById("custom-style").innerText = "";
+ finishJSTest();
+ } else {
+ if (!delayed) {
+ setTimeout(function() { mouseUpHandler(event, true); }, 0);
+ } else {
+ sendMouseDown();
+ }
+ }
+ }
+ target.addEventListener('mouseup', mouseUpHandler);
+
+
+ debug("Initial state");
+ shouldBe('elementsWithActiveStyle()', '[]');
+ shouldBe('elementsMatchingActiveSelector()', '[]');
+
+ sendMouseDown();
+ </script>
+ <script src=""
+</body>
+</html>
Added: trunk/LayoutTests/fast/css/pseudo-active-with-programmatic-focus-expected.txt (0 => 202470)
--- trunk/LayoutTests/fast/css/pseudo-active-with-programmatic-focus-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/css/pseudo-active-with-programmatic-focus-expected.txt 2016-06-26 04:34:23 UTC (rev 202470)
@@ -0,0 +1,35 @@
+
+
+Verify that :active remain consistent when the focus is changing.
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+ Initial state
+PASS elementsWithActiveStyle() is []
+PASS elementsMatchingSelector(":active") is []
+PASS elementsMatchingSelector(":focus") is []
+On Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":active") is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+After Mouse Down
+PASS elementsWithActiveStyle() is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":active") is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+Focus the target
+PASS elementsWithActiveStyle() is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":active") is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":focus") is ["target"]
+Focus input2
+PASS elementsWithActiveStyle() is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":active") is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":focus") is ["input2"]
+Focus the target
+PASS elementsWithActiveStyle() is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":active") is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":focus") is ["target"]
+Focus input1
+PASS elementsWithActiveStyle() is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":active") is ["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]
+PASS elementsMatchingSelector(":focus") is ["input1"]
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/css/pseudo-active-with-programmatic-focus.html (0 => 202470)
--- trunk/LayoutTests/fast/css/pseudo-active-with-programmatic-focus.html (rev 0)
+++ trunk/LayoutTests/fast/css/pseudo-active-with-programmatic-focus.html 2016-06-26 04:34:23 UTC (rev 202470)
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<html id="html">
+<head>
+<style id="custom-style">
+ * {
+ background-color: white;
+ margin: 0;
+ padding: 0;
+ }
+ :active {
+ background-color: rgb(50, 100, 150) !important;
+ }
+ #target {
+ display: block;
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ }
+</style>
+</head>
+<script src=""
+<body id="body">
+ <div id="webkit-test">
+ <div id="labelable-ancestor">
+ <div id="labelable-parent">
+ <textarea id="target">Label</textarea>
+ </div>
+ </div>
+ <div id="next-group" style="display:block">
+ <input id="input1" value="Input1">
+ <input id="input2" value="Input2">
+ <input id="input3" value="Input3">
+ </div>
+ </div>
+ <div id="console">
+ </div>
+ <script>
+ description("Verify that :active remain consistent when the focus is changing.");
+ window.jsTestIsAsync = true;
+
+ function elementsWithActiveStyle() {
+ let elements = [];
+ for (let element of document.querySelectorAll("*")) {
+ if (getComputedStyle(element).backgroundColor === "rgb(50, 100, 150)")
+ elements.push(element.id);
+ }
+ return elements;
+ }
+ function elementsMatchingSelector(selector) {
+ let elements = [];
+ for (let element of document.querySelectorAll(selector)) {
+ elements.push(element.id);
+ }
+ return elements;
+ }
+
+ if (window.eventSender) {
+ eventSender.mouseMoveTo(50, 50);
+ } else {
+ debug("");
+ debug("To run Manually, click-hold-release on the green rect. All the results below should say PASS.")
+ debug("");
+ }
+
+ function sendMouseDown() {
+ if (window.eventSender) {
+ eventSender.mouseDown();
+ }
+ }
+
+ function sendMouseUp() {
+ if (window.eventSender) {
+ eventSender.mouseUp();
+ }
+ }
+
+ var target = document.getElementById('target');
+ function afterMouseDown(event) {
+ debug("After Mouse Down");
+ shouldBe('elementsWithActiveStyle()', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":active")', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ debug("Focus the target");
+ target.focus();
+ shouldBe('elementsWithActiveStyle()', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":active")', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":focus")', '["target"]');
+ debug("Focus input2");
+ let input2 = document.getElementById("input2");
+ input2.focus();
+ shouldBe('elementsWithActiveStyle()', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":active")', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":focus")', '["input2"]');
+ debug("Focus the target");
+ target.focus();
+ shouldBe('elementsWithActiveStyle()', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":active")', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":focus")', '["target"]');
+ debug("Focus input1");
+ let input1 = document.getElementById("input1");
+ input1.focus();
+ shouldBe('elementsWithActiveStyle()', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":active")', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":focus")', '["input1"]');
+
+ finishJSTest();
+ }
+ function mouseDownHandler(event) {
+ debug("On Mouse Down");
+ shouldBe('elementsWithActiveStyle()', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+ shouldBe('elementsMatchingSelector(":active")', '["html", "body", "webkit-test", "labelable-ancestor", "labelable-parent", "target"]');
+
+ setTimeout(function() { afterMouseDown(event); }, 0);
+ }
+
+ target.addEventListener('mousedown', mouseDownHandler);
+
+
+ debug("Initial state");
+ shouldBe('elementsWithActiveStyle()', '[]');
+ shouldBe('elementsMatchingSelector(":active")', '[]');
+ shouldBe('elementsMatchingSelector(":focus")', '[]');
+
+ sendMouseDown();
+ </script>
+ <script src=""
+</body>
+</html>
Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (202469 => 202470)
--- trunk/LayoutTests/platform/ios-simulator/TestExpectations 2016-06-25 19:07:15 UTC (rev 202469)
+++ trunk/LayoutTests/platform/ios-simulator/TestExpectations 2016-06-26 04:34:23 UTC (rev 202470)
@@ -1435,6 +1435,8 @@
fast/css/negative-text-indent-in-inline-block.html [ ImageOnlyFailure ]
fast/css/object-fit/object-fit-input-image.html [ ImageOnlyFailure ]
fast/css/outline-auto-empty-rects.html [ Failure ]
+fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus.html [ Failure ]
+fast/css/pseudo-active-with-programmatic-focus.html [ Failure ]
fast/css/pseudo-first-line-border-width.html [ Failure ]
fast/css/read-only-read-write-input-basics.html [ ImageOnlyFailure ]
fast/css/replaced-element-implicit-size.html [ Failure ]
Modified: trunk/Source/WebCore/ChangeLog (202469 => 202470)
--- trunk/Source/WebCore/ChangeLog 2016-06-25 19:07:15 UTC (rev 202469)
+++ trunk/Source/WebCore/ChangeLog 2016-06-26 04:34:23 UTC (rev 202470)
@@ -1,3 +1,43 @@
+2016-06-25 Benjamin Poulain <[email protected]>
+
+ The active state of elements can break when focus changes
+ https://bugs.webkit.org/show_bug.cgi?id=159112
+
+ Reviewed by Antti Koivisto.
+
+ The pseudo class :active was behaving weirdly when used
+ with label elements with an associated form element.
+ The form element would get the :active state on the first click
+ then no longer get the state until the focus changes.
+
+ What was happenning is setFocusedElement() was clearing active
+ for some unknown reason. When you really do that on an active element,
+ you end up in an inconsistent state where no invalidation works.
+
+ The two tests illustrates 2 ways this breaks.
+
+ The test "pseudo-active-on-labeled-element-not-canceled-by-focus" clicks
+ several time on a lable element. The first time, the input element gets
+ the focus. The second time, it already has the focus, setFocusedElement()
+ clears :active before finding the focusable element and end up clearing
+ the active state on a target in the active chain.
+
+ The test "pseudo-active-with-programmatic-focus.html" shows how to invalidate
+ arbitrary elements using _javascript_. This can cause severely broken active
+ chains where invalidation never cleans some ancestors.
+
+ Tests: fast/css/pseudo-active-on-labeled-element-not-canceled-by-focus.html
+ fast/css/pseudo-active-with-programmatic-focus.html
+
+ * dom/Document.cpp:
+ (WebCore::Document::setFocusedElement): Deleted.
+
+ * page/EventHandler.cpp:
+ (WebCore::EventHandler::handleMouseDoubleClickEvent):
+ This is WebKit1 specific. The double click event was dispatching
+ the mouseUp and Click with after doing an Active hit test.
+ This causes us to have :active state in and after mouseUp in WebKit1.
+
2016-06-24 Jer Noble <[email protected]>
Consider exposing or hiding knowledge of a redirect from clients of WebCoreNSURLSession
Modified: trunk/Source/WebCore/dom/Document.cpp (202469 => 202470)
--- trunk/Source/WebCore/dom/Document.cpp 2016-06-25 19:07:15 UTC (rev 202469)
+++ trunk/Source/WebCore/dom/Document.cpp 2016-06-26 04:34:23 UTC (rev 202470)
@@ -3777,9 +3777,6 @@
// Remove focus from the existing focus node (if any)
if (oldFocusedElement) {
- if (oldFocusedElement->active())
- oldFocusedElement->setActive(false);
-
oldFocusedElement->setFocus(false);
setFocusNavigationStartingNode(nullptr);
Modified: trunk/Source/WebCore/page/EventHandler.cpp (202469 => 202470)
--- trunk/Source/WebCore/page/EventHandler.cpp 2016-06-25 19:07:15 UTC (rev 202469)
+++ trunk/Source/WebCore/page/EventHandler.cpp 2016-06-26 04:34:23 UTC (rev 202470)
@@ -1752,7 +1752,7 @@
m_mousePressed = false;
setLastKnownMousePosition(platformMouseEvent);
- HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
+ HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent);
Frame* subframe = subframeForHitTestResult(mouseEvent);
if (m_eventHandlerWillResetCapturingMouseEventsElement)