Bug 1137572 part.8 Callers of methods to dispatch composition events of TextEventDispatcher should be able to specify specific time/timeStamp r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 16 Mar 2016 13:47:48 +0900
changeset 328762 782cc1725295b36e34a854a4d64159da3b16f4e9
parent 328761 2982777d071d36ffa9761d4ec2347ff36640627c
child 328763 fc409d9244ce5b34fd484f52b2d81ceb300cbd9c
push id1146
push userCallek@gmail.com
push dateMon, 25 Jul 2016 16:35:44 +0000
treeherdermozilla-release@a55778f9cd5a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1137572
milestone48.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 1137572 part.8 Callers of methods to dispatch composition events of TextEventDispatcher should be able to specify specific time/timeStamp r=smaug
widget/BasicEvents.h
widget/EventForwards.h
widget/TextEventDispatcher.cpp
widget/TextEventDispatcher.h
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -168,54 +168,86 @@ struct EventFlags : public BaseEventFlag
 {
   EventFlags()
   {
     Clear();
   }
 };
 
 /******************************************************************************
+ * mozilla::WidgetEventTime
+ ******************************************************************************/
+
+class WidgetEventTime
+{
+public:
+  // 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).
+  TimeStamp timeStamp;
+
+  WidgetEventTime()
+    : time(0)
+    , timeStamp(TimeStamp::Now())
+  {
+  }
+
+  WidgetEventTime(uint64_t aTime,
+                  TimeStamp aTimeStamp)
+    : time(aTime)
+    , timeStamp(aTimeStamp)
+  {
+  }
+
+  void AssignEventTime(const WidgetEventTime& aOther)
+  {
+    time = aOther.time;
+    timeStamp = aOther.timeStamp;
+  }
+};
+
+/******************************************************************************
  * mozilla::WidgetEvent
  ******************************************************************************/
 
-class WidgetEvent
+class WidgetEvent : public WidgetEventTime
 {
 protected:
   WidgetEvent(bool aIsTrusted,
               EventMessage aMessage,
               EventClassID aEventClassID)
-    : mClass(aEventClassID)
+    : WidgetEventTime()
+    , mClass(aEventClassID)
     , mMessage(aMessage)
     , refPoint(0, 0)
     , lastRefPoint(0, 0)
-    , time(0)
-    , timeStamp(TimeStamp::Now())
     , userType(nullptr)
   {
     MOZ_COUNT_CTOR(WidgetEvent);
     mFlags.Clear();
     mFlags.mIsTrusted = aIsTrusted;
     mFlags.mCancelable = true;
     mFlags.mBubbles = true;
   }
 
   WidgetEvent()
-    : time(0)
+    : WidgetEventTime()
   {
     MOZ_COUNT_CTOR(WidgetEvent);
   }
 
 public:
   WidgetEvent(bool aIsTrusted, EventMessage aMessage)
-    : mClass(eBasicEventClass)
+    : WidgetEventTime()
+    , mClass(eBasicEventClass)
     , mMessage(aMessage)
     , refPoint(0, 0)
     , lastRefPoint(0, 0)
-    , time(0)
-    , timeStamp(TimeStamp::Now())
     , userType(nullptr)
   {
     MOZ_COUNT_CTOR(WidgetEvent);
     mFlags.Clear();
     mFlags.mIsTrusted = aIsTrusted;
     mFlags.mCancelable = true;
     mFlags.mBubbles = true;
   }
@@ -243,22 +275,16 @@ public:
 
   EventClassID mClass;
   EventMessage mMessage;
   // 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
 
@@ -268,18 +294,17 @@ public:
   nsCOMPtr<dom::EventTarget> originalTarget;
 
   void AssignEventData(const WidgetEvent& aEvent, bool aCopyTargets)
   {
     // mClass should be initialized with the constructor.
     // mMessage should be initialized with the constructor.
     refPoint = aEvent.refPoint;
     // lastRefPoint doesn't need to be copied.
-    time = aEvent.time;
-    timeStamp = aEvent.timeStamp;
+    AssignEventTime(aEvent);
     // 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/EventForwards.h
+++ b/widget/EventForwards.h
@@ -130,16 +130,18 @@ namespace mozilla {
 
 #undef NS_EVENT_CLASS
 #undef NS_ROOT_EVENT_CLASS
 
 // BasicEvents.h
 struct BaseEventFlags;
 struct EventFlags;
 
+class WidgetEventTime;
+
 // TextEvents.h
 struct AlternativeCharCode;
 
 // TextRange.h
 struct TextRangeStyle;
 struct TextRange;
 
 class TextRangeArray;
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -208,50 +208,55 @@ TextEventDispatcher::DispatchInputEvent(
     rv = widget->DispatchEvent(&aEvent, aStatus);
   }
 
   mDispatchingEvent--;
   return rv;
 }
 
 nsresult
-TextEventDispatcher::StartComposition(nsEventStatus& aStatus)
+TextEventDispatcher::StartComposition(nsEventStatus& aStatus,
+                                      const WidgetEventTime* aEventTime)
 {
   aStatus = nsEventStatus_eIgnore;
 
   nsresult rv = GetState();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (NS_WARN_IF(mIsComposing)) {
     return NS_ERROR_FAILURE;
   }
 
   mIsComposing = true;
   WidgetCompositionEvent compositionStartEvent(true, eCompositionStart,
                                                mWidget);
   InitEvent(compositionStartEvent);
+  if (aEventTime) {
+    compositionStartEvent.AssignEventTime(*aEventTime);
+  }
   rv = DispatchEvent(mWidget, compositionStartEvent, aStatus);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 TextEventDispatcher::StartCompositionAutomaticallyIfNecessary(
-                       nsEventStatus& aStatus)
+                       nsEventStatus& aStatus,
+                       const WidgetEventTime* aEventTime)
 {
   if (IsComposing()) {
     return NS_OK;
   }
 
-  nsresult rv = StartComposition(aStatus);
+  nsresult rv = StartComposition(aStatus, aEventTime);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If started composition has already been committed, we shouldn't dispatch
   // the compositionchange event.
   if (!IsComposing()) {
     aStatus = nsEventStatus_eConsumeNoDefault;
@@ -270,17 +275,18 @@ TextEventDispatcher::StartCompositionAut
   }
 
   aStatus = nsEventStatus_eIgnore;
   return NS_OK;
 }
 
 nsresult
 TextEventDispatcher::CommitComposition(nsEventStatus& aStatus,
-                                       const nsAString* aCommitString)
+                                       const nsAString* aCommitString,
+                                       const WidgetEventTime* aEventTime)
 {
   aStatus = nsEventStatus_eIgnore;
 
   nsresult rv = GetState();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -288,31 +294,34 @@ TextEventDispatcher::CommitComposition(n
   // with non-existing composition string nor commit composition with empty
   // string.
   if (NS_WARN_IF(!IsComposing() &&
                  (!aCommitString || aCommitString->IsEmpty()))) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIWidget> widget(mWidget);
-  rv = StartCompositionAutomaticallyIfNecessary(aStatus);
+  rv = StartCompositionAutomaticallyIfNecessary(aStatus, aEventTime);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     return NS_OK;
   }
 
   // End current composition and make this free for other IMEs.
   mIsComposing = false;
 
   EventMessage message = aCommitString ? eCompositionCommit :
                                          eCompositionCommitAsIs;
   WidgetCompositionEvent compositionCommitEvent(true, message, widget);
   InitEvent(compositionCommitEvent);
+  if (aEventTime) {
+    compositionCommitEvent.AssignEventTime(*aEventTime);
+  }
   if (message == eCompositionCommit) {
     compositionCommitEvent.mData = *aCommitString;
   }
   rv = DispatchEvent(widget, compositionCommitEvent, aStatus);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -650,18 +659,20 @@ TextEventDispatcher::PendingComposition:
       EnsureClauseArray();
       mClauses->AppendElement(range);
     }
   }
   return NS_OK;
 }
 
 nsresult
-TextEventDispatcher::PendingComposition::Flush(TextEventDispatcher* aDispatcher,
-                                               nsEventStatus& aStatus)
+TextEventDispatcher::PendingComposition::Flush(
+                                           TextEventDispatcher* aDispatcher,
+                                           nsEventStatus& aStatus,
+                                           const WidgetEventTime* aEventTime)
 {
   aStatus = nsEventStatus_eIgnore;
 
   nsresult rv = aDispatcher->GetState();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -681,29 +692,33 @@ TextEventDispatcher::PendingComposition:
     EnsureClauseArray();
     mClauses->AppendElement(mCaret);
   }
 
   RefPtr<TextEventDispatcher> kungFuDeathGrip(aDispatcher);
   nsCOMPtr<nsIWidget> widget(aDispatcher->mWidget);
   WidgetCompositionEvent compChangeEvent(true, eCompositionChange, widget);
   aDispatcher->InitEvent(compChangeEvent);
+  if (aEventTime) {
+    compChangeEvent.AssignEventTime(*aEventTime);
+  }
   compChangeEvent.mData = mString;
   if (mClauses) {
     MOZ_ASSERT(!mClauses->IsEmpty(),
                "mClauses must be non-empty array when it's not nullptr");
     compChangeEvent.mRanges = mClauses;
   }
 
   // While this method dispatches a composition event, some other event handler
   // cause more clauses to be added.  So, we should clear pending composition
   // before dispatching the event.
   Clear();
 
-  rv = aDispatcher->StartCompositionAutomaticallyIfNecessary(aStatus);
+  rv = aDispatcher->StartCompositionAutomaticallyIfNecessary(aStatus,
+                                                             aEventTime);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     return NS_OK;
   }
   rv = aDispatcher->DispatchEvent(widget, compChangeEvent, aStatus);
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/widget/TextEventDispatcher.h
+++ b/widget/TextEventDispatcher.h
@@ -109,28 +109,37 @@ public:
         mInputTransactionType == eNativeInputTransaction) {
       return nullptr;
     }
     return const_cast<TextEventDispatcher*>(this);
   }
 
   /**
    * StartComposition() starts composition explicitly.
+   *
+   * @param aEventTime  If this is not nullptr, WidgetCompositionEvent will
+   *                    be initialized with this.  Otherwise, initialized
+   *                    with the time at initializing.
    */
-  nsresult StartComposition(nsEventStatus& aStatus);
+  nsresult StartComposition(nsEventStatus& aStatus,
+                            const WidgetEventTime* aEventTime = nullptr);
 
   /**
    * CommitComposition() commits composition.
    *
    * @param aCommitString   If this is null, commits with the last composition
    *                        string.  Otherwise, commits the composition with
    *                        this value.
+   * @param aEventTime      If this is not nullptr, WidgetCompositionEvent will
+   *                        be initialized with this.  Otherwise, initialized
+   *                        with the time at initializing.
    */
    nsresult CommitComposition(nsEventStatus& aStatus,
-                              const nsAString* aCommitString = nullptr);
+                              const nsAString* aCommitString = nullptr,
+                              const WidgetEventTime* aEventTime = nullptr);
 
   /**
    * SetPendingCompositionString() sets new composition string which will be
    * dispatched with eCompositionChange event by calling Flush().
    *
    * @param aString         New composition string.
    */
   nsresult SetPendingCompositionString(const nsAString& aString)
@@ -191,20 +200,25 @@ public:
   }
 
   /**
    * FlushPendingComposition() sends the pending composition string
    * to the widget of the store DOM window.  Before calling this, IME needs to
    * set pending composition string with SetPendingCompositionString(),
    * AppendClauseToPendingComposition() and/or
    * SetCaretInPendingComposition().
+   *
+   * @param aEventTime      If this is not nullptr, WidgetCompositionEvent will
+   *                        be initialized with this.  Otherwise, initialized
+   *                        with the time at initializing.
    */
-  nsresult FlushPendingComposition(nsEventStatus& aStatus)
+  nsresult FlushPendingComposition(nsEventStatus& aStatus,
+                                   const WidgetEventTime* aEventTime = nullptr)
   {
-    return mPendingComposition.Flush(this, aStatus);
+    return mPendingComposition.Flush(this, aStatus, aEventTime);
   }
 
   /**
    * ClearPendingComposition() makes this instance forget pending composition.
    */
   void ClearPendingComposition()
   {
     mPendingComposition.Clear();
@@ -279,17 +293,19 @@ private:
   class PendingComposition
   {
   public:
     PendingComposition();
     nsresult SetString(const nsAString& aString);
     nsresult AppendClause(uint32_t aLength, uint32_t aAttribute);
     nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
     nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
-    nsresult Flush(TextEventDispatcher* aDispatcher, nsEventStatus& aStatus);
+    nsresult Flush(TextEventDispatcher* aDispatcher,
+                   nsEventStatus& aStatus,
+                   const WidgetEventTime* aEventTime);
     void Clear();
 
   private:
     nsString mString;
     RefPtr<TextRangeArray> mClauses;
     TextRange mCaret;
 
     void EnsureClauseArray();
@@ -390,21 +406,26 @@ private:
    * been started it yet.
    *
    * @param aStatus         If it succeeded to start composition normally, this
    *                        returns nsEventStatus_eIgnore.  Otherwise, e.g.,
    *                        the composition is canceled during dispatching
    *                        compositionstart event, this returns
    *                        nsEventStatus_eConsumeNoDefault.  In this case,
    *                        the caller shouldn't keep doing its job.
+   * @param aEventTime      If this is not nullptr, WidgetCompositionEvent will
+   *                        be initialized with this.  Otherwise, initialized
+   *                        with the time at initializing.
    * @return                Only when something unexpected occurs, this returns
    *                        an error.  Otherwise, returns NS_OK even if aStatus
    *                        is nsEventStatus_eConsumeNoDefault.
    */
-  nsresult StartCompositionAutomaticallyIfNecessary(nsEventStatus& aStatus);
+  nsresult StartCompositionAutomaticallyIfNecessary(
+             nsEventStatus& aStatus,
+             const WidgetEventTime* aEventTime);
 
   /**
    * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
    *
    * @param aMessage        Must be eKeyDown, eKeyUp or eKeyPress.
    * @param aKeyboardEvent  A keyboard event.  If aMessage is eKeyPress and
    *                        the event is for second or later character, its
    *                        mKeyValue should be empty string.