Allow synthetic input events to be dispatched asynchronously. (bug 1146243, r=kats)
authorDavid Anderson <danderson@mozilla.com>
Tue, 24 Mar 2015 15:00:52 -0700
changeset 265760 884d44a41ac74419b85b1500656de50928f7d482
parent 265759 361db8ededbc4b1931a268f824dcb66ec4382a7b
child 265761 3eb419228c1f4d9206190262d86e07afd23e3c81
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1146243
milestone39.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
Allow synthetic input events to be dispatched asynchronously. (bug 1146243, r=kats)
dom/base/nsDOMWindowUtils.cpp
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
gfx/thebes/gfxPrefs.h
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/TextEventDispatcher.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -807,32 +807,36 @@ nsDOMWindowUtils::SendMouseEventCommon(c
 
   nsPresContext* presContext = GetPresContext();
   if (!presContext)
     return NS_ERROR_FAILURE;
 
   event.refPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
   event.ignoreRootScrollFrame = aIgnoreRootScrollFrame;
 
-  nsEventStatus status;
+  nsEventStatus status = nsEventStatus_eIgnore;
   if (aToWindow) {
     nsCOMPtr<nsIPresShell> presShell;
     nsView* view = GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
     if (!presShell || !view) {
       return NS_ERROR_FAILURE;
     }
-    status = nsEventStatus_eIgnore;
     return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
   }
-  nsresult rv = widget->DispatchEvent(&event, status);
+  if (gfxPrefs::TestEventsAsyncEnabled()) {
+    status = widget->DispatchInputEvent(&event);
+  } else {
+    nsresult rv = widget->DispatchEvent(&event, status);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
   if (aPreventDefault) {
     *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
   }
 
-  return rv;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType,
                                          float aX,
                                          float aY,
                                          int32_t aButton,
                                          int32_t aClickCount,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -487,17 +487,19 @@ parent:
                  uint64_t layersId);
 
     /**
      * Sent by the child to the parent to inform it that an update to the
      * dimensions has been requested, likely through win.moveTo or resizeTo
      */
     async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
 
-    prio(high) sync SynthesizedMouseWheelEvent(WidgetWheelEvent event);
+    prio(high) sync DispatchWheelEvent(WidgetWheelEvent event);
+    prio(high) sync DispatchMouseEvent(WidgetMouseEvent event);
+    prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
 
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
      * |Show()| and |Move()| take IntSizes rather than Rects because
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1214,31 +1214,63 @@ bool TabParent::SendMouseWheelEvent(Widg
 
   ScrollableLayerGuid guid;
   uint64_t blockId;
   ApzAwareEventRoutingToChild(&guid, &blockId);
   event.refPoint += GetChildProcessOffset();
   return PBrowserParent::SendMouseWheelEvent(event, guid, blockId);
 }
 
-bool TabParent::RecvSynthesizedMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent)
+bool TabParent::RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return true;
   }
 
   WidgetWheelEvent localEvent(aEvent);
   localEvent.widget = widget;
   localEvent.refPoint -= GetChildProcessOffset();
 
   widget->DispatchAPZAwareEvent(&localEvent);
   return true;
 }
 
+bool
+TabParent::RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    return true;
+  }
+
+  WidgetMouseEvent localEvent(aEvent);
+  localEvent.widget = widget;
+  localEvent.refPoint -= GetChildProcessOffset();
+
+  widget->DispatchInputEvent(&localEvent);
+  return true;
+}
+
+bool
+TabParent::RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    return true;
+  }
+
+  WidgetKeyboardEvent localEvent(aEvent);
+  localEvent.widget = widget;
+  localEvent.refPoint -= GetChildProcessOffset();
+
+  widget->DispatchInputEvent(&localEvent);
+  return true;
+}
+
 static void
 DoCommandCallback(mozilla::Command aCommand, void* aData)
 {
   static_cast<InfallibleTArray<mozilla::CommandInt>*>(aData)->AppendElement(aCommand);
 }
 
 bool
 TabParent::RecvRequestNativeKeyBindings(const WidgetKeyboardEvent& aEvent,
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -206,17 +206,19 @@ public:
                                            const ZoomConstraints& aConstraints) override;
     virtual bool RecvContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
                                                const uint64_t& aInputBlockId,
                                                const bool& aPreventDefault) override;
     virtual bool RecvSetTargetAPZC(const uint64_t& aInputBlockId,
                                    nsTArray<ScrollableLayerGuid>&& aTargets) override;
     virtual bool RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId,
                                              nsTArray<TouchBehaviorFlags>&& aTargets) override;
-    virtual bool RecvSynthesizedMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override;
+    virtual bool RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override;
+    virtual bool RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) override;
+    virtual bool RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
 
     virtual PColorPickerParent*
     AllocPColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) override;
     virtual bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override;
 
     void LoadURL(nsIURI* aURI);
     // XXX/cjones: it's not clear what we gain by hiding these
     // message-sending functions under a layer of indirection and
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -334,19 +334,22 @@ private:
   // This and code dependent on it should be removed once containerless scrolling looks stable.
   DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers",   LayoutUseContainersForRootFrames, bool, true);
 
   // This affects whether events will be routed through APZ or not.
   DECL_GFX_PREF(Once, "mousewheel.system_scroll_override_on_root_content.enabled",
                                                                MouseWheelHasScrollDeltaOverride, bool, false);
   DECL_GFX_PREF(Live, "mousewheel.transaction.ignoremovedelay",MouseWheelIgnoreMoveDelayMs, int32_t, (int32_t)100);
   DECL_GFX_PREF(Live, "mousewheel.transaction.timeout",        MouseWheelTransactionTimeoutMs, int32_t, (int32_t)1500);
+
+  DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
+
+  DECL_GFX_PREF(Live, "test.events.async.enabled",             TestEventsAsyncEnabled, bool, false);
   DECL_GFX_PREF(Live, "test.mousescroll",                      MouseScrollTestingEnabled, bool, false);
 
-  DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
   DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay",     UiClickHoldContextMenusDelay, int32_t, 500);
   DECL_GFX_PREF(Once, "webgl.angle.force-d3d11",               WebGLANGLEForceD3D11, bool, false);
   DECL_GFX_PREF(Once, "webgl.angle.try-d3d11",                 WebGLANGLETryD3D11, bool, false);
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
 
   // WARNING:
   // Please make sure that you've added your new preference to the list above in alphabetical order.
   // Please do not just append it to the end of the list.
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/Hal.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
+#include "mozilla/unused.h"
 #include "PuppetWidget.h"
 #include "nsIWidgetListener.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
@@ -322,33 +323,56 @@ PuppetWidget::DispatchEvent(WidgetGUIEve
 
   if (mAttachedWidgetListener) {
     aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
   }
 
   return NS_OK;
 }
 
+nsEventStatus
+PuppetWidget::DispatchInputEvent(WidgetInputEvent* aEvent)
+{
+  if (!mTabChild) {
+    return nsEventStatus_eIgnore;
+  }
+
+  switch (aEvent->mClass) {
+    case eMouseEventClass:
+      unused <<
+        mTabChild->SendDispatchMouseEvent(*aEvent->AsMouseEvent());
+      break;
+    case eKeyboardEventClass:
+      unused <<
+        mTabChild->SendDispatchKeyboardEvent(*aEvent->AsKeyboardEvent());
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unsupported event type");
+  }
+
+  return nsEventStatus_eIgnore;
+}
 
 nsEventStatus
 PuppetWidget::DispatchAPZAwareEvent(WidgetInputEvent* aEvent)
 {
   if (!gfxPrefs::AsyncPanZoomEnabled()) {
     nsEventStatus status = nsEventStatus_eIgnore;
     DispatchEvent(aEvent, status);
     return status;
   }
 
   if (!mTabChild) {
     return nsEventStatus_eIgnore;
   }
 
   switch (aEvent->mClass) {
     case eWheelEventClass:
-      mTabChild->SendSynthesizedMouseWheelEvent(*aEvent->AsWheelEvent());
+      unused <<
+        mTabChild->SendDispatchWheelEvent(*aEvent->AsWheelEvent());
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("unsupported event type");
   }
 
   return nsEventStatus_eIgnore;
 }
 
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -124,16 +124,17 @@ public:
   // PuppetWidgets are always at <0, 0>.
   virtual mozilla::LayoutDeviceIntPoint WidgetToScreenOffset() override
   { return mozilla::LayoutDeviceIntPoint(0, 0); }
 
   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,
                                  bool aDoCapture) override
   { return NS_ERROR_UNEXPECTED; }
 
   NS_IMETHOD_(bool)
   ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
                           const mozilla::WidgetKeyboardEvent& aEvent,
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -119,17 +119,26 @@ TextEventDispatcher::InitEvent(WidgetGUI
 nsresult
 TextEventDispatcher::DispatchEvent(nsIWidget* aWidget,
                                    WidgetGUIEvent& aEvent,
                                    nsEventStatus& aStatus)
 {
   nsRefPtr<TextEventDispatcher> kungFuDeathGrip(this);
   nsCOMPtr<nsIWidget> widget(aWidget);
   mDispatchingEvent++;
-  nsresult rv = widget->DispatchEvent(&aEvent, aStatus);
+
+  nsresult rv = NS_OK;
+  if (aEvent.AsInputEvent() &&
+      (!aEvent.mFlags.mIsSynthesizedForTests || gfxPrefs::TestEventsAsyncEnabled()))
+  {
+    aStatus = widget->DispatchInputEvent(aEvent.AsInputEvent());
+  } else {
+    rv = widget->DispatchEvent(&aEvent, aStatus);
+  }
+
   mDispatchingEvent--;
   return rv;
 }
 
 nsresult
 TextEventDispatcher::StartComposition(nsEventStatus& aStatus)
 {
   aStatus = nsEventStatus_eIgnore;
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -228,17 +228,17 @@ public:
               bool             aForceUseIWidgetParent = false) override;
   NS_IMETHOD              AttachViewToTopLevel(bool aUseAttachedEvents) override;
   virtual nsIWidgetListener* GetAttachedWidgetListener() override;
   virtual void               SetAttachedWidgetListener(nsIWidgetListener* aListener) override;
   NS_IMETHOD_(TextEventDispatcher*) GetTextEventDispatcher() override final;
 
   // Helper function for dispatching events which are not processed by APZ,
   // but need to be transformed by APZ.
-  nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent);
+  nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) override;
 
   // Dispatch an event that must be first be routed through APZ.
   nsEventStatus DispatchAPZAwareEvent(mozilla::WidgetInputEvent* aEvent) override;
 
   void NotifyWindowDestroyed();
   void NotifySizeMoveDone();
   void NotifyWindowMoved(int32_t aX, int32_t aY);
 
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1700,16 +1700,23 @@ class nsIWidget : public nsISupports {
     /**
      * Dispatches an event that must be handled by APZ first, when APZ is
      * enabled. If invoked in the child process, it is forwarded to the
      * parent process synchronously.
      */
     virtual nsEventStatus DispatchAPZAwareEvent(mozilla::WidgetInputEvent* aEvent) = 0;
 
     /**
+     * Dispatches an event that must be transformed by APZ first, but is not
+     * actually handled by APZ. If invoked in the child process, it is
+     * forwarded to the parent process synchronously.
+     */
+    virtual nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) = 0;
+
+    /**
      * Enables the dropping of files to a widget (XXX this is temporary)
      *
      */
     NS_IMETHOD EnableDragDrop(bool aEnable) = 0;
    
     /**
      * Enables/Disables system mouse capture.
      * @param aCapture true enables mouse capture, false disables mouse capture