Bug 1146349 - Make the native event synthesization functions in DOMWindowUtils async. r=smaug
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 14 Apr 2015 11:36:36 -0400
changeset 239017 30b7130a25e436a98ec9e004dfdfbb5c9d067b44
parent 239016 e4132183a4fa6d1f0b3d36a9f1377aa098bc66a0
child 239018 0490493b12e8b58e66b899034f6e5c11b3f50ac1
push id58403
push userkgupta@mozilla.com
push dateTue, 14 Apr 2015 15:37:00 +0000
treeherdermozilla-inbound@371a7026a1df [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1146349
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 1146349 - Make the native event synthesization functions in DOMWindowUtils async. r=smaug
dom/base/nsDOMWindowUtils.cpp
dom/events/EventStateManager.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/ipc/TabParent.cpp
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/gonk/nsWindow.cpp
widget/gonk/nsWindow.h
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
widget/tests/TestAppShellSteadyState.cpp
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/nsWindowBase.cpp
widget/windows/nsWindowBase.h
widget/windows/winrt/MetroWidget.cpp
widget/windows/winrt/MetroWidget.h
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1296,123 +1296,145 @@ nsDOMWindowUtils::SendKeyEvent(const nsA
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                      int32_t aNativeKeyCode,
                                      int32_t aModifiers,
                                      const nsAString& aCharacters,
-                                     const nsAString& aUnmodifiedCharacters)
+                                     const nsAString& aUnmodifiedCharacters,
+                                     nsIObserver* aObserver)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
-  return widget->SynthesizeNativeKeyEvent(aNativeKeyboardLayout, aNativeKeyCode,
-                                          aModifiers, aCharacters, aUnmodifiedCharacters);
+  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+    <int32_t, int32_t, uint32_t, nsString, nsString, nsIObserver*>
+    (widget, &nsIWidget::SynthesizeNativeKeyEvent, aNativeKeyboardLayout,
+    aNativeKeyCode, aModifiers, aCharacters, aUnmodifiedCharacters, aObserver));
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX,
                                        int32_t aScreenY,
                                        int32_t aNativeMessage,
                                        int32_t aModifierFlags,
-                                       nsIDOMElement* aElement)
+                                       nsIDOMElement* aElement,
+                                       nsIObserver* aObserver)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidgetForElement(aElement);
   if (!widget)
     return NS_ERROR_FAILURE;
 
-  return widget->SynthesizeNativeMouseEvent(LayoutDeviceIntPoint(aScreenX, aScreenY),
-                                            aNativeMessage, aModifierFlags);
+  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+    <LayoutDeviceIntPoint, int32_t, int32_t, nsIObserver*>
+    (widget, &nsIWidget::SynthesizeNativeMouseEvent,
+    LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage, aModifierFlags,
+    aObserver));
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeMouseScrollEvent(int32_t aScreenX,
                                              int32_t aScreenY,
                                              uint32_t aNativeMessage,
                                              double aDeltaX,
                                              double aDeltaY,
                                              double aDeltaZ,
                                              uint32_t aModifierFlags,
                                              uint32_t aAdditionalFlags,
-                                             nsIDOMElement* aElement)
+                                             nsIDOMElement* aElement,
+                                             nsIObserver* aObserver)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidgetForElement(aElement);
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
-  return widget->SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint(aScreenX,
-                                                                       aScreenY),
-                                                  aNativeMessage,
-                                                  aDeltaX, aDeltaY, aDeltaZ,
-                                                  aModifierFlags,
-                                                  aAdditionalFlags);
+  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+    <mozilla::LayoutDeviceIntPoint, uint32_t, double, double, double, uint32_t, uint32_t, nsIObserver*>
+    (widget, &nsIWidget::SynthesizeNativeMouseScrollEvent,
+    LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage, aDeltaX, aDeltaY,
+    aDeltaZ, aModifierFlags, aAdditionalFlags, aObserver));
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId,
                                        uint32_t aTouchState,
                                        int32_t aScreenX,
                                        int32_t aScreenY,
                                        double aPressure,
-                                       uint32_t aOrientation)
+                                       uint32_t aOrientation,
+                                       nsIObserver* aObserver)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
   if (aPressure < 0 || aPressure > 1 || aOrientation > 359) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  return widget->SynthesizeNativeTouchPoint(aPointerId,
-                                            (nsIWidget::TouchPointerState)aTouchState,
-                                            nsIntPoint(aScreenX, aScreenY),
-                                            aPressure, aOrientation);
+  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+    <uint32_t, nsIWidget::TouchPointerState, nsIntPoint, double, uint32_t, nsIObserver*>
+    (widget, &nsIWidget::SynthesizeNativeTouchPoint, aPointerId,
+    (nsIWidget::TouchPointerState)aTouchState, nsIntPoint(aScreenX, aScreenY),
+    aPressure, aOrientation, aObserver));
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX,
                                      int32_t aScreenY,
-                                     bool aLongTap)
+                                     bool aLongTap,
+                                     nsIObserver* aObserver)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
-  return widget->SynthesizeNativeTouchTap(nsIntPoint(aScreenX, aScreenY), aLongTap);
+
+  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+    <nsIntPoint, bool, nsIObserver*>
+    (widget, &nsIWidget::SynthesizeNativeTouchTap,
+    nsIntPoint(aScreenX, aScreenY), aLongTap, aObserver));
+  return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::ClearNativeTouchSequence()
+nsDOMWindowUtils::ClearNativeTouchSequence(nsIObserver* aObserver)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
-  return widget->ClearNativeTouchSequence();
+
+  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs<nsIObserver*>
+    (widget, &nsIWidget::ClearNativeTouchSequence, aObserver));
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   // get the widget to send the event to
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -4083,17 +4083,17 @@ EventStateManager::GenerateMouseEnterExi
         if (aMouseEvent->refPoint != center) {
           // Mouse move doesn't finish at the center of the window. Dispatch a
           // synthetic native mouse event to move the pointer back to the center
           // of the window, to faciliate more movement. But first, record that
           // we've dispatched a synthetic mouse movement, so we can cancel it
           // in the other branch here.
           sSynthCenteringPoint = center;
           aMouseEvent->widget->SynthesizeNativeMouseMove(
-            center + aMouseEvent->widget->WidgetToScreenOffset());
+            center + aMouseEvent->widget->WidgetToScreenOffset(), nullptr);
         } else if (aMouseEvent->refPoint == sSynthCenteringPoint) {
           // This is the "synthetic native" event we dispatched to re-center the
           // pointer. Cancel it so we don't expose the centering move to content.
           aMouseEvent->mFlags.mPropagationStopped = true;
           // Clear sSynthCenteringPoint so we don't cancel other events
           // targeted at the center.
           sSynthCenteringPoint = kInvalidRefPoint;
         }
@@ -4218,32 +4218,34 @@ EventStateManager::SetPointerLock(nsIWid
     mPreLockPoint = sLastRefPoint;
 
     // Fire a synthetic mouse move to ensure event state is updated. We first
     // set the mouse to the center of the window, so that the mouse event
     // doesn't report any movement.
     sLastRefPoint = GetWindowInnerRectCenter(aElement->OwnerDoc()->GetWindow(),
                                              aWidget,
                                              mPresContext);
-    aWidget->SynthesizeNativeMouseMove(sLastRefPoint + aWidget->WidgetToScreenOffset());
+    aWidget->SynthesizeNativeMouseMove(sLastRefPoint + aWidget->WidgetToScreenOffset(),
+                                       nullptr);
 
     // Retarget all events to this element via capture.
     nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
 
     // Suppress DnD
     if (dragService) {
       dragService->Suppress();
     }
   } else {
     // Unlocking, so return pointer to the original position by firing a
     // synthetic mouse event. We first reset sLastRefPoint to its
     // pre-pointerlock position, so that the synthetic mouse event reports
     // no movement.
     sLastRefPoint = mPreLockPoint;
-    aWidget->SynthesizeNativeMouseMove(mPreLockPoint + aWidget->WidgetToScreenOffset());
+    aWidget->SynthesizeNativeMouseMove(mPreLockPoint + aWidget->WidgetToScreenOffset(),
+                                       nullptr);
 
     // Don't retarget events to this element any more.
     nsIPresShell::SetCapturingContent(nullptr, CAPTURE_POINTERLOCK);
 
     // Unsuppress DnD
     if (dragService) {
       dragService->Unsuppress();
     }
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -44,18 +44,19 @@ interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsITranslationNodeList;
 interface nsIJSRAIIHelper;
 interface nsIContentPermissionRequest;
+interface nsIObserver;
 
-[scriptable, uuid(7f2f44ab-2857-4cc2-8c9d-3d9816f5a4d6)]
+[scriptable, uuid(7ecfd6e7-120a-4567-85f7-14277f4c6d9f)]
 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.
@@ -566,35 +567,45 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * See nsIWidget::SynthesizeNativeKeyEvent
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
    *
    * When you use this for tests, use the constants defined in NativeKeyCodes.js
+   *
+   * NOTE: The synthesized native event will be fired asynchronously, and upon
+   * completion the observer, if provided, will be notified with a "keyevent"
+   * topic.
    */
   void sendNativeKeyEvent(in long aNativeKeyboardLayout,
                           in long aNativeKeyCode,
                           in long aModifierFlags,
                           in AString aCharacters,
-                          in AString aUnmodifiedCharacters);
+                          in AString aUnmodifiedCharacters,
+                          [optional] in nsIObserver aObserver);
 
   /**
    * See nsIWidget::SynthesizeNativeMouseEvent
    *
    * Will be called on the widget that contains aElement.
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
+   *
+   * NOTE: The synthesized native event will be fired asynchronously, and upon
+   * completion the observer, if provided, will be notified with a "mouseevent"
+   * topic.
    */
   void sendNativeMouseEvent(in long aScreenX,
                             in long aScreenY,
                             in long aNativeMessage,
                             in long aModifierFlags,
-                            in nsIDOMElement aElement);
+                            in nsIDOMElement aElement,
+                            [optional] in nsIObserver aObserver);
 
   /**
    * The values for sendNativeMouseScrollEvent's aAdditionalFlags.
    */
 
   /**
    * If MOUSESCROLL_PREFER_WIDGET_AT_POINT is set, widget will dispatch
    * the event to a widget which is under the cursor.  Otherwise, dispatch to
@@ -615,31 +626,34 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * See nsIWidget::SynthesizeNativeMouseScrollEvent
    *
    * Will be called on the widget that contains aElement.
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
    *
-   * NOTE: The synthesized native event may be fired asynchronously.
+   * NOTE: The synthesized native event will be fired asynchronously, and upon
+   * completion the observer, if provided, will be notified with a
+   * "mousescrollevent" topic.
    *
    * @param aNativeMessage
    *   On Windows:  WM_MOUSEWHEEL (0x020A), WM_MOUSEHWHEEL(0x020E),
    *                WM_VSCROLL (0x0115) or WM_HSCROLL (0x114).
    */
   void sendNativeMouseScrollEvent(in long aScreenX,
                                   in long aScreenY,
                                   in unsigned long aNativeMessage,
                                   in double aDeltaX,
                                   in double aDeltaY,
                                   in double aDeltaZ,
                                   in unsigned long aModifierFlags,
                                   in unsigned long aAdditionalFlags,
-                                  in nsIDOMElement aElement);
+                                  in nsIDOMElement aElement,
+                                  [optional] in nsIObserver aObserver);
 
   /**
    * Touch states for sendNativeTouchPoint. These values match
    * nsIWidget's TouchPointerState.
    */
 
   // The pointer is in a hover state above the digitizer
   const long TOUCH_HOVER   = 0x01;
@@ -660,61 +674,75 @@ interface nsIDOMWindowUtils : nsISupport
    * sent as individual calls. For example:
    * tap - msg1:TOUCH_CONTACT, msg2:TOUCH_REMOVE
    * drag - msg1-n:TOUCH_CONTACT (moving), msgn+1:TOUCH_REMOVE
    * hover drag - msg1-n:TOUCH_HOVER (moving), msgn+1:TOUCH_REMOVE
    *
    * Widget support: Windows 8.0+, Winrt/Win32. Gonk supports CONTACT, REMOVE,
    * and CANCEL but no HOVER. Other widgets will throw.
    *
+   * NOTE: The synthesized native event will be fired asynchronously, and upon
+   * completion the observer, if provided, will be notified with a "touchpoint"
+   * topic.
+   *
    * @param aPointerId The touch point id to create or update.
    * @param aTouchState one or more of the touch states listed above
    * @param aScreenX, aScreenY screen coords of this event
    * @param aPressure 0.0 -> 1.0 float val indicating pressure
    * @param aOrientation 0 -> 359 degree value indicating the
    * orientation of the pointer. Use 90 for normal taps.
    */
   void sendNativeTouchPoint(in unsigned long aPointerId,
                             in unsigned long aTouchState,
                             in long aScreenX,
                             in long aScreenY,
                             in double aPressure,
-                            in unsigned long aOrientation);
+                            in unsigned long aOrientation,
+                            [optional] in nsIObserver aObserver);
 
   /**
    * Simulates native touch based taps on the input digitizer. Events
    * triggered by this call are injected at the os level. Events do not
    * bypass widget level input processing and as such can be used to
    * test widget event logic and async pan-zoom controller functionality.
    * Cannot be accessed from an unprivileged context.
    *
    * Long taps (based on the aLongTap parameter) will be completed
    * asynchrnously after the call returns. Long tap delay is based on
    * the ui.click_hold_context_menus.delay pref or 1500 msec if pref
    * is not set.
    *
    * Widget support: Windows 8.0+, Winrt/Win32. Other widgets will
    * throw.
    *
+   * NOTE: The synthesized native event will be fired asynchronously, and upon
+   * completion the observer, if provided, will be notified, with a "touchtap"
+   * topic.
+   *
    * @param aScreenX, aScreenY screen coords of this event
    * @param aLongTap true if the tap should be long, false for a short
    * tap.
    */
   void sendNativeTouchTap(in long aScreenX,
                           in long aScreenY,
-                          in boolean aLongTap);
+                          in boolean aLongTap,
+                          [optional] in nsIObserver aObserver);
 
   /**
    * Cancel any existing touch points or long tap delays. Calling this is safe
    * even if you're sure there aren't any pointers recorded. You should call
    * this when tests shut down to reset the digitizer driver. Not doing so can
    * leave the digitizer in an undetermined state which can screw up subsequent
    * tests and native input.
+   *
+   * NOTE: The synthesized native event will be fired asynchronously, and upon
+   * completion the observer, if provided, will be notified with a "cleartouch"
+   * topic.
    */
-  void clearNativeTouchSequence();
+  void clearNativeTouchSequence([optional] in nsIObserver aObserver);
 
   /**
    * See nsIWidget::ActivateNativeMenuItemAt
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
    */
   void activateNativeMenuItemAt(in AString indexString);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2249,17 +2249,17 @@ TabParent::RecvIsParentWindowMainWidgetV
 }
 
 bool
 TabParent::RecvSynthesizeNativeMouseMove(const mozilla::LayoutDeviceIntPoint& aPoint)
 {
   // The widget associated with the browser window
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
-    widget->SynthesizeNativeMouseMove(aPoint);
+    widget->SynthesizeNativeMouseMove(aPoint, nullptr);
   }
   return true;
 }
 
 bool
 TabParent::RecvGetDPI(float* aValue)
 {
   TryCacheDPIAndScale();
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -850,18 +850,20 @@ PuppetWidget::SetCursor(nsCursor aCursor
 
   mCursor = aCursor;
   mUpdateCursor = false;
 
   return NS_OK;
 }
 
 nsresult
-PuppetWidget::SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint)
+PuppetWidget::SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+                                        nsIObserver* aObserver)
 {
+  //XXX Do something wtih aObserver (will happen in a later patch)
   if (mTabChild &&
       !mTabChild->SendSynthesizeNativeMouseMove(aPoint)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -128,17 +128,18 @@ public:
   { return NS_ERROR_UNEXPECTED; }
 
   virtual mozilla::LayoutDeviceIntPoint WidgetToScreenOffset() override
   { return LayoutDeviceIntPoint::FromUntyped(GetWindowPosition() + GetChromeDimensions()); }
 
   // Synthesized mouse events we need to forwarded to chrome. Event
   // state manager uses this api to position the mouse in the center
   // of the window for pointer lock.
-  virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint) override;
+  virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+                                             nsIObserver* aObserver) override;
 
   void InitEvent(WidgetGUIEvent& aEvent, nsIntPoint* aPoint = nullptr);
 
   NS_IMETHOD DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override;
   nsEventStatus DispatchAPZAwareEvent(WidgetInputEvent* aEvent) override;
   nsEventStatus DispatchInputEvent(WidgetInputEvent* aEvent) override;
 
   NS_IMETHOD CaptureRollupEvents(nsIRollupListener* aListener,
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -451,24 +451,27 @@ public:
 
   virtual nsTransparencyMode GetTransparencyMode() override;
   virtual void                SetTransparencyMode(nsTransparencyMode aMode) override;
 
   virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                             int32_t aNativeKeyCode,
                                             uint32_t aModifierFlags,
                                             const nsAString& aCharacters,
-                                            const nsAString& aUnmodifiedCharacters) override;
+                                            const nsAString& aUnmodifiedCharacters,
+                                            nsIObserver* aObserver) override;
 
   virtual nsresult SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                               uint32_t aNativeMessage,
-                                              uint32_t aModifierFlags) override;
+                                              uint32_t aModifierFlags,
+                                              nsIObserver* aObserver) override;
 
-  virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint) override
-  { return SynthesizeNativeMouseEvent(aPoint, NSMouseMoved, 0); }
+  virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+                                             nsIObserver* aObserver) override
+  { return SynthesizeNativeMouseEvent(aPoint, NSMouseMoved, 0, aObserver); }
 
   // Mac specific methods
   
   virtual bool      DispatchWindowEvent(mozilla::WidgetGUIEvent& event);
 
   void WillPaintWindow();
   bool PaintWindow(nsIntRegion aRegion);
 
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1111,31 +1111,36 @@ bool nsChildView::ShowsResizeIndicator(n
   }
   return true;
 }
 
 nsresult nsChildView::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                                int32_t aNativeKeyCode,
                                                uint32_t aModifierFlags,
                                                const nsAString& aCharacters,
-                                               const nsAString& aUnmodifiedCharacters)
-{
+                                               const nsAString& aUnmodifiedCharacters,
+                                               nsIObserver* aObserver)
+{
+  AutoObserverNotifier notifier(aObserver, "keyevent");
   return mTextInputHandler->SynthesizeNativeKeyEvent(aNativeKeyboardLayout,
                                                      aNativeKeyCode,
                                                      aModifierFlags,
                                                      aCharacters,
                                                      aUnmodifiedCharacters);
 }
 
 nsresult nsChildView::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                                  uint32_t aNativeMessage,
-                                                 uint32_t aModifierFlags)
+                                                 uint32_t aModifierFlags,
+                                                 nsIObserver* aObserver)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
+  AutoObserverNotifier notifier(aObserver, "mouseevent");
+
   NSPoint pt =
     nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor());
 
   // Move the mouse cursor to the requested position and reconnect it to the mouse.
   CGWarpMouseCursorPosition(NSPointToCGPoint(pt));
   CGAssociateMouseAndMouseCursorPosition(true);
 
   // aPoint is given with the origin on the top left, but convertScreenToBase
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -323,17 +323,18 @@ public:
     virtual void SetDrawsTitle(bool aDrawTitle) override;
     virtual void SetUseBrightTitlebarForeground(bool aBrightForeground) override;
     NS_IMETHOD SetNonClientMargins(nsIntMargin &margins) override;
     NS_IMETHOD SetWindowTitlebarColor(nscolor aColor, bool aActive) override;
     virtual void SetDrawsInTitlebar(bool aState) override;
     virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override;
     virtual nsresult SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                 uint32_t aNativeMessage,
-                                                uint32_t aModifierFlags) override;
+                                                uint32_t aModifierFlags,
+                                                nsIObserver* aObserver) override;
 
     void DispatchSizeModeEvent();
 
     // be notified that a some form of drag event needs to go into Gecko
     virtual bool DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers);
 
     bool HasModalDescendents() { return mNumModalDescendents > 0; }
     NSWindow *GetCocoaWindow() { return mWindow; }
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -2067,23 +2067,25 @@ void nsCocoaWindow::SetDrawsInTitlebar(b
   if (mWindow)
     [mWindow setDrawsContentsIntoWindowFrame:aState];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 NS_IMETHODIMP nsCocoaWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                                         uint32_t aNativeMessage,
-                                                        uint32_t aModifierFlags)
+                                                        uint32_t aModifierFlags,
+                                                        nsIObserver* aObserver)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
+  AutoObserverNotifier notifier(aObserver, "mouseevent");
   if (mPopupContentView)
     return mPopupContentView->SynthesizeNativeMouseEvent(aPoint, aNativeMessage,
-                                                         aModifierFlags);
+                                                         aModifierFlags, nullptr);
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 void nsCocoaWindow::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
--- a/widget/gonk/nsWindow.cpp
+++ b/widget/gonk/nsWindow.cpp
@@ -352,18 +352,21 @@ private:
     MultiTouchInput mInput;
 };
 
 nsresult
 nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                      TouchPointerState aPointerState,
                                      nsIntPoint aPointerScreenPoint,
                                      double aPointerPressure,
-                                     uint32_t aPointerOrientation)
+                                     uint32_t aPointerOrientation,
+                                     nsIObserver* aObserver)
 {
+    AutoObserverNotifier notifier(aObserver, "touchpoint");
+
     if (aPointerState == TOUCH_HOVER) {
         return NS_ERROR_UNEXPECTED;
     }
 
     if (!mSynthesizedTouchInput) {
         mSynthesizedTouchInput = new MultiTouchInput();
     }
 
--- a/widget/gonk/nsWindow.h
+++ b/widget/gonk/nsWindow.h
@@ -93,17 +93,18 @@ public:
                                   const uint64_t aInputBlockId,
                                   nsEventStatus aApzResponse);
     NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
                              nsEventStatus& aStatus);
     virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                                 TouchPointerState aPointerState,
                                                 nsIntPoint aPointerScreenPoint,
                                                 double aPointerPressure,
-                                                uint32_t aPointerOrientation) override;
+                                                uint32_t aPointerOrientation,
+                                                nsIObserver* aObserver) override;
 
     NS_IMETHOD CaptureRollupEvents(nsIRollupListener *aListener,
                                    bool aDoCapture)
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
     NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);
 
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -6550,18 +6550,21 @@ nsWindow::GdkRectToDevicePixels(GdkRecta
                      rect.y * scale,
                      rect.width * scale,
                      rect.height * scale);
 }
 
 nsresult
 nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                      uint32_t aNativeMessage,
-                                     uint32_t aModifierFlags)
-{
+                                     uint32_t aModifierFlags,
+                                     nsIObserver* aObserver)
+{
+  AutoObserverNotifier notifier(aObserver, "mouseevent");
+
   if (!mGdkWindow) {
     return NS_OK;
   }
 
   GdkDisplay* display = gdk_window_get_display(mGdkWindow);
 
   // When a button-release event is requested, create it here and put it in the
   // event queue. This will not emit a motion event - this needs to be done
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -295,20 +295,22 @@ public:
 #if (MOZ_WIDGET_GTK == 2)
     static already_AddRefed<gfxASurface> GetSurfaceForGdkDrawable(GdkDrawable* aDrawable,
                                                                   const nsIntSize& aSize);
 #endif
     NS_IMETHOD         ReparentNativeWidget(nsIWidget* aNewParent) override;
 
     virtual nsresult SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                 uint32_t aNativeMessage,
-                                                uint32_t aModifierFlags) override;
+                                                uint32_t aModifierFlags,
+                                                nsIObserver* aObserver) override;
 
-    virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint) override
-    { return SynthesizeNativeMouseEvent(aPoint, GDK_MOTION_NOTIFY, 0); }
+    virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+                                               nsIObserver* aObserver) override
+    { return SynthesizeNativeMouseEvent(aPoint, GDK_MOTION_NOTIFY, 0, aObserver); }
 
 protected:
     virtual ~nsWindow();
 
     // event handling code
     void DispatchActivateEvent(void);
     void DispatchDeactivateEvent(void);
     void DispatchResized(int32_t aWidth, int32_t aHeight);
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1701,43 +1701,46 @@ nsBaseWidget::GetRootAccessible()
   }
 
   return nullptr;
 }
 
 #endif // ACCESSIBILITY
 
 nsresult
-nsIWidget::SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, bool aLongTap)
+nsIWidget::SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, bool aLongTap,
+                                    nsIObserver* aObserver)
 {
+  AutoObserverNotifier notifier(aObserver, "touchtap");
+
   if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) {
     sPointerIdCounter = 0;
   }
   int pointerId = sPointerIdCounter;
   sPointerIdCounter++;
   nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT,
-                                           aPointerScreenPoint, 1.0, 90);
+                                           aPointerScreenPoint, 1.0, 90, nullptr);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (!aLongTap) {
     nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE,
-                                             aPointerScreenPoint, 0, 0);
+                                             aPointerScreenPoint, 0, 0, nullptr);
     return rv;
   }
 
   // initiate a long tap
   int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay",
                                    TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC);
   if (!mLongTapTimer) {
     mLongTapTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
     if (NS_FAILED(rv)) {
       SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL,
-                                 aPointerScreenPoint, 0, 0);
+                                 aPointerScreenPoint, 0, 0, nullptr);
       return NS_ERROR_UNEXPECTED;
     }
     // Windows requires recuring events, so we set this to a smaller window
     // than the pref value.
     int timeout = elapse;
     if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) {
       timeout = TOUCH_INJECT_PUMP_TIMER_MSEC;
     }
@@ -1745,21 +1748,23 @@ nsIWidget::SynthesizeNativeTouchTap(nsIn
                                         timeout,
                                         nsITimer::TYPE_REPEATING_SLACK);
   }
 
   // If we already have a long tap pending, cancel it. We only allow one long
   // tap to be active at a time.
   if (mLongTapTouchPoint) {
     SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
-                               mLongTapTouchPoint->mPosition, 0, 0);
+                               mLongTapTouchPoint->mPosition, 0, 0, nullptr);
   }
 
   mLongTapTouchPoint = new LongTapInfo(pointerId, aPointerScreenPoint,
-                                       TimeDuration::FromMilliseconds(elapse));
+                                       TimeDuration::FromMilliseconds(elapse),
+                                       aObserver);
+  notifier.SkipNotification();  // we'll do it in the long-tap callback
   return NS_OK;
 }
 
 // static
 void
 nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure)
 {
   nsIWidget *self = static_cast<nsIWidget *>(aClosure);
@@ -1767,41 +1772,45 @@ nsIWidget::OnLongTapTimerCallback(nsITim
   if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) >
       TimeStamp::Now()) {
 #ifdef XP_WIN
     // Windows needs us to keep pumping feedback to the digitizer, so update
     // the pointer id with the same position.
     self->SynthesizeNativeTouchPoint(self->mLongTapTouchPoint->mPointerId,
                                      TOUCH_CONTACT,
                                      self->mLongTapTouchPoint->mPosition,
-                                     1.0, 90);
+                                     1.0, 90, nullptr);
 #endif
     return;
   }
 
+  AutoObserverNotifier notiifer(self->mLongTapTouchPoint->mObserver, "touchtap");
+
   // finished, remove the touch point
   self->mLongTapTimer->Cancel();
   self->mLongTapTimer = nullptr;
   self->SynthesizeNativeTouchPoint(self->mLongTapTouchPoint->mPointerId,
                                    TOUCH_REMOVE,
                                    self->mLongTapTouchPoint->mPosition,
-                                   0, 0);
+                                   0, 0, nullptr);
   self->mLongTapTouchPoint = nullptr;
 }
 
 nsresult
-nsIWidget::ClearNativeTouchSequence()
+nsIWidget::ClearNativeTouchSequence(nsIObserver* aObserver)
 {
+  AutoObserverNotifier notifier(aObserver, "cleartouch");
+
   if (!mLongTapTimer) {
     return NS_OK;
   }
   mLongTapTimer->Cancel();
   mLongTapTimer = nullptr;
   SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
-                             mLongTapTouchPoint->mPosition, 0, 0);
+                             mLongTapTouchPoint->mPosition, 0, 0, nullptr);
   mLongTapTouchPoint = nullptr;
   return NS_OK;
 }
 
 void
 nsBaseWidget::RegisterPluginWindowForRemoteUpdates()
 {
 #if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -348,42 +348,62 @@ protected:
   {
     return mLastRollup;
   }
 
   virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                             int32_t aNativeKeyCode,
                                             uint32_t aModifierFlags,
                                             const nsAString& aCharacters,
-                                            const nsAString& aUnmodifiedCharacters) override
-  { return NS_ERROR_UNEXPECTED; }
+                                            const nsAString& aUnmodifiedCharacters,
+                                            nsIObserver* aObserver) override
+  {
+    mozilla::widget::AutoObserverNotifier notifier(aObserver, "keyevent");
+    return NS_ERROR_UNEXPECTED;
+  }
 
   virtual nsresult SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                               uint32_t aNativeMessage,
-                                              uint32_t aModifierFlags) override
-  { return NS_ERROR_UNEXPECTED; }
+                                              uint32_t aModifierFlags,
+                                              nsIObserver* aObserver) override
+  {
+    mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
+    return NS_ERROR_UNEXPECTED;
+  }
 
-  virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint) override
-  { return NS_ERROR_UNEXPECTED; }
+  virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+                                             nsIObserver* aObserver) override
+  {
+    mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
+    return NS_ERROR_UNEXPECTED;
+  }
 
   virtual nsresult SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                     uint32_t aNativeMessage,
                                                     double aDeltaX,
                                                     double aDeltaY,
                                                     double aDeltaZ,
                                                     uint32_t aModifierFlags,
-                                                    uint32_t aAdditionalFlags) override
-  { return NS_ERROR_UNEXPECTED; }
+                                                    uint32_t aAdditionalFlags,
+                                                    nsIObserver* aObserver) override
+  {
+    mozilla::widget::AutoObserverNotifier notifier(aObserver, "mousescrollevent");
+    return NS_ERROR_UNEXPECTED;
+  }
 
   virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                               TouchPointerState aPointerState,
                                               nsIntPoint aPointerScreenPoint,
                                               double aPointerPressure,
-                                              uint32_t aPointerOrientation) override
-  { return NS_ERROR_UNEXPECTED; }
+                                              uint32_t aPointerOrientation,
+                                              nsIObserver* aObserver) override
+  {
+    mozilla::widget::AutoObserverNotifier notifier(aObserver, "touchpoint");
+    return NS_ERROR_UNEXPECTED;
+  }
 
   virtual nsresult NotifyIMEInternal(const IMENotification& aIMENotification)
   { return NS_ERROR_NOT_IMPLEMENTED; }
 
 protected:
   // Utility to check if an array of clip rects is equal to our
   // internally stored clip rect array mClipRects.
   bool IsWindowClipRegionEqual(const nsTArray<nsIntRect>& aRects);
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -18,16 +18,17 @@
 #include "nsITheme.h"
 #include "nsITimer.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/Point.h"
+#include "nsIObserver.h"
 #include "Units.h"
 
 // forward declarations
 class   nsFontMetrics;
 class   nsDeviceContext;
 struct  nsFont;
 class   nsIRollupListener;
 class   imgIContainer;
@@ -708,16 +709,41 @@ struct IMENotification
       case NOTIFY_IME_OF_TEXT_CHANGE:
         return mTextChangeData.mCausedByComposition;
       default:
         return false;
     }
   }
 };
 
+struct AutoObserverNotifier {
+  AutoObserverNotifier(nsIObserver* aObserver,
+                       const char* aTopic)
+    : mObserver(aObserver)
+    , mTopic(aTopic)
+  {
+  }
+
+  void SkipNotification()
+  {
+    mObserver = nullptr;
+  }
+
+  ~AutoObserverNotifier()
+  {
+    if (mObserver) {
+      mObserver->Observe(nullptr, mTopic, nullptr);
+    }
+  }
+
+private:
+  nsCOMPtr<nsIObserver> mObserver;
+  const char* mTopic;
+};
+
 } // namespace widget
 } // namespace mozilla
 
 /**
  * The base class for all the widgets. It provides the interface for
  * all basic and necessary functionality.
  */
 class nsIWidget : public nsISupports {
@@ -759,17 +785,17 @@ class nsIWidget : public nsISupports {
     nsIWidget()
       : mLastChild(nullptr)
       , mPrevSibling(nullptr)
       , mOnDestroyCalled(false)
       , mWindowType(eWindowType_child)
       , mZIndex(0)
 
     {
-      ClearNativeTouchSequence();
+      ClearNativeTouchSequence(nullptr);
     }
 
         
     /**
      * Create and initialize a widget. 
      *
      * All the arguments can be null in which case a top level window
      * with size 0 is created. The event callback function has to be
@@ -1847,48 +1873,57 @@ class nsIWidget : public nsISupports {
      * modifiers. Windows ignores COMMAND, NUMERIC_KEY_PAD, HELP and
      * FUNCTION.
      * @param aCharacters characters that the OS would decide to generate
      * from the event. On Windows, this is the charCode passed by
      * WM_CHAR.
      * @param aUnmodifiedCharacters characters that the OS would decide
      * to generate from the event if modifier keys (other than shift)
      * were assumed inactive. Needed on Mac, ignored on Windows.
+     * @param aObserver the observer that will get notified once the events
+     * have been dispatched.
      * @return NS_ERROR_NOT_AVAILABLE to indicate that the keyboard
      * layout is not supported and the event was not fired
      */
     virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                               int32_t aNativeKeyCode,
                                               uint32_t aModifierFlags,
                                               const nsAString& aCharacters,
-                                              const nsAString& aUnmodifiedCharacters) = 0;
+                                              const nsAString& aUnmodifiedCharacters,
+                                              nsIObserver* aObserver) = 0;
 
     /**
      * Utility method intended for testing. Dispatches native mouse events
      * may even move the mouse cursor. On Mac the events are guaranteed to
      * be sent to the window containing this widget, but on Windows they'll go
      * to whatever's topmost on the screen at that position, so for
      * cross-platform testing ensure that your window is at the top of the
      * z-order.
      * @param aPoint screen location of the mouse, in device
      * pixels, with origin at the top left
      * @param aNativeMessage *platform-specific* event type (e.g. on Mac,
      * NSMouseMoved; on Windows, MOUSEEVENTF_MOVE, MOUSEEVENTF_LEFTDOWN etc)
      * @param aModifierFlags *platform-specific* modifier flags (ignored
      * on Windows)
+     * @param aObserver the observer that will get notified once the events
+     * have been dispatched.
      */
     virtual nsresult SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                 uint32_t aNativeMessage,
-                                                uint32_t aModifierFlags) = 0;
+                                                uint32_t aModifierFlags,
+                                                nsIObserver* aObserver) = 0;
 
     /**
      * A shortcut to SynthesizeNativeMouseEvent, abstracting away the native message.
      * aPoint is location in device pixels to which the mouse pointer moves to.
+     * @param aObserver the observer that will get notified once the events
+     * have been dispatched.
      */
-    virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint) = 0;
+    virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+                                               nsIObserver* aObserver) = 0;
 
     /**
      * Utility method intended for testing. Dispatching native mouse scroll
      * events may move the mouse cursor.
      *
      * @param aPoint            Mouse cursor position in screen coordinates.
      *                          In device pixels, the origin at the top left of
      *                          the primary display.
@@ -1900,24 +1935,27 @@ class nsIWidget : public nsISupports {
      *                          message doesn't indicate Y direction scrolling,
      *                          this may be ignored.
      * @param aDeltaZ           The delta value for Z direction.  If the native
      *                          message doesn't indicate Z direction scrolling,
      *                          this may be ignored.
      * @param aModifierFlags    Must be values of Modifiers, or zero.
      * @param aAdditionalFlags  See nsIDOMWidnowUtils' consts and their
      *                          document.
+     * @param aObserver         The observer that will get notified once the
+     *                          events have been dispatched.
      */
     virtual nsresult SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                       uint32_t aNativeMessage,
                                                       double aDeltaX,
                                                       double aDeltaY,
                                                       double aDeltaZ,
                                                       uint32_t aModifierFlags,
-                                                      uint32_t aAdditionalFlags) = 0;
+                                                      uint32_t aAdditionalFlags,
+                                                      nsIObserver* aObserver) = 0;
 
     /*
      * TouchPointerState states for SynthesizeNativeTouchPoint. Match
      * touch states in nsIDOMWindowUtils.idl.
      */
     enum TouchPointerState {
       // The pointer is in a hover state above the digitizer
       TOUCH_HOVER    = 0x01,
@@ -1939,55 +1977,66 @@ class nsIWidget : public nsISupports {
      * sent as individual messages.
      *
      * @param aPointerId The touch point id to create or update.
      * @param aPointerState one or more of the touch states listed above
      * @param aScreenX, aScreenY screen coords of this event
      * @param aPressure 0.0 -> 1.0 float val indicating pressure
      * @param aOrientation 0 -> 359 degree value indicating the
      * orientation of the pointer. Use 90 for normal taps.
+     * @param aObserver The observer that will get notified once the events
+     * have been dispatched.
      */
     virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                                 TouchPointerState aPointerState,
                                                 nsIntPoint aPointerScreenPoint,
                                                 double aPointerPressure,
-                                                uint32_t aPointerOrientation) = 0;
-
-    /*
-     * Cancels all active simulated touch input points and pending long taps.
-     * Native widgets should track existing points such that they can clear the
-     * digitizer state when this call is made.
-     */
-    virtual nsresult ClearNativeTouchSequence();
+                                                uint32_t aPointerOrientation,
+                                                nsIObserver* aObserver) = 0;
 
     /*
      * Helper for simulating a simple tap event with one touch point. When
      * aLongTap is true, simulates a native long tap with a duration equal to
      * ui.click_hold_context_menus.delay. This pref is compatible with the
      * apzc long tap duration. Defaults to 1.5 seconds.
+     * @param aObserver The observer that will get notified once the events
+     * have been dispatched.
      */
     nsresult SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint,
-                                      bool aLongTap);
+                                      bool aLongTap,
+                                      nsIObserver* aObserver);
+
+    /*
+     * Cancels all active simulated touch input points and pending long taps.
+     * Native widgets should track existing points such that they can clear the
+     * digitizer state when this call is made.
+     * @param aObserver The observer that will get notified once the touch
+     * sequence has been cleared.
+     */
+    virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver);
 
 private:
   class LongTapInfo
   {
   public:
     LongTapInfo(int32_t aPointerId, nsIntPoint& aPoint,
-                mozilla::TimeDuration aDuration) :
+                mozilla::TimeDuration aDuration,
+                nsIObserver* aObserver) :
       mPointerId(aPointerId),
       mPosition(aPoint),
       mDuration(aDuration),
+      mObserver(aObserver),
       mStamp(mozilla::TimeStamp::Now())
     {
     }
 
     int32_t mPointerId;
     nsIntPoint mPosition;
     mozilla::TimeDuration mDuration;
+    nsCOMPtr<nsIObserver> mObserver;
     mozilla::TimeStamp mStamp;
   };
 
   static void OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure);
 
   nsAutoPtr<LongTapInfo> mLongTapTouchPoint;
   nsCOMPtr<nsITimer> mLongTapTimer;
   static int32_t sPointerIdCounter;
--- a/widget/tests/TestAppShellSteadyState.cpp
+++ b/widget/tests/TestAppShellSteadyState.cpp
@@ -248,17 +248,17 @@ public:
       } else {
         passed("StableStateRunnable state correct (false)");
       }
 
       int32_t layout = 0x409; // US
       int32_t keyCode = 0x41; // VK_A
       NS_NAMED_LITERAL_STRING(a, "a");
 
-      if (NS_FAILED(utils->SendNativeKeyEvent(layout, keyCode, 0, a, a))) {
+      if (NS_FAILED(utils->SendNativeKeyEvent(layout, keyCode, 0, a, a, nullptr))) {
         fail("Failed to synthesize native event");
       }
 
       return;
     }
 
     KillTimer(nullptr, idEvent);
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -5783,29 +5783,35 @@ LRESULT nsWindow::ProcessKeyDownMessage(
   return result;
 }
 
 nsresult
 nsWindow::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                    int32_t aNativeKeyCode,
                                    uint32_t aModifierFlags,
                                    const nsAString& aCharacters,
-                                   const nsAString& aUnmodifiedCharacters)
-{
+                                   const nsAString& aUnmodifiedCharacters,
+                                   nsIObserver* aObserver)
+{
+  AutoObserverNotifier notifier(aObserver, "keyevent");
+
   KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
   return keyboardLayout->SynthesizeNativeKeyEvent(
            this, aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags,
            aCharacters, aUnmodifiedCharacters);
 }
 
 nsresult
 nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                      uint32_t aNativeMessage,
-                                     uint32_t aModifierFlags)
-{
+                                     uint32_t aModifierFlags,
+                                     nsIObserver* aObserver)
+{
+  AutoObserverNotifier notifier(aObserver, "mouseevent");
+
   ::SetCursorPos(aPoint.x, aPoint.y);
 
   INPUT input;
   memset(&input, 0, sizeof(input));
 
   input.type = INPUT_MOUSE;
   input.mi.dwFlags = aNativeMessage;
   ::SendInput(1, &input, sizeof(INPUT));
@@ -5815,18 +5821,20 @@ nsWindow::SynthesizeNativeMouseEvent(Lay
 
 nsresult
 nsWindow::SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,
                                            uint32_t aNativeMessage,
                                            double aDeltaX,
                                            double aDeltaY,
                                            double aDeltaZ,
                                            uint32_t aModifierFlags,
-                                           uint32_t aAdditionalFlags)
-{
+                                           uint32_t aAdditionalFlags,
+                                           nsIObserver* aObserver)
+{
+  AutoObserverNotifier notifier(aObserver, "mousescrollevent");
   return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
            this, aPoint, aNativeMessage,
            (aNativeMessage == WM_MOUSEWHEEL || aNativeMessage == WM_VSCROLL) ?
              static_cast<int32_t>(aDeltaY) : static_cast<int32_t>(aDeltaX),
            aModifierFlags, aAdditionalFlags);
 }
 
 /**************************************************************
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -157,31 +157,35 @@ public:
                                                          double aOriginalDeltaY,
                                                          double& aOverriddenDeltaX,
                                                          double& aOverriddenDeltaY);
 
   virtual nsresult        SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                                    int32_t aNativeKeyCode,
                                                    uint32_t aModifierFlags,
                                                    const nsAString& aCharacters,
-                                                   const nsAString& aUnmodifiedCharacters);
+                                                   const nsAString& aUnmodifiedCharacters,
+                                                   nsIObserver* aObserver) override;
   virtual nsresult        SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                      uint32_t aNativeMessage,
-                                                     uint32_t aModifierFlags);
+                                                     uint32_t aModifierFlags,
+                                                     nsIObserver* aObserver) override;
 
-  virtual nsresult        SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint)
-                          { return SynthesizeNativeMouseEvent(aPoint, MOUSEEVENTF_MOVE, 0); }
+  virtual nsresult        SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+                                                    nsIObserver* aObserver) override
+                          { return SynthesizeNativeMouseEvent(aPoint, MOUSEEVENTF_MOVE, 0, aObserver); }
 
   virtual nsresult        SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                            uint32_t aNativeMessage,
                                                            double aDeltaX,
                                                            double aDeltaY,
                                                            double aDeltaZ,
                                                            uint32_t aModifierFlags,
-                                                           uint32_t aAdditionalFlags);
+                                                           uint32_t aAdditionalFlags,
+                                                           nsIObserver* aObserver) override;
   NS_IMETHOD_(void)       SetInputContext(const InputContext& aContext,
                                           const InputContextAction& aAction);
   NS_IMETHOD_(InputContext) GetInputContext();
   NS_IMETHOD              GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState);
 #ifdef MOZ_XUL
   virtual void            SetTransparencyMode(nsTransparencyMode aMode);
   virtual nsTransparencyMode GetTransparencyMode();
   virtual void            UpdateOpaqueRegion(const nsIntRegion& aOpaqueRegion);
--- a/widget/windows/nsWindowBase.cpp
+++ b/widget/windows/nsWindowBase.cpp
@@ -105,18 +105,21 @@ nsWindowBase::InjectTouchPoint(uint32_t 
   return true;
 }
 
 nsresult
 nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                          nsIWidget::TouchPointerState aPointerState,
                                          nsIntPoint aPointerScreenPoint,
                                          double aPointerPressure,
-                                         uint32_t aPointerOrientation)
+                                         uint32_t aPointerOrientation,
+                                         nsIObserver* aObserver)
 {
+  AutoObserverNotifier notifier(aObserver, "touchpoint");
+
   if (!InitTouchInjection()) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   bool hover = aPointerState & TOUCH_HOVER;
   bool contact = aPointerState & TOUCH_CONTACT;
   bool remove = aPointerState & TOUCH_REMOVE;
   bool cancel = aPointerState & TOUCH_CANCEL;
@@ -175,26 +178,27 @@ PLDHashOperator
 nsWindowBase::CancelTouchPoints(const unsigned int& aPointerId, nsAutoPtr<PointerInfo>& aInfo, void* aUserArg)
 {
   nsWindowBase* self = static_cast<nsWindowBase*>(aUserArg);
   self->InjectTouchPoint(aInfo.get()->mPointerId, aInfo.get()->mPosition, POINTER_FLAG_CANCELED);
   return (PLDHashOperator)(PL_DHASH_NEXT|PL_DHASH_REMOVE);
 }
 
 nsresult
-nsWindowBase::ClearNativeTouchSequence()
+nsWindowBase::ClearNativeTouchSequence(nsIObserver* aObserver)
 {
+  AutoObserverNotifier notifier(aObserver, "cleartouch");
   if (!sTouchInjectInitialized) {
     return NS_OK;
   }
 
   // cancel all input points
   mActivePointers.Enumerate(CancelTouchPoints, (void*)this);
 
-  nsBaseWidget::ClearNativeTouchSequence();
+  nsBaseWidget::ClearNativeTouchSequence(nullptr);
 
   return NS_OK;
 }
 
 bool
 nsWindowBase::HandleAppCommandMsg(const MSG& aAppCommandMsg,
                                   LRESULT *aRetValue)
 {
--- a/widget/windows/nsWindowBase.h
+++ b/widget/windows/nsWindowBase.h
@@ -87,18 +87,19 @@ public:
 
   /*
    * Touch input injection apis
    */
   virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                               TouchPointerState aPointerState,
                                               nsIntPoint aPointerScreenPoint,
                                               double aPointerPressure,
-                                              uint32_t aPointerOrientation);
-  virtual nsresult ClearNativeTouchSequence();
+                                              uint32_t aPointerOrientation,
+                                              nsIObserver* aObserver) override;
+  virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override;
 
   /*
    * WM_APPCOMMAND common handler.
    * Sends events via NativeKey::HandleAppCommandMessage().
    */
   virtual bool HandleAppCommandMsg(const MSG& aAppCommandMsg,
                                    LRESULT *aRetValue);
 
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -578,31 +578,35 @@ MetroWidget::SetCursor(nsCursor aCursor)
   return NS_OK;
 }
 
 nsresult
 MetroWidget::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                       int32_t aNativeKeyCode,
                                       uint32_t aModifierFlags,
                                       const nsAString& aCharacters,
-                                      const nsAString& aUnmodifiedCharacters)
+                                      const nsAString& aUnmodifiedCharacters,
+                                      nsIObserver* aObserver)
 {
+  AutoObserverNotifier notifier(aObserver, "keyevent");
   KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
   return keyboardLayout->SynthesizeNativeKeyEvent(
            this, aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags,
            aCharacters, aUnmodifiedCharacters);
 }
 
 nsresult
 MetroWidget::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                         uint32_t aNativeMessage,
-                                        uint32_t aModifierFlags)
+                                        uint32_t aModifierFlags,
+                                        nsIObserver* aObserver)
 {
   WinUtils::Log("ENTERED SynthesizeNativeMouseEvent");
 
+  AutoObserverNotifier notifier(aObserver, "mouseevent");
   INPUT inputs[2];
   memset(inputs, 0, 2*sizeof(INPUT));
   inputs[0].type = inputs[1].type = INPUT_MOUSE;
   inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
   // Inexplicably, the x and y coordinates that we want to move the mouse to
   // are specified as values in the range (0, 65535). (0,0) represents the
   // top left of the primary monitor and (65535, 65535) represents the
   // bottom right of the primary monitor.
@@ -617,18 +621,20 @@ MetroWidget::SynthesizeNativeMouseEvent(
 
 nsresult
 MetroWidget::SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,
                                               uint32_t aNativeMessage,
                                               double aDeltaX,
                                               double aDeltaY,
                                               double aDeltaZ,
                                               uint32_t aModifierFlags,
-                                              uint32_t aAdditionalFlags)
+                                              uint32_t aAdditionalFlags
+                                              nsIObserver* aObserver)
 {
+  AutoObserverNotifier notifier(aObserver, "mousescrollevent");
   return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
            this, aPoint, aNativeMessage,
            (aNativeMessage == WM_MOUSEWHEEL || aNativeMessage == WM_VSCROLL) ?
              static_cast<int32_t>(aDeltaY) : static_cast<int32_t>(aDeltaX),
            aModifierFlags, aAdditionalFlags);
 }
 
 static void
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -122,27 +122,30 @@ public:
   NS_IMETHOD    SetTitle(const nsAString& aTitle);
   NS_IMETHOD    CaptureRollupEvents(nsIRollupListener * aListener,
                                     bool aDoCapture);
   NS_IMETHOD    ReparentNativeWidget(nsIWidget* aNewParent);
   virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                             int32_t aNativeKeyCode,
                                             uint32_t aModifierFlags,
                                             const nsAString& aCharacters,
-                                            const nsAString& aUnmodifiedCharacters);
+                                            const nsAString& aUnmodifiedCharacters,
+                                            nsIObserver* aObserver);
   virtual nsresult SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                               uint32_t aNativeMessage,
-                                              uint32_t aModifierFlags);
+                                              uint32_t aModifierFlags,
+                                              nsIObserver* aObserver);
   virtual nsresult SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
                                                     uint32_t aNativeMessage,
                                                     double aDeltaX,
                                                     double aDeltaY,
                                                     double aDeltaZ,
                                                     uint32_t aModifierFlags,
-                                                    uint32_t aAdditionalFlags);
+                                                    uint32_t aAdditionalFlags,
+                                                    nsIObserver* aObserver);
   virtual bool  HasPendingInputEvent();
   virtual double GetDefaultScaleInternal();
   float         GetDPI();
   mozilla::LayoutDeviceIntPoint CSSIntPointToLayoutDeviceIntPoint(const mozilla::CSSIntPoint &aCSSPoint);
   void          ChangedDPI();
   virtual uint32_t GetMaxTouchPoints() const override;
   virtual bool  IsVisible() const;
   virtual bool  IsEnabled() const;