Bug 805357 part.1 nsIMEStateManager should always call nsIWidget::OnIMEFocusChange(false) when our editor loses focus r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 31 Oct 2012 08:22:22 +0900
changeset 111966 4a4fd71c32521e09e1b8ae70bc6840becd631873
parent 111965 012d146779e29131da86b53bb20dafd167652057
child 111967 f38bee8c58b9df639ed829ad6b4fdb6444be66da
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewerssmaug
bugs805357
milestone19.0a1
Bug 805357 part.1 nsIMEStateManager should always call nsIWidget::OnIMEFocusChange(false) when our editor loses focus r=smaug
content/events/src/nsIMEStateManager.cpp
content/events/src/nsIMEStateManager.h
widget/cocoa/TextInputHandler.mm
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -111,16 +111,19 @@ nsIMEStateManager::OnDestroyPresContext(
                    TextCompositionArray::NoIndex);
     }
   }
 
   if (aPresContext != sPresContext)
     return NS_OK;
   nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
   if (widget) {
+    if (IsEditableIMEState(widget)) {
+      widget->OnIMEFocusChange(false);
+    }
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
   NS_IF_RELEASE(sContent);
   sPresContext = nullptr;
   DestroyTextStateManager();
@@ -170,16 +173,19 @@ nsIMEStateManager::OnRemoveContent(nsPre
   if (!sPresContext || !sContent ||
       !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
     return NS_OK;
   }
 
   // Current IME transaction should commit
   nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
   if (widget) {
+    if (IsEditableIMEState(widget)) {
+      widget->OnIMEFocusChange(false);
+    }
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
 
   NS_IF_RELEASE(sContent);
   sPresContext = nullptr;
@@ -197,26 +203,45 @@ nsIMEStateManager::OnChangeFocus(nsPresC
   return OnChangeFocusInternal(aPresContext, aContent, action);
 }
 
 nsresult
 nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
                                          nsIContent* aContent,
                                          InputContextAction aAction)
 {
+  bool focusActuallyChanging =
+    (sContent != aContent || sPresContext != aPresContext);
+
+  nsCOMPtr<nsIWidget> oldWidget =
+    sPresContext ? sPresContext->GetNearestWidget() : nullptr;
+  if (oldWidget && focusActuallyChanging) {
+    // If we're deactivating, we shouldn't commit composition forcibly because
+    // the user may want to continue the composition.
+    if (aPresContext) {
+      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+    }
+    // Notify IME of losing focus even if we're deactivating.
+    if (IsEditableIMEState(oldWidget)) {
+      oldWidget->OnIMEFocusChange(false);
+    }
+  }
+
   if (sTextStateObserver &&
       !sTextStateObserver->IsManaging(aPresContext, aContent)) {
     DestroyTextStateManager();
   }
 
   if (!aPresContext) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
+  nsCOMPtr<nsIWidget> widget =
+    (sPresContext == aPresContext) ? oldWidget.get() :
+                                     aPresContext->GetNearestWidget();
   if (!widget) {
     return NS_OK;
   }
 
   // Handle secure input mode for password field input.
   bool contentIsPassword = false;
   if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML) {
     if (aContent->Tag() == nsGkAtoms::input) {
@@ -235,45 +260,39 @@ nsIMEStateManager::OnChangeFocusInternal
     if (contentIsPassword) {
       if (NS_SUCCEEDED(widget->BeginSecureKeyboardInput())) {
         sInSecureInputMode = true;
       }
     }
   }
 
   IMEState newState = GetNewIMEState(aPresContext, aContent);
-  if (aPresContext == sPresContext && aContent == sContent) {
+  if (!focusActuallyChanging) {
     // actual focus isn't changing, but if IME enabled state is changing,
     // we should do it.
     InputContext context = widget->GetInputContext();
     if (context.mIMEState.mEnabled == newState.mEnabled) {
       // the enabled state isn't changing.
       return NS_OK;
     }
     aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
+
+    // Even if focus isn't changing actually, we should commit current
+    // composition here since the IME state is changing.
+    if (sPresContext && oldWidget && !focusActuallyChanging) {
+      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+    }
   } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
     // If aContent isn't null or aContent is null but editable, somebody gets
     // focus.
     bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
     aAction.mFocusChange =
       gotFocus ? InputContextAction::GOT_FOCUS : InputContextAction::LOST_FOCUS;
   }
 
-  // Current IME transaction should commit
-  if (sPresContext) {
-    nsCOMPtr<nsIWidget> oldWidget;
-    if (sPresContext == aPresContext)
-      oldWidget = widget;
-    else
-      oldWidget = sPresContext->GetNearestWidget();
-    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);
     NS_IF_ADDREF(sContent = aContent);
   }
@@ -931,25 +950,40 @@ nsIMEStateManager::GetRootEditableNode(n
   if (aPresContext) {
     nsIDocument* document = aPresContext->Document();
     if (document && document->IsEditable())
       return document;
   }
   return nullptr;
 }
 
+bool
+nsIMEStateManager::IsEditableIMEState(nsIWidget* aWidget)
+{
+  switch (aWidget->GetInputContext().mIMEState.mEnabled) {
+    case widget::IMEState::ENABLED:
+    case widget::IMEState::PASSWORD:
+      return true;
+    case widget::IMEState::PLUGIN:
+    case widget::IMEState::DISABLED:
+      return false;
+    default:
+      MOZ_NOT_REACHED("Unknown IME enable state");
+      return false;
+  }
+}
+
 void
 nsIMEStateManager::DestroyTextStateManager()
 {
   if (!sTextStateObserver || sTextStateObserver->mDestroying) {
     return;
   }
 
   sTextStateObserver->mDestroying = true;
-  sTextStateObserver->mWidget->OnIMEFocusChange(false);
   sTextStateObserver->Destroy();
   NS_RELEASE(sTextStateObserver);
 }
 
 void
 nsIMEStateManager::CreateTextStateManager()
 {
   if (sTextStateObserver) {
@@ -959,22 +993,18 @@ nsIMEStateManager::CreateTextStateManage
   }
 
   nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
   if (!widget) {
     return; // Sometimes, there are no widgets.
   }
 
   // If it's not text ediable, we don't need to create nsTextStateManager.
-  switch (widget->GetInputContext().mIMEState.mEnabled) {
-    case widget::IMEState::ENABLED:
-    case widget::IMEState::PASSWORD:
-      break;
-    default:
-      return;
+  if (!IsEditableIMEState(widget)) {
+    return;
   }
 
   nsINode *editableNode = GetRootEditableNode(sPresContext, sContent);
   if (!editableNode) {
     return;
   }
 
   nsresult rv = widget->OnIMEFocusChange(true);
--- a/content/events/src/nsIMEStateManager.h
+++ b/content/events/src/nsIMEStateManager.h
@@ -118,16 +118,18 @@ protected:
   static void EnsureTextCompositionArray();
   static void CreateTextStateManager();
   static void DestroyTextStateManager();
 
   static bool IsEditable(nsINode* node);
   static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
                                       nsIContent* aContent);
 
+  static bool IsEditableIMEState(nsIWidget* aWidget);
+
   static nsIContent*    sContent;
   static nsPresContext* sPresContext;
   static bool           sInstalledMenuKeyboardListener;
   static bool           sInSecureInputMode;
 
   static nsTextStateManager* sTextStateObserver;
 
   // All active compositions in the process are stored by this array.
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -2966,18 +2966,16 @@ IMEInputHandler::OnFocusChangeInGecko(bo
 {
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p IMEInputHandler::OnFocusChangeInGecko, aFocus=%s, Destroyed()=%s, "
      "sFocusedIMEHandler=%p",
      this, TrueOrFalse(aFocus), TrueOrFalse(Destroyed()), sFocusedIMEHandler));
 
   // This is called when the native focus is changed and when the native focus
   // isn't changed but the focus is changed in Gecko.
-  // XXX currently, we're not called this method with false, we need to
-  // improve the nsIMEStateManager implementation.
   if (!aFocus) {
     if (sFocusedIMEHandler == this)
       sFocusedIMEHandler = nullptr;
     return;
   }
 
   sFocusedIMEHandler = this;
   mIsInFocusProcessing = true;