Bug 716549. Flush on every mousemove, because otherwise we can end up with mouse events (mousemove, mousein, mouseout) dispatched to the wrong elements. r=smaug
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 23 Dec 2011 22:52:26 -0500
changeset 85267 04e2d8313601d768e2c3bf21edb5821017e813c7
parent 85266 e94c3b3085197c4163a4425b2077470903e9d724
child 85268 8e7225a8b1cc65184cf6a57da336dee5d34fd584
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs716549
milestone12.0a1
Bug 716549. Flush on every mousemove, because otherwise we can end up with mouse events (mousemove, mousein, mouseout) dispatched to the wrong elements. r=smaug
content/events/src/nsEventStateManager.cpp
content/events/test/Makefile.in
content/events/test/test_bug635465.html
testing/mochitest/tests/SimpleTest/EventUtils.js
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1141,16 +1141,19 @@ nsEventStateManager::PreHandleEvent(nsPr
     // has completed and so |aTargetFrame| may have been deleted (moving
     // a bookmark, for example).  If this is the case, however, we know
     // that ClearFrameRefs() has been called and it cleared out
     // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
     // into UpdateCursor().
     GenerateDragGesture(aPresContext, (nsMouseEvent*)aEvent);
     UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
     GenerateMouseEnterExit((nsGUIEvent*)aEvent);
+    // Flush pending layout changes, so that later mouse move events
+    // will go to the right nodes.
+    FlushPendingEvents(aPresContext);
     break;
   case NS_DRAGDROP_GESTURE:
     if (mClickHoldContextMenu) {
       // an external drag gesture event came in, not generated internally
       // by Gecko. Make sure we get rid of the click-hold timer.
       KillClickHoldTimer();
     }
     break;
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -108,16 +108,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug662678.html \
 		test_bug667919-1.html \
 		test_bug667919-2.html \
 		test_bug667612.html \
 		empty.js \
 		test_bug689564.html \
 		test_bug698929.html \
 		test_eventctors.html \
+		test_bug635465.html \
 		$(NULL)
 
 #bug 585630
 ifneq (mobile,$(MOZ_BUILD_APP))
 _TEST_FILES += \
 		test_dragstart.html \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug635465.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=635465
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 635465</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style type="text/css">
+      #item {
+        position: relative;
+      }
+      .s-menu-section-submenu {
+        position: absolute;
+        display: none;
+      }
+      .open .s-menu-section-submenu {
+        display: block;
+      }
+</style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635465">Mozilla Bug 635465</a>
+<div id="display">
+  <div class="item" id="item"
+       onmouseover="showSubmenu(event)" onmouseout="hideSubmenu(event)">
+    <a href="#" id="firsthover">Hover me</a>
+    <div class="s-menu-section-submenu" id="menu">
+      <a href="#" id="secondhover">Now hover me</a>
+    </div>
+  </div>
+</div>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+/** Test for Bug 635465 **/
+function showSubmenu(event) {
+  var item = document.getElementById('item');
+
+  var width = item.offsetWidth;   // IT WORKS IF YOU REMOVE THIS LINE
+  
+  item.className='open';
+}
+
+function hideSubmenu(event) {
+  document.getElementById('item').className='';
+}
+
+SimpleTest.waitForExplicitFinish();
+
+function executeTests() {
+  // First flush out layout of firsthover
+  ok($("firsthover").getBoundingClientRect().height > 0, true,
+     "Should have a nonzero height before hover");
+
+  // Now trigger a mouseover on firsthover
+  synthesizeMouseAtCenter($("firsthover"), { type: "mousemove" });
+
+  ok($("secondhover").getBoundingClientRect().height > 0, true,
+     "Should have a nonzero height for submenu after hover");
+
+  // Now determine where secondhover is hanging out
+  var rect = $("secondhover").getBoundingClientRect();
+  synthesizeMouseAtCenter($("secondhover"), { type: "mousemove" });
+
+  // And another mouseover one pixel to the right of where the center used to be
+  synthesizeMouseAtPoint(rect.left + rect.width/2 + 1,
+                         rect.top + rect.height/2,
+                         { type: "mousemove" });
+
+  ok($("secondhover").getBoundingClientRect().height > 0, true,
+     "Should have a nonzero height for submenu after second hover");
+
+  // And check computed display of the menu
+  is(getComputedStyle($("menu"), "").display, "block", "Should have block display");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(executeTests);
+</script>
+</pre>
+</body>
+</html>
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -141,28 +141,41 @@ function _parseModifiers(aEvent)
  *
  * If the type is specified, an mouse event of that type is fired. Otherwise,
  * a mousedown followed by a mouse up is performed.
  *
  * aWindow is optional, and defaults to the current window object.
  */
 function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
 {
+  var rect = aTarget.getBoundingClientRect();
+  synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
+			 aEvent, aWindow);
+}
+
+/*
+ * Synthesize a mouse event at a particular point in aWindow.
+ *
+ * aEvent is an object which may contain the properties:
+ *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
+ *
+ * If the type is specified, an mouse event of that type is fired. Otherwise,
+ * a mousedown followed by a mouse up is performed.
+ *
+ * aWindow is optional, and defaults to the current window object.
+ */
+function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
+{
   var utils = _getDOMWindowUtils(aWindow);
 
   if (utils) {
     var button = aEvent.button || 0;
     var clickCount = aEvent.clickCount || 1;
     var modifiers = _parseModifiers(aEvent);
 
-    var rect = aTarget.getBoundingClientRect();
-
-    var left = rect.left + aOffsetX;
-    var top = rect.top + aOffsetY;
-
     if (aEvent.type) {
       utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
     }
     else {
       utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
       utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
     }
   }