REGRESSION (r151839): Subframe keeps getting mousemove events with the same coordinat...
authorakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 Apr 2014 21:55:59 +0000 (21:55 +0000)
committerakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 Apr 2014 21:55:59 +0000 (21:55 +0000)
<https://webkit.org/b/131974>
<rdar://problem/15907469>

Source/WebCore:
When the currently hovered element disappears as a result of style recalc,
we send a fake mousemove event to the page, to see if anything newly added
should become hovered.

The faking mechanism lives in EventHandler and simply synthesizes a new
mousemove event using the last seen mouse location.

The problem here is that we were sending this fake mousemove event to the
subframe where the hovered element lived. Since subframes aren't kept up
to date on recent mouse locations, this could cause some strange behavior
where a subframe would dispatch mousemove events with stale coordinates.

The solution is to always dispatch fake mousemove events from the main
frame's event handler. This is how real event delivery happens, and hit
testing will then find the appropriate subframe, if any.

Reviewed by Benjamin Poulain.

Test: fast/events/ghostly-mousemoves-in-subframe.html

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

LayoutTests:
Add a test that triggers the weirdness where removing the renderer from
a hovered element in a subframe would leave the subframe's EventHandler
in a state where it could dispatch fake mousemove events with stale
coordinates in response to style recalc.

Note that the final 500ms delay is because fake mousemove events are
sent on 250ms delay timers so we need to give it some time to catch up.

Reviewed by Benjamin Poulain.

* fast/events/ghostly-mousemoves-in-subframe-expected.txt: Added.
* fast/events/ghostly-mousemoves-in-subframe.html: Added.
* platform/mac-wk2/fast/events/resources/ghostly-mousemoves-in-subframe-the-actual-subframe.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/fast/events/ghostly-mousemoves-in-subframe.html [new file with mode: 0644]
LayoutTests/fast/events/resources/ghostly-mousemoves-in-subframe-the-actual-subframe.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/fast/events/ghostly-mousemoves-in-subframe-expected.txt [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp

index 3d76bece1e5b8a3b0edc21f5e6a2e661a44a9741..f40a8c2c0a7f8e45122bc676c4da985e1d39519e 100644 (file)
@@ -1,3 +1,23 @@
+2014-04-22  Andreas Kling  <akling@apple.com>
+
+        REGRESSION (r151839): Subframe keeps getting mousemove events with the same coordinates after hiding a hovered element.
+        <https://webkit.org/b/131974>
+        <rdar://problem/15907469>
+
+        Add a test that triggers the weirdness where removing the renderer from
+        a hovered element in a subframe would leave the subframe's EventHandler
+        in a state where it could dispatch fake mousemove events with stale
+        coordinates in response to style recalc.
+
+        Note that the final 500ms delay is because fake mousemove events are
+        sent on 250ms delay timers so we need to give it some time to catch up.
+
+        Reviewed by Benjamin Poulain.
+
+        * fast/events/ghostly-mousemoves-in-subframe-expected.txt: Added.
+        * fast/events/ghostly-mousemoves-in-subframe.html: Added.
+        * platform/mac-wk2/fast/events/resources/ghostly-mousemoves-in-subframe-the-actual-subframe.html: Added.
+
 2014-04-22  Tim Horton  <timothy_horton@apple.com>
 
         REGRESSION: JSRegress's js/slow-stress/new-spread.html fails sometimes
diff --git a/LayoutTests/fast/events/ghostly-mousemoves-in-subframe.html b/LayoutTests/fast/events/ghostly-mousemoves-in-subframe.html
new file mode 100644 (file)
index 0000000..5c25625
--- /dev/null
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html id="main_frame">
+<head>
+<meta charset="utf-8">
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+#subframe {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 200px;
+    height: 200px;
+    background: white;
+}
+
+#overlapping_div_in_main_frame {
+    position: absolute;
+    background: black;
+    top: 0;
+    left: 0;
+    height: 50px;
+    width: 50px;
+    display: none;
+    z-index: 10;
+}
+</style>
+</head>
+<body>
+<iframe id="subframe" src="resources/ghostly-mousemoves-in-subframe-the-actual-subframe.html"></iframe>
+<div id="overlapping_div_in_main_frame"></div>
+<script>
+
+description("Test for http://webkit.org/b/131974 REGRESSION (r151839): Subframe keeps getting mousemove events with the same coordinates after hiding a hovered element.");
+var jsTestIsAsync = true;
+
+function logEvent(e) {
+    debug(e.target.id + " got " + event.type + " at " + event.x + "," + event.y);
+}
+
+window.onload = function() {
+    window.onclick = function(event) {
+        logEvent(event);
+
+        var clickable_div_in_subframe = document.querySelector("iframe").contentDocument.querySelector("#clickable_div_in_subframe");
+        clickable_div_in_subframe.style.display = "none";
+        clickable_div_in_subframe.clientHeight;
+        clickable_div_in_subframe.style.display = "block";
+        clickable_div_in_subframe.clientHeight;
+    };
+
+    if (!window.eventSender) {
+        debug("This test requires eventSender from the WebKit layout test internals.");
+        return;
+    }
+
+    // Move the mouse over clickable_div_in_subframe so it becomes the subframe document's hovered element.
+    eventSender.mouseMoveTo(15, 15);
+    setTimeout(function() {
+        // Fire the subframe's clickable_div_in_subframe.onclick handler.
+        // This will cause a overlapping_div_in_main_frame to overlap the currently hovered element in the subframe.
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        setTimeout(function() {
+            // Jiggle the mouse inside the newly hovered element a bit.
+            eventSender.mouseMoveTo(16, 16);
+            setTimeout(function() {
+                eventSender.mouseMoveTo(400, 400);
+                setTimeout(function() {
+                    // Cause a click in the main frame, so the above window.onclick handler will run,
+                    // killing and then recreating clickable_div_in_subframe's renderer.
+                    eventSender.mouseDown();
+                    eventSender.mouseUp();
+                    setTimeout(function() {
+                        finishJSTest();
+                    }, 500);
+                }, 0);
+            }, 0);
+        }, 0);
+    }, 0);
+};
+
+window.onmousemove = function(event) {
+    logEvent(event);
+};
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/resources/ghostly-mousemoves-in-subframe-the-actual-subframe.html b/LayoutTests/fast/events/resources/ghostly-mousemoves-in-subframe-the-actual-subframe.html
new file mode 100644 (file)
index 0000000..692e10a
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <div id="clickable_div_in_subframe">beautiful brown eyes</div>
+</body>
+<head>
+    <script>
+        function debug(s) {
+            top.document.querySelector("#console").innerText += s + "\n";
+        }
+
+        function logEvent(e) {
+            debug(e.target.id + " got " + event.type + " at " + event.x + "," + event.y);
+        }
+
+        window.onload = function() {
+            var clickable_div_in_subframe = document.querySelector("#clickable_div_in_subframe");
+
+            clickable_div_in_subframe.onmousemove = function(event) {
+                logEvent(event);
+            };
+
+            clickable_div_in_subframe.onmouseover= function(event) {
+                logEvent(event);
+            };
+
+            clickable_div_in_subframe.onclick = function(event) {
+                logEvent(event);
+
+                var overlapping_div_in_main_frame = top.document.querySelector("#overlapping_div_in_main_frame");
+
+                // Give overlapping_div_in_main_frame a renderer. It should now be right under the cursor.
+                overlapping_div_in_main_frame.style.display = "block";
+
+                // Force clickable_div_in_subframe to lose its renderer.
+                this.style.display = "none";
+                this.clientHeight;
+
+                // Force clickable_div_in_subframe to gain a new renderer.
+                this.style.display = "block";
+                this.clientHeight;
+            };
+        };
+    </script>
+</head>
+</html>
diff --git a/LayoutTests/platform/mac-wk2/fast/events/ghostly-mousemoves-in-subframe-expected.txt b/LayoutTests/platform/mac-wk2/fast/events/ghostly-mousemoves-in-subframe-expected.txt
new file mode 100644 (file)
index 0000000..3c2560f
--- /dev/null
@@ -0,0 +1,17 @@
+Test for http://webkit.org/b/131974 REGRESSION (r151839): Subframe keeps getting mousemove events with the same coordinates after hiding a hovered element.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+clickable_div_in_subframe got mouseover at 13,13
+clickable_div_in_subframe got mousemove at 13,13
+clickable_div_in_subframe got click at 13,13
+clickable_div_in_subframe got mousemove at 14,14
+overlapping_div_in_main_frame got mousemove at 16,16
+main_frame got mousemove at 400,400
+main_frame got click at 400,400
+main_frame got mousemove at 400,400
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
index 586cef7764b5c8a55f8287ea0eb81a3341c44e0c..87b321430c1cdc87ef9bdfa87520f1f024f400ea 100644 (file)
@@ -1,3 +1,32 @@
+2014-04-22  Andreas Kling  <akling@apple.com>
+
+        REGRESSION (r151839): Subframe keeps getting mousemove events with the same coordinates after hiding a hovered element.
+        <https://webkit.org/b/131974>
+        <rdar://problem/15907469>
+
+        When the currently hovered element disappears as a result of style recalc,
+        we send a fake mousemove event to the page, to see if anything newly added
+        should become hovered.
+
+        The faking mechanism lives in EventHandler and simply synthesizes a new
+        mousemove event using the last seen mouse location.
+
+        The problem here is that we were sending this fake mousemove event to the
+        subframe where the hovered element lived. Since subframes aren't kept up
+        to date on recent mouse locations, this could cause some strange behavior
+        where a subframe would dispatch mousemove events with stale coordinates.
+
+        The solution is to always dispatch fake mousemove events from the main
+        frame's event handler. This is how real event delivery happens, and hit
+        testing will then find the appropriate subframe, if any.
+
+        Reviewed by Benjamin Poulain.
+
+        Test: fast/events/ghostly-mousemoves-in-subframe.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::recalcStyle):
+
 2014-04-22  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [OS X] Glyph spacing for system fonts may be incorrect
index 45bb47d7263ee0647a788b0034dd7cb6fac80a2f..008880f65ea565757f1288d50fe6bb80ca707e25 100644 (file)
@@ -1794,7 +1794,7 @@ void Document::recalcStyle(Style::Change change)
     // detached (for example, by setting display:none in the :hover style), schedule another mouseMove event
     // to check if any other elements ended up under the mouse pointer due to re-layout.
     if (m_hoveredElement && !m_hoveredElement->renderer())
-        frameView.frame().eventHandler().dispatchFakeMouseMoveEventSoon();
+        frameView.frame().mainFrame().eventHandler().dispatchFakeMouseMoveEventSoon();
 
     // Style change may reset the focus, e.g. display: none, visibility: hidden.
     resetHiddenFocusElementSoon();