Bug 1029451 - Preserve mIsSynthesizedForTests on mouse events. r=ehsan
☠☠ backed out by 0b743302cbfb ☠ ☠
authorTom Tromey <tromey@mozilla.com>
Fri, 03 Apr 2015 08:17:00 -0400
changeset 267604 8882af540247a8c068aa16e958c2b56832a80261
parent 267603 d084a35e8d79a6ebf6c8da1785add5c6ec41420f
child 267605 e68045bf8272bb70e0ac9ee0451b68a4c0ce718a
push id4830
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:18:48 +0000
treeherdermozilla-beta@4c2175bb0420 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1029451
milestone40.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 1029451 - Preserve mIsSynthesizedForTests on mouse events. r=ehsan
dom/base/test/browser.ini
dom/base/test/browser_disableNonTestMouseEvents.js
dom/base/test/disableNonTestMouseEvents-frame.js
dom/base/test/disableNonTestMouseEvents.html
dom/events/EventStateManager.cpp
layout/base/RestyleManager.cpp
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/generic/nsGfxScrollFrame.cpp
layout/svg/nsSVGOuterSVGFrame.cpp
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -1,16 +1,19 @@
 [DEFAULT]
 support-files =
   file_messagemanager_unload.html
+  disableNonTestMouseEvents.html
+  disableNonTestMouseEvents-frame.js
 
 [browser_bug593387.js]
 skip-if = e10s # Bug ?????? - test directly touches content (contentWindow.iframe.addEventListener)
 [browser_bug902350.js]
 skip-if = e10s # Bug ?????? - test e10s utils don't support load events from iframe etc, which this test relies on.
+[browser_disableNonTestMouseEvents.js]
 [browser_messagemanager_loadprocessscript.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_messagemanager_unload.js]
 [browser_state_notifications.js]
 # skip-if = e10s # Bug ?????? - content-document-* notifications come while document's URI is still about:blank, but test expects real URL.
 skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above once fixed
 [browser_bug1058164.js]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/browser_disableNonTestMouseEvents.js
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cu = Components.utils;
+const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
+
+const FRAME_URI = "chrome://mochitests/content/browser/dom/base/test/" +
+                  "disableNonTestMouseEvents-frame.js";
+const TEST_URI = "http://example.com/browser/dom/base/test/" +
+                 "disableNonTestMouseEvents.html";
+const TYPES = ["click", "mousedown", "mousemove", "mouseover", "mouseup"];
+
+EventUtils.disableNonTestMouseEvents(true);
+
+let triggered = false;
+
+function addTab(url) {
+  window.focus();
+
+  let tabSwitchPromise = new Promise((resolve, reject) => {
+    window.gBrowser.addEventListener("TabSwitchDone", function listener() {
+      window.gBrowser.removeEventListener("TabSwitchDone", listener);
+      resolve();
+    });
+  });
+
+  let loadPromise = new Promise(function(resolve, reject) {
+    let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
+    let linkedBrowser = tab.linkedBrowser;
+
+    linkedBrowser.addEventListener("load", function onload() {
+      linkedBrowser.removeEventListener("load", onload, true);
+      resolve(tab);
+    }, true);
+  });
+
+  return Promise.all([tabSwitchPromise, loadPromise]);
+}
+
+function triggerEvent(type) {
+  let mm = gBrowser.selectedBrowser.messageManager;
+
+  return new Promise(function(resolve, reject) {
+    let handler = function() {
+      info("in triggerEvent handler");
+      triggered = true;
+      mm.removeMessageListener("Test:EventReply", handler);
+      resolve();
+    };
+
+    mm.addMessageListener("Test:EventReply", handler);
+    info("sending synthesize message: " + type);
+    mm.sendAsyncMessage("Test:SynthesizeEvent", type);
+  });
+}
+
+add_task(function*() {
+  for (let type of TYPES) {
+    triggered = false;
+
+    info("adding new tab");
+    yield addTab(TEST_URI);
+
+    let mm = gBrowser.selectedBrowser.messageManager;
+    info("sending frame script");
+    mm.loadFrameScript(FRAME_URI, false);
+
+    yield triggerEvent(type);
+
+    ok(triggered, type + " event was triggered");
+
+    gBrowser.removeCurrentTab();
+  }
+});
new file mode 100644
--- /dev/null
+++ b/dom/base/test/disableNonTestMouseEvents-frame.js
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let {classes: Cc, interfaces: Ci} = Components;
+let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+            .getService(Ci.mozIJSSubScriptLoader);
+let EventUtils = {};
+loader.loadSubScript("chrome://marionette/content/EventUtils.js", EventUtils);
+
+function sendTestEvent(message) {
+  let type = message.data;
+
+  let divone = content.document.getElementById("one");
+  let divtwo = content.document.getElementById("two");
+
+  divone.addEventListener(type, function(event) {
+    if (event.isSynthesized) {
+      sendAsyncMessage("Test:EventReply");
+    }
+  });
+
+  EventUtils.synthesizeMouse(divtwo, 5, 5, {type: "mousemove"}, divtwo.ownerGlobal);
+  if (type === "click") {
+    EventUtils.synthesizeMouse(divone, 5, 5, {}, divone.ownerGlobal);
+  } else {
+    EventUtils.synthesizeMouse(divone, 5, 5, {type: type}, divone.ownerGlobal);
+  }
+}
+
+addMessageListener("Test:SynthesizeEvent", sendTestEvent);
new file mode 100644
--- /dev/null
+++ b/dom/base/test/disableNonTestMouseEvents.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <style>
+    div {
+      background: red;
+      width: 20px;
+      height:20px;
+    }
+
+    div:hover {
+      background: blue;
+    }
+  </style>
+</head>
+<body>
+  <div id="one"></div>
+  <div id="two"></div>
+</body>
+</html>
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3676,16 +3676,18 @@ CreateMouseOrPointerWidgetEvent(WidgetMo
   }
   aNewEvent->refPoint = aMouseEvent->refPoint;
   aNewEvent->modifiers = aMouseEvent->modifiers;
   aNewEvent->button = aMouseEvent->button;
   aNewEvent->buttons = aMouseEvent->buttons;
   aNewEvent->pressure = aMouseEvent->pressure;
   aNewEvent->mPluginEvent = aMouseEvent->mPluginEvent;
   aNewEvent->inputSource = aMouseEvent->inputSource;
+  aNewEvent->mFlags.mIsSynthesizedForTests =
+    aMouseEvent->mFlags.mIsSynthesizedForTests;
 }
 
 nsIFrame*
 EventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent,
                                                uint32_t aMessage,
                                                nsIContent* aTargetContent,
                                                nsIContent* aRelatedContent)
 {
@@ -4444,16 +4446,17 @@ EventStateManager::CheckForAndDispatchCl
                            aEvent->widget, WidgetMouseEvent::eReal);
     event.refPoint = aEvent->refPoint;
     event.clickCount = aEvent->clickCount;
     event.modifiers = aEvent->modifiers;
     event.buttons = aEvent->buttons;
     event.time = aEvent->time;
     event.timeStamp = aEvent->timeStamp;
     event.mFlags.mNoContentDispatch = notDispatchToContents;
+    event.mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
     event.button = aEvent->button;
     event.inputSource = aEvent->inputSource;
 
     nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
     if (presShell) {
       nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
       // Click events apply to *elements* not nodes. At this point the target
       // content may have been reset to some non-element content, and so we need
@@ -4475,16 +4478,18 @@ EventStateManager::CheckForAndDispatchCl
         //fire double click
         WidgetMouseEvent event2(aEvent->mFlags.mIsTrusted, NS_MOUSE_DOUBLECLICK,
                                 aEvent->widget, WidgetMouseEvent::eReal);
         event2.refPoint = aEvent->refPoint;
         event2.clickCount = aEvent->clickCount;
         event2.modifiers = aEvent->modifiers;
         event2.buttons = aEvent->buttons;
         event2.mFlags.mNoContentDispatch = notDispatchToContents;
+        event2.mFlags.mIsSynthesizedForTests =
+          aEvent->mFlags.mIsSynthesizedForTests;
         event2.button = aEvent->button;
         event2.inputSource = aEvent->inputSource;
 
         ret = presShell->HandleEventWithTarget(&event2, currentTarget,
                                                mouseContent, aStatus);
       }
     }
   }
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -900,17 +900,17 @@ RestyleManager::ProcessRestyledFrames(ns
                    nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
               mOverflowChangedTracker.AddFrame(cont->GetParent(),
                                    OverflowChangedTracker::CHILDREN_CHANGED);
             }
           }
         }
       }
       if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
-        mPresContext->PresShell()->SynthesizeMouseMove(false);
+        mPresContext->PresShell()->SynthesizeMouseMove(false, false);
         didUpdateCursor = true;
       }
     }
   }
 
   FrameConstructor()->EndUpdate();
 
   // cleanup references and verify the style tree.  Note that the latter needs
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1417,17 +1417,18 @@ public:
 
   uint32_t GetPresShellId() { return mPresShellId; }
 
   /**
    * Dispatch a mouse move event based on the most recent mouse position if
    * this PresShell is visible. This is used when the contents of the page
    * moved (aFromScroll is false) or scrolled (aFromScroll is true).
    */
-  virtual void SynthesizeMouseMove(bool aFromScroll) = 0;
+  virtual void SynthesizeMouseMove(bool aFromScroll,
+                                   bool aIsSynthesizedForTests) = 0;
 
   enum PaintFlags {
     /* Update the layer tree and paint PaintedLayers. If this is not specified,
      * we may still have to do it if the layer tree lost PaintedLayer contents
      * we need for compositing. */
     PAINT_LAYERS = 0x01,
     /* Composite layers to the window. */
     PAINT_COMPOSITE = 0x02,
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -3999,17 +3999,17 @@ PresShell::UnsuppressAndInvalidate()
   }
 
   // now that painting is unsuppressed, focus may be set on the document
   nsPIDOMWindow *win = mDocument->GetWindow();
   if (win)
     win->SetReadyForFocus();
 
   if (!mHaveShutDown) {
-    SynthesizeMouseMove(false);
+    SynthesizeMouseMove(false, false);
     ScheduleImageVisibilityUpdate();
   }
 }
 
 void
 PresShell::UnsuppressPainting()
 {
   if (mPaintSuppressionTimer) {
@@ -5558,39 +5558,40 @@ void PresShell::SetRenderingState(const 
       FrameLayerBuilder::InvalidateAllLayers(manager);
     }
   }
 
   mRenderFlags = aState.mRenderFlags;
   mResolution = aState.mResolution;
 }
 
-void PresShell::SynthesizeMouseMove(bool aFromScroll)
+void PresShell::SynthesizeMouseMove(bool aFromScroll,
+                                    bool aIsSynthesizedForTests)
 {
   if (!sSynthMouseMove)
     return;
 
   if (mPaintingSuppressed || !mIsActive || !mPresContext) {
     return;
   }
 
   if (!mPresContext->IsRoot()) {
     nsIPresShell* rootPresShell = GetRootPresShell();
     if (rootPresShell) {
-      rootPresShell->SynthesizeMouseMove(aFromScroll);
+      rootPresShell->SynthesizeMouseMove(aFromScroll, aIsSynthesizedForTests);
     }
     return;
   }
 
   if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
     return;
 
   if (!mSynthMouseMoveEvent.IsPending()) {
     nsRefPtr<nsSynthMouseMoveEvent> ev =
-        new nsSynthMouseMoveEvent(this, aFromScroll);
+        new nsSynthMouseMoveEvent(this, aFromScroll, aIsSynthesizedForTests);
 
     if (!GetPresContext()->RefreshDriver()->AddRefreshObserver(ev,
                                                                Flush_Display)) {
       NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
       return;
     }
 
     mSynthMouseMoveEvent = ev;
@@ -5665,17 +5666,18 @@ static nsView* FindViewContaining(nsView
     if (r)
       return r;
   }
 
   return aView;
 }
 
 void
-PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
+PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll,
+                                      bool aIsSynthesizedForTests)
 {
   // If drag session has started, we shouldn't synthesize mousemove event.
   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
   if (dragSession) {
     mSynthMouseMoveEvent.Forget();
     return;
   }
 
@@ -5741,16 +5743,17 @@ PresShell::ProcessSynthMouseMoveEvent(bo
     refpoint -= view->GetOffsetTo(rootView);
     refpoint += view->ViewToWidgetOffset();
   }
   NS_ASSERTION(view->GetWidget(), "view should have a widget here");
   WidgetMouseEvent event(true, NS_MOUSE_MOVE, view->GetWidget(),
                          WidgetMouseEvent::eSynthesized);
   event.refPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD);
   event.time = PR_IntervalNow();
+  event.mFlags.mIsSynthesizedForTests = aIsSynthesizedForTests;
   // XXX set event.modifiers ?
   // XXX mnakano I think that we should get the latest information from widget.
 
   nsCOMPtr<nsIPresShell> shell = pointVM->GetPresShell();
   if (shell) {
     shell->DispatchSynthMouseMove(&event, !aFromScroll);
   }
 
@@ -6747,17 +6750,17 @@ PresShell::RecordMouseLocation(WidgetGUI
 #ifdef DEBUG_MOUSE_LOCATION
     if (aEvent->message == NS_MOUSE_ENTER)
       printf("[ps=%p]got mouse enter for %p\n",
              this, aEvent->widget);
     printf("[ps=%p]setting mouse location to (%d,%d)\n",
            this, mMouseLocation.x, mMouseLocation.y);
 #endif
     if (aEvent->message == NS_MOUSE_ENTER)
-      SynthesizeMouseMove(false);
+      SynthesizeMouseMove(false, aEvent->mFlags.mIsSynthesizedForTests);
   } else if (aEvent->message == NS_MOUSE_EXIT) {
     // Although we only care about the mouse moving into an area for which this
     // pres shell doesn't receive mouse move events, we don't check which widget
     // the mouse exit was for since this seems to vary by platform.  Hopefully
     // this won't matter at all since we'll get the mouse move or enter after
     // the mouse exit when the mouse moves from one of our widgets into another.
     mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
 #ifdef DEBUG_MOUSE_LOCATION
@@ -8962,17 +8965,17 @@ PresShell::DidDoReflow(bool aInterruptib
 
   nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell();
   if (docShell) {
     DOMHighResTimeStamp now = GetPerformanceNow();
     docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
   }
 
   if (sSynthMouseMove) {
-    SynthesizeMouseMove(false);
+    SynthesizeMouseMove(false, false);
   }
 
   if (mTouchCaret) {
     mTouchCaret->UpdatePositionIfNeeded();
   }
 
   mPresContext->NotifyMissingFonts();
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -611,18 +611,20 @@ protected:
     explicit DelayedKeyEvent(mozilla::WidgetKeyboardEvent* aEvent);
   };
 
   // Check if aEvent is a mouse event and record the mouse location for later
   // synth mouse moves.
   void RecordMouseLocation(mozilla::WidgetGUIEvent* aEvent);
   class nsSynthMouseMoveEvent final : public nsARefreshObserver {
   public:
-    nsSynthMouseMoveEvent(PresShell* aPresShell, bool aFromScroll)
-      : mPresShell(aPresShell), mFromScroll(aFromScroll) {
+    nsSynthMouseMoveEvent(PresShell* aPresShell, bool aFromScroll,
+                          bool aIsSynthesizedForTests)
+      : mPresShell(aPresShell), mFromScroll(aFromScroll)
+      , mIsSynthesizedForTests(aIsSynthesizedForTests) {
       NS_ASSERTION(mPresShell, "null parameter");
     }
 
   private:
   // Private destructor, to discourage deletion outside of Release():
     ~nsSynthMouseMoveEvent() {
       Revoke();
     }
@@ -635,24 +637,26 @@ protected:
         mPresShell->GetPresContext()->RefreshDriver()->
           RemoveRefreshObserver(this, Flush_Display);
         mPresShell = nullptr;
       }
     }
     virtual void WillRefresh(mozilla::TimeStamp aTime) override {
       if (mPresShell) {
         nsRefPtr<PresShell> shell = mPresShell;
-        shell->ProcessSynthMouseMoveEvent(mFromScroll);
+        shell->ProcessSynthMouseMoveEvent(mFromScroll, mIsSynthesizedForTests);
       }
     }
   private:
     PresShell* mPresShell;
     bool mFromScroll;
+    bool mIsSynthesizedForTests;
   };
-  void ProcessSynthMouseMoveEvent(bool aFromScroll);
+  void ProcessSynthMouseMoveEvent(bool aFromScroll,
+                                  bool aIsSynthesizedForTests);
 
   void QueryIsActive();
   nsresult UpdateImageLockingState();
 
 #ifdef ANDROID
   nsIDocument* GetTouchEventTargetDocument();
 #endif
   bool InZombieDocument(nsIContent *aContent);
@@ -698,17 +702,18 @@ protected:
   void GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
                                            nsIContent **aTargetToUse,
                                            mozilla::LayoutDeviceIntPoint& aTargetPt,
                                            nsIWidget *aRootWidget);
 
   void FireResizeEvent();
   static void AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell);
 
-  virtual void SynthesizeMouseMove(bool aFromScroll) override;
+  virtual void SynthesizeMouseMove(bool aFromScroll,
+                                   bool aIsSynthesizedForTests) override;
 
   PresShell* GetRootPresShell();
 
   nscolor GetDefaultBackgroundColorToDraw();
 
   DOMHighResTimeStamp GetPerformanceNow();
 
   // The callback for the mPaintSuppressionTimer timer.
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2315,17 +2315,17 @@ ClampAndAlignWithLayerPixels(const nsPoi
 /* static */ void
 ScrollFrameHelper::ScrollActivityCallback(nsITimer *aTimer, void* anInstance)
 {
   ScrollFrameHelper* self = static_cast<ScrollFrameHelper*>(anInstance);
 
   // Fire the synth mouse move.
   self->mScrollActivityTimer->Cancel();
   self->mScrollActivityTimer = nullptr;
-  self->mOuter->PresContext()->PresShell()->SynthesizeMouseMove(true);
+  self->mOuter->PresContext()->PresShell()->SynthesizeMouseMove(true, false);
 }
 
 
 void
 ScrollFrameHelper::ScheduleSyntheticMouseMove()
 {
   if (!mScrollActivityTimer) {
     mScrollActivityTimer = do_CreateInstance("@mozilla.org/timer;1");
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -491,17 +491,17 @@ void
 nsSVGOuterSVGFrame::DidReflow(nsPresContext*   aPresContext,
                               const nsHTMLReflowState*  aReflowState,
                               nsDidReflowStatus aStatus)
 {
   nsSVGOuterSVGFrameBase::DidReflow(aPresContext,aReflowState,aStatus);
 
   // Make sure elements styled by :hover get updated if script/animation moves
   // them under or out from under the pointer:
-  PresContext()->PresShell()->SynthesizeMouseMove(false);
+  PresContext()->PresShell()->SynthesizeMouseMove(false, false);
 }
 
 /* virtual */ bool
 nsSVGOuterSVGFrame::UpdateOverflow()
 {
   // See the comments in Reflow above.
 
   // WARNING!! Keep this in sync with Reflow above!