Bug 1217700 part.2 IMEContentObserver should observe all possible notifications and check if it should be notified when it occurs r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 13 Apr 2017 14:32:12 +0900
changeset 566331 0ea1fc4888f06e0b9dde84ad60aa7632fc2ccb1a
parent 566330 640ff6dddc6cb411d55b9c897670fa1a690865d1
child 566332 ab221fb1ba32534ad3df2478051161c37579791e
push id55180
push userjjong@mozilla.com
push dateFri, 21 Apr 2017 09:36:13 +0000
reviewersm_kato
bugs1217700
milestone55.0a1
Bug 1217700 part.2 IMEContentObserver should observe all possible notifications and check if it should be notified when it occurs r=m_kato IMEContentObserver can store pointer of IMENotificationRequests of its mWidget. Therefore, it can check the requests dynamically when it receives content change or layout change. This patch makes IMEContentObserver stores IMENotificationRequests as pointer and check it at every change notification received. Additionally, notification request may be changed due to focus move or something. Therefore, this patch makes IMEContentObserver and IMEContentObserver::IMENotificationSender() check if the notifications are still necessary. MozReview-Commit-ID: 2uU2wN15D8v
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
widget/IMEData.h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -130,18 +130,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IM
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootContent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
 
-  tmp->mIMENotificationRequests.mWantUpdates =
-    IMENotificationRequests::NOTIFY_NOTHING;
+  tmp->mIMENotificationRequests = nullptr;
   tmp->mESM = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootContent)
@@ -163,16 +162,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
 
 IMEContentObserver::IMEContentObserver()
   : mESM(nullptr)
+  , mIMENotificationRequests(nullptr)
   , mSuppressNotifications(0)
   , mPreCharacterDataChangeLength(-1)
   , mSendingNotification(NOTIFY_IME_OF_NOTHING)
   , mIsObserving(false)
   , mIMEHasFocus(false)
   , mNeedsToNotifyIMEOfFocusSet(false)
   , mNeedsToNotifyIMEOfTextChange(false)
   , mNeedsToNotifyIMEOfSelectionChange(false)
@@ -204,16 +204,17 @@ IMEContentObserver::Init(nsIWidget* aWid
     // Clear members which may not be initialized again.
     Clear();
   }
 
   mESM = aPresContext->EventStateManager();
   mESM->OnStartToObserveContent(this);
 
   mWidget = aWidget;
+  mIMENotificationRequests = &mWidget->IMENotificationRequestsRef();
 
   if (aWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
     if (!InitWithPlugin(aPresContext, aContent)) {
       Clear();
       return;
     }
   } else {
     if (!InitWithEditor(aPresContext, aContent, aEditor)) {
@@ -413,61 +414,60 @@ void
 IMEContentObserver::ObserveEditableNode()
 {
   MOZ_RELEASE_ASSERT(mSelection);
   MOZ_RELEASE_ASSERT(mRootContent);
   MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
 
   // If this is called before sending NOTIFY_IME_OF_FOCUS (it's possible when
   // the editor is reframed before sending NOTIFY_IME_OF_FOCUS asynchronously),
-  // the update preference of mWidget may be different from after the widget
+  // the notification requests of mWidget may be different from after the widget
   // receives NOTIFY_IME_OF_FOCUS.   So, this should be called again by
   // OnIMEReceivedFocus() which is called after sending NOTIFY_IME_OF_FOCUS.
   if (!mIMEHasFocus) {
     MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
                mSendingNotification == NOTIFY_IME_OF_FOCUS,
                "Wow, OnIMEReceivedFocus() won't be called?");
     return;
   }
 
   mIsObserving = true;
   if (mEditor) {
     mEditor->AddEditorObserver(this);
   }
 
-  mIMENotificationRequests = mWidget->IMENotificationRequestsRef();
   if (!WasInitializedWithPlugin()) {
     // Add selection change listener only when this starts to observe
     // non-plugin content since we cannot detect selection changes in
     // plugins.
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
     NS_ENSURE_TRUE_VOID(selPrivate);
     nsresult rv = selPrivate->AddSelectionListener(this);
     NS_ENSURE_SUCCESS_VOID(rv);
-  }
-
-  if (mIMENotificationRequests.WantTextChange()) {
-    // add text change observer
+    // Add text change observer only when this starts to observe
+    // non-plugin content since we cannot detect text changes in
+    // plugins.
     mRootContent->AddMutationObserver(this);
   }
 
-  if (mIMENotificationRequests.WantPositionChanged() && mDocShell) {
-    // Add scroll position listener and reflow observer to detect position and
-    // size changes
+  if (mDocShell) {
+    // Add scroll position listener and reflow observer to detect position
+    // and size changes
     mDocShell->AddWeakScrollObserver(this);
     mDocShell->AddWeakReflowObserver(this);
   }
 }
 
 void
 IMEContentObserver::NotifyIMEOfBlur()
 {
   // Prevent any notifications to be sent IME.
   nsCOMPtr<nsIWidget> widget;
   mWidget.swap(widget);
+  mIMENotificationRequests = nullptr;
 
   // If we hasn't been set focus, we shouldn't send blur notification to IME.
   if (!mIMEHasFocus) {
     return;
   }
 
   // mWidget must have been non-nullptr if IME has focus.
   MOZ_RELEASE_ASSERT(widget);
@@ -510,21 +510,21 @@ IMEContentObserver::UnregisterObservers(
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
     if (selPrivate) {
       selPrivate->RemoveSelectionListener(this);
     }
     mSelectionData.Clear();
     mFocusedWidget = nullptr;
   }
 
-  if (mIMENotificationRequests.WantTextChange() && mRootContent) {
+  if (mRootContent) {
     mRootContent->RemoveMutationObserver(this);
   }
 
-  if (mIMENotificationRequests.WantPositionChanged() && mDocShell) {
+  if (mDocShell) {
     mDocShell->RemoveWeakScrollObserver(this);
     mDocShell->RemoveWeakReflowObserver(this);
   }
 }
 
 nsPresContext*
 IMEContentObserver::GetPresContext() const
 {
@@ -536,18 +536,17 @@ IMEContentObserver::Destroy()
 {
   // WARNING: When you change this method, you have to check Unlink() too.
 
   NotifyIMEOfBlur();
   UnregisterObservers();
   Clear();
 
   mWidget = nullptr;
-  mIMENotificationRequests.mWantUpdates =
-    IMENotificationRequests::NOTIFY_NOTHING;
+  mIMENotificationRequests = nullptr;
 
   if (mESM) {
     mESM->OnStopObservingContent(this);
     mESM = nullptr;
   }
 }
 
 void
@@ -685,31 +684,43 @@ IMEContentObserver::NotifySelectionChang
                                     duringComposition);
   }
   return NS_OK;
 }
 
 void
 IMEContentObserver::ScrollPositionChanged()
 {
+  if (!NeedsPositionChangeNotification()) {
+    return;
+  }
+
   MaybeNotifyIMEOfPositionChange();
 }
 
 NS_IMETHODIMP
 IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
                            DOMHighResTimeStamp aEnd)
 {
+  if (!NeedsPositionChangeNotification()) {
+    return NS_OK;
+  }
+
   MaybeNotifyIMEOfPositionChange();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
                                         DOMHighResTimeStamp aEnd)
 {
+  if (!NeedsPositionChangeNotification()) {
+    return NS_OK;
+  }
+
   MaybeNotifyIMEOfPositionChange();
   return NS_OK;
 }
 
 nsresult
 IMEContentObserver::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
 {
   // If the instance has normal selection cache and the query event queries
@@ -781,17 +792,18 @@ IMEContentObserver::HandleQueryContentEv
   }
   return rv;
 }
 
 bool
 IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext,
                                        WidgetMouseEvent* aMouseEvent)
 {
-  if (!mIMENotificationRequests.WantMouseButtonEventOnChar()) {
+  if (!mIMENotificationRequests ||
+      !mIMENotificationRequests->WantMouseButtonEventOnChar()) {
     return false;
   }
   if (!aMouseEvent->IsTrusted() ||
       aMouseEvent->DefaultPrevented() ||
       !aMouseEvent->mWidget) {
     return false;
   }
   // Now, we need to notify only mouse down and mouse up event.
@@ -868,16 +880,20 @@ IMEContentObserver::CharacterDataWillCha
                                             CharacterDataChangeInfo* aInfo)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "character data changed for non-text node");
   MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
              "CharacterDataChanged() should've reset "
              "mPreCharacterDataChangeLength");
 
+  if (!NeedsTextChangeNotification()) {
+    return;
+  }
+
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
   mPreCharacterDataChangeLength =
     ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
                                              aInfo->mChangeEnd);
   MOZ_ASSERT(mPreCharacterDataChangeLength >=
                aInfo->mChangeEnd - aInfo->mChangeStart,
              "The computed length must be same as or larger than XP length");
@@ -886,16 +902,20 @@ IMEContentObserver::CharacterDataWillCha
 void
 IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
                                          nsIContent* aContent,
                                          CharacterDataChangeInfo* aInfo)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "character data changed for non-text node");
 
+  if (!NeedsTextChangeNotification()) {
+    return;
+  }
+
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
 
   int64_t removedLength = mPreCharacterDataChangeLength;
   mPreCharacterDataChangeLength = -1;
 
   MOZ_ASSERT(removedLength >= 0,
              "mPreCharacterDataChangeLength should've been set by "
@@ -926,16 +946,20 @@ IMEContentObserver::CharacterDataChanged
   MaybeNotifyIMEOfTextChange(data);
 }
 
 void
 IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
                                        int32_t aStartIndex,
                                        int32_t aEndIndex)
 {
+  if (!NeedsTextChangeNotification()) {
+    return;
+  }
+
   mStartOfRemovingTextRangeCache.Clear();
 
   uint32_t offset = 0;
   nsresult rv = NS_OK;
   if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
     mEndOfAddedTextCache.Clear();
     rv = ContentEventHandler::GetFlatTextLengthInRange(
                                 NodePosition(mRootContent, 0),
@@ -998,16 +1022,20 @@ IMEContentObserver::ContentInserted(nsID
 
 void
 IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
                                    nsIContent* aContainer,
                                    nsIContent* aChild,
                                    int32_t aIndexInContainer,
                                    nsIContent* aPreviousSibling)
 {
+  if (!NeedsTextChangeNotification()) {
+    return;
+  }
+
   mEndOfAddedTextCache.Clear();
 
   nsINode* containerNode = NODE_FROM(aContainer, aDocument);
 
   uint32_t offset = 0;
   nsresult rv = NS_OK;
   if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) {
     // At removing a child node of aContainer, we need the line break caused
@@ -1056,28 +1084,36 @@ IMEContentObserver::ContentRemoved(nsIDo
 void
 IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
                                         dom::Element* aElement,
                                         int32_t aNameSpaceID,
                                         nsIAtom* aAttribute,
                                         int32_t aModType,
                                         const nsAttrValue* aNewValue)
 {
+  if (!NeedsTextChangeNotification()) {
+    return;
+  }
+
   mPreAttrChangeLength =
     ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
 }
 
 void
 IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
                                      dom::Element* aElement,
                                      int32_t aNameSpaceID,
                                      nsIAtom* aAttribute,
                                      int32_t aModType,
                                      const nsAttrValue* aOldValue)
 {
+  if (!NeedsTextChangeNotification()) {
+    return;
+  }
+
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
 
   uint32_t postAttrChangeLength =
     ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
   if (postAttrChangeLength == mPreAttrChangeLength) {
     return;
   }
@@ -1210,16 +1246,25 @@ IMEContentObserver::MaybeNotifyIMEOfText
      this, TextChangeDataToString(aTextChangeData).get()));
 
   mTextChangeData += aTextChangeData;
   PostTextChangeNotification();
   FlushMergeableNotifications();
 }
 
 void
+IMEContentObserver::CancelNotifyingIMEOfTextChange()
+{
+  MOZ_LOG(sIMECOLog, LogLevel::Debug,
+    ("0x%p IMEContentObserver::CancelNotifyingIMEOfTextChange()", this));
+  mTextChangeData.Clear();
+  mNeedsToNotifyIMEOfTextChange = false;
+}
+
+void
 IMEContentObserver::MaybeNotifyIMEOfSelectionChange(
                       bool aCausedByComposition,
                       bool aCausedBySelectionEvent,
                       bool aOccurredDuringComposition)
 {
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::MaybeNotifyIMEOfSelectionChange("
      "aCausedByComposition=%s, aCausedBySelectionEvent=%s, "
@@ -1250,16 +1295,24 @@ IMEContentObserver::MaybeNotifyIMEOfPosi
        "NOTIY_IME_OF_POSITION_CHANGE", this));
     return;
   }
   PostPositionChangeNotification();
   FlushMergeableNotifications();
 }
 
 void
+IMEContentObserver::CancelNotifyingIMEOfPositionChange()
+{
+  MOZ_LOG(sIMECOLog, LogLevel::Debug,
+    ("0x%p IMEContentObserver::CancelNotifyIMEOfPositionChange()", this));
+  mNeedsToNotifyIMEOfPositionChange = false;
+}
+
+void
 IMEContentObserver::MaybeNotifyCompositionEventHandled()
 {
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::MaybeNotifyCompositionEventHandled()",
      this));
 
   PostCompositionEventHandledNotification();
   FlushMergeableNotifications();
@@ -1391,16 +1444,25 @@ IMEContentObserver::FlushMergeableNotifi
   if (mQueuedSender) {
     // So, if this is already called, this should do nothing.
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::FlushMergeableNotifications(), "
        "FAILED, due to already flushing pending notifications", this));
     return;
   }
 
+  // If text change notification and/or position change notification becomes
+  // unnecessary, let's cancel them.
+  if (mNeedsToNotifyIMEOfTextChange && !NeedsTextChangeNotification()) {
+    CancelNotifyingIMEOfTextChange();
+  }
+  if (mNeedsToNotifyIMEOfPositionChange && !NeedsPositionChangeNotification()) {
+    CancelNotifyingIMEOfPositionChange();
+  }
+
   if (!NeedsToNotifyIMEOfSomething()) {
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::FlushMergeableNotifications(), "
        "FAILED, due to no pending notifications", this));
     return;
   }
 
   // NOTE: Reset each pending flag because sending notification may cause
@@ -1775,16 +1837,26 @@ IMEContentObserver::IMENotificationSende
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendTextChange(), retrying to send NOTIFY_IME_OF_TEXT_CHANGE...",
        this));
     mIMEContentObserver->PostTextChangeNotification();
     return;
   }
 
+  // If text change notification is unnecessary anymore, just cancel it.
+  if (!mIMEContentObserver->NeedsTextChangeNotification()) {
+    MOZ_LOG(sIMECOLog, LogLevel::Warning,
+      ("0x%p   IMEContentObserver::IMENotificationSender::"
+       "SendTextChange(), canceling sending NOTIFY_IME_OF_TEXT_CHANGE",
+       this));
+    mIMEContentObserver->CancelNotifyingIMEOfTextChange();
+    return;
+  }
+
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendTextChange(), sending NOTIFY_IME_OF_TEXT_CHANGE... "
      "mIMEContentObserver={ mTextChangeData=%s }",
      this, TextChangeDataToString(mIMEContentObserver->mTextChangeData).get()));
 
   IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
   notification.SetData(mIMEContentObserver->mTextChangeData);
@@ -1816,16 +1888,26 @@ IMEContentObserver::IMENotificationSende
     MOZ_LOG(sIMECOLog, LogLevel::Debug,
       ("0x%p   IMEContentObserver::IMENotificationSender::"
        "SendPositionChange(), retrying to send "
        "NOTIFY_IME_OF_POSITION_CHANGE...", this));
     mIMEContentObserver->PostPositionChangeNotification();
     return;
   }
 
+  // If position change notification is unnecessary anymore, just cancel it.
+  if (!mIMEContentObserver->NeedsPositionChangeNotification()) {
+    MOZ_LOG(sIMECOLog, LogLevel::Warning,
+      ("0x%p   IMEContentObserver::IMENotificationSender::"
+       "SendPositionChange(), canceling sending NOTIFY_IME_OF_POSITION_CHANGE",
+       this));
+    mIMEContentObserver->CancelNotifyingIMEOfPositionChange();
+    return;
+  }
+
   MOZ_LOG(sIMECOLog, LogLevel::Info,
     ("0x%p IMEContentObserver::IMENotificationSender::"
      "SendPositionChange(), sending NOTIFY_IME_OF_POSITION_CHANGE...", this));
 
   MOZ_RELEASE_ASSERT(mIMEContentObserver->mSendingNotification ==
                        NOTIFY_IME_OF_NOTHING);
   mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
   IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE),
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -94,17 +94,18 @@ public:
                          nsIContent* aContent,
                          nsIEditor* aEditor);
   bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent) const;
   bool IsManaging(const TextComposition* aTextComposition) const;
   bool WasInitializedWithPlugin() const;
   bool IsEditorHandlingEventForComposition() const;
   bool KeepAliveDuringDeactive() const
   {
-    return mIMENotificationRequests.WantDuringDeactive();
+    return mIMENotificationRequests &&
+           mIMENotificationRequests->WantDuringDeactive();
   }
   nsIWidget* GetWidget() const { return mWidget; }
   nsIEditor* GetEditor() const { return mEditor; }
   void SuppressNotifyingIME();
   void UnsuppressNotifyingIME();
   nsPresContext* GetPresContext() const;
   nsresult GetSelectionAndRoot(nsISelection** aSelection,
                                nsIContent** aRoot) const;
@@ -142,35 +143,47 @@ private:
   bool IsReflowLocked() const;
   bool IsSafeToNotifyIME() const;
   bool IsEditorComposing() const;
 
   void PostFocusSetNotification();
   void MaybeNotifyIMEOfFocusSet();
   void PostTextChangeNotification();
   void MaybeNotifyIMEOfTextChange(const TextChangeDataBase& aTextChangeData);
+  void CancelNotifyingIMEOfTextChange();
   void PostSelectionChangeNotification();
   void MaybeNotifyIMEOfSelectionChange(bool aCausedByComposition,
                                        bool aCausedBySelectionEvent,
                                        bool aOccurredDuringComposition);
   void PostPositionChangeNotification();
   void MaybeNotifyIMEOfPositionChange();
+  void CancelNotifyingIMEOfPositionChange();
   void PostCompositionEventHandledNotification();
 
   void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
   void ObserveEditableNode();
   /**
    *  NotifyIMEOfBlur() notifies IME of blur.
    */
   void NotifyIMEOfBlur();
   /**
    *  UnregisterObservers() unregisters all listeners and observers.
    */
   void UnregisterObservers();
   void FlushMergeableNotifications();
+  bool NeedsTextChangeNotification() const
+  {
+    return mIMENotificationRequests &&
+           mIMENotificationRequests->WantTextChange();
+  }
+  bool NeedsPositionChangeNotification() const
+  {
+    return mIMENotificationRequests &&
+           mIMENotificationRequests->WantPositionChanged();
+  }
   void ClearPendingNotifications()
   {
     mNeedsToNotifyIMEOfFocusSet = false;
     mNeedsToNotifyIMEOfTextChange = false;
     mNeedsToNotifyIMEOfSelectionChange = false;
     mNeedsToNotifyIMEOfPositionChange = false;
     mNeedsToNotifyIMEOfCompositionEventHandled = false;
     mTextChangeData.Clear();
@@ -321,17 +334,17 @@ private:
 
   // mSelectionData is the last selection data which was notified.  The
   // selection information is modified by UpdateSelectionCache().  The reason
   // of the selection change is modified by MaybeNotifyIMEOfSelectionChange().
   SelectionChangeData mSelectionData;
 
   EventStateManager* mESM;
 
-  IMENotificationRequests mIMENotificationRequests;
+  const IMENotificationRequests* mIMENotificationRequests;
   uint32_t mPreAttrChangeLength;
   uint32_t mSuppressNotifications;
   int64_t mPreCharacterDataChangeLength;
 
   // mSendingNotification is a notification which is now sending from
   // IMENotificationSender.  When the value is NOTIFY_IME_OF_NOTHING, it's
   // not sending any notification.
   IMEMessage mSendingNotification;
--- a/widget/IMEData.h
+++ b/widget/IMEData.h
@@ -68,16 +68,20 @@ struct IMENotificationRequests final
   {
     return IMENotificationRequests(aOther.mWantUpdates | mWantUpdates);
   }
   IMENotificationRequests& operator|=(const IMENotificationRequests& aOther)
   {
     mWantUpdates |= aOther.mWantUpdates;
     return *this;
   }
+  bool operator==(const IMENotificationRequests& aOther) const
+  {
+    return mWantUpdates == aOther.mWantUpdates;
+  }
 
   bool WantTextChange() const
   {
     return !!(mWantUpdates & NOTIFY_TEXT_CHANGE);
   }
 
   bool WantPositionChanged() const
   {