Bug 965685 Use IMENotification struct for nsIWidget::NotifyIME() and merge NotifyIMEOfTextChange() with it r=smaug, sr=roc
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 18 Feb 2014 09:00:15 +0900
changeset 169210 43e799fdb80b9723d32d11772efe38dfffa6f7a3
parent 169209 6a957f4fe3086ebbfc4d75210967ee35ede48ec9
child 169211 57f2c6e6895e2152a6d6b03870251ef28b26daf0
push id4792
push userphilringnalda@gmail.com
push dateTue, 18 Feb 2014 05:00:33 +0000
treeherderb2g-inbound@c97246bac7b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, roc
bugs965685
milestone30.0a1
Bug 965685 Use IMENotification struct for nsIWidget::NotifyIME() and merge NotifyIMEOfTextChange() with it r=smaug, sr=roc
dom/events/TextComposition.cpp
dom/events/TextComposition.h
dom/events/nsIMEStateManager.cpp
dom/events/nsIMEStateManager.h
dom/ipc/TabParent.cpp
widget/android/AndroidBridge.h
widget/android/nsWindow.cpp
widget/android/nsWindow.h
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsIWidget.h
widget/windows/WinIMEHandler.cpp
widget/windows/WinIMEHandler.h
widget/windows/nsTextStore.cpp
widget/windows/nsTextStore.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/winrt/MetroWidget.cpp
widget/windows/winrt/MetroWidget.h
widget/xpwidgets/PuppetWidget.cpp
widget/xpwidgets/PuppetWidget.h
widget/xpwidgets/nsBaseWidget.h
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -11,16 +11,18 @@
 #include "nsIContent.h"
 #include "nsIEditor.h"
 #include "nsIMEStateManager.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/TextEvents.h"
 
+using namespace mozilla::widget;
+
 namespace mozilla {
 
 /******************************************************************************
  * TextComposition
  ******************************************************************************/
 
 TextComposition::TextComposition(nsPresContext* aPresContext,
                                  nsINode* aNode,
@@ -103,17 +105,17 @@ TextComposition::NotityUpdateComposition
       if (range.mRangeType == NS_TEXTRANGE_SELECTEDRAWTEXT ||
           range.mRangeType == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT) {
         mCompositionTargetOffset += range.mStartOffset;
         break;
       }
     }
   }
 
-  NotifyIME(widget::NotificationToIME::NOTIFY_IME_OF_COMPOSITION_UPDATE);
+  NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE);
 }
 
 void
 TextComposition::DispatchCompsotionEventRunnable(uint32_t aEventMessage,
                                                  const nsAString& aData)
 {
   nsContentUtils::AddScriptRunner(
     new CompositionEventDispatcher(mPresContext, mNode,
@@ -128,20 +130,20 @@ TextComposition::SynthesizeCommit(bool a
   if (mLastData != data) {
     DispatchCompsotionEventRunnable(NS_COMPOSITION_UPDATE, data);
     DispatchCompsotionEventRunnable(NS_TEXT_TEXT, data);
   }
   DispatchCompsotionEventRunnable(NS_COMPOSITION_END, data);
 }
 
 nsresult
-TextComposition::NotifyIME(widget::NotificationToIME aNotification)
+TextComposition::NotifyIME(IMEMessage aMessage)
 {
   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
-  return nsIMEStateManager::NotifyIME(aNotification, mPresContext);
+  return nsIMEStateManager::NotifyIME(aMessage, mPresContext);
 }
 
 void
 TextComposition::EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent)
 {
   mIsComposing = aTextEvent->IsComposing();
 
   MOZ_ASSERT(mLastData == aTextEvent->theText,
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -69,17 +69,17 @@ public:
    * @param aDiscard true when committing with empty string.  Otherwise, false.
    */
   void SynthesizeCommit(bool aDiscard);
 
   /**
    * Send a notification to IME.  It depends on the IME or platform spec what
    * will occur (or not occur).
    */
-  nsresult NotifyIME(widget::NotificationToIME aNotification);
+  nsresult NotifyIME(widget::IMEMessage aMessage);
 
   /**
    * the offset of first selected clause or start of of compositon
    */
   uint32_t OffsetOfTargetClause() const { return mCompositionTargetOffset; }
 
   /**
    * Returns true if there is non-empty composition string and it's not fixed.
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/nsIMEStateManager.cpp
@@ -581,44 +581,45 @@ nsIMEStateManager::DispatchCompositionEv
     if (i != TextCompositionArray::NoIndex) {
       sTextCompositions->RemoveElementAt(i);
     }
   }
 }
 
 // static
 nsresult
-nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+nsIMEStateManager::NotifyIME(IMEMessage aMessage,
                              nsIWidget* aWidget)
 {
   NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
 
   nsRefPtr<TextComposition> composition;
   if (sTextCompositions) {
     composition = sTextCompositions->GetCompositionFor(aWidget);
   }
   if (!composition || !composition->IsSynthesizedForTests()) {
-    switch (aNotification) {
+    switch (aMessage) {
       case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
-        return aWidget->NotifyIME(aNotification);
+        return aWidget->NotifyIME(IMENotification(aMessage));
       case REQUEST_TO_COMMIT_COMPOSITION:
       case REQUEST_TO_CANCEL_COMPOSITION:
       case NOTIFY_IME_OF_COMPOSITION_UPDATE:
-        return composition ? aWidget->NotifyIME(aNotification) : NS_OK;
+        return composition ?
+          aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK;
       default:
         MOZ_CRASH("Unsupported notification");
     }
     MOZ_CRASH(
       "Failed to handle the notification for non-synthesized composition");
   }
 
   // If the composition is synthesized events for automated tests, we should
   // dispatch composition events for emulating the native composition behavior.
   // NOTE: The dispatched events are discarded if it's not safe to run script.
-  switch (aNotification) {
+  switch (aMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION: {
       nsCOMPtr<nsIWidget> widget(aWidget);
       nsEventStatus status = nsEventStatus_eIgnore;
       if (!composition->LastData().IsEmpty()) {
         WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
         textEvent.theText = composition->LastData();
         textEvent.mFlags.mIsSynthesizedForTests = true;
         widget->DispatchEvent(&textEvent, status);
@@ -667,26 +668,26 @@ nsIMEStateManager::NotifyIME(Notificatio
     }
     default:
       return NS_OK;
   }
 }
 
 // static
 nsresult
-nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+nsIMEStateManager::NotifyIME(IMEMessage aMessage,
                              nsPresContext* aPresContext)
 {
   NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
 
   nsIWidget* widget = aPresContext->GetRootWidget();
   if (!widget) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  return NotifyIME(aNotification, widget);
+  return NotifyIME(aMessage, widget);
 }
 
 void
 nsTextStateManager::Init(nsIWidget* aWidget,
                          nsPresContext* aPresContext,
                          nsIContent* aContent)
 {
   mWidget = aWidget;
@@ -735,17 +736,17 @@ nsTextStateManager::Init(nsIWidget* aWid
   NS_ENSURE_TRUE_VOID(mRootContent);
 
   if (nsIMEStateManager::sIsTestingIME) {
     nsIDocument* doc = aPresContext->Document();
     (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusIn"),
                          false, false))->RunDOMEventWhenSafe();
   }
 
-  aWidget->NotifyIME(NOTIFY_IME_OF_FOCUS);
+  aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
 
   // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager
   // instance via nsIMEStateManager::UpdateIMEState().  So, this
   // instance might already have been destroyed, check it.
   if (!mRootContent) {
     return;
   }
 
@@ -772,24 +773,24 @@ nsTextStateManager::ObserveEditableNode(
     mRootContent->AddMutationObserver(this);
   }
 }
 
 void
 nsTextStateManager::Destroy(void)
 {
   // If CreateTextStateManager failed, mRootContent will be null,
-  // and we should not call NotifyIME(NOTIFY_IME_OF_BLUR)
+  // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
   if (mRootContent) {
     if (nsIMEStateManager::sIsTestingIME && mEditableNode) {
       nsIDocument* doc = mEditableNode->OwnerDoc();
       (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
                            false, false))->RunDOMEventWhenSafe();
     }
-    mWidget->NotifyIME(NOTIFY_IME_OF_BLUR);
+    mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
   }
   // Even if there are some pending notification, it'll never notify the widget.
   mWidget = nullptr;
   if ((mObserving & nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE) && mSel) {
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
     if (selPrivate)
       selPrivate->RemoveSelectionListener(this);
   }
@@ -826,17 +827,18 @@ public:
   SelectionChangeEvent(nsTextStateManager *aDispatcher)
     : mDispatcher(aDispatcher)
   {
     MOZ_ASSERT(mDispatcher);
   }
 
   NS_IMETHOD Run() {
     if (mDispatcher->mWidget) {
-      mDispatcher->mWidget->NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE);
+      mDispatcher->mWidget->NotifyIME(
+        IMENotification(NOTIFY_IME_OF_SELECTION_CHANGE));
     }
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsTextStateManager> mDispatcher;
 };
 
@@ -864,17 +866,21 @@ public:
     , mOldEnd(oldEnd)
     , mNewEnd(newEnd)
   {
     MOZ_ASSERT(mDispatcher);
   }
 
   NS_IMETHOD Run() {
     if (mDispatcher->mWidget) {
-      mDispatcher->mWidget->NotifyIMEOfTextChange(mStart, mOldEnd, mNewEnd);
+      IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
+      notification.mTextChangeData.mStartOffset = mStart;
+      notification.mTextChangeData.mOldEndOffset = mOldEnd;
+      notification.mTextChangeData.mNewEndOffset = mNewEnd;
+      mDispatcher->mWidget->NotifyIME(notification);
     }
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsTextStateManager> mDispatcher;
   uint32_t mStart, mOldEnd, mNewEnd;
 };
--- a/dom/events/nsIMEStateManager.h
+++ b/dom/events/nsIMEStateManager.h
@@ -26,16 +26,17 @@ class TextComposition;
 /*
  * IME state manager
  */
 
 class nsIMEStateManager
 {
   friend class nsTextStateManager;
 protected:
+  typedef mozilla::widget::IMEMessage IMEMessage;
   typedef mozilla::widget::IMEState IMEState;
   typedef mozilla::widget::InputContext InputContext;
   typedef mozilla::widget::InputContextAction InputContextAction;
 
 public:
   static void Shutdown();
 
   static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
@@ -109,20 +110,18 @@ public:
    */
   static already_AddRefed<mozilla::TextComposition>
     GetTextCompositionFor(mozilla::WidgetGUIEvent* aEvent);
 
   /**
    * Send a notification to IME.  It depends on the IME or platform spec what
    * will occur (or not occur).
    */
-  static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
-                            nsIWidget* aWidget);
-  static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
-                            nsPresContext* aPresContext);
+  static nsresult NotifyIME(IMEMessage aMessage, nsIWidget* aWidget);
+  static nsresult NotifyIME(IMEMessage aMessage, nsPresContext* aPresContext);
 
 protected:
   static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
                                         nsIContent* aContent,
                                         InputContextAction aAction);
   static void SetIMEState(const IMEState &aState,
                           nsIContent* aContent,
                           nsIWidget* aWidget,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1039,17 +1039,18 @@ TabParent::RecvNotifyIMEFocus(const bool
     aPreference->mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
     return true;
   }
 
   *aSeqno = mIMESeqno;
   mIMETabParent = aFocus ? this : nullptr;
   mIMESelectionAnchor = 0;
   mIMESelectionFocus = 0;
-  widget->NotifyIME(aFocus ? NOTIFY_IME_OF_FOCUS : NOTIFY_IME_OF_BLUR);
+  widget->NotifyIME(IMENotification(aFocus ? NOTIFY_IME_OF_FOCUS :
+                                             NOTIFY_IME_OF_BLUR));
 
   if (aFocus) {
     *aPreference = widget->GetIMEUpdatePreference();
   } else {
     mIMECacheText.Truncate(0);
   }
   return true;
 }
@@ -1061,17 +1062,21 @@ TabParent::RecvNotifyIMETextChange(const
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return true;
 
   NS_ASSERTION(widget->GetIMEUpdatePreference().WantTextChange(),
                "Don't call Send/RecvNotifyIMETextChange without NOTIFY_TEXT_CHANGE");
 
-  widget->NotifyIMEOfTextChange(aStart, aEnd, aNewEnd);
+  IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
+  notification.mTextChangeData.mStartOffset = aStart;
+  notification.mTextChangeData.mOldEndOffset = aEnd;
+  notification.mTextChangeData.mNewEndOffset = aNewEnd;
+  widget->NotifyIME(notification);
   return true;
 }
 
 bool
 TabParent::RecvNotifyIMESelectedCompositionRect(const uint32_t& aOffset,
                                                 const nsIntRect& aRect,
                                                 const nsIntRect& aCaretRect)
 {
@@ -1079,34 +1084,34 @@ TabParent::RecvNotifyIMESelectedComposit
   mIMECompositionRectOffset = aOffset;
   mIMECompositionRect = aRect;
   mIMECaretRect = aCaretRect;
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return true;
   }
-  widget->NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE);
+  widget->NotifyIME(IMENotification(NOTIFY_IME_OF_COMPOSITION_UPDATE));
   return true;
 }
 
 bool
 TabParent::RecvNotifyIMESelection(const uint32_t& aSeqno,
                                   const uint32_t& aAnchor,
                                   const uint32_t& aFocus)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return true;
 
   if (aSeqno == mIMESeqno) {
     mIMESelectionAnchor = aAnchor;
     mIMESelectionFocus = aFocus;
     if (widget->GetIMEUpdatePreference().WantSelectionChange()) {
-      widget->NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE);
+      widget->NotifyIME(IMENotification(NOTIFY_IME_OF_SELECTION_CHANGE));
     }
   }
   return true;
 }
 
 bool
 TabParent::RecvNotifyIMETextHint(const nsString& aText)
 {
@@ -1353,18 +1358,18 @@ TabParent::RecvEndIMEComposition(const b
                                  nsString* aComposition)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return true;
 
   mIMECompositionEnding = true;
 
-  widget->NotifyIME(aCancel ? REQUEST_TO_CANCEL_COMPOSITION :
-                              REQUEST_TO_COMMIT_COMPOSITION);
+  widget->NotifyIME(IMENotification(aCancel ? REQUEST_TO_CANCEL_COMPOSITION :
+                                              REQUEST_TO_COMMIT_COMPOSITION));
 
   mIMECompositionEnding = false;
   *aComposition = mIMECompositionText;
   mIMECompositionText.Truncate(0);  
   return true;
 }
 
 bool
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -119,17 +119,17 @@ private:
 };
 
 
 class AndroidBridge MOZ_FINAL : public mozilla::layers::GeckoContentController
 {
 public:
     enum {
         // Values for NotifyIME, in addition to values from the Gecko
-        // NotificationToIME enum; use negative values here to prevent conflict
+        // IMEMessage enum; use negative values here to prevent conflict
         NOTIFY_IME_OPEN_VKB = -2,
         NOTIFY_IME_REPLY_EVENT = -1,
     };
 
     enum {
         LAYER_CLIENT_TYPE_NONE = 0,
         LAYER_CLIENT_TYPE_GL = 2            // AndroidGeckoGLLayerClient
     };
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1831,17 +1831,20 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *
             // The focusing handshake sequence is complete, and Java is waiting
             // on Gecko. Now we can notify Java of the newly focused content
             mIMETextChanges.Clear();
             mIMESelectionChanged = false;
             // NotifyIMEOfTextChange also notifies selection
             // Use 'INT32_MAX / 2' here because subsequent text changes might
             // combine with this text change, and overflow might occur if
             // we just use INT32_MAX
-            NotifyIMEOfTextChange(0, INT32_MAX / 2, INT32_MAX / 2);
+            IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
+            notification.mTextChangeData.mOldEndOffset =
+                notification.mTextChangeData.mNewEndOffset = INT32_MAX / 2;
+            NotifyIMEOfTextChange(notification);
             FlushIMEChanges();
         }
         GeckoAppShell::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT);
         return;
     } else if (ae->Action() == AndroidGeckoEvent::IME_UPDATE_CONTEXT) {
         GeckoAppShell::NotifyIMEContext(mInputContext.mIMEState.mEnabled,
                                         mInputContext.mHTMLInputType,
                                         mInputContext.mHTMLInputInputmode,
@@ -2089,19 +2092,19 @@ nsWindow::UserActivity()
   }
 
   if (mIdleService) {
     mIdleService->ResetIdleTimeOut(0);
   }
 }
 
 NS_IMETHODIMP
-nsWindow::NotifyIME(NotificationToIME aNotification)
+nsWindow::NotifyIME(const IMENotification& aIMENotification)
 {
-    switch (aNotification) {
+    switch (aIMENotification.mMessage) {
         case REQUEST_TO_COMMIT_COMPOSITION:
             //ALOGIME("IME: REQUEST_TO_COMMIT_COMPOSITION: s=%d", aState);
             RemoveIMEComposition();
             GeckoAppShell::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
             return NS_OK;
         case REQUEST_TO_CANCEL_COMPOSITION:
             ALOGIME("IME: REQUEST_TO_CANCEL_COMPOSITION");
 
@@ -2142,16 +2145,18 @@ nsWindow::NotifyIME(NotificationToIME aN
                 return NS_OK;
             }
 
             ALOGIME("IME: NOTIFY_IME_OF_SELECTION_CHANGE");
 
             PostFlushIMEChanges();
             mIMESelectionChanged = true;
             return NS_OK;
+        case NOTIFY_IME_OF_TEXT_CHANGE:
+            return NotifyIMEOfTextChange(aIMENotification);
         default:
             return NS_ERROR_NOT_IMPLEMENTED;
     }
 }
 
 NS_IMETHODIMP_(void)
 nsWindow::SetInputContext(const InputContext& aContext,
                           const InputContextAction& aAction)
@@ -2268,41 +2273,46 @@ nsWindow::FlushIMEChanges()
 
         GeckoAppShell::NotifyIMEChange(EmptyString(),
                              (int32_t) event.GetSelectionStart(),
                              (int32_t) event.GetSelectionEnd(), -1);
         mIMESelectionChanged = false;
     }
 }
 
-NS_IMETHODIMP
-nsWindow::NotifyIMEOfTextChange(uint32_t aStart,
-                                uint32_t aOldEnd,
-                                uint32_t aNewEnd)
+nsresult
+nsWindow::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
 {
+    MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
+               "NotifyIMEOfTextChange() is called with invaild notification");
+
     if (mIMEMaskTextUpdate)
         return NS_OK;
 
     ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d",
-            aStart, aOldEnd, aNewEnd);
+            aIMENotification.mTextChangeData.mStartOffset,
+            aIMENotification.mTextChangeData.mOldEndOffset,
+            aIMENotification.mTextChangeData.mNewEndOffset);
 
     /* Make sure Java's selection is up-to-date */
     mIMESelectionChanged = false;
     NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE);
     PostFlushIMEChanges();
 
-    mIMETextChanges.AppendElement(IMEChange(aStart, aOldEnd, aNewEnd));
+    mIMETextChanges.AppendElement(IMEChange(aIMENotification));
     // Now that we added a new range we need to go back and
     // update all the ranges before that.
     // Ranges that have offsets which follow this new range
     // need to be updated to reflect new offsets
-    int32_t delta = (int32_t)(aNewEnd - aOldEnd);
+    int32_t delta = aIMENotification.mTextChangeData.AdditionalLength();
     for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
         IMEChange &previousChange = mIMETextChanges[i];
-        if (previousChange.mStart > (int32_t)aOldEnd) {
+        if (previousChange.mStart >
+                static_cast<int32_t>(
+                    aIMENotification.mTextChangeData.mOldEndOffset)) {
             previousChange.mStart += delta;
             previousChange.mOldEnd += delta;
             previousChange.mNewEnd += delta;
         }
     }
 
     // Now go through all ranges to merge any ranges that are connected
     // srcIndex is the index of the range to merge from
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -130,24 +130,22 @@ public:
     NS_IMETHOD GetAttention(int32_t aCycleCount) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                                int32_t aHorizontal,
                                int32_t aVertical)
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
-    NS_IMETHOD NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE;
+    NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE;
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
 
-    NS_IMETHOD NotifyIMEOfTextChange(uint32_t aStart,
-                                     uint32_t aOldEnd,
-                                     uint32_t aNewEnd) MOZ_OVERRIDE;
+    nsresult NotifyIMEOfTextChange(const IMENotification& aIMENotification);
     virtual nsIMEUpdatePreference GetIMEUpdatePreference();
 
     LayerManager* GetLayerManager (PLayerTransactionChild* aShadowManager = nullptr,
                                    LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                                    LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                                    bool* aAllowRetaining = nullptr);
 
     NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);
@@ -205,19 +203,26 @@ protected:
 
     struct IMEChange {
         int32_t mStart, mOldEnd, mNewEnd;
 
         IMEChange() :
             mStart(-1), mOldEnd(-1), mNewEnd(-1)
         {
         }
-        IMEChange(int32_t start, int32_t oldEnd, int32_t newEnd) :
-            mStart(start), mOldEnd(oldEnd), mNewEnd(newEnd)
+        IMEChange(const IMENotification& aIMENotification)
+            : mStart(aIMENotification.mTextChangeData.mStartOffset)
+            , mOldEnd(aIMENotification.mTextChangeData.mOldEndOffset)
+            , mNewEnd(aIMENotification.mTextChangeData.mNewEndOffset)
         {
+            MOZ_ASSERT(aIMENotification.mMessage ==
+                           mozilla::widget::NOTIFY_IME_OF_TEXT_CHANGE,
+                       "IMEChange initialized with wrong notification");
+            MOZ_ASSERT(aIMENotification.mTextChangeData.IsInInt32Range(),
+                       "The text change notification is out of range");
         }
         bool IsEmpty()
         {
             return mStart < 0;
         }
     };
     nsAutoTArray<IMEChange, 4> mIMETextChanges;
     bool mIMESelectionChanged;
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -512,17 +512,17 @@ public:
 
   NS_IMETHOD        GetAttention(int32_t aCycleCount);
 
   virtual bool HasPendingInputEvent();
 
   NS_IMETHOD        ActivateNativeMenuItemAt(const nsAString& indexString);
   NS_IMETHOD        ForceUpdateNativeMenuAt(const nsAString& indexString);
 
-  NS_IMETHOD        NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE;
+  NS_IMETHOD        NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE;
   NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                     const InputContextAction& aAction);
   NS_IMETHOD_(InputContext) GetInputContext();
   virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE;
   NS_IMETHOD        GetToggledKeyState(uint32_t aKeyCode,
                                        bool* aLEDState);
 
   // nsIPluginWidget
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1853,19 +1853,19 @@ void nsChildView::UpdateCurrentInputEven
 bool nsChildView::HasPendingInputEvent()
 {
   return DoHasPendingInputEvent();
 }
 
 #pragma mark -
 
 NS_IMETHODIMP
-nsChildView::NotifyIME(NotificationToIME aNotification)
-{
-  switch (aNotification) {
+nsChildView::NotifyIME(const IMENotification& aIMENotification)
+{
+  switch (aIMENotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
       mTextInputHandler->CommitIMEComposition();
       return NS_OK;
     case REQUEST_TO_CANCEL_COMPOSITION:
       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
       mTextInputHandler->CancelIMEComposition();
       return NS_OK;
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -321,17 +321,17 @@ public:
     virtual bool DragEvent(unsigned int aMessage, Point aMouseGlobal, UInt16 aKeyModifiers);
 
     bool HasModalDescendents() { return mNumModalDescendents > 0; }
     NSWindow *GetCocoaWindow() { return mWindow; }
 
     void SetMenuBar(nsMenuBarX* aMenuBar);
     nsMenuBarX *GetMenuBar();
 
-    NS_IMETHOD NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE;
+    NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE;
     NS_IMETHOD_(void) SetInputContext(
                         const InputContext& aContext,
                         const InputContextAction& aAction) MOZ_OVERRIDE;
     NS_IMETHOD_(InputContext) GetInputContext()
     {
       NSView* view = mWindow ? [mWindow contentView] : nil;
       if (view) {
         mInputContext.mNativeIMEContext = [view inputContext];
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -2115,19 +2115,19 @@ bool nsCocoaWindow::ShouldFocusPlugin()
 {
   if (!mWindow || IsChildInFailingLeftClickThrough([mWindow contentView]))
     return false;
 
   return true;
 }
 
 NS_IMETHODIMP
-nsCocoaWindow::NotifyIME(NotificationToIME aNotification)
+nsCocoaWindow::NotifyIME(const IMENotification& aIMENotification)
 {
-  switch (aNotification) {
+  switch (aIMENotification.mMessage) {
     case NOTIFY_IME_OF_FOCUS:
       if (mInputContext.IsPasswordEditor()) {
         TextInputHandler::EnableSecureEventInput();
       }
       return NS_OK;
     case NOTIFY_IME_OF_BLUR:
       // When we're going to be deactive, we must disable the secure event input
       // mode, see the Carbon Event Manager Reference.
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -5860,31 +5860,31 @@ nsChildWindow::nsChildWindow()
 {
 }
 
 nsChildWindow::~nsChildWindow()
 {
 }
 
 NS_IMETHODIMP
-nsWindow::NotifyIME(NotificationToIME aNotification)
+nsWindow::NotifyIME(const IMENotification& aIMENotification)
 {
     if (MOZ_UNLIKELY(!mIMModule)) {
-        switch (aNotification) {
+        switch (aIMENotification.mMessage) {
             case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
             case REQUEST_TO_COMMIT_COMPOSITION:
             case REQUEST_TO_CANCEL_COMPOSITION:
             case NOTIFY_IME_OF_FOCUS:
             case NOTIFY_IME_OF_BLUR:
               return NS_ERROR_NOT_AVAILABLE;
             default:
               break;
         }
     }
-    switch (aNotification) {
+    switch (aIMENotification.mMessage) {
         // TODO: We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
         //       NOTIFY_IME_OF_SELECTION_CHANGE.  The required behavior is
         //       really different from committing composition.
         case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
         case REQUEST_TO_COMMIT_COMPOSITION:
             return mIMModule->CommitIMEComposition(this);
         case REQUEST_TO_CANCEL_COMPOSITION:
             return mIMModule->CancelIMEComposition(this);
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -258,17 +258,17 @@ public:
                                          guint aTime);
     static void        UpdateDragStatus (GdkDragContext *aDragContext,
                                          nsIDragService *aDragService);
     // If this dispatched the keydown event actually, this returns TRUE,
     // otherwise, FALSE.
     bool               DispatchKeyDownEvent(GdkEventKey *aEvent,
                                             bool *aIsCancelled);
 
-    NS_IMETHOD NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE;
+    NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE;
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
     NS_IMETHOD GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState);
 
     // These methods are for toplevel windows only.
     void               ResizeTransparencyBitmap();
     void               ApplyTransparencyBitmap();
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -95,18 +95,18 @@ typedef void* nsNativeWidget;
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #define NS_NATIVE_ICOREWINDOW          103 // winrt specific
 #endif
 
 #define NS_IWIDGET_IID \
-{ 0x67da44c4, 0xe21b, 0x4742, \
-  { 0x9c, 0x2b, 0x26, 0xc7, 0x70, 0x21, 0xde, 0x87 } }
+{ 0xb979c607, 0xf0aa, 0x4fee, \
+  { 0xb2, 0x7b, 0xd4, 0x46, 0xa2, 0xe, 0x8b, 0x27 } }
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -202,22 +202,22 @@ enum nsTopLevelWidgetZPlacement { // for
  * notified.
  */
 #define NS_WIDGET_RESUME_PROCESS_OBSERVER_TOPIC "resume_process_notification"
 
 /**
  * Preference for receiving IME updates
  *
  * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
- * change and/or selection change and call nsIWidget::NotifyIMEOfTextChange()
- * and/or nsIWidget::NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE).
+ * change and/or selection change and call nsIWidget::NotifyIME() with
+ * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
  * Please note that the text change observing cost is very expensive especially
  * on an HTML editor has focus.
  * If the IME implementation on a particular platform doesn't care about
- * NotifyIMEOfTextChange() and/or NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE),
+ * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
  * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
  * If the IME implementation needs notifications even while our process is
  * deactive, it should also set NOTIFY_DURING_DEACTIVE.
  */
 struct nsIMEUpdatePreference {
 
   typedef uint8_t Notifications;
 
@@ -443,31 +443,85 @@ struct SizeConstraints {
     mMaxSize(aMaxSize)
   {
   }
 
   nsIntSize mMinSize;
   nsIntSize mMaxSize;
 };
 
-// NotificationToIME is shared by nsIMEStateManager and TextComposition.
-enum NotificationToIME {
+// IMEMessage is shared by nsIMEStateManager and TextComposition.
+// XXX Negative values are used in Android...
+enum IMEMessage MOZ_ENUM_TYPE(int8_t)
+{
   // XXX We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
   //     NOTIFY_IME_OF_SELECTION_CHANGE later.
   NOTIFY_IME_OF_CURSOR_POS_CHANGED,
   // An editable content is getting focus
   NOTIFY_IME_OF_FOCUS,
   // An editable content is losing focus
   NOTIFY_IME_OF_BLUR,
   // Selection in the focused editable content is changed
   NOTIFY_IME_OF_SELECTION_CHANGE,
+  // Text in the focused editable content is changed
+  NOTIFY_IME_OF_TEXT_CHANGE,
+  // Composition string has been updated
+  NOTIFY_IME_OF_COMPOSITION_UPDATE,
+  // Request to commit current composition to IME
+  // (some platforms may not support)
   REQUEST_TO_COMMIT_COMPOSITION,
-  REQUEST_TO_CANCEL_COMPOSITION,
-  // Composition string has been updated
-  NOTIFY_IME_OF_COMPOSITION_UPDATE
+  // Request to cancel current composition to IME
+  // (some platforms may not support)
+  REQUEST_TO_CANCEL_COMPOSITION
+};
+
+struct IMENotification
+{
+  IMENotification(IMEMessage aMessage)
+    : mMessage(aMessage)
+  {
+    switch (aMessage) {
+      case NOTIFY_IME_OF_TEXT_CHANGE:
+        mTextChangeData.mStartOffset = 0;
+        mTextChangeData.mOldEndOffset = 0;
+        mTextChangeData.mNewEndOffset = 0;
+        break;
+      default:
+        break;
+    }
+  }
+
+  IMEMessage mMessage;
+
+  union
+  {
+    // NOTIFY_IME_OF_TEXT_CHANGE specific data
+    struct
+    {
+      uint32_t mStartOffset;
+      uint32_t mOldEndOffset;
+      uint32_t mNewEndOffset;
+
+      uint32_t OldLength() const { return mOldEndOffset - mStartOffset; }
+      uint32_t NewLength() const { return mNewEndOffset - mStartOffset; }
+      int32_t AdditionalLength() const
+      {
+        return static_cast<int32_t>(mNewEndOffset - mOldEndOffset);
+      }
+      bool IsInInt32Range() const
+      {
+        return mStartOffset <= INT32_MAX &&
+               mOldEndOffset <= INT32_MAX &&
+               mNewEndOffset <= INT32_MAX;
+      }
+    } mTextChangeData;
+  };
+
+private:
+  IMENotification();
 };
 
 } // namespace widget
 } // namespace mozilla
 
 /**
  * The base class for all the widgets. It provides the interface for
  * all basic and necessary functionality.
@@ -478,17 +532,18 @@ class nsIWidget : public nsISupports {
 
   public:
     typedef mozilla::layers::Composer2D Composer2D;
     typedef mozilla::layers::CompositorChild CompositorChild;
     typedef mozilla::layers::LayerManager LayerManager;
     typedef mozilla::layers::LayerManagerComposite LayerManagerComposite;
     typedef mozilla::layers::LayersBackend LayersBackend;
     typedef mozilla::layers::PLayerTransactionChild PLayerTransactionChild;
-    typedef mozilla::widget::NotificationToIME NotificationToIME;
+    typedef mozilla::widget::IMEMessage IMEMessage;
+    typedef mozilla::widget::IMENotification IMENotification;
     typedef mozilla::widget::IMEState IMEState;
     typedef mozilla::widget::InputContext InputContext;
     typedef mozilla::widget::InputContextAction InputContextAction;
     typedef mozilla::widget::SizeConstraints SizeConstraints;
 
     // Used in UpdateThemeGeometries.
     struct ThemeGeometry {
       // The -moz-appearance value for the themed widget
@@ -1700,17 +1755,17 @@ public:
      * If this is called with an empty string it forces a full reload of the
      * menu system.
      */
     virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) = 0;
 
     /**
      * Notify IME of the specified notification.
      */
-    NS_IMETHOD NotifyIME(NotificationToIME aNotification) = 0;
+    NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) = 0;
 
     /*
      * Notifies the input context changes.
      */
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction) = 0;
 
     /*
@@ -1730,26 +1785,16 @@ public:
      * aLEDState is the result for current LED state of the key.
      * If the LED is 'ON', it returns TRUE, otherwise, FALSE.
      * If the platform doesn't support the LED state (or we cannot get the
      * state), this method returns NS_ERROR_NOT_IMPLEMENTED.
      */
     NS_IMETHOD GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) = 0;
 
     /*
-     * Text content of the focused node has changed
-     * aStart is the starting offset of the change
-     * aOldEnd is the ending offset of the change
-     * aNewEnd is the caret offset after the change
-     */
-    NS_IMETHOD NotifyIMEOfTextChange(uint32_t aStart,
-                                     uint32_t aOldEnd,
-                                     uint32_t aNewEnd) = 0;
-
-    /*
      * Retrieves preference for IME updates
      */
     virtual nsIMEUpdatePreference GetIMEUpdatePreference() = 0;
 
     /*
      * Call this method when a dialog is opened which has a default button.
      * The button's rectangle should be supplied in aButtonRect.
      */ 
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -153,23 +153,25 @@ IMEHandler::IsComposingOn(nsWindow* aWin
 #endif // #ifdef NS_ENABLE_TSF
 
   return nsIMM32Handler::IsComposingOn(aWindow);
 }
 
 // static
 nsresult
 IMEHandler::NotifyIME(nsWindow* aWindow,
-                      NotificationToIME aNotification)
+                      const IMENotification& aIMENotification)
 {
 #ifdef NS_ENABLE_TSF
   if (IsTSFAvailable()) {
-    switch (aNotification) {
+    switch (aIMENotification.mMessage) {
       case NOTIFY_IME_OF_SELECTION_CHANGE:
         return nsTextStore::OnSelectionChange();
+      case NOTIFY_IME_OF_TEXT_CHANGE:
+        return nsTextStore::OnTextChange(aIMENotification);
       case NOTIFY_IME_OF_FOCUS:
         return nsTextStore::OnFocusChange(true, aWindow,
                  aWindow->GetInputContext().mIMEState.mEnabled);
       case NOTIFY_IME_OF_BLUR:
         return nsTextStore::OnFocusChange(false, aWindow,
                  aWindow->GetInputContext().mIMEState.mEnabled);
       case REQUEST_TO_COMMIT_COMPOSITION:
         if (nsTextStore::IsComposingOn(aWindow)) {
@@ -182,17 +184,17 @@ IMEHandler::NotifyIME(nsWindow* aWindow,
         }
         return NS_OK;
       default:
         return NS_ERROR_NOT_IMPLEMENTED;
     }
   }
 #endif //NS_ENABLE_TSF
 
-  switch (aNotification) {
+  switch (aIMENotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
       nsIMM32Handler::CommitComposition(aWindow);
       return NS_OK;
     case REQUEST_TO_CANCEL_COMPOSITION:
       nsIMM32Handler::CancelComposition(aWindow);
       return NS_OK;
     case NOTIFY_IME_OF_COMPOSITION_UPDATE:
       nsIMM32Handler::OnUpdateComposition(aWindow);
@@ -208,31 +210,16 @@ IMEHandler::NotifyIME(nsWindow* aWindow,
       return NS_ERROR_NOT_IMPLEMENTED;
 #endif //NS_ENABLE_TSF
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 // static
-nsresult
-IMEHandler::NotifyIMEOfTextChange(uint32_t aStart,
-                                  uint32_t aOldEnd,
-                                  uint32_t aNewEnd)
-{
-#ifdef NS_ENABLE_TSF
-  if (IsTSFAvailable()) {
-    return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd);
-  }
-#endif //NS_ENABLE_TSF
-
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-// static
 nsIMEUpdatePreference
 IMEHandler::GetUpdatePreference()
 {
 #ifdef NS_ENABLE_TSF
   if (IsTSFAvailable()) {
     return nsTextStore::GetIMEUpdatePreference();
   }
 #endif //NS_ENABLE_TSF
@@ -272,17 +259,17 @@ IMEHandler::OnDestroyWindow(nsWindow* aW
 
 // static
 void
 IMEHandler::SetInputContext(nsWindow* aWindow,
                             InputContext& aInputContext,
                             const InputContextAction& aAction)
 {
   // FYI: If there is no composition, this call will do nothing.
-  NotifyIME(aWindow, REQUEST_TO_COMMIT_COMPOSITION);
+  NotifyIME(aWindow, IMENotification(REQUEST_TO_COMMIT_COMPOSITION));
 
   const InputContext& oldInputContext = aWindow->GetInputContext();
 
   // Assume that SetInputContext() is called only when aWindow has focus.
   sPluginHasFocus = (aInputContext.mIMEState.mEnabled == IMEState::PLUGIN);
 
   bool enable = WinUtils::IsIMEEnabled(aInputContext);
   bool adjustOpenState = (enable &&
--- a/widget/windows/WinIMEHandler.h
+++ b/widget/windows/WinIMEHandler.h
@@ -63,24 +63,17 @@ public:
    * Otherwise, false.
    */
   static bool IsComposingOn(nsWindow* aWindow);
 
   /**
    * Notifies IME of the notification (a request or an event).
    */
   static nsresult NotifyIME(nsWindow* aWindow,
-                            NotificationToIME aNotification);
-
-  /**
-   * Notifies IME of text change in the focused editable content.
-   */
-  static nsresult NotifyIMEOfTextChange(uint32_t aStart,
-                                        uint32_t aOldEnd,
-                                        uint32_t aNewEnd);
+                            const IMENotification& aIMENotification);
 
   /**
    * Returns update preferences.
    */
   static nsIMEUpdatePreference GetUpdatePreference();
 
   /**
    * Returns IME open state on the window.
--- a/widget/windows/nsTextStore.cpp
+++ b/widget/windows/nsTextStore.cpp
@@ -3181,39 +3181,41 @@ nsTextStore::GetIMEUpdatePreference()
                        nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE |
                        nsIMEUpdatePreference::NOTIFY_DURING_DEACTIVE);
     }
   }
   return nsIMEUpdatePreference(notifications);
 }
 
 nsresult
-nsTextStore::OnTextChangeInternal(uint32_t aStart,
-                                  uint32_t aOldEnd,
-                                  uint32_t aNewEnd)
+nsTextStore::OnTextChangeInternal(const IMENotification& aIMENotification)
 {
   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
-         ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(aStart=%lu, "
-          "aOldEnd=%lu, aNewEnd=%lu), mSink=0x%p, mSinkMask=%s, "
+         ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(aIMENotification={ "
+          "mMessage=0x%08X, mTextChangeData={ mStartOffset=%lu, "
+          "mOldEndOffset=%lu, mNewEndOffset=%lu}), mSink=0x%p, mSinkMask=%s, "
           "mComposition.IsComposing()=%s",
-          this, aStart, aOldEnd, aNewEnd, mSink.get(),
+          this, aIMENotification.mMessage,
+          aIMENotification.mTextChangeData.mStartOffset,
+          aIMENotification.mTextChangeData.mOldEndOffset,
+          aIMENotification.mTextChangeData.mNewEndOffset, mSink.get(),
           GetSinkMaskNameStr(mSinkMask).get(),
           GetBoolName(mComposition.IsComposing())));
 
   if (IsReadLocked()) {
     return NS_OK;
   }
 
   mSelection.MarkDirty();
 
   if (!mSink || !(mSinkMask & TS_AS_TEXT_CHANGE)) {
     return NS_OK;
   }
 
-  if (aStart >= INT32_MAX || aOldEnd >= INT32_MAX || aNewEnd >= INT32_MAX) {
+  if (!aIMENotification.mTextChangeData.IsInInt32Range()) {
     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
            ("TSF: 0x%p   nsTextStore::OnTextChangeInternal() FAILED due to "
             "offset is too big for calling mSink->OnTextChange()...",
             this));
     return NS_OK;
   }
 
   // Some TIPs are confused by text change notification during composition.
@@ -3224,22 +3226,25 @@ nsTextStore::OnTextChangeInternal(uint32
            ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(), "
             "committing the composition for avoiding making TIP confused...",
             this));
     CommitCompositionInternal(false);
     return NS_OK;
   }
 
   TS_TEXTCHANGE textChange;
-  textChange.acpStart = static_cast<LONG>(aStart);
-  textChange.acpOldEnd = static_cast<LONG>(aOldEnd);
-  textChange.acpNewEnd = static_cast<LONG>(aNewEnd);
+  textChange.acpStart =
+    static_cast<LONG>(aIMENotification.mTextChangeData.mStartOffset);
+  textChange.acpOldEnd =
+    static_cast<LONG>(aIMENotification.mTextChangeData.mOldEndOffset);
+  textChange.acpNewEnd =
+    static_cast<LONG>(aIMENotification.mTextChangeData.mNewEndOffset);
 
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
-         ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(), calling"
+         ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(), calling "
           "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
           "acpNewEnd=%ld })...", this, textChange.acpStart,
           textChange.acpOldEnd, textChange.acpNewEnd));
   mSink->OnTextChange(0, &textChange);
 
   return NS_OK;
 }
 
--- a/widget/windows/nsTextStore.h
+++ b/widget/windows/nsTextStore.h
@@ -100,16 +100,17 @@ public: /*ITfActiveLanguageProfileNotify
   STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile,
                            BOOL fActivated);
 
 public: /*ITfInputProcessorProfileActivationSink*/
   STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID,
                            HKL, DWORD);
 
 protected:
+  typedef mozilla::widget::IMENotification IMENotification;
   typedef mozilla::widget::IMEState IMEState;
   typedef mozilla::widget::InputContext InputContext;
   typedef mozilla::widget::InputContextAction InputContextAction;
 
 public:
   static void     Initialize(void);
   static void     Terminate(void);
 
@@ -130,22 +131,20 @@ public:
 
   static void SetInputContext(nsWindowBase* aWidget,
                               const InputContext& aContext,
                               const InputContextAction& aAction);
 
   static nsresult OnFocusChange(bool aGotFocus,
                                 nsWindowBase* aFocusedWidget,
                                 IMEState::Enabled aIMEEnabled);
-  static nsresult OnTextChange(uint32_t aStart,
-                               uint32_t aOldEnd,
-                               uint32_t aNewEnd)
+  static nsresult OnTextChange(const IMENotification& aIMENotification)
   {
     NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
-    return sTsfTextStore->OnTextChangeInternal(aStart, aOldEnd, aNewEnd);
+    return sTsfTextStore->OnTextChangeInternal(aIMENotification);
   }
 
   static nsresult OnSelectionChange(void)
   {
     NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
     return sTsfTextStore->OnSelectionChangeInternal();
   }
 
@@ -253,17 +252,17 @@ protected:
   // this is called during IME composing.  aDispatchTextEvent should be true
   // only when this is called from SetSelection.  Because otherwise, the text
   // event should not be sent from here.
   HRESULT  SetSelectionInternal(const TS_SELECTION_ACP*,
                                 bool aDispatchTextEvent = false);
   bool     InsertTextAtSelectionInternal(const nsAString &aInsertStr,
                                          TS_TEXTCHANGE* aTextChange);
   void     CommitCompositionInternal(bool);
-  nsresult OnTextChangeInternal(uint32_t, uint32_t, uint32_t);
+  nsresult OnTextChangeInternal(const IMENotification& aIMENotification);
   nsresult OnSelectionChangeInternal(void);
   HRESULT  GetDisplayAttribute(ITfProperty* aProperty,
                                ITfRange* aRange,
                                TF_DISPLAYATTRIBUTE* aResult);
   HRESULT  RestartCompositionIfNecessary(ITfRange* pRangeNew = nullptr);
 
   // Following methods record composing action(s) to mPendingActions.
   // They will be flushed FlushPendingActions().
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6609,19 +6609,19 @@ nsWindow::OnSysColorChanged()
  ** BLOCK: IME management and accessibility
  **
  ** Handles managing IME input and accessibility.
  **
  **************************************************************
  **************************************************************/
 
 NS_IMETHODIMP
-nsWindow::NotifyIME(NotificationToIME aNotification)
-{
-  return IMEHandler::NotifyIME(this, aNotification);
+nsWindow::NotifyIME(const IMENotification& aIMENotification)
+{
+  return IMEHandler::NotifyIME(this, aIMENotification);
 }
 
 NS_IMETHODIMP_(void)
 nsWindow::SetInputContext(const InputContext& aContext,
                           const InputContextAction& aAction)
 {
   InputContext newInputContext = aContext;
   IMEHandler::SetInputContext(this, newInputContext, aAction);
@@ -6646,24 +6646,16 @@ nsWindow::GetToggledKeyState(uint32_t aK
 #ifdef DEBUG_KBSTATE
   PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("GetToggledKeyState\n"));
 #endif 
   NS_ENSURE_ARG_POINTER(aLEDState);
   *aLEDState = (::GetKeyState(aKeyCode) & 1) != 0;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsWindow::NotifyIMEOfTextChange(uint32_t aStart,
-                                uint32_t aOldEnd,
-                                uint32_t aNewEnd)
-{
-  return IMEHandler::NotifyIMEOfTextChange(aStart, aOldEnd, aNewEnd);
-}
-
 nsIMEUpdatePreference
 nsWindow::GetIMEUpdatePreference()
 {
   return IMEHandler::GetUpdatePreference();
 }
 
 #ifdef ACCESSIBILITY
 #ifdef DEBUG
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -172,31 +172,28 @@ public:
 
   virtual nsresult        SynthesizeNativeMouseScrollEvent(nsIntPoint aPoint,
                                                            uint32_t aNativeMessage,
                                                            double aDeltaX,
                                                            double aDeltaY,
                                                            double aDeltaZ,
                                                            uint32_t aModifierFlags,
                                                            uint32_t aAdditionalFlags);
-  NS_IMETHOD              NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE;
+  NS_IMETHOD              NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE;
   NS_IMETHOD_(void)       SetInputContext(const InputContext& aContext,
                                           const InputContextAction& aAction);
   NS_IMETHOD_(InputContext) GetInputContext();
   NS_IMETHOD              GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState);
   NS_IMETHOD              RegisterTouchWindow();
   NS_IMETHOD              UnregisterTouchWindow();
 #ifdef MOZ_XUL
   virtual void            SetTransparencyMode(nsTransparencyMode aMode);
   virtual nsTransparencyMode GetTransparencyMode();
   virtual void            UpdateOpaqueRegion(const nsIntRegion& aOpaqueRegion);
 #endif // MOZ_XUL
-  NS_IMETHOD              NotifyIMEOfTextChange(uint32_t aStart,
-                                                uint32_t aOldEnd,
-                                                uint32_t aNewEnd) MOZ_OVERRIDE;
   virtual nsIMEUpdatePreference GetIMEUpdatePreference();
   NS_IMETHOD              GetNonClientMargins(nsIntMargin &margins);
   NS_IMETHOD              SetNonClientMargins(nsIntMargin &margins);
   void                    SetDrawsInTitlebar(bool aState);
   mozilla::TemporaryRef<mozilla::gfx::DrawTarget> StartRemoteDrawing() MOZ_OVERRIDE;
   virtual void            EndRemoteDrawing() MOZ_OVERRIDE;
 
   /**
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -1581,54 +1581,48 @@ MetroWidget::SetInputContext(const Input
 
 NS_IMETHODIMP_(nsIWidget::InputContext)
 MetroWidget::GetInputContext()
 {
   return mInputContext;
 }
 
 NS_IMETHODIMP
-MetroWidget::NotifyIME(NotificationToIME aNotification)
+MetroWidget::NotifyIME(const IMENotification& aIMENotification)
 {
-  switch (aNotification) {
+  switch (aIMENotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
       nsTextStore::CommitComposition(false);
       return NS_OK;
     case REQUEST_TO_CANCEL_COMPOSITION:
       nsTextStore::CommitComposition(true);
       return NS_OK;
     case NOTIFY_IME_OF_FOCUS:
       return nsTextStore::OnFocusChange(true, this,
                                         mInputContext.mIMEState.mEnabled);
     case NOTIFY_IME_OF_BLUR:
       return nsTextStore::OnFocusChange(false, this,
                                         mInputContext.mIMEState.mEnabled);
     case NOTIFY_IME_OF_SELECTION_CHANGE:
       return nsTextStore::OnSelectionChange();
+    case NOTIFY_IME_OF_TEXT_CHANGE:
+      return nsTextStore::OnTextChange(aIMENotification);
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 NS_IMETHODIMP
 MetroWidget::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState)
 {
   NS_ENSURE_ARG_POINTER(aLEDState);
   *aLEDState = (::GetKeyState(aKeyCode) & 1) != 0;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-MetroWidget::NotifyIMEOfTextChange(uint32_t aStart,
-                                   uint32_t aOldEnd,
-                                   uint32_t aNewEnd)
-{
-  return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd);
-}
-
 nsIMEUpdatePreference
 MetroWidget::GetIMEUpdatePreference()
 {
   return nsTextStore::GetIMEUpdatePreference();
 }
 
 NS_IMETHODIMP
 MetroWidget::ReparentNativeWidget(nsIWidget* aNewParent)
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -155,21 +155,18 @@ public:
                                         LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                                         bool* aAllowRetaining = nullptr);
   virtual void GetPreferredCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aHints) { aHints.AppendElement(mozilla::layers::LayersBackend::LAYERS_D3D11); }
 
   // IME related interfaces
   NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                     const InputContextAction& aAction);
   NS_IMETHOD_(nsIWidget::InputContext) GetInputContext();
-  NS_IMETHOD    NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE;
+  NS_IMETHOD    NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE;
   NS_IMETHOD    GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState);
-  NS_IMETHOD    NotifyIMEOfTextChange(uint32_t aStart,
-                                      uint32_t aOldEnd,
-                                      uint32_t aNewEnd) MOZ_OVERRIDE;
   virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE;
 
   // FrameworkView helpers
   void SizeModeChanged();
   void Activated(bool aActiveated);
   void Paint(const nsIntRegion& aInvalidRegion);
 
   MetroWidget* MetroWidget::GetTopLevelWindow(bool aStopOnDialogOrPopup) { return this; }
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -372,30 +372,32 @@ PuppetWidget::IMEEndComposition(bool aCa
   WidgetCompositionEvent compEvent(true, NS_COMPOSITION_END, this);
   InitEvent(compEvent, nullptr);
   compEvent.mSeqno = mIMELastReceivedSeqno;
   DispatchEvent(&compEvent, status);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PuppetWidget::NotifyIME(NotificationToIME aNotification)
+PuppetWidget::NotifyIME(const IMENotification& aIMENotification)
 {
-  switch (aNotification) {
+  switch (aIMENotification.mMessage) {
     case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
     case REQUEST_TO_COMMIT_COMPOSITION:
       return IMEEndComposition(false);
     case REQUEST_TO_CANCEL_COMPOSITION:
       return IMEEndComposition(true);
     case NOTIFY_IME_OF_FOCUS:
       return NotifyIMEOfFocusChange(true);
     case NOTIFY_IME_OF_BLUR:
       return NotifyIMEOfFocusChange(false);
     case NOTIFY_IME_OF_SELECTION_CHANGE:
       return NotifyIMEOfSelectionChange();
+    case NOTIFY_IME_OF_TEXT_CHANGE:
+      return NotifyIMEOfTextChange(aIMENotification);
     case NOTIFY_IME_OF_COMPOSITION_UPDATE:
       return NotifyIMEOfUpdateComposition();
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 NS_IMETHODIMP_(void)
@@ -521,20 +523,18 @@ PuppetWidget::GetIMEUpdatePreference()
                                nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
                                nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
 #else
   // B2G doesn't handle IME as widget-level.
   return nsIMEUpdatePreference();
 #endif
 }
 
-NS_IMETHODIMP
-PuppetWidget::NotifyIMEOfTextChange(uint32_t aStart,
-                                    uint32_t aEnd,
-                                    uint32_t aNewEnd)
+nsresult
+PuppetWidget::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
 {
 #ifndef MOZ_CROSS_PROCESS_IME
   return NS_OK;
 #endif
 
   if (!mTabChild)
     return NS_ERROR_FAILURE;
 
@@ -546,17 +546,20 @@ PuppetWidget::NotifyIMEOfTextChange(uint
 
   if (queryEvent.mSucceeded) {
     mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
   }
 
   // TabParent doesn't this this to cache.  we don't send the notification
   // if parent process doesn't request NOTIFY_TEXT_CHANGE.
   if (mIMEPreferenceOfParent.WantTextChange()) {
-    mTabChild->SendNotifyIMETextChange(aStart, aEnd, aNewEnd);
+    mTabChild->SendNotifyIMETextChange(
+      aIMENotification.mTextChangeData.mStartOffset,
+      aIMENotification.mTextChangeData.mOldEndOffset,
+      aIMENotification.mTextChangeData.mNewEndOffset);
   }
   return NS_OK;
 }
 
 nsresult
 PuppetWidget::NotifyIMEOfSelectionChange()
 {
 #ifndef MOZ_CROSS_PROCESS_IME
--- a/widget/xpwidgets/PuppetWidget.h
+++ b/widget/xpwidgets/PuppetWidget.h
@@ -144,22 +144,20 @@ public:
 
   virtual LayerManager*
   GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                   LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                   LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                   bool* aAllowRetaining = nullptr);
   virtual gfxASurface*      GetThebesSurface();
 
-  NS_IMETHOD NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE;
+  NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE;
   NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                     const InputContextAction& aAction);
   NS_IMETHOD_(InputContext) GetInputContext();
-  NS_IMETHOD NotifyIMEOfTextChange(uint32_t aOffset, uint32_t aEnd,
-                                   uint32_t aNewEnd) MOZ_OVERRIDE;
   virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE;
 
   NS_IMETHOD SetCursor(nsCursor aCursor);
   NS_IMETHOD SetCursor(imgIContainer* aCursor,
                        uint32_t aHotspotX, uint32_t aHotspotY)
   {
     return nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
   }
@@ -179,16 +177,17 @@ private:
   nsresult Paint();
 
   void SetChild(PuppetWidget* aChild);
 
   nsresult IMEEndComposition(bool aCancel);
   nsresult NotifyIMEOfFocusChange(bool aFocus);
   nsresult NotifyIMEOfSelectionChange();
   nsresult NotifyIMEOfUpdateComposition();
+  nsresult NotifyIMEOfTextChange(const IMENotification& aIMENotification);
 
   class PaintTask : public nsRunnable {
   public:
     NS_DECL_NSIRUNNABLE
     PaintTask(PuppetWidget* widget) : mWidget(widget) {}
     void Revoke() { mWidget = nullptr; }
   private:
     PuppetWidget* mWidget;
--- a/widget/xpwidgets/nsBaseWidget.h
+++ b/widget/xpwidgets/nsBaseWidget.h
@@ -181,22 +181,21 @@ public:
   virtual bool            ShowsResizeIndicator(nsIntRect* aResizerRect);
   virtual void            FreeNativeData(void * data, uint32_t aDataType) {}
   NS_IMETHOD              BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                                           int32_t aHorizontal,
                                           int32_t aVertical);
   NS_IMETHOD              BeginMoveDrag(mozilla::WidgetMouseEvent* aEvent);
   virtual nsresult        ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual nsresult        ForceUpdateNativeMenuAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
-  NS_IMETHOD              NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD              NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              SetLayersAcceleration(bool aEnabled);
   virtual bool            GetLayersAcceleration() { return mUseLayersAcceleration; }
   virtual bool            ComputeShouldAccelerate(bool aDefault);
   NS_IMETHOD              GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) { return NS_ERROR_NOT_IMPLEMENTED; }
-  NS_IMETHOD              NotifyIMEOfTextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE { return nsIMEUpdatePreference(); }
   NS_IMETHOD              OnDefaultButtonLoaded(const nsIntRect &aButtonRect) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              OverrideSystemMouseScrollSpeed(double aOriginalDeltaX,
                                                          double aOriginalDeltaY,
                                                          double& aOverriddenDeltaX,
                                                          double& aOverriddenDeltaY);
   virtual already_AddRefed<nsIWidget>
   CreateChild(const nsIntRect  &aRect,