Bug 943537 - Handle modifiers on tap events in RemoteContentController and ChromeProcessController. r=kats,mbrubeck
authorBotond Ballo <botond@mozilla.com>
Mon, 02 Mar 2015 16:41:02 -0500
changeset 246899 f9b76a07fcc6b65b950e19bde199fa199a00e3f6
parent 246898 caf0d391ceaefe2f3cd3d358fe42951f5e5dac00
child 246900 97d3ba6cfed8b3c412d084fbadaf9e34539df42a
push id884
push userdburns@mozilla.com
push dateTue, 03 Mar 2015 15:29:12 +0000
reviewerskats, mbrubeck
bugs943537
milestone39.0a1
Bug 943537 - Handle modifiers on tap events in RemoteContentController and ChromeProcessController. r=kats,mbrubeck
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
gfx/layers/apz/util/APZCCallbackHelper.cpp
gfx/layers/apz/util/APZCCallbackHelper.h
gfx/layers/apz/util/APZEventState.cpp
gfx/layers/apz/util/APZEventState.h
gfx/layers/apz/util/ChromeProcessController.cpp
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -47,16 +47,17 @@ using struct nsRect from "nsRect.h";
 using class mozilla::WidgetSelectionEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h";
 using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::ScreenOrientation from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::CSSPoint from "Units.h";
 using mozilla::CSSToScreenScale from "Units.h";
 using mozilla::CommandInt from "mozilla/EventForwards.h";
+using mozilla::Modifiers from "mozilla/EventForwards.h";
 using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
 using mozilla::WritingMode from "WritingModes.h";
 
 namespace mozilla {
 namespace dom {
 
 struct NativeKeyBinding
 {
@@ -507,20 +508,20 @@ child:
                      nsIntPoint chromeDisp) compress;
 
     UpdateFrame(FrameMetrics frame);
 
     // The following methods correspond to functions on the GeckoContentController
     // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
     // in that file for these functions.
     AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
-    HandleDoubleTap(CSSPoint point, ScrollableLayerGuid aGuid);
-    HandleSingleTap(CSSPoint point, ScrollableLayerGuid aGuid);
-    HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
-    HandleLongTapUp(CSSPoint point, ScrollableLayerGuid aGuid);
+    HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
+    HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
+    HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+    HandleLongTapUp(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid);
     NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
 
 
     /**
      * Sending an activate message moves focus to the child.
      */
     Activate();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2041,63 +2041,65 @@ bool
 TabChild::RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
                                       const uint32_t& aScrollGeneration)
 {
   APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
   return true;
 }
 
 bool
-TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
 {
     TABC_LOG("Handling double tap at %s with %p %p\n",
       Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
 
     if (!mGlobal || !mTabChildGlobal) {
         return true;
     }
 
+    // Note: there is nothing to do with the modifiers here, as we are not
+    // synthesizing any sort of mouse event.
     CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid,
         GetPresShellResolution());
     nsString data;
     data.AppendLiteral("{ \"x\" : ");
     data.AppendFloat(point.x);
     data.AppendLiteral(", \"y\" : ");
     data.AppendFloat(point.y);
     data.AppendLiteral(" }");
 
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Gesture:DoubleTap"), data);
 
     return true;
 }
 
 bool
-TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
 {
   if (mGlobal && mTabChildGlobal) {
-    mAPZEventState->ProcessSingleTap(aPoint, aGuid, GetPresShellResolution());
+    mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid, GetPresShellResolution());
   }
   return true;
 }
 
 bool
-TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
+TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
 {
   if (mGlobal && mTabChildGlobal) {
-    mAPZEventState->ProcessLongTap(GetDOMWindowUtils(), aPoint, aGuid,
+    mAPZEventState->ProcessLongTap(GetDOMWindowUtils(), aPoint, aModifiers, aGuid,
         aInputBlockId, GetPresShellResolution());
   }
   return true;
 }
 
 bool
-TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
 {
   if (mGlobal && mTabChildGlobal) {
-    mAPZEventState->ProcessLongTapUp(aPoint, aGuid, GetPresShellResolution());
+    mAPZEventState->ProcessLongTapUp(aPoint, aModifiers, aGuid, GetPresShellResolution());
   }
   return true;
 }
 
 bool
 TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
                                    const APZStateChange& aChange,
                                    const int& aArg)
@@ -2263,20 +2265,20 @@ TabChild::UpdateTapState(const WidgetTou
   case NS_TOUCH_MOVE:
     if (std::abs(currentPoint.x - mGestureDownPoint.x) > sDragThreshold.width ||
         std::abs(currentPoint.y - mGestureDownPoint.y) > sDragThreshold.height) {
       CancelTapTracking();
     }
     return;
 
   case NS_TOUCH_END:
-    if (!TouchManager::gPreventMouseEvents) {
-      APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, currentPoint, mWidget);
-      APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, currentPoint, mWidget);
-      APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, currentPoint, mWidget);
+    if (!nsIPresShell::gPreventMouseEvents) {
+      APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, currentPoint, 0, mWidget);
+      APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, currentPoint, 0, mWidget);
+      APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, currentPoint, 0, mWidget);
     }
     // fall through
   case NS_TOUCH_CANCEL:
     CancelTapTracking();
     return;
 
   default:
     NS_WARNING("Unknown touch event type");
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -317,23 +317,27 @@ public:
     virtual bool RecvUpdateDimensions(const nsIntRect& rect,
                                       const nsIntSize& size,
                                       const ScreenOrientation& orientation,
                                       const nsIntPoint& chromeDisp) MOZ_OVERRIDE;
     virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
     virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
                                              const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
     virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
+                                     const Modifiers& aModifiers,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
+                                     const Modifiers& aModifiers,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
+                                   const Modifiers& aModifiers,
                                    const mozilla::layers::ScrollableLayerGuid& aGuid,
                                    const uint64_t& aInputBlockId) MOZ_OVERRIDE;
     virtual bool RecvHandleLongTapUp(const CSSPoint& aPoint,
+                                     const Modifiers& aModifiers,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
                                           const APZStateChange& aChange,
                                           const int& aArg) MOZ_OVERRIDE;
     virtual bool RecvActivate() MOZ_OVERRIDE;
     virtual bool RecvDeactivate() MOZ_OVERRIDE;
     virtual bool RecvMouseEvent(const nsString& aType,
                                 const float&    aX,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -994,46 +994,45 @@ TabParent::AcknowledgeScrollUpdate(const
   }
 }
 
 void TabParent::HandleDoubleTap(const CSSPoint& aPoint,
                                 Modifiers aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
   if (!mIsDestroyed) {
-    unused << SendHandleDoubleTap(aPoint, aGuid);
+    unused << SendHandleDoubleTap(aPoint, aModifiers, aGuid);
   }
 }
 
 void TabParent::HandleSingleTap(const CSSPoint& aPoint,
                                 Modifiers aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
-  // TODO Send the modifier data to TabChild for use in mouse events.
   if (!mIsDestroyed) {
-    unused << SendHandleSingleTap(aPoint, aGuid);
+    unused << SendHandleSingleTap(aPoint, aModifiers, aGuid);
   }
 }
 
 void TabParent::HandleLongTap(const CSSPoint& aPoint,
                               Modifiers aModifiers,
                               const ScrollableLayerGuid &aGuid,
                               uint64_t aInputBlockId)
 {
   if (!mIsDestroyed) {
-    unused << SendHandleLongTap(aPoint, aGuid, aInputBlockId);
+    unused << SendHandleLongTap(aPoint, aModifiers, aGuid, aInputBlockId);
   }
 }
 
 void TabParent::HandleLongTapUp(const CSSPoint& aPoint,
                                 Modifiers aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
   if (!mIsDestroyed) {
-    unused << SendHandleLongTapUp(aPoint, aGuid);
+    unused << SendHandleLongTapUp(aPoint, aModifiers, aGuid);
   }
 }
 
 void TabParent::NotifyAPZStateChange(ViewID aViewId,
                                      APZStateChange aChange,
                                      int aArg)
 {
   if (!mIsDestroyed) {
@@ -1234,50 +1233,50 @@ TabParent::GetLayoutDeviceToCSSScale()
     : 0.0f);
 }
 
 CSSPoint TabParent::AdjustTapToChildWidget(const CSSPoint& aPoint)
 {
   return aPoint + (LayoutDevicePoint(mChildProcessOffsetAtTouchStart) * GetLayoutDeviceToCSSScale());
 }
 
-bool TabParent::SendHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
-  return PBrowserParent::SendHandleSingleTap(AdjustTapToChildWidget(aPoint), aGuid);
+  return PBrowserParent::SendHandleSingleTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
 }
 
-bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
+bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
 {
   if (mIsDestroyed) {
     return false;
   }
 
-  return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aGuid, aInputBlockId);
+  return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid, aInputBlockId);
 }
 
-bool TabParent::SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
-  return PBrowserParent::SendHandleLongTapUp(AdjustTapToChildWidget(aPoint), aGuid);
+  return PBrowserParent::SendHandleLongTapUp(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
 }
 
-bool TabParent::SendHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
+bool TabParent::SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
 {
   if (mIsDestroyed) {
     return false;
   }
 
-  return PBrowserParent::SendHandleDoubleTap(AdjustTapToChildWidget(aPoint), aGuid);
+  return PBrowserParent::SendHandleDoubleTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
 }
 
 bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -282,20 +282,20 @@ public:
                         int32_t aModifiers, bool aIgnoreRootScrollFrame);
     void SendKeyEvent(const nsAString& aType, int32_t aKeyCode,
                       int32_t aCharCode, int32_t aModifiers,
                       bool aPreventDefault);
     bool SendRealMouseEvent(mozilla::WidgetMouseEvent& event);
     bool SendMouseWheelEvent(mozilla::WidgetWheelEvent& event);
     bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event);
     bool SendRealTouchEvent(WidgetTouchEvent& event);
-    bool SendHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
-    bool SendHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId);
-    bool SendHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
-    bool SendHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid);
+    bool SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
+    bool SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId);
+    bool SendHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
+    bool SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
 
     virtual PDocumentRendererParent*
     AllocPDocumentRendererParent(const nsRect& documentRect,
                                  const gfx::Matrix& transform,
                                  const nsString& bgcolor,
                                  const uint32_t& renderFlags,
                                  const bool& flushLayout,
                                  const nsIntSize& renderSize) MOZ_OVERRIDE;
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -394,31 +394,33 @@ APZCCallbackHelper::DispatchWidgetEvent(
                     nsEventStatus_eConsumeNoDefault);
   return status;
 }
 
 nsEventStatus
 APZCCallbackHelper::DispatchSynthesizedMouseEvent(uint32_t aMsg,
                                                   uint64_t aTime,
                                                   const LayoutDevicePoint& aRefPoint,
+                                                  Modifiers aModifiers,
                                                   nsIWidget* aWidget)
 {
   MOZ_ASSERT(aMsg == NS_MOUSE_MOVE || aMsg == NS_MOUSE_BUTTON_DOWN ||
              aMsg == NS_MOUSE_BUTTON_UP || aMsg == NS_MOUSE_MOZLONGTAP);
 
   WidgetMouseEvent event(true, aMsg, nullptr,
                          WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
   event.refPoint = LayoutDeviceIntPoint(aRefPoint.x, aRefPoint.y);
   event.time = aTime;
   event.button = WidgetMouseEvent::eLeftButton;
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   event.ignoreRootScrollFrame = true;
   if (aMsg != NS_MOUSE_MOVE) {
     event.clickCount = 1;
   }
+  event.modifiers = aModifiers;
   event.widget = aWidget;
 
   return DispatchWidgetEvent(event);
 }
 
 bool
 APZCCallbackHelper::DispatchMouseEvent(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
                                        const nsString& aType,
@@ -435,27 +437,28 @@ APZCCallbackHelper::DispatchMouseEvent(c
   aUtils->SendMouseEvent(aType, aPoint.x, aPoint.y, aButton, aClickCount, aModifiers,
                          aIgnoreRootScrollFrame, 0, aInputSourceArg, false, 4, &defaultPrevented);
   return defaultPrevented;
 }
 
 
 void
 APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint,
+                                       Modifiers aModifiers,
                                        nsIWidget* aWidget)
 {
   if (aWidget->Destroyed()) {
     return;
   }
   APZCCH_LOG("Dispatching single-tap component events to %s\n",
     Stringify(aPoint).c_str());
   int time = 0;
-  DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint, aWidget);
-  DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint, aWidget);
-  DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, aWidget);
+  DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint, aModifiers, aWidget);
+  DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint, aModifiers, aWidget);
+  DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, aModifiers, aWidget);
 }
 
 static nsIScrollableFrame*
 GetScrollableAncestorFrame(nsIFrame* aTarget)
 {
   if (!aTarget) {
     return nullptr;
   }
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -118,32 +118,34 @@ public:
      * intercept the event. */
     static nsEventStatus DispatchWidgetEvent(WidgetGUIEvent& aEvent);
 
     /* Synthesize a mouse event with the given parameters, and dispatch it
      * via the given widget. */
     static nsEventStatus DispatchSynthesizedMouseEvent(uint32_t aMsg,
                                                        uint64_t aTime,
                                                        const LayoutDevicePoint& aRefPoint,
+                                                       Modifiers aModifiers,
                                                        nsIWidget* aWidget);
 
     /* Dispatch a mouse event with the given parameters.
      * Return whether or not any listeners have called preventDefault on the event. */
     static bool DispatchMouseEvent(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
                                    const nsString& aType,
                                    const CSSPoint& aPoint,
                                    int32_t aButton,
                                    int32_t aClickCount,
                                    int32_t aModifiers,
                                    bool aIgnoreRootScrollFrame,
                                    unsigned short aInputSourceArg);
 
     /* Fire a single-tap event at the given point. The event is dispatched
      * via the given widget. */
     static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
+                                   Modifiers aModifiers,
                                    nsIWidget* aWidget);
 
     /* Perform hit-testing on the touch points of |aEvent| to determine
      * which scrollable frames they target. If any of these frames don't have
      * a displayport, set one. Finally, invoke the provided callback with
      * the guids of the target frames. If any displayports needed to be set,
      * the callback is invoked after the next refresh, otherwise it's invoked
      * right away. */
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -5,24 +5,76 @@
 
 #include "APZEventState.h"
 
 #include "ActiveElementManager.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/Preferences.h"
 #include "nsCOMPtr.h"
 #include "nsDocShell.h"
+#include "nsIDOMWindowUtils.h"
 #include "nsITimer.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsIWidget.h"
 #include "TouchManager.h"
 
 #define APZES_LOG(...)
 // #define APZES_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__)
 
+// Static helper functions
+namespace {
+
+int32_t
+WidgetModifiersToDOMModifiers(mozilla::Modifiers aModifiers)
+{
+  int32_t result = 0;
+  if (aModifiers & mozilla::MODIFIER_SHIFT) {
+    result |= nsIDOMWindowUtils::MODIFIER_SHIFT;
+  }
+  if (aModifiers & mozilla::MODIFIER_CONTROL) {
+    result |= nsIDOMWindowUtils::MODIFIER_CONTROL;
+  }
+  if (aModifiers & mozilla::MODIFIER_ALT) {
+    result |= nsIDOMWindowUtils::MODIFIER_ALT;
+  }
+  if (aModifiers & mozilla::MODIFIER_META) {
+    result |= nsIDOMWindowUtils::MODIFIER_META;
+  }
+  if (aModifiers & mozilla::MODIFIER_ALTGRAPH) {
+    result |= nsIDOMWindowUtils::MODIFIER_ALTGRAPH;
+  }
+  if (aModifiers & mozilla::MODIFIER_CAPSLOCK) {
+    result |= nsIDOMWindowUtils::MODIFIER_CAPSLOCK;
+  }
+  if (aModifiers & mozilla::MODIFIER_FN) {
+    result |= nsIDOMWindowUtils::MODIFIER_FN;
+  }
+  if (aModifiers & mozilla::MODIFIER_FNLOCK) {
+    result |= nsIDOMWindowUtils::MODIFIER_FNLOCK;
+  }
+  if (aModifiers & mozilla::MODIFIER_NUMLOCK) {
+    result |= nsIDOMWindowUtils::MODIFIER_NUMLOCK;
+  }
+  if (aModifiers & mozilla::MODIFIER_SCROLLLOCK) {
+    result |= nsIDOMWindowUtils::MODIFIER_SCROLLLOCK;
+  }
+  if (aModifiers & mozilla::MODIFIER_SYMBOL) {
+    result |= nsIDOMWindowUtils::MODIFIER_SYMBOL;
+  }
+  if (aModifiers & mozilla::MODIFIER_SYMBOLLOCK) {
+    result |= nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK;
+  }
+  if (aModifiers & mozilla::MODIFIER_OS) {
+    result |= nsIDOMWindowUtils::MODIFIER_OS;
+  }
+  return result;
+}
+
+}
+
 namespace mozilla {
 namespace layers {
 
 static int32_t sActiveDurationMs = 10;
 static bool sActiveDurationMsSet = false;
 
 APZEventState::APZEventState(nsIWidget* aWidget,
                              const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback)
@@ -52,51 +104,55 @@ APZEventState::~APZEventState()
 
 class DelayedFireSingleTapEvent MOZ_FINAL : public nsITimerCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   DelayedFireSingleTapEvent(nsWeakPtr aWidget,
                             LayoutDevicePoint& aPoint,
+                            Modifiers aModifiers,
                             nsITimer* aTimer)
     : mWidget(aWidget)
     , mPoint(aPoint)
+    , mModifiers(aModifiers)
     // Hold the reference count until we are called back.
     , mTimer(aTimer)
   {
   }
 
   NS_IMETHODIMP Notify(nsITimer*) MOZ_OVERRIDE
   {
     if (nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget)) {
-      APZCCallbackHelper::FireSingleTapEvent(mPoint, widget);
+      APZCCallbackHelper::FireSingleTapEvent(mPoint, mModifiers, widget);
     }
     mTimer = nullptr;
     return NS_OK;
   }
 
   void ClearTimer() {
     mTimer = nullptr;
   }
 
 private:
   ~DelayedFireSingleTapEvent()
   {
   }
 
   nsWeakPtr mWidget;
   LayoutDevicePoint mPoint;
+  Modifiers mModifiers;
   nsCOMPtr<nsITimer> mTimer;
 };
 
 NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
 
 void
 APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
+                                Modifiers aModifiers,
                                 const ScrollableLayerGuid& aGuid,
                                 float aPresShellResolution)
 {
   APZES_LOG("Handling single tap at %s on %s with %d\n",
     Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mTouchEndCancelled);
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
@@ -109,81 +165,87 @@ APZEventState::ProcessSingleTap(const CS
 
   LayoutDevicePoint currentPoint =
       APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution)
     * widget->GetDefaultScale();;
   if (!mActiveElementManager->ActiveElementUsesStyle()) {
     // If the active element isn't visually affected by the :active style, we
     // have no need to wait the extra sActiveDurationMs to make the activation
     // visually obvious to the user.
-    APZCCallbackHelper::FireSingleTapEvent(currentPoint, widget);
+    APZCCallbackHelper::FireSingleTapEvent(currentPoint, aModifiers, widget);
     return;
   }
 
   APZES_LOG("Active element uses style, scheduling timer for click event\n");
   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
   nsRefPtr<DelayedFireSingleTapEvent> callback =
-    new DelayedFireSingleTapEvent(mWidget, currentPoint, timer);
+    new DelayedFireSingleTapEvent(mWidget, currentPoint, aModifiers, timer);
   nsresult rv = timer->InitWithCallback(callback,
                                         sActiveDurationMs,
                                         nsITimer::TYPE_ONE_SHOT);
   if (NS_FAILED(rv)) {
     // Make |callback| not hold the timer, so they will both be destructed when
     // we leave the scope of this function.
     callback->ClearTimer();
   }
 }
 
 void
 APZEventState::ProcessLongTap(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
                               const CSSPoint& aPoint,
+                              Modifiers aModifiers,
                               const ScrollableLayerGuid& aGuid,
                               uint64_t aInputBlockId,
                               float aPresShellResolution)
 {
   APZES_LOG("Handling long tap at %s\n", Stringify(aPoint).c_str());
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return;
   }
 
   SendPendingTouchPreventedResponse(false, aGuid);
 
+  // Converting the modifiers to DOM format for the DispatchMouseEvent call
+  // is the most useless thing ever because nsDOMWindowUtils::SendMouseEvent
+  // just converts them back to widget format, but that API has many callers,
+  // including in JS code, so it's not trivial to change.
   bool eventHandled =
       APZCCallbackHelper::DispatchMouseEvent(aUtils, NS_LITERAL_STRING("contextmenu"),
                          APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution),
-                         2, 1, 0, true,
+                         2, 1, WidgetModifiersToDOMModifiers(aModifiers), true,
                          nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   APZES_LOG("Contextmenu event handled: %d\n", eventHandled);
 
   // If no one handle context menu, fire MOZLONGTAP event
   if (!eventHandled) {
     LayoutDevicePoint currentPoint =
         APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution)
       * widget->GetDefaultScale();
     int time = 0;
     nsEventStatus status =
-        APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, widget);
+        APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, aModifiers, widget);
     eventHandled = (status == nsEventStatus_eConsumeNoDefault);
     APZES_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
   }
 
   mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, eventHandled);
 }
 
 void
 APZEventState::ProcessLongTapUp(const CSSPoint& aPoint,
+                                Modifiers aModifiers,
                                 const ScrollableLayerGuid& aGuid,
                                 float aPresShellResolution)
 {
   APZES_LOG("Handling long tap up at %s\n", Stringify(aPoint).c_str());
 
-  ProcessSingleTap(aPoint, aGuid, aPresShellResolution);
+  ProcessSingleTap(aPoint, aModifiers, aGuid, aPresShellResolution);
 }
 
 void
 APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId)
 {
   if (aEvent.message == NS_TOUCH_START && aEvent.touches.Length() > 0) {
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -45,24 +45,27 @@ class APZEventState {
   typedef FrameMetrics::ViewID ViewID;
 public:
   APZEventState(nsIWidget* aWidget,
                 const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback);
 
   NS_INLINE_DECL_REFCOUNTING(APZEventState);
 
   void ProcessSingleTap(const CSSPoint& aPoint,
+                        Modifiers aModifiers,
                         const ScrollableLayerGuid& aGuid,
                         float aPresShellResolution);
   void ProcessLongTap(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
                       const CSSPoint& aPoint,
+                      Modifiers aModifiers,
                       const ScrollableLayerGuid& aGuid,
                       uint64_t aInputBlockId,
                       float aPresShellResolution);
   void ProcessLongTapUp(const CSSPoint& aPoint,
+                        Modifiers aModifiers,
                         const ScrollableLayerGuid& aGuid,
                         float aPresShellResolution);
   void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId);
   void ProcessWheelEvent(const WidgetWheelEvent& aEvent,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId);
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -135,49 +135,49 @@ ChromeProcessController::HandleSingleTap
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(
         FROM_HERE,
         NewRunnableMethod(this, &ChromeProcessController::HandleSingleTap,
                           aPoint, aModifiers, aGuid));
     return;
   }
 
-  mAPZEventState->ProcessSingleTap(aPoint, aGuid, GetPresShellResolution());
+  mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid, GetPresShellResolution());
 }
 
 void
 ChromeProcessController::HandleLongTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
                                        const ScrollableLayerGuid& aGuid,
                                        uint64_t aInputBlockId)
 {
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(
         FROM_HERE,
         NewRunnableMethod(this, &ChromeProcessController::HandleLongTap,
                           aPoint, aModifiers, aGuid, aInputBlockId));
     return;
   }
 
-  mAPZEventState->ProcessLongTap(GetDOMWindowUtils(), aPoint, aGuid,
+  mAPZEventState->ProcessLongTap(GetDOMWindowUtils(), aPoint, aModifiers, aGuid,
       aInputBlockId, GetPresShellResolution());
 }
 
 void
 ChromeProcessController::HandleLongTapUp(const CSSPoint& aPoint, Modifiers aModifiers,
                                          const ScrollableLayerGuid& aGuid)
 {
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(
         FROM_HERE,
         NewRunnableMethod(this, &ChromeProcessController::HandleLongTapUp,
                           aPoint, aModifiers, aGuid));
     return;
   }
 
-  mAPZEventState->ProcessLongTapUp(aPoint, aGuid, GetPresShellResolution());
+  mAPZEventState->ProcessLongTapUp(aPoint, aModifiers, aGuid, GetPresShellResolution());
 }
 
 void
 ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                               APZStateChange aChange,
                                               int aArg)
 {
   if (MessageLoop::current() != mUILoop) {