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 207315 55d21a229ee27460df88741273e698022a5c724f
parent 207314 87ed776cbacdf3f6a684a563134a9eed0afa787a
child 207316 16f49acfc4e9cb839c5027ede91bfac9d93c795c
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs77992
milestone32.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 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: