Bug 598482 part 24. Flush on every mousemove, because otherwise we can end up with mouse events (mousemove, mousein, mouseout) dispatched to the wrong elements. r=smaug
☠☠ backed out by 25224a78f895 ☠ ☠
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 23 Dec 2011 22:52:26 -0500
changeset 84552 1ac4cb2e7c320d624b863e295b5085a1acbaf2ef
parent 84551 9cb424c5127d65a0874d64fdf295613f5170389f
child 84553 41200991490f0d4e37202500578cd8c5e9815e63
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
bugs598482
milestone12.0a1
Bug 598482 part 24. 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);
     }
   }