Bug 891882 - DOMWindowUtils.sendMouseEvent dispatch events with isSynthesized chrome attribute set to true. r=smaug
authorAlexandre Poirot <poirot.alex@gmail.com>
Wed, 15 Jan 2014 09:28:04 -0500
changeset 163451 f890a8991588269623a85db8747b8df51962f296
parent 163450 2da820ed25873f93ab2addff7ac2ae9f65fac686
child 163452 16494b084901f84486f811a726a74a5f55112856
push id3848
push userryanvm@gmail.com
push dateWed, 15 Jan 2014 14:28:10 +0000
treeherderb2g-inbound@539b03915ad7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs891882
milestone29.0a1
Bug 891882 - DOMWindowUtils.sendMouseEvent dispatch events with isSynthesized chrome attribute set to true. r=smaug
content/events/src/nsDOMEvent.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsDOMWindowUtils.h
dom/base/test/mochitest.ini
dom/base/test/test_domwindowutils.html
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/ipc/TabChild.cpp
dom/webidl/Event.webidl
testing/mochitest/tests/SimpleTest/EventUtils.js
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -174,16 +174,21 @@ public:
     return mEvent->mFlags.mMultipleActionsPrevented;
   }
 
   bool IsTrusted() const
   {
     return mEvent->mFlags.mIsTrusted;
   }
 
+  bool IsSynthesized() const
+  {
+    return mEvent->mFlags.mIsSynthesizedForTests;
+  }
+
   uint64_t TimeStamp() const
   {
     return mEvent->time;
   }
 
   void InitEvent(const nsAString& aType, bool aBubbles, bool aCancelable,
                  mozilla::ErrorResult& aRv)
   {
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -599,38 +599,44 @@ nsDOMWindowUtils::SendMouseEvent(const n
                                  float aX,
                                  float aY,
                                  int32_t aButton,
                                  int32_t aClickCount,
                                  int32_t aModifiers,
                                  bool aIgnoreRootScrollFrame,
                                  float aPressure,
                                  unsigned short aInputSourceArg,
+                                 bool aIsSynthesized,
+                                 uint8_t aOptionalArgCount,
                                  bool *aPreventDefault)
 {
   return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
                               aIgnoreRootScrollFrame, aPressure,
-                              aInputSourceArg, false, aPreventDefault);
+                              aInputSourceArg, false, aPreventDefault,
+                              aOptionalArgCount >= 4 ? aIsSynthesized : true);
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
                                          float aX,
                                          float aY,
                                          int32_t aButton,
                                          int32_t aClickCount,
                                          int32_t aModifiers,
                                          bool aIgnoreRootScrollFrame,
                                          float aPressure,
-                                         unsigned short aInputSourceArg)
+                                         unsigned short aInputSourceArg,
+                                         bool aIsSynthesized,
+                                         uint8_t aOptionalArgCount)
 {
   PROFILER_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow");
   return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
                               aIgnoreRootScrollFrame, aPressure,
-                              aInputSourceArg, true, nullptr);
+                              aInputSourceArg, true, nullptr,
+                              aOptionalArgCount >= 4 ? aIsSynthesized : true);
 }
 
 static LayoutDeviceIntPoint
 ToWidgetPoint(const CSSPoint& aPoint, const nsPoint& aOffset,
               nsPresContext* aPresContext)
 {
   return LayoutDeviceIntPoint::FromAppUnitsRounded(
     CSSPoint::ToAppUnits(aPoint) + aOffset,
@@ -663,17 +669,18 @@ nsDOMWindowUtils::SendMouseEventCommon(c
                                        float aY,
                                        int32_t aButton,
                                        int32_t aClickCount,
                                        int32_t aModifiers,
                                        bool aIgnoreRootScrollFrame,
                                        float aPressure,
                                        unsigned short aInputSourceArg,
                                        bool aToWindow,
-                                       bool *aPreventDefault)
+                                       bool *aPreventDefault,
+                                       bool aIsSynthesized)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // get the widget to send the event to
   nsPoint offset;
   nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
@@ -710,17 +717,17 @@ nsDOMWindowUtils::SendMouseEventCommon(c
   event.modifiers = GetWidgetModifiers(aModifiers);
   event.button = aButton;
   event.buttons = GetButtonsFlagForButton(aButton);
   event.widget = widget;
   event.pressure = aPressure;
   event.inputSource = aInputSourceArg;
   event.clickCount = aClickCount;
   event.time = PR_IntervalNow();
-  event.mFlags.mIsSynthesizedForTests = true;
+  event.mFlags.mIsSynthesizedForTests = aIsSynthesized;
 
   nsPresContext* presContext = GetPresContext();
   if (!presContext)
     return NS_ERROR_FAILURE;
 
   event.refPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
   event.ignoreRootScrollFrame = aIgnoreRootScrollFrame;
 
--- a/dom/base/nsDOMWindowUtils.h
+++ b/dom/base/nsDOMWindowUtils.h
@@ -41,17 +41,18 @@ protected:
                                   float aY,
                                   int32_t aButton,
                                   int32_t aClickCount,
                                   int32_t aModifiers,
                                   bool aIgnoreRootScrollFrame,
                                   float aPressure,
                                   unsigned short aInputSourceArg,
                                   bool aToWindow,
-                                  bool *aPreventDefault);
+                                  bool *aPreventDefault,
+                                  bool aIsSynthesized);
 
   NS_IMETHOD SendTouchEventCommon(const nsAString& aType,
                                   uint32_t* aIdentifiers,
                                   int32_t* aXs,
                                   int32_t* aYs,
                                   uint32_t* aRxs,
                                   uint32_t* aRys,
                                   float* aRotationAngles,
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -44,8 +44,9 @@ support-files =
 [test_urlSearchParams.html]
 [test_urlutils_stringify.html]
 [test_window_constructor.html]
 [test_window_cross_origin_props.html]
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_writable-replaceable.html]
+[test_domwindowutils.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_domwindowutils.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Test for DOMWindowUtils</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var utils = SpecialPowers.getDOMWindowUtils(window);
+function test_sendMouseEventDefaults() {
+  var x = 1, y = 2, button = 1, clickCount = 2,
+      modifiers = SpecialPowers.Ci.nsIDOMNSEvent.SHIFT_MASK;
+
+  window.addEventListener("mousedown", function listener(evt) {
+    window.removeEventListener("mousedown", listener);
+    // Mandatory args
+    is(evt.clientX, x, "check x");
+    is(evt.clientY, y, "check y");
+    is(evt.button, button, "check button");
+    is(evt.detail, clickCount, "check click count");
+    is(evt.getModifierState("Shift"), true, "check modifiers");
+
+    // Default value for optionals
+    is(evt.mozPressure, 0, "check pressure");
+    is(evt.mozInputSource, SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE, "check input source");
+    is(evt.isSynthesized, undefined, "check isSynthesized is undefined in content");
+    is(SpecialPowers.wrap(evt).isSynthesized, true, "check isSynthesized is true from chrome");
+    next();
+  });
+
+  // Only pass mandatory arguments and check default values
+  utils.sendMouseEvent("mousedown", x, y, button, clickCount, modifiers);
+}
+
+function test_sendMouseEventOptionals() {
+  var x = 1, y = 2, button = 1, clickCount = 3,
+      modifiers = SpecialPowers.Ci.nsIDOMNSEvent.SHIFT_MASK,
+      pressure = 0.5,
+      source = SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_KEYBOARD;
+
+  window.addEventListener("mouseup", function listener(evt) {
+    window.removeEventListener("mouseup", listener);
+    is(evt.mozInputSource, source, "explicit input source is valid");
+    is(SpecialPowers.wrap(evt).isSynthesized, false, "we can dispatch event that don't look synthesized");
+    next();
+  });
+
+  // Check explicit value for optional args
+  utils.sendMouseEvent("mouseup", x, y, button, clickCount, modifiers,
+                       false, pressure, source, false);
+}
+
+var tests = [
+  test_sendMouseEventDefaults,
+  test_sendMouseEventOptionals
+];
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+function start() {
+  SimpleTest.waitForExplicitFinish();
+  SimpleTest.executeSoon(next);
+}
+
+window.addEventListener("load", start);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -38,17 +38,17 @@ interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsICompositionStringSynthesizer;
 
-[scriptable, uuid(c6efd629-7282-4f0d-9db8-0fa59c191dd5)]
+[scriptable, uuid(fa0fe174-7c07-11e3-a5ba-000c290c393e)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -244,28 +244,33 @@ interface nsIDOMWindowUtils : nsISupport
    * @param aButton button to synthesize
    * @param aClickCount number of clicks that have been performed
    * @param aModifiers modifiers pressed, using constants defined as MODIFIER_*
    * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds
    *                           during dispatch
    * @param aPressure touch input pressure: 0.0 -> 1.0
    * @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
    *        defaults to mouse input.
+   * @param aIsSynthesized controls nsIDOMEvent.isSynthesized value
+   *                       that helps identifying test related events,
+   *                       defaults to true
    *
    * returns true if the page called prevent default on this event
    */
+  [optional_argc]
   boolean sendMouseEvent(in AString aType,
                          in float aX,
                          in float aY,
                          in long aButton,
                          in long aClickCount,
                          in long aModifiers,
                          [optional] in boolean aIgnoreRootScrollFrame,
                          [optional] in float aPressure,
-                         [optional] in unsigned short aInputSourceArg);
+                         [optional] in unsigned short aInputSourceArg,
+                         [optional] in boolean aIsSynthesized);
 
   /** Synthesize a touch event. The event types supported are:
    *    touchstart, touchend, touchmove, and touchcancel
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
@@ -298,25 +303,27 @@ interface nsIDOMWindowUtils : nsISupport
                          [array, size_is(count)] in float aForces,
                          in uint32_t count,
                          in long aModifiers,
                          [optional] in boolean aIgnoreRootScrollFrame);
 
   /** The same as sendMouseEvent but ensures that the event is dispatched to
    *  this DOM window or one of its children.
    */
+  [optional_argc]
   void sendMouseEventToWindow(in AString aType,
                               in float aX,
                               in float aY,
                               in long aButton,
                               in long aClickCount,
                               in long aModifiers,
                               [optional] in boolean aIgnoreRootScrollFrame,
                               [optional] in float aPressure,
-                              [optional] in unsigned short aInputSourceArg);
+                              [optional] in unsigned short aInputSourceArg,
+                              [optional] in boolean aIsSynthesized);
 
   /** The same as sendTouchEvent but ensures that the event is dispatched to
    *  this DOM window or one of its children.
    */
   boolean sendTouchEventToWindow(in AString aType,
                                  [array, size_is(count)] in uint32_t aIdentifiers,
                                  [array, size_is(count)] in int32_t aXs,
                                  [array, size_is(count)] in int32_t aYs,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2382,17 +2382,17 @@ TabChild::DispatchMouseEvent(const nsStr
                              const bool&           aIgnoreRootScrollFrame,
                              const unsigned short& aInputSourceArg)
 {
   nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
   NS_ENSURE_TRUE(utils, true);
   
   bool defaultPrevented = false;
   utils->SendMouseEvent(aType, aPoint.x, aPoint.y, aButton, aClickCount, aModifiers,
-                        aIgnoreRootScrollFrame, 0, aInputSourceArg, &defaultPrevented);
+                        aIgnoreRootScrollFrame, 0, aInputSourceArg, false, 4, &defaultPrevented);
   return defaultPrevented;
 }
 
 void
 TabChild::MakeVisible()
 {
     if (mWidget) {
         mWidget->Show(true);
--- a/dom/webidl/Event.webidl
+++ b/dom/webidl/Event.webidl
@@ -51,16 +51,17 @@ partial interface Event {
   const long ALT_MASK     = 0x00000001;
   const long CONTROL_MASK = 0x00000002;
   const long SHIFT_MASK   = 0x00000004;
   const long META_MASK    = 0x00000008;
 
   readonly attribute EventTarget? originalTarget;
   readonly attribute EventTarget? explicitOriginalTarget;
   [ChromeOnly] readonly attribute boolean multipleActionsPrevented;
+  [ChromeOnly] readonly attribute boolean isSynthesized;
 
   boolean getPreventDefault();
 };
 
 dictionary EventInit {
   boolean bubbles = false;
   boolean cancelable = false;
 };
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -244,19 +244,23 @@ function synthesizeMouseAtPoint(left, to
   var defaultPrevented = false;
 
   if (utils) {
     var button = aEvent.button || 0;
     var clickCount = aEvent.clickCount || 1;
     var modifiers = _parseModifiers(aEvent);
     var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
     var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
+    var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true;
 
     if (("type" in aEvent) && aEvent.type) {
-      defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers, false, pressure, inputSource);
+      defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button,
+                                              clickCount, modifiers, false,
+                                              pressure, inputSource,
+                                              synthesized);
     }
     else {
       utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
       utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
     }
   }
 
   return defaultPrevented;