Bug 917322 part.7 TextEventDispatcher should manage if it has composition r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 28 Jan 2015 15:27:31 +0900
changeset 254985 994fee1e166c6a7a9ba0a8b33366b886067f47d1
parent 254984 80f1425197337ed5807ad32a67fd58ed009e2013
child 254986 a80685d0ff6f1766090a9dc3aa995b4f0cc7847f
push id721
push userjlund@mozilla.com
push dateTue, 21 Apr 2015 23:03:33 +0000
treeherdermozilla-release@d27c9211ebb3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs917322
milestone38.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 917322 part.7 TextEventDispatcher should manage if it has composition r=smaug
dom/events/TextComposition.cpp
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/TextEventDispatcher.cpp
widget/TextEventDispatcher.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/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/winrt/MetroWidget.cpp
widget/windows/winrt/MetroWidget.h
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -335,45 +335,24 @@ TextComposition::RequestToCommit(nsIWidg
     AutoRestore<bool> saveRequestingCommit(mIsRequestingCommit);
     if (aDiscard) {
       mIsRequestingCancel = true;
       mIsRequestingCommit = false;
     } else {
       mIsRequestingCancel = false;
       mIsRequestingCommit = true;
     }
-    if (!mIsSynthesizedForTests) {
-      // FYI: CompositionEvents caused by a call of NotifyIME() may be
-      //      discarded by PresShell if it's not safe to dispatch the event.
-      nsresult rv =
-        aWidget->NotifyIME(IMENotification(aDiscard ?
-                                             REQUEST_TO_CANCEL_COMPOSITION :
-                                             REQUEST_TO_COMMIT_COMPOSITION));
-      if (rv == NS_ERROR_NOT_IMPLEMENTED) {
-        return rv;
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    } else {
-      // Emulates to commit or cancel the composition
-      // FYI: These events may be discarded by PresShell if it's not safe to
-      //      dispatch the event.
-      nsCOMPtr<nsIWidget> widget(aWidget);
-      nsAutoString commitData(aDiscard ? EmptyString() : lastData);
-      bool isChanging = commitData != mLastData;
-      uint32_t message =
-        isChanging ? NS_COMPOSITION_COMMIT : NS_COMPOSITION_COMMIT_AS_IS;
-      WidgetCompositionEvent commitEvent(true, message, widget);
-      if (commitEvent.message == NS_COMPOSITION_COMMIT) {
-        commitEvent.mData = commitData;
-      }
-      commitEvent.mFlags.mIsSynthesizedForTests = true;
-      nsEventStatus status = nsEventStatus_eIgnore;
-      widget->DispatchEvent(&commitEvent, status);
+    // FYI: CompositionEvents caused by a call of NotifyIME() may be
+    //      discarded by PresShell if it's not safe to dispatch the event.
+    nsresult rv =
+      aWidget->NotifyIME(IMENotification(aDiscard ?
+                                           REQUEST_TO_CANCEL_COMPOSITION :
+                                           REQUEST_TO_COMMIT_COMPOSITION));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
   }
 
   mRequestedToCommitOrCancel = true;
 
   // If the request is performed synchronously, this must be already destroyed.
   if (Destroyed()) {
     return NS_OK;
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -423,18 +423,18 @@ PuppetWidget::IMEEndComposition(bool aCa
 
   WidgetCompositionEvent compositionEndEvent(true, NS_COMPOSITION_END, this);
   InitEvent(compositionEndEvent, nullptr);
   compositionEndEvent.mSeqno = mIMELastReceivedSeqno;
   DispatchEvent(&compositionEndEvent, status);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-PuppetWidget::NotifyIME(const IMENotification& aIMENotification)
+nsresult
+PuppetWidget::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
   switch (aIMENotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
       return IMEEndComposition(false);
     case REQUEST_TO_CANCEL_COMPOSITION:
       return IMEEndComposition(true);
     case NOTIFY_IME_OF_FOCUS:
       return NotifyIMEOfFocusChange(true);
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -158,17 +158,16 @@ public:
   { return eTransparencyTransparent; }
 
   virtual LayerManager*
   GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                   LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                   LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                   bool* aAllowRetaining = nullptr) 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() MOZ_OVERRIDE;
   virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE;
 
   NS_IMETHOD SetCursor(nsCursor aCursor) MOZ_OVERRIDE;
   NS_IMETHOD SetCursor(imgIContainer* aCursor,
                        uint32_t aHotspotX, uint32_t aHotspotY) MOZ_OVERRIDE
@@ -199,16 +198,19 @@ public:
 
   // Get the screen position of the application window.
   nsIntPoint GetWindowPosition();
 
 protected:
   bool mEnabled;
   bool mVisible;
 
+  virtual nsresult NotifyIMEInternal(
+                     const IMENotification& aIMENotification) MOZ_OVERRIDE;
+
 private:
   nsresult Paint();
 
   void SetChild(PuppetWidget* aChild);
 
   nsresult IMEEndComposition(bool aCancel);
   nsresult NotifyIMEOfFocusChange(bool aFocus);
   nsresult NotifyIMEOfSelectionChange(const IMENotification& aIMENotification);
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -18,37 +18,40 @@ namespace widget {
 /******************************************************************************
  * TextEventDispatcher
  *****************************************************************************/
 
 TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
   : mWidget(aWidget)
   , mInitialized(false)
   , mForTests(false)
+  , mIsComposing(false)
 {
   MOZ_RELEASE_ASSERT(mWidget, "aWidget must not be nullptr");
 }
 
 nsresult
 TextEventDispatcher::Init()
 {
   if (mInitialized) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
+  MOZ_ASSERT(!mIsComposing, "There should not be active composition");
   mInitialized = true;
   mForTests = false;
   return NS_OK;
 }
 
 nsresult
 TextEventDispatcher::InitForTests()
 {
   if (mInitialized) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
+  MOZ_ASSERT(!mIsComposing, "There should not be active composition");
   mInitialized = true;
   mForTests = true;
   return NS_OK;
 }
 
 void
 TextEventDispatcher::OnDestroyWidget()
 {
@@ -80,16 +83,21 @@ TextEventDispatcher::StartComposition(ns
 {
   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;
   nsCOMPtr<nsIWidget> widget(mWidget);
   WidgetCompositionEvent compositionStartEvent(true, NS_COMPOSITION_START,
                                                widget);
   InitEvent(compositionStartEvent);
   rv = widget->DispatchEvent(&compositionStartEvent, aStatus);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -103,32 +111,57 @@ TextEventDispatcher::CommitComposition(n
 {
   aStatus = nsEventStatus_eIgnore;
 
   nsresult rv = GetState();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  // End current composition and make this free for other IMEs.
+  mIsComposing = false;
+  mInitialized = false;
+
   nsCOMPtr<nsIWidget> widget(mWidget);
   uint32_t message = aCommitString ? NS_COMPOSITION_COMMIT :
                                      NS_COMPOSITION_COMMIT_AS_IS;
   WidgetCompositionEvent compositionCommitEvent(true, message, widget);
   InitEvent(compositionCommitEvent);
   if (message == NS_COMPOSITION_COMMIT) {
     compositionCommitEvent.mData = *aCommitString;
   }
   rv = widget->DispatchEvent(&compositionCommitEvent, aStatus);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
+nsresult
+TextEventDispatcher::NotifyIME(const IMENotification& aIMENotification)
+{
+  switch (aIMENotification.mMessage) {
+    case REQUEST_TO_COMMIT_COMPOSITION: {
+      NS_ASSERTION(mIsComposing, "Why is this requested without composition?");
+      nsEventStatus status = nsEventStatus_eIgnore;
+      CommitComposition(status);
+      return NS_OK;
+    }
+    case REQUEST_TO_CANCEL_COMPOSITION: {
+      NS_ASSERTION(mIsComposing, "Why is this requested without composition?");
+      nsEventStatus status = nsEventStatus_eIgnore;
+      CommitComposition(status, &EmptyString());
+      return NS_OK;
+    }
+    default:
+      return NS_ERROR_NOT_IMPLEMENTED;
+  }
+}
+
 /******************************************************************************
  * TextEventDispatcher::PendingComposition
  *****************************************************************************/
 
 TextEventDispatcher::PendingComposition::PendingComposition()
 {
   Clear();
 }
--- a/widget/TextEventDispatcher.h
+++ b/widget/TextEventDispatcher.h
@@ -12,16 +12,18 @@
 #include "mozilla/EventForwards.h"
 #include "mozilla/TextRange.h"
 
 class nsIWidget;
 
 namespace mozilla {
 namespace widget {
 
+struct IMENotification;
+
 /**
  * TextEventDispatcher is a helper class for dispatching widget events defined
  * in TextEvents.h.  Currently, this is a helper for dispatching
  * WidgetCompositionEvent.  However, WidgetKeyboardEvent and/or
  * WidgetQueryContentEvent may be supported by this class in the future.
  * An instance of this class is created by nsIWidget instance and owned by it.
  * This is typically created only by the top level widgets because only they
  * handle IME.
@@ -59,16 +61,22 @@ public:
    *                NS_ERROR_NOT_INITIALIZED: Init() or InitForTests() should
    *                                          be called.
    *                NS_ERROR_NOT_AVAILABLE: The widget isn't available for
    *                                        composition.
    */
   nsresult GetState() const;
 
   /**
+   * IsComposing() returns true after calling StartComposition() and before
+   * calling CommitComposition().
+   */
+  bool IsComposing() const { return mIsComposing; }
+
+  /**
    * StartComposition() starts composition explicitly.
    */
   nsresult StartComposition(nsEventStatus& aStatus);
 
   /**
    * CommitComposition() commits composition.
    *
    * @param aCommitString   If this is null, commits with the last composition
@@ -130,16 +138,21 @@ public:
    * AppendClauseToPendingComposition() and/or
    * SetCaretInPendingComposition().
    */
   nsresult FlushPendingComposition(nsEventStatus& aStatus)
   {
     return mPendingComposition.Flush(this, aStatus);
   }
 
+  /**
+   * @see nsIWidget::NotifyIME()
+   */
+  nsresult NotifyIME(const IMENotification& aIMENotification);
+
 private:
   // mWidget is owner of the instance.  When this is created, this is set.
   // And when mWidget is released, this is cleared by OnDestroyWidget().
   // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
   // return true).
   nsIWidget* mWidget;
 
   // mPendingComposition stores new composition string temporarily.
@@ -163,16 +176,18 @@ private:
     TextRange mCaret;
 
     void EnsureClauseArray();
   };
   PendingComposition mPendingComposition;
 
   bool mInitialized;
   bool mForTests;
+  // See IsComposing().
+  bool mIsComposing;
 
   /**
    * InitEvent() initializes aEvent.  This must be called before dispatching
    * the event.
    */
   void InitEvent(WidgetCompositionEvent& aEvent) const;
 };
 
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2042,18 +2042,18 @@ nsWindow::UserActivity()
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
 
   if (mIdleService) {
     mIdleService->ResetIdleTimeOut(0);
   }
 }
 
-NS_IMETHODIMP
-nsWindow::NotifyIME(const IMENotification& aIMENotification)
+nsresult
+nsWindow::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
     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:
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -127,17 +127,16 @@ 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(const IMENotification& aIMENotification) MOZ_OVERRIDE;
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
 
     nsresult NotifyIMEOfTextChange(const IMENotification& aIMENotification);
     virtual nsIMEUpdatePreference GetIMEUpdatePreference();
 
     LayerManager* GetLayerManager (PLayerTransactionChild* aShadowManager = nullptr,
@@ -223,16 +222,19 @@ protected:
             return mStart < 0;
         }
     };
     nsAutoTArray<IMEChange, 4> mIMETextChanges;
     bool mIMESelectionChanged;
 
     InputContext mInputContext;
 
+    virtual nsresult NotifyIMEInternal(
+                         const IMENotification& aIMENotification) MOZ_OVERRIDE;
+
     static void DumpWindows();
     static void DumpWindows(const nsTArray<nsWindow*>& wins, int indent = 0);
     static void LogWindow(nsWindow *win, int index, int indent);
 
 private:
     void InitKeyEvent(mozilla::WidgetKeyboardEvent& event,
                       mozilla::AndroidGeckoEvent& key,
                       ANPEvent* pluginEvent);
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -420,17 +420,16 @@ public:
 
   NS_IMETHOD        GetAttention(int32_t aCycleCount) MOZ_OVERRIDE;
 
   virtual bool HasPendingInputEvent() MOZ_OVERRIDE;
 
   NS_IMETHOD        ActivateNativeMenuItemAt(const nsAString& indexString) MOZ_OVERRIDE;
   NS_IMETHOD        ForceUpdateNativeMenuAt(const nsAString& indexString) 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() MOZ_OVERRIDE;
   NS_IMETHOD        AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
                       NativeKeyBindingsType aType,
                       const mozilla::WidgetKeyboardEvent& aEvent,
                       DoCommandCallback aCallback,
@@ -563,16 +562,19 @@ protected:
   void UpdateTitlebarCGContext();
 
   nsIntRect RectContainingTitlebarControls();
   void UpdateVibrancy(const nsTArray<ThemeGeometry>& aThemeGeometries);
   mozilla::VibrancyManager& EnsureVibrancyManager();
 
   nsIWidget* GetWidgetForListenerEvents();
 
+  virtual nsresult NotifyIMEInternal(
+                     const IMENotification& aIMENotification) MOZ_OVERRIDE;
+
 protected:
 
   NSView<mozView>*      mView;      // my parallel cocoa view (ChildView or NativeScrollbarView), [STRONG]
   nsRefPtr<mozilla::widget::TextInputHandler> mTextInputHandler;
   InputContext          mInputContext;
 
   NSView<mozView>*      mParentView;
   nsIWidget*            mParentWidget;
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1574,18 +1574,18 @@ void nsChildView::UpdateCurrentInputEven
 
 bool nsChildView::HasPendingInputEvent()
 {
   return DoHasPendingInputEvent();
 }
 
 #pragma mark -
 
-NS_IMETHODIMP
-nsChildView::NotifyIME(const IMENotification& aIMENotification)
+nsresult
+nsChildView::NotifyIMEInternal(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);
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -325,17 +325,16 @@ 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(const IMENotification& aIMENotification) MOZ_OVERRIDE;
     NS_IMETHOD_(void) SetInputContext(
                         const InputContext& aContext,
                         const InputContextAction& aAction) MOZ_OVERRIDE;
     NS_IMETHOD_(InputContext) GetInputContext() MOZ_OVERRIDE
     {
       NSView* view = mWindow ? [mWindow contentView] : nil;
       if (view) {
         mInputContext.mNativeIMEContext = [view inputContext];
@@ -376,16 +375,19 @@ protected:
   virtual already_AddRefed<nsIWidget>
   AllocateChildPopupWidget() MOZ_OVERRIDE
   {
     static NS_DEFINE_IID(kCPopUpCID, NS_POPUP_CID);
     nsCOMPtr<nsIWidget> widget = do_CreateInstance(kCPopUpCID);
     return widget.forget();
   }
 
+  virtual nsresult NotifyIMEInternal(
+                     const IMENotification& aIMENotification) MOZ_OVERRIDE;
+
   nsIWidget*           mParent;         // if we're a popup, this is our parent [WEAK]
   BaseWindow*          mWindow;         // our cocoa window [STRONG]
   WindowDelegate*      mDelegate;       // our delegate for processing window msgs [STRONG]
   nsRefPtr<nsMenuBarX> mMenuBar;
   NSWindow*            mSheetWindowParent; // if this is a sheet, this is the NSWindow it's attached to
   nsChildView*         mPopupContentView; // if this is a popup, this is its content widget
   int32_t              mShadowStyle;
 
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -2106,18 +2106,18 @@ void nsCocoaWindow::SetPopupWindowLevel(
   else {
     // Otherwise, this is a top-level or parent popup. Parent popups always
     // appear just above their parent and essentially ignore the level.
     [mWindow setLevel:NSPopUpMenuWindowLevel];
     [mWindow setHidesOnDeactivate:NO];
   }
 }
 
-NS_IMETHODIMP
-nsCocoaWindow::NotifyIME(const IMENotification& aIMENotification)
+nsresult
+nsCocoaWindow::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
   switch (aIMENotification.mMessage) {
     case NOTIFY_IME_OF_FOCUS:
       if (mInputContext.IsPasswordEditor()) {
         TextInputHandler::EnableSecureEventInput();
       } else {
         TextInputHandler::EnsureSecureEventInputDisabled();
       }
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -5972,18 +5972,18 @@ nsWindow::DispatchRestoreEventAccessible
 nsChildWindow::nsChildWindow()
 {
 }
 
 nsChildWindow::~nsChildWindow()
 {
 }
 
-NS_IMETHODIMP
-nsWindow::NotifyIME(const IMENotification& aIMENotification)
+nsresult
+nsWindow::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
     if (MOZ_UNLIKELY(!mIMModule)) {
         return NS_ERROR_NOT_AVAILABLE;
     }
     switch (aIMENotification.mMessage) {
         case REQUEST_TO_COMMIT_COMPOSITION:
         case REQUEST_TO_CANCEL_COMPOSITION:
             return mIMModule->EndIMEComposition(this);
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -255,17 +255,16 @@ 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(const IMENotification& aIMENotification) MOZ_OVERRIDE;
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction) MOZ_OVERRIDE;
     NS_IMETHOD_(InputContext) GetInputContext() MOZ_OVERRIDE;
     virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE;
     bool ExecuteNativeKeyBindingRemapped(
                         NativeKeyBindingsType aType,
                         const mozilla::WidgetKeyboardEvent& aEvent,
                         DoCommandCallback aCallback,
@@ -313,16 +312,20 @@ protected:
     void DispatchDeactivateEvent(void);
     void DispatchResized(int32_t aWidth, int32_t aHeight);
 
     // Helper for SetParent and ReparentNativeWidget.
     void ReparentNativeWidgetInternal(nsIWidget* aNewParent,
                                       GtkWidget* aNewContainer,
                                       GdkWindow* aNewParentWindow,
                                       GtkWidget* aOldContainer);
+
+    virtual nsresult NotifyIMEInternal(
+                         const IMENotification& aIMENotification) MOZ_OVERRIDE;
+
     nsCOMPtr<nsIWidget> mParent;
     // Is this a toplevel window?
     bool                mIsTopLevel;
     // Has this widget been destroyed yet?
     bool                mIsDestroyed;
 
     // This is a flag that tracks if we need to resize a widget or
     // window when we show it.
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -68,16 +68,17 @@ static int32_t gNumWidgets;
 #ifdef XP_MACOSX
 #include "nsCocoaFeatures.h"
 #endif
 
 nsIRollupListener* nsBaseWidget::gRollupListener = nullptr;
 
 using namespace mozilla::layers;
 using namespace mozilla::ipc;
+using namespace mozilla::widget;
 using namespace mozilla;
 using base::Thread;
 
 nsIContent* nsBaseWidget::mLastRollup = nullptr;
 // Global user preference for disabling native theme. Used
 // in NativeWindowTheme.
 bool            gDisableNativeTheme               = false;
 
@@ -1580,16 +1581,35 @@ nsBaseWidget::NotifyUIStateChanged(UISta
   if (doc) {
     nsPIDOMWindow* win = doc->GetWindow();
     if (win) {
       win->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
     }
   }
 }
 
+NS_IMETHODIMP
+nsBaseWidget::NotifyIME(const IMENotification& aIMENotification)
+{
+  switch (aIMENotification.mMessage) {
+    case REQUEST_TO_COMMIT_COMPOSITION:
+    case REQUEST_TO_CANCEL_COMPOSITION:
+      // Currently, if native IME handler doesn't use TextEventDispatcher,
+      // the request may be notified to mTextEventDispatcher or native IME
+      // directly.  Therefore, if mTextEventDispatcher has a composition,
+      // the request should be handled by the mTextEventDispatcher.
+      if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) {
+        return mTextEventDispatcher->NotifyIME(aIMENotification);
+      }
+      // Otherwise, call NotifyIMEInternal() for native IME handlers.
+    default:
+      return NotifyIMEInternal(aIMENotification);
+  }
+}
+
 NS_IMETHODIMP_(nsIWidget::TextEventDispatcher*)
 nsBaseWidget::GetTextEventDispatcher()
 {
   if (!mTextEventDispatcher) {
     mTextEventDispatcher = new TextEventDispatcher(this);
   }
   return mTextEventDispatcher;
 }
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -190,17 +190,17 @@ public:
   virtual bool            ShowsResizeIndicator(nsIntRect* aResizerRect) MOZ_OVERRIDE;
   virtual void            FreeNativeData(void * data, uint32_t aDataType) MOZ_OVERRIDE {}
   NS_IMETHOD              BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                                           int32_t aHorizontal,
                                           int32_t aVertical) MOZ_OVERRIDE;
   NS_IMETHOD              BeginMoveDrag(mozilla::WidgetMouseEvent* aEvent) MOZ_OVERRIDE;
   virtual nsresult        ActivateNativeMenuItemAt(const nsAString& indexString) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual nsresult        ForceUpdateNativeMenuAt(const nsAString& indexString) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
-  NS_IMETHOD              NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD              NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE MOZ_FINAL;
   NS_IMETHOD              AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD_(bool)       ExecuteNativeKeyBinding(
                             NativeKeyBindingsType aType,
                             const mozilla::WidgetKeyboardEvent& aEvent,
                             DoCommandCallback aCallback,
                             void* aCallbackData) MOZ_OVERRIDE { return false; }
   NS_IMETHOD              SetLayersAcceleration(bool aEnabled) MOZ_OVERRIDE;
   virtual bool            ComputeShouldAccelerate(bool aDefault);
@@ -353,16 +353,19 @@ protected:
 
   virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                               TouchPointerState aPointerState,
                                               nsIntPoint aPointerScreenPoint,
                                               double aPointerPressure,
                                               uint32_t aPointerOrientation) MOZ_OVERRIDE
   { return NS_ERROR_UNEXPECTED; }
 
+  virtual nsresult NotifyIMEInternal(const IMENotification& aIMENotification)
+  { return NS_ERROR_NOT_IMPLEMENTED; }
+
 protected:
   // Stores the clip rectangles in aRects into mClipRects. Returns true
   // if the new rectangles are different from the old rectangles.
   bool StoreWindowClipRegion(const nsTArray<nsIntRect>& aRects);
 
   virtual already_AddRefed<nsIWidget>
   AllocateChildPopupWidget()
   {
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6698,18 +6698,18 @@ nsWindow::OnSysColorChanged()
  **
  ** BLOCK: IME management and accessibility
  **
  ** Handles managing IME input and accessibility.
  **
  **************************************************************
  **************************************************************/
 
-NS_IMETHODIMP
-nsWindow::NotifyIME(const IMENotification& aIMENotification)
+nsresult
+nsWindow::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
   return IMEHandler::NotifyIME(this, aIMENotification);
 }
 
 NS_IMETHODIMP_(void)
 nsWindow::SetInputContext(const InputContext& aContext,
                           const InputContextAction& aAction)
 {
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -170,17 +170,16 @@ public:
 
   virtual nsresult        SynthesizeNativeMouseScrollEvent(nsIntPoint aPoint,
                                                            uint32_t aNativeMessage,
                                                            double aDeltaX,
                                                            double aDeltaY,
                                                            double aDeltaZ,
                                                            uint32_t aModifierFlags,
                                                            uint32_t aAdditionalFlags);
-  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);
@@ -277,16 +276,19 @@ public:
 
   virtual bool ShouldUseOffMainThreadCompositing();
 
 protected:
   virtual ~nsWindow();
 
   virtual void WindowUsesOMTC() MOZ_OVERRIDE;
 
+  virtual nsresult NotifyIMEInternal(
+                     const IMENotification& aIMENotification) MOZ_OVERRIDE;
+
   // A magic number to identify the FAKETRACKPOINTSCROLLABLE window created
   // when the trackpoint hack is enabled.
   enum { eFakeTrackPointScrollableID = 0x46545053 };
 
   /**
    * Callbacks
    */
   static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -1522,18 +1522,18 @@ MetroWidget::SetInputContext(const Input
 }
 
 NS_IMETHODIMP_(nsIWidget::InputContext)
 MetroWidget::GetInputContext()
 {
   return mInputContext;
 }
 
-NS_IMETHODIMP
-MetroWidget::NotifyIME(const IMENotification& aIMENotification)
+nsresult
+MetroWidget::NotifyIMEInternal(const IMENotification& aIMENotification)
 {
   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;
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -155,17 +155,16 @@ 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(const IMENotification& aIMENotification) MOZ_OVERRIDE;
   NS_IMETHOD    GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState);
   virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE;
 
   // FrameworkView helpers
   void SizeModeChanged();
   void Activated(bool aActiveated);
   void Paint(const nsIntRegion& aInvalidRegion);
 
@@ -247,16 +246,19 @@ protected:
   // nsBaseWidget
   void ConfigureAPZCTreeManager() MOZ_OVERRIDE;
   already_AddRefed<GeckoContentController> NewRootContentController() MOZ_OVERRIDE;
 
   void SetSubclass();
   void RemoveSubclass();
   nsIWidgetListener* GetPaintListener();
 
+  virtual nsresult NotifyIMEInternal(
+                     const IMENotification& aIMENotification) MOZ_OVERRIDE;
+
   // Async event dispatching
   void DispatchAsyncScrollEvent(DispatchMsg* aEvent);
   void DeliverNextScrollEvent();
   void DeliverNextKeyboardEvent();
 
 protected:
   OleInitializeWrapper mOleInitializeWrapper;
   WindowHook mWindowHook;