Bug 77992 part 1.0 - Add timeStamp to WidgetEvent; r=smaug
authorBrian Birtles <birtles@gmail.com>
Fri, 06 Jun 2014 14:29:49 +0900
changeset 187009 55d21a229ee27460df88741273e698022a5c724f
parent 187008 87ed776cbacdf3f6a684a563134a9eed0afa787a
child 187010 16f49acfc4e9cb839c5027ede91bfac9d93c795c
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs77992
milestone32.0a1
Bug 77992 part 1.0 - Add timeStamp to WidgetEvent; r=smaug This patch adds a timeStamp member to WidgetEvent alongside the existing 'time' member. In the future we would like to remove 'time' and just keep timeStamp but that depends on it being web-compatible. For now we introduce both members side-by-side. Later we will add a pref to determine which one to return. If no compatibility issues arise we will remove 'time' altogether.
dom/events/EventStateManager.cpp
gfx/layers/apz/src/GestureEventListener.cpp
gfx/tests/gtest/TestAsyncPanZoomController.cpp
layout/base/nsPresShell.cpp
widget/BasicEvents.h
widget/InputData.h
widget/TouchEvents.h
widget/android/AndroidJavaWrappers.cpp
widget/nsGUIEventIPC.h
widget/xpwidgets/InputData.cpp
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -2176,16 +2176,17 @@ EventStateManager::SendLineScrollEvent(n
 
   WidgetMouseScrollEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_SCROLL,
                                aEvent->widget);
   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.refPoint = aEvent->refPoint;
   event.widget = aEvent->widget;
   event.time = aEvent->time;
+  event.timeStamp = aEvent->timeStamp;
   event.modifiers = aEvent->modifiers;
   event.buttons = aEvent->buttons;
   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.delta = aDelta;
   event.inputSource = aEvent->inputSource;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
@@ -2215,16 +2216,17 @@ EventStateManager::SendPixelScrollEvent(
 
   WidgetMouseScrollEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_PIXEL_SCROLL,
                                aEvent->widget);
   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.refPoint = aEvent->refPoint;
   event.widget = aEvent->widget;
   event.time = aEvent->time;
+  event.timeStamp = aEvent->timeStamp;
   event.modifiers = aEvent->modifiers;
   event.buttons = aEvent->buttons;
   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.delta = aPixelDelta;
   event.inputSource = aEvent->inputSource;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
@@ -4352,16 +4354,17 @@ EventStateManager::CheckForAndDispatchCl
 
     WidgetMouseEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_CLICK,
                            aEvent->widget, WidgetMouseEvent::eReal);
     event.refPoint = aEvent->refPoint;
     event.clickCount = aEvent->clickCount;
     event.modifiers = aEvent->modifiers;
     event.buttons = aEvent->buttons;
     event.time = aEvent->time;
+    event.timeStamp = aEvent->timeStamp;
     event.mFlags.mNoContentDispatch = notDispatchToContents;
     event.button = aEvent->button;
     event.inputSource = aEvent->inputSource;
 
     nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
     if (presShell) {
       nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
       if (!mouseContent && !mCurrentTarget) {
--- a/gfx/layers/apz/src/GestureEventListener.cpp
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -46,17 +46,17 @@ float GetCurrentSpan(const MultiTouchInp
   return float(NS_hypot(delta.x, delta.y));
 }
 
 GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController)
   : mAsyncPanZoomController(aAsyncPanZoomController),
     mState(GESTURE_NONE),
     mSpanChange(0.0f),
     mPreviousSpan(0.0f),
-    mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, 0)
+    mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0)
 {
 }
 
 GestureEventListener::~GestureEventListener()
 {
 }
 
 nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
@@ -223,16 +223,17 @@ nsEventStatus GestureEventListener::Hand
 
     float currentSpan = GetCurrentSpan(mLastTouchInput);
 
     mSpanChange += fabsf(currentSpan - mPreviousSpan);
     if (mSpanChange > PINCH_START_THRESHOLD) {
       SetState(GESTURE_PINCH);
       PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
                                    mLastTouchInput.mTime,
+                                   mLastTouchInput.mTimeStamp,
                                    GetCurrentFocus(mLastTouchInput),
                                    currentSpan,
                                    currentSpan,
                                    mLastTouchInput.modifiers);
 
       rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
     }
 
@@ -247,16 +248,17 @@ nsEventStatus GestureEventListener::Hand
       rv = nsEventStatus_eConsumeNoDefault;
       break;
     }
 
     float currentSpan = GetCurrentSpan(mLastTouchInput);
 
     PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
                                  mLastTouchInput.mTime,
+                                 mLastTouchInput.mTimeStamp,
                                  GetCurrentFocus(mLastTouchInput),
                                  currentSpan,
                                  mPreviousSpan,
                                  mLastTouchInput.modifiers);
 
     rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
     mPreviousSpan = currentSpan;
 
@@ -285,16 +287,17 @@ nsEventStatus GestureEventListener::Hand
     // GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore.
     break;
 
   case GESTURE_FIRST_SINGLE_TOUCH_DOWN: {
     CancelLongTapTimeoutTask();
     CancelMaxTapTimeoutTask();
     TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_UP,
                              mLastTouchInput.mTime,
+                             mLastTouchInput.mTimeStamp,
                              mLastTouchInput.mTouches[0].mScreenPoint,
                              mLastTouchInput.modifiers);
     nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(tapEvent);
     if (tapupStatus == nsEventStatus_eIgnore) {
       SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
       CreateMaxTapTimeoutTask();
     } else {
       // We sent the tapup into content without waiting for a double tap
@@ -303,32 +306,34 @@ nsEventStatus GestureEventListener::Hand
     break;
   }
 
   case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
     CancelMaxTapTimeoutTask();
     SetState(GESTURE_NONE);
     TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_DOUBLE,
                              mLastTouchInput.mTime,
+                             mLastTouchInput.mTimeStamp,
                              mLastTouchInput.mTouches[0].mScreenPoint,
                              mLastTouchInput.modifiers);
     mAsyncPanZoomController->HandleGestureEvent(tapEvent);
     break;
   }
 
   case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
     CancelLongTapTimeoutTask();
     SetState(GESTURE_NONE);
     TriggerSingleTapConfirmedEvent();
     break;
 
   case GESTURE_LONG_TOUCH_DOWN: {
     SetState(GESTURE_NONE);
     TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG_UP,
                              mLastTouchInput.mTime,
+                             mLastTouchInput.mTimeStamp,
                              mLastTouchInput.mTouches[0].mScreenPoint,
                              mLastTouchInput.modifiers);
     mAsyncPanZoomController->HandleGestureEvent(tapEvent);
     break;
   }
 
   case GESTURE_MULTI_TOUCH_DOWN:
     if (mTouches.Length() < 2) {
@@ -336,16 +341,17 @@ nsEventStatus GestureEventListener::Hand
     }
     break;
 
   case GESTURE_PINCH:
     if (mTouches.Length() < 2) {
       SetState(GESTURE_NONE);
       PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
                                    mLastTouchInput.mTime,
+                                   mLastTouchInput.mTimeStamp,
                                    ScreenPoint(),
                                    1.0f,
                                    1.0f,
                                    mLastTouchInput.modifiers);
       mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
     }
 
     rv = nsEventStatus_eConsumeNoDefault;
@@ -375,16 +381,17 @@ void GestureEventListener::HandleInputTi
   case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
     // just in case MAX_TAP_TIME > ContextMenuDelay cancel MAX_TAP timer
     // and fall through
     CancelMaxTapTimeoutTask();
   case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
     SetState(GESTURE_LONG_TOUCH_DOWN);
     TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG,
                              mLastTouchInput.mTime,
+                             mLastTouchInput.mTimeStamp,
                              mLastTouchInput.mTouches[0].mScreenPoint,
                              mLastTouchInput.modifiers);
     mAsyncPanZoomController->HandleGestureEvent(tapEvent);
     break;
   }
   default:
     NS_WARNING("Unhandled state upon long tap timeout");
     SetState(GESTURE_NONE);
@@ -407,16 +414,17 @@ void GestureEventListener::HandleInputTi
     SetState(GESTURE_NONE);
   }
 }
 
 void GestureEventListener::TriggerSingleTapConfirmedEvent()
 {
   TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_CONFIRMED,
                            mLastTouchInput.mTime,
+                           mLastTouchInput.mTimeStamp,
                            mLastTouchInput.mTouches[0].mScreenPoint,
                            mLastTouchInput.modifiers);
   mAsyncPanZoomController->HandleGestureEvent(tapEvent);
 }
 
 void GestureEventListener::SetState(GestureState aState)
 {
   mState = aState;
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -199,17 +199,18 @@ void ApzcPan(AsyncPanZoomController* apz
     // APZC shouldn't consume the start event now, instead queueing it up
     // waiting for content's response.
     touchStartStatus = nsEventStatus_eIgnore;
   } else {
     // APZC should go into the touching state and therefore consume the event.
     touchStartStatus = nsEventStatus_eConsumeNoDefault;
   }
 
-  mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0);
+  mti =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
   aTime += TIME_BETWEEN_TOUCH_EVENT;
   // Make sure the move is large enough to not be handled as a tap
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY+OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
   status = apzc->ReceiveInputEvent(mti);
   EXPECT_EQ(touchStartStatus, status);
   // APZC should be in TOUCHING state
 
   // Allowed touch behaviours must be set after sending touch-start.
@@ -222,29 +223,32 @@ void ApzcPan(AsyncPanZoomController* apz
     // APZC should ignore panning, be in TOUCHING state and therefore return eIgnore.
     // The same applies to all consequent touch move events.
     touchMoveStatus = nsEventStatus_eIgnore;
   } else {
     // APZC should go into the panning state and therefore consume the event.
     touchMoveStatus = nsEventStatus_eConsumeNoDefault;
   }
 
-  mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, 0);
+  mti =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
   aTime += TIME_BETWEEN_TOUCH_EVENT;
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
   status = apzc->ReceiveInputEvent(mti);
   EXPECT_EQ(touchMoveStatus, status);
 
-  mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, 0);
+  mti =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
   aTime += TIME_BETWEEN_TOUCH_EVENT;
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
   status = apzc->ReceiveInputEvent(mti);
   EXPECT_EQ(touchMoveStatus, status);
 
-  mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0);
+  mti =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
   aTime += TIME_BETWEEN_TOUCH_EVENT;
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
   status = apzc->ReceiveInputEvent(mti);
 
   // Since we've explicitly built the overscroll handoff chain before
   // touch-start, we need to explicitly clear it after touch-end.
   aTreeManager->ClearOverscrollHandoffChain();
 }
@@ -314,30 +318,33 @@ static void ApzcPinchWithPinchInput(Asyn
 
   nsEventStatus expectedStatus = aShouldTriggerPinch
     ? nsEventStatus_eConsumeNoDefault
     : nsEventStatus_eIgnore;
   nsEventStatus actualStatus;
 
   actualStatus = aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
                                             0,
+                                            TimeStamp(),
                                             ScreenPoint(aFocusX, aFocusY),
                                             10.0,
                                             10.0,
                                             0));
   EXPECT_EQ(actualStatus, expectedStatus);
   actualStatus = aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
                                             0,
+                                            TimeStamp(),
                                             ScreenPoint(aFocusX, aFocusY),
                                             10.0 * aScale,
                                             10.0,
                                             0));
   EXPECT_EQ(actualStatus, expectedStatus);
   aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
                                             0,
+                                            TimeStamp(),
                                             ScreenPoint(aFocusX, aFocusY),
                                             // note: negative values here tell APZC
                                             //       not to turn the pinch into a pan
                                             -1.0,
                                             -1.0,
                                             0));
 }
 
@@ -353,38 +360,42 @@ static void ApzcPinchWithTouchMoveInput(
   float pinchLength = 100.0;
   float pinchLengthScaled = pinchLength * aScale;
 
   nsEventStatus expectedStatus = aShouldTriggerPinch
     ? nsEventStatus_eConsumeNoDefault
     : nsEventStatus_eIgnore;
   nsEventStatus actualStatus;
 
-  MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, 0);
+  MultiTouchInput mtiStart =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
   mtiStart.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiStart.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
   aApzc->ReceiveInputEvent(mtiStart);
 
   if (aAllowedTouchBehaviors) {
     aApzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
   }
 
-  MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, 0);
+  MultiTouchInput mtiMove1 =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
   mtiMove1.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiMove1.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
   actualStatus = aApzc->ReceiveInputEvent(mtiMove1);
   EXPECT_EQ(actualStatus, expectedStatus);
 
-  MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, 0);
+  MultiTouchInput mtiMove2 =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
   mtiMove2.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiMove2.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
   actualStatus = aApzc->ReceiveInputEvent(mtiMove2);
   EXPECT_EQ(actualStatus, expectedStatus);
 
-  MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, 0);
+  MultiTouchInput mtiEnd =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
   mtiEnd.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
   mtiEnd.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
   aApzc->ReceiveInputEvent(mtiEnd);
   inputId += 2;
 }
 
 static
 void DoPinchTest(bool aUseGestureRecognizer, bool aShouldTriggerPinch,
@@ -468,24 +479,26 @@ void DoPinchTest(bool aUseGestureRecogni
     EXPECT_EQ(5, fm.GetScrollOffset().y);
   }
 
   apzc->Destroy();
 }
 
 static nsEventStatus
 ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
-  MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0);
+  MultiTouchInput mti =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
   return apzc->ReceiveInputEvent(mti);
 }
 
 static nsEventStatus
 ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
-  MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0);
+  MultiTouchInput mti =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
   return apzc->ReceiveInputEvent(mti);
 }
 
 static nsEventStatus
 ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime, int aTapLength, MockContentControllerDelayed* mcc = nullptr) {
   nsEventStatus status = ApzcDown(apzc, aX, aY, aTime);
   if (mcc != nullptr) {
@@ -1006,17 +1019,18 @@ DoLongPressPreventDefaultTest(bool aShou
   // Clear the waiting-for-content timeout task, then send the signal that
   // content has handled this long tap. This takes the place of the
   // "contextmenu" event.
   mcc->ClearDelayedTask();
   apzc->ContentReceivedTouch(true);
 
   time += 1000;
 
-  MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, 0);
+  MultiTouchInput mti =
+    MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
   status = apzc->ReceiveInputEvent(mti);
   EXPECT_EQ(nsEventStatus_eIgnore, status);
 
   EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
   status = ApzcUp(apzc, touchX, touchEndY, time);
   EXPECT_EQ(nsEventStatus_eIgnore, status);
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6600,16 +6600,17 @@ DispatchPointerFromMouseOrTouch(PresShel
       event.refPoint.x = touch->mRefPoint.x;
       event.refPoint.y = touch->mRefPoint.y;
       event.modifiers = touchEvent->modifiers;
       event.width = touch->RadiusX();
       event.height = touch->RadiusY();
       event.tiltX = touch->tiltX;
       event.tiltY = touch->tiltY;
       event.time = touchEvent->time;
+      event.timeStamp = touchEvent->timeStamp;
       event.mFlags = touchEvent->mFlags;
       event.button = WidgetMouseEvent::eLeftButton;
       event.buttons = WidgetMouseEvent::eLeftButtonFlag;
       event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
       event.convertToPointer = touch->convertToPointer = false;
       aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
     }
   }
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_BasicEvents_h__
 #define mozilla_BasicEvents_h__
 
 #include <stdint.h>
 
 #include "mozilla/dom/EventTarget.h"
+#include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsISupportsImpl.h"
 #include "nsIWidget.h"
 #include "nsString.h"
 #include "Units.h"
 
 /******************************************************************************
@@ -618,34 +619,34 @@ struct EventFlags : public BaseEventFlag
  ******************************************************************************/
 
 class WidgetEvent
 {
 protected:
   WidgetEvent(bool aIsTrusted, uint32_t aMessage,
               nsEventStructType aStructType) :
     eventStructType(aStructType), message(aMessage), refPoint(0, 0),
-    lastRefPoint(0, 0), time(0), userType(0)
+    lastRefPoint(0, 0), time(0), timeStamp(TimeStamp::Now()), userType(0)
   {
     MOZ_COUNT_CTOR(WidgetEvent);
     mFlags.Clear();
     mFlags.mIsTrusted = aIsTrusted;
     mFlags.mCancelable = true;
     mFlags.mBubbles = true;
   }
 
   WidgetEvent()
   {
     MOZ_COUNT_CTOR(WidgetEvent);
   }
 
 public:
   WidgetEvent(bool aIsTrusted, uint32_t aMessage) :
     eventStructType(NS_EVENT), message(aMessage), refPoint(0, 0),
-    lastRefPoint(0, 0), time(0), userType(0)
+    lastRefPoint(0, 0), time(0), timeStamp(TimeStamp::Now()), userType(0)
   {
     MOZ_COUNT_CTOR(WidgetEvent);
     mFlags.Clear();
     mFlags.mIsTrusted = aIsTrusted;
     mFlags.mCancelable = true;
     mFlags.mBubbles = true;
   }
 
@@ -677,16 +678,19 @@ public:
   // Relative to the widget of the event, or if there is no widget then it is
   // in screen coordinates. Not modified by layout code.
   LayoutDeviceIntPoint refPoint;
   // The previous refPoint, if known, used to calculate mouse movement deltas.
   LayoutDeviceIntPoint lastRefPoint;
   // Elapsed time, in milliseconds, from a platform-specific zero time
   // to the time the message was created
   uint64_t time;
+  // Timestamp when the message was created. Set in parallel to 'time' until we
+  // determine if it is safe to drop 'time' (see bug 77992).
+  mozilla::TimeStamp timeStamp;
   // See BaseEventFlags definition for the detail.
   BaseEventFlags mFlags;
 
   // Additional type info for user defined events
   nsCOMPtr<nsIAtom> userType;
 
   nsString typeString; // always set on non-main-thread events
 
@@ -700,16 +704,17 @@ public:
     // eventStructType should be initialized with the constructor.
     // However, NS_SVGZOOM_EVENT and NS_SMIL_TIME_EVENT are set after that.
     // Therefore, we need to copy eventStructType here.
     eventStructType = aEvent.eventStructType;
     // message should be initialized with the constructor.
     refPoint = aEvent.refPoint;
     // lastRefPoint doesn't need to be copied.
     time = aEvent.time;
+    timeStamp = aEvent.timeStamp;
     // mFlags should be copied manually if it's necessary.
     userType = aEvent.userType;
     // typeString should be copied manually if it's necessary.
     target = aCopyTargets ? aEvent.target : nullptr;
     currentTarget = aCopyTargets ? aEvent.currentTarget : nullptr;
     originalTarget = aCopyTargets ? aEvent.originalTarget : nullptr;
   }
 
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -6,16 +6,17 @@
 #ifndef InputData_h__
 #define InputData_h__
 
 #include "nsDebug.h"
 #include "nsPoint.h"
 #include "nsTArray.h"
 #include "Units.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/TimeStamp.h"
 
 namespace mozilla {
 
 
 enum InputType
 {
   MULTITOUCH_INPUT,
   PINCHGESTURE_INPUT,
@@ -43,31 +44,36 @@ class InputData
 public:
   InputType mInputType;
   // Time in milliseconds that this data is relevant to. This only really
   // matters when this data is used as an event. We use uint32_t instead of
   // TimeStamp because it is easier to convert from WidgetInputEvent. The time
   // is platform-specific but it in the case of B2G and Fennec it is since
   // startup.
   uint32_t mTime;
+  // Set in parallel to mTime until we determine it is safe to drop
+  // platform-specific event times (see bug 77992).
+  TimeStamp mTimeStamp;
 
   Modifiers modifiers;
 
   INPUTDATA_AS_CHILD_TYPE(MultiTouchInput, MULTITOUCH_INPUT)
   INPUTDATA_AS_CHILD_TYPE(PinchGestureInput, PINCHGESTURE_INPUT)
   INPUTDATA_AS_CHILD_TYPE(TapGestureInput, TAPGESTURE_INPUT)
 
   InputData()
   {
   }
 
 protected:
-  InputData(InputType aInputType, uint32_t aTime, Modifiers aModifiers)
+  InputData(InputType aInputType, uint32_t aTime, TimeStamp aTimeStamp,
+            Modifiers aModifiers)
     : mInputType(aInputType),
       mTime(aTime),
+      mTimeStamp(aTimeStamp),
       modifiers(aModifiers)
   {
 
 
   }
 };
 
 /**
@@ -146,18 +152,19 @@ public:
     MULTITOUCH_START,
     MULTITOUCH_MOVE,
     MULTITOUCH_END,
     MULTITOUCH_ENTER,
     MULTITOUCH_LEAVE,
     MULTITOUCH_CANCEL
   };
 
-  MultiTouchInput(MultiTouchType aType, uint32_t aTime, Modifiers aModifiers)
-    : InputData(MULTITOUCH_INPUT, aTime, aModifiers),
+  MultiTouchInput(MultiTouchType aType, uint32_t aTime, TimeStamp aTimeStamp,
+                  Modifiers aModifiers)
+    : InputData(MULTITOUCH_INPUT, aTime, aTimeStamp, aModifiers),
       mType(aType)
   {
 
 
   }
 
   MultiTouchInput()
   {
@@ -189,21 +196,22 @@ public:
   {
     PINCHGESTURE_START,
     PINCHGESTURE_SCALE,
     PINCHGESTURE_END
   };
 
   PinchGestureInput(PinchGestureType aType,
                     uint32_t aTime,
+                    TimeStamp aTimeStamp,
                     const ScreenPoint& aFocusPoint,
                     float aCurrentSpan,
                     float aPreviousSpan,
                     Modifiers aModifiers)
-    : InputData(PINCHGESTURE_INPUT, aTime, aModifiers),
+    : InputData(PINCHGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
       mType(aType),
       mFocusPoint(aFocusPoint),
       mCurrentSpan(aCurrentSpan),
       mPreviousSpan(aPreviousSpan)
   {
 
 
   }
@@ -243,19 +251,20 @@ public:
     TAPGESTURE_UP,
     TAPGESTURE_CONFIRMED,
     TAPGESTURE_DOUBLE,
     TAPGESTURE_CANCEL
   };
 
   TapGestureInput(TapGestureType aType,
                   uint32_t aTime,
+                  TimeStamp aTimeStamp,
                   const ScreenIntPoint& aPoint,
                   Modifiers aModifiers)
-    : InputData(TAPGESTURE_INPUT, aTime, aModifiers),
+    : InputData(TAPGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
       mType(aType),
       mPoint(aPoint)
   {
 
 
   }
 
   TapGestureType mType;
--- a/widget/TouchEvents.h
+++ b/widget/TouchEvents.h
@@ -162,16 +162,17 @@ public:
   }
 
   WidgetTouchEvent(const WidgetTouchEvent& aOther) :
     WidgetInputEvent(aOther.mFlags.mIsTrusted, aOther.message, aOther.widget,
                      NS_TOUCH_EVENT)
   {
     modifiers = aOther.modifiers;
     time = aOther.time;
+    timeStamp = aOther.timeStamp;
     touches.AppendElements(aOther.touches);
     mFlags.mCancelable = message != NS_TOUCH_CANCEL;
     MOZ_COUNT_CTOR(WidgetTouchEvent);
   }
 
   WidgetTouchEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget) :
     WidgetInputEvent(aIsTrusted, aMessage, aWidget, NS_TOUCH_EVENT)
   {
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AndroidJavaWrappers.h"
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIWidget.h"
 #include "mozilla/BasicEvents.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/TouchEvents.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::widget::android;
 
 jclass AndroidGeckoEvent::jGeckoEventClass = 0;
 jfieldID AndroidGeckoEvent::jActionField = 0;
@@ -749,17 +750,17 @@ AndroidGeckoEvent::MakeMultiTouchInput(n
         }
         case AndroidMotionEvent::ACTION_OUTSIDE:
         case AndroidMotionEvent::ACTION_CANCEL: {
             type = MultiTouchInput::MULTITOUCH_CANCEL;
             break;
         }
     }
 
-    MultiTouchInput event(type, Time(), 0);
+    MultiTouchInput event(type, Time(), TimeStamp(), 0);
     event.modifiers = DOMModifiers();
 
     if (type < 0) {
         // An event we don't know about
         return event;
     }
 
     const nsIntPoint& offset = widget->WidgetToScreenOffset();
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -44,26 +44,28 @@ struct ParamTraits<mozilla::WidgetEvent>
   typedef mozilla::WidgetEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, (uint8_t) aParam.eventStructType);
     WriteParam(aMsg, aParam.message);
     WriteParam(aMsg, aParam.refPoint);
     WriteParam(aMsg, aParam.time);
+    WriteParam(aMsg, aParam.timeStamp);
     WriteParam(aMsg, aParam.mFlags);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     uint8_t eventStructType = 0;
     bool ret = ReadParam(aMsg, aIter, &eventStructType) &&
                ReadParam(aMsg, aIter, &aResult->message) &&
                ReadParam(aMsg, aIter, &aResult->refPoint) &&
                ReadParam(aMsg, aIter, &aResult->time) &&
+               ReadParam(aMsg, aIter, &aResult->timeStamp) &&
                ReadParam(aMsg, aIter, &aResult->mFlags);
     aResult->eventStructType = static_cast<nsEventStructType>(eventStructType);
     return ret;
   }
 };
 
 template<>
 struct ParamTraits<mozilla::WidgetGUIEvent>
--- a/widget/xpwidgets/InputData.cpp
+++ b/widget/xpwidgets/InputData.cpp
@@ -11,17 +11,18 @@
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent)
-  : InputData(MULTITOUCH_INPUT, aTouchEvent.time, aTouchEvent.modifiers)
+  : InputData(MULTITOUCH_INPUT, aTouchEvent.time, aTouchEvent.timeStamp,
+              aTouchEvent.modifiers)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(),
                     "Can only copy from WidgetTouchEvent on main thread");
 
   switch (aTouchEvent.message) {
     case NS_TOUCH_START:
       mType = MULTITOUCH_START;
       break;
@@ -69,17 +70,18 @@ MultiTouchInput::MultiTouchInput(const W
 
 // This conversion from WidgetMouseEvent to MultiTouchInput is needed because on
 // the B2G emulator we can only receive mouse events, but we need to be able
 // to pan correctly. To do this, we convert the events into a format that the
 // panning code can handle. This code is very limited and only supports
 // SingleTouchData. It also sends garbage for the identifier, radius, force
 // and rotation angle.
 MultiTouchInput::MultiTouchInput(const WidgetMouseEvent& aMouseEvent)
-  : InputData(MULTITOUCH_INPUT, aMouseEvent.time, aMouseEvent.modifiers)
+  : InputData(MULTITOUCH_INPUT, aMouseEvent.time, aMouseEvent.timeStamp,
+              aMouseEvent.modifiers)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(),
                     "Can only copy from WidgetMouseEvent on main thread");
   switch (aMouseEvent.message) {
   case NS_MOUSE_BUTTON_DOWN:
     mType = MULTITOUCH_START;
     break;
   case NS_MOUSE_MOVE: