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 84042 04e2d8313601d768e2c3bf21edb5821017e813c7
parent 84041 e94c3b3085197c4163a4425b2077470903e9d724
child 84043 8e7225a8b1cc65184cf6a57da336dee5d34fd584
push id21819
push userbmo@edmorley.co.uk
push dateMon, 09 Jan 2012 20:36:23 +0000
treeherdermozilla-central@c713003d3226 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs716549
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
     }
   }