Bug 705057 part.3 XP level code shouldn't call nsIWidget::ResetInputState() and nsIWidget::CancelIMEComposition() directly r=smaug+ehsan
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 26 Sep 2012 14:47:51 +0900
changeset 110603 bc1f210a91f6b8f5975d8e9bf9ebc541d536e3a5
parent 110602 bbb31887247b728a892e5cdd7c0d8758d265fd29
child 110604 ce3c78a357ab896e2e446e46c4628b35472e0c45
push id23700
push userryanvm@gmail.com
push dateThu, 18 Oct 2012 02:10:26 +0000
treeherdermozilla-central@5142bbd4da12 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs705057
milestone19.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 705057 part.3 XP level code shouldn't call nsIWidget::ResetInputState() and nsIWidget::CancelIMEComposition() directly r=smaug+ehsan
content/events/src/TextComposition.cpp
content/events/src/TextComposition.h
content/events/src/nsIMEStateManager.cpp
content/events/src/nsIMEStateManager.h
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
widget/nsEvent.h
--- a/content/events/src/TextComposition.cpp
+++ b/content/events/src/TextComposition.cpp
@@ -84,16 +84,23 @@ TextComposition::SynthesizeCommit(bool a
   nsAutoString data(aDiscard ? EmptyString() : composition.mLastData);
   if (composition.mLastData != data) {
     composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_UPDATE, data);
     composition.DispatchCompsotionEventRunnable(NS_TEXT_TEXT, data);
   }
   composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_END, data);
 }
 
+nsresult
+TextComposition::NotifyIME(widget::NotificationToIME aNotification)
+{
+  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
+  return nsIMEStateManager::NotifyIME(aNotification, mPresContext);
+}
+
 /******************************************************************************
  * TextComposition::CompositionEventDispatcher
  ******************************************************************************/
 
 TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
                                                nsPresContext* aPresContext,
                                                nsINode* aEventTarget,
                                                uint32_t aEventMessage,
--- a/content/events/src/TextComposition.h
+++ b/content/events/src/TextComposition.h
@@ -56,16 +56,22 @@ public:
   /**
    * SynthesizeCommit() dispatches compositionupdate, text and compositionend
    * events for emulating commit on the content.
    *
    * @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);
+
 private:
   // This class holds nsPresContext weak.  This instance shouldn't block
   // destroying it.  When the presContext is being destroyed, it's notified to
   // nsIMEStateManager::OnDestroyPresContext(), and then, it destroy
   // this instance.
   nsPresContext* mPresContext;
   nsCOMPtr<nsINode> mNode;
 
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -107,19 +107,20 @@ nsIMEStateManager::OnRemoveContent(nsPre
       // Store the composition before accessing the native IME.
       TextComposition storedComposition = *compositionInContent;
       // Try resetting the native IME state.  Be aware, typically, this method
       // is called during the content being removed.  Then, the native
       // composition events which are caused by following APIs are ignored due
       // to unsafe to run script (in PresShell::HandleEvent()).
       nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
       if (widget) {
-        nsresult rv = widget->CancelIMEComposition();
+        nsresult rv =
+          storedComposition.NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
         if (NS_FAILED(rv)) {
-          widget->ResetInputState();
+          storedComposition.NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
         }
         // By calling the APIs, the composition may have been finished normally.
         compositionInContent =
           sTextCompositions->GetCompositionFor(
                                storedComposition.GetPresContext(),
                                storedComposition.GetEventTargetNode());
       }
     }
@@ -215,18 +216,19 @@ nsIMEStateManager::OnChangeFocusInternal
 
   // Current IME transaction should commit
   if (sPresContext) {
     nsCOMPtr<nsIWidget> oldWidget;
     if (sPresContext == aPresContext)
       oldWidget = widget;
     else
       oldWidget = GetWidget(sPresContext);
-    if (oldWidget)
-      oldWidget->ResetInputState();
+    if (oldWidget) {
+      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+    }
   }
 
   // Update IME state for new focus widget
   SetIMEState(newState, aContent, widget, aAction);
 
   sPresContext = aPresContext;
   if (sContent != aContent) {
     NS_IF_RELEASE(sContent);
@@ -302,17 +304,17 @@ nsIMEStateManager::UpdateIMEState(const 
 
   // Don't update IME state when enabled state isn't actually changed.
   InputContext context = widget->GetInputContext();
   if (context.mIMEState.mEnabled == aNewIMEState.mEnabled) {
     return;
   }
 
   // commit current composition
-  widget->ResetInputState();
+  NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget);
 
   InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                             InputContextAction::FOCUS_NOT_CHANGED);
   SetIMEState(aNewIMEState, aContent, widget, action);
 }
 
 IMEState
 nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
@@ -490,16 +492,54 @@ nsIMEStateManager::DispatchCompositionEv
     TextCompositionArray::index_type i =
       sTextCompositions->IndexOf(GUIEvent->widget);
     if (i != TextCompositionArray::NoIndex) {
       sTextCompositions->RemoveElementAt(i);
     }
   }
 }
 
+// static
+nsresult
+nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+                             nsIWidget* aWidget)
+{
+  NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
+
+  TextComposition* composition = nullptr;
+  if (sTextCompositions) {
+    composition = sTextCompositions->GetCompositionFor(aWidget);
+  }
+  switch (aNotification) {
+    case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
+      return aWidget->ResetInputState();
+    case REQUEST_TO_COMMIT_COMPOSITION:
+      return composition ? aWidget->ResetInputState() : NS_OK;
+    case REQUEST_TO_CANCEL_COMPOSITION:
+      return composition ? aWidget->CancelIMEComposition() : NS_OK;
+    default:
+      MOZ_NOT_REACHED("Unsupported notification");
+      return NS_ERROR_INVALID_ARG;
+  }
+}
+
+// static
+nsresult
+nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+                             nsPresContext* aPresContext)
+{
+  NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
+
+  nsIWidget* widget = aPresContext->GetNearestWidget();
+  if (!widget) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  return NotifyIME(aNotification, widget);
+}
+
 
 // nsTextStateManager notifies widget of any text and selection changes
 //  in the currently focused editor
 // sTextStateObserver points to the currently active nsTextStateManager
 // sTextStateObserver is null if there is no focused editor
 
 class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
                                      public nsStubMutationObserver
--- a/content/events/src/nsIMEStateManager.h
+++ b/content/events/src/nsIMEStateManager.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIMEStateManager_h__
 #define nsIMEStateManager_h__
 
 #include "nscore.h"
+#include "nsEvent.h"
 #include "nsIWidget.h"
 
 class nsDispatchingCallback;
 class nsIContent;
 class nsIDOMMouseEvent;
 class nsINode;
 class nsPIDOMWindow;
 class nsPresContext;
@@ -91,16 +92,25 @@ public:
    * stored composition automatically.
    */
   static void DispatchCompositionEvent(nsINode* aEventTargetNode,
                                        nsPresContext* aPresContext,
                                        nsEvent* aEvent,
                                        nsEventStatus* aStatus,
                                        nsDispatchingCallback* aCallBack);
 
+  /**
+   * 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);
+
 protected:
   static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
                                         nsIContent* aContent,
                                         InputContextAction aAction);
   static void SetIMEState(const IMEState &aState,
                           nsIContent* aContent,
                           nsIWidget* aWidget,
                           InputContextAction aAction);
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -2026,83 +2026,41 @@ nsEditor::GetPhonetic(nsAString& aPhonet
   if (mPhonetic)
     aPhonetic = *mPhonetic;
   else
     aPhonetic.Truncate(0);
 
   return NS_OK;
 }
 
-
-static nsresult
-GetEditorContentWindow(dom::Element *aRoot, nsIWidget **aResult)
-{
-  NS_ENSURE_TRUE(aRoot && aResult, NS_ERROR_NULL_POINTER);
-
-  *aResult = 0;
-
-  // Not ref counted
-  nsIFrame *frame = aRoot->GetPrimaryFrame();
-
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
-  *aResult = frame->GetNearestWidget();
-  NS_ENSURE_TRUE(*aResult, NS_ERROR_FAILURE);
-
-  NS_ADDREF(*aResult);
-  return NS_OK;
-}
-
-nsresult
-nsEditor::GetWidget(nsIWidget **aWidget)
-{
-  NS_ENSURE_TRUE(aWidget, NS_ERROR_NULL_POINTER);
-  *aWidget = nullptr;
-
-  nsCOMPtr<nsIWidget> widget;
-  nsresult res = GetEditorContentWindow(GetRoot(), getter_AddRefs(widget));
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(widget, NS_ERROR_NOT_AVAILABLE);
-
-  NS_ADDREF(*aWidget = widget);
-
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 nsEditor::ForceCompositionEnd()
 {
-
-// We can test mInIMEMode and do some optimization for Mac and Window
-// Howerver, since UNIX support over-the-spot, we cannot rely on that 
-// flag for Unix.
-// We should use LookAndFeel to resolve this
-
-#if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_OS2)
-  // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
-  // "commit the composition" and "cursor is moved".  This method name is
-  // "ForceCompositionEnd", so, ResetInputState() should be used only for the
-  // former here.  However, ResetInputState() is also used for the latter here
-  // because even if we don't have composition, we call ResetInputState() on
-  // Linux.  Currently, nsGtkIMModule can know the timing of the cursor move,
-  // so, the latter meaning should be gone and we should remove this #if.
-  if(! mInIMEMode)
-    return NS_OK;
-#endif
-
-  nsCOMPtr<nsIWidget> widget;
-  nsresult res = GetWidget(getter_AddRefs(widget));
-  NS_ENSURE_SUCCESS(res, res);
-
-  if (widget) {
-    res = widget->ResetInputState();
-    NS_ENSURE_SUCCESS(res, res);
+  nsCOMPtr<nsIPresShell> ps = GetPresShell();
+  if (!ps) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  nsPresContext* pc = ps->GetPresContext();
+  if (!pc) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return NS_OK;
+  if (!mInIMEMode) {
+    // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
+    // "commit the composition" and "cursor is moved".  This method name is
+    // "ForceCompositionEnd", so, ResetInputState() should be used only for the
+    // former here.  However, ResetInputState() is also used for the latter here
+    // because even if we don't have composition, we call ResetInputState() on
+    // Linux.  Currently, nsGtkIMModule can know the timing of the cursor move,
+    // so, the latter meaning should be gone.
+    // XXX This may commit a composition in another editor.
+    return nsIMEStateManager::NotifyIME(NOTIFY_IME_OF_CURSOR_POS_CHANGED, pc);
+  }
+
+  return nsIMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc);
 }
 
 NS_IMETHODIMP
 nsEditor::GetPreferredIMEState(IMEState *aState)
 {
   NS_ENSURE_ARG_POINTER(aState);
   aState->mEnabled = IMEState::ENABLED;
   aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -383,20 +383,16 @@ protected:
   // stub.  see comment in source.                     
   virtual bool IsBlockNode(nsINode* aNode);
   
   // helper for GetPriorNode and GetNextNode
   nsIContent* FindNextLeafNode(nsINode  *aCurrentNode,
                                bool      aGoForward,
                                bool      bNoBlockCrossing);
 
-  // Get nsIWidget interface
-  nsresult GetWidget(nsIWidget **aWidget);
-
-
   // install the event listeners for the editor 
   virtual nsresult InstallEventListeners();
 
   virtual void CreateEventListeners();
 
   // unregister and release our event listeners
   virtual void RemoveEventListeners();
 
--- a/widget/nsEvent.h
+++ b/widget/nsEvent.h
@@ -93,16 +93,23 @@ enum Modifier {
   MODIFIER_SCROLLLOCK = 0x0080,
   MODIFIER_SHIFT      = 0x0100,
   MODIFIER_SYMBOLLOCK = 0x0200,
   MODIFIER_OS         = 0x0400
 };
 
 typedef uint16_t Modifiers;
 
+// NotificationToIME is shared by nsIMEStateManager and TextComposition.
+enum NotificationToIME {
+  NOTIFY_IME_OF_CURSOR_POS_CHANGED,
+  REQUEST_TO_COMMIT_COMPOSITION,
+  REQUEST_TO_CANCEL_COMPOSITION
+};
+
 } // namespace widget
 } // namespace mozilla
 
 #define NS_DOM_KEYNAME_ALT        "Alt"
 #define NS_DOM_KEYNAME_ALTGRAPH   "AltGraph"
 #define NS_DOM_KEYNAME_CAPSLOCK   "CapsLock"
 #define NS_DOM_KEYNAME_CONTROL    "Control"
 #define NS_DOM_KEYNAME_FN         "Fn"