Bug 582893 IME isn't disabled when password fields on sheet dialog get focus r=smichaud, b2.0=final+
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 16 Aug 2010 17:20:27 +0900
changeset 50663 ce4d646e8a1cb9a873311a0eb00359a90cd019f6
parent 50662 bffe7baa4e00e21ad5468cd4843a22e653b0f80d
child 50664 68b886f9b3c3668ec0e4dbc9e44812788b8e490d
push idunknown
push userunknown
push dateunknown
reviewerssmichaud
bugs582893
milestone2.0b4pre
Bug 582893 IME isn't disabled when password fields on sheet dialog get focus r=smichaud, b2.0=final+
widget/src/cocoa/nsCocoaTextInputHandler.h
widget/src/cocoa/nsCocoaTextInputHandler.mm
widget/tests/test_imestate.html
--- a/widget/src/cocoa/nsCocoaTextInputHandler.h
+++ b/widget/src/cocoa/nsCocoaTextInputHandler.h
@@ -254,16 +254,20 @@ public:
 
   void EnableIME(PRBool aEnableIME);
   void SetIMEOpenState(PRBool aOpen);
   void SetASCIICapableOnly(PRBool aASCIICapableOnly);
 
   static CFArrayRef CreateAllIMEModeList();
   static void DebugPrintAllIMEModes(PRLogModuleInfo* aLogModuleInfo);
 
+  // Don't use ::TSMGetActiveDocument() API directly, the document may not
+  // be what you want.
+  static TSMDocumentID GetCurrentTSMDocumentID();
+
 protected:
   // The owner of this instance.  The result of mOwnerWidget->TextInputHandler
   // returns this instance.  This must not be null after initialized.
   nsChildView* mOwnerWidget;
 
   // The native focused view, this is the native NSView of mOwnerWidget.
   // This view handling the actual text inputting.
   NSView<mozView>* mView;
--- a/widget/src/cocoa/nsCocoaTextInputHandler.mm
+++ b/widget/src/cocoa/nsCocoaTextInputHandler.mm
@@ -677,16 +677,27 @@ nsCocoaIMEHandler::DebugPrintAllIMEModes
             NS_ConvertUTF16toUTF8(name).get(),
             NS_ConvertUTF16toUTF8(isid).get(),
             tis.IsASCIICapable() ? "" : "\t(Isn't ASCII capable)",
             tis.IsEnabled() ? "" : "\t(Isn't Enabled)"));
   }
   ::CFRelease(list);
 }
 
+//static
+TSMDocumentID
+nsCocoaIMEHandler::GetCurrentTSMDocumentID()
+{
+  // On OS X 10.6.x at least, ::TSMGetActiveDocument() has a bug that prevents
+  // it from returning accurate results unless
+  // [NSInputManager currentInputManager] is called first.
+  // So, we need to call [NSInputManager currentInputManager] first here.
+  [NSInputManager currentInputManager];
+  return ::TSMGetActiveDocument();
+}
 
 #pragma mark -
 
 
 /******************************************************************************
  *
  *  nsCocoaIMEHandler implementation #1
  *    The methods are releated to the pending methods.  Some jobs should be
@@ -701,30 +712,30 @@ nsCocoaIMEHandler::DebugPrintAllIMEModes
 void
 nsCocoaIMEHandler::ResetIMEWindowLevel()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
 #ifdef DEBUG_IME_HANDLER
   DebugPrintPointer(this);
   NSLog(@"nsCocoaIMEHandler::ResetIMEWindowLevel");
-  NSLog(@"  IsFocused:%s ::TSMGetActiveDocument():%p",
-        TrueOrFalse(IsFocused()), ::TSMGetActiveDocument());
+  NSLog(@"  IsFocused:%s GetCurrentTSMDocumentID():%p",
+        TrueOrFalse(IsFocused()), GetCurrentTSMDocumentID());
 #endif // DEBUG_IME_HANDLER
 
   if (!mView)
     return;
 
   if (!IsFocused()) {
     // retry at next focus event
     mPendingMethods |= kResetIMEWindowLevel;
     return;
   }
 
-  TSMDocumentID doc = ::TSMGetActiveDocument();
+  TSMDocumentID doc = GetCurrentTSMDocumentID();
   if (!doc) {
     // retry
     mPendingMethods |= kResetIMEWindowLevel;
     NS_WARNING("Application is active but there is no active document");
     ResetTimer();
     return;
   }
 
@@ -747,17 +758,17 @@ nsCocoaIMEHandler::ResetIMEWindowLevel()
         GetWindowLevelName(windowLevel), windowLevel);
 #endif // DEBUG_IME_HANDLER
 
   // Chinese IMEs on 10.5 don't work fine if the level is NSNormalWindowLevel,
   // then, we need to increment the value.
   if (windowLevel == NSNormalWindowLevel)
     windowLevel++;
 
-  ::TSMSetDocumentProperty(::TSMGetActiveDocument(),
+  ::TSMSetDocumentProperty(GetCurrentTSMDocumentID(),
                            kTSMDocumentWindowLevelPropertyTag,
                            sizeof(windowLevel), &windowLevel);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void
 nsCocoaIMEHandler::DiscardIMEComposition()
@@ -798,30 +809,30 @@ nsCocoaIMEHandler::DiscardIMEComposition
 void
 nsCocoaIMEHandler::SyncASCIICapableOnly()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
 #ifdef DEBUG_IME_HANDLER
   DebugPrintPointer(this);
   NSLog(@"nsCocoaIMEHandler::SyncASCIICapableOnly");
-  NSLog(@"  IsFocused:%s ::TSMGetActiveDocument():%p",
-        TrueOrFalse(IsFocused()), ::TSMGetActiveDocument());
+  NSLog(@"  IsFocused:%s GetCurrentTSMDocumentID():%p",
+        TrueOrFalse(IsFocused()), GetCurrentTSMDocumentID());
 #endif
 
   if (!mView)
     return;
 
   if (!IsFocused()) {
     // retry at next focus event
     mPendingMethods |= kSyncASCIICapableOnly;
     return;
   }
 
-  TSMDocumentID doc = ::TSMGetActiveDocument();
+  TSMDocumentID doc = GetCurrentTSMDocumentID();
   if (!doc) {
     // retry
     mPendingMethods |= kSyncASCIICapableOnly;
     NS_WARNING("Application is active but there is no active document");
     ResetTimer();
     return;
   }
 
@@ -842,46 +853,44 @@ nsCocoaIMEHandler::SyncASCIICapableOnly(
 
 void
 nsCocoaIMEHandler::ResetTimer()
 {
   NS_ASSERTION(mPendingMethods != 0,
                "There are not pending methods, why this is called?");
   if (mTimer) {
     mTimer->Cancel();
-    return;
-  }
-  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  if (!mTimer) {
-    NS_ERROR("mTimer is null");
-    return;
+  } else {
+    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    NS_ENSURE_TRUE(mTimer, );
   }
   mTimer->InitWithFuncCallback(FlushPendingMethods, this, 0,
                                nsITimer::TYPE_ONE_SHOT);
 }
 
 void
 nsCocoaIMEHandler::ExecutePendingMethods()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
+  if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nsnull;
+  }
+
   if (![[NSApplication sharedApplication] isActive]) {
     mIsInFocusProcessing = PR_FALSE;
     // If we're not active, we should retry at focus event
     return;
   }
 
   PRUint32 pendingMethods = mPendingMethods;
   // First, reset the pending method flags because if each methods cannot
   // run now, they can reentry to the pending flags by theirselves.
   mPendingMethods = 0;
-  if (mTimer) {
-    mTimer->Cancel();
-    mTimer = nsnull;
-  }
 
   if (pendingMethods & kDiscardIMEComposition)
     DiscardIMEComposition();
   if (pendingMethods & kSyncASCIICapableOnly)
     SyncASCIICapableOnly();
   if (pendingMethods & kResetIMEWindowLevel)
     ResetIMEWindowLevel();
 
@@ -1159,17 +1168,18 @@ nsCocoaIMEHandler::CancelIMEComposition(
 PRBool
 nsCocoaIMEHandler::IsFocused()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NS_ENSURE_TRUE(mView, PR_FALSE);
   NSWindow* window = [mView window];
   NS_ENSURE_TRUE(window, PR_FALSE);
-  return [window firstResponder] == mView && [window isMainWindow] &&
+  return [window firstResponder] == mView &&
+         ([window isMainWindow] || [window isSheet]) &&
          [[NSApplication sharedApplication] isActive];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
 }
 
 PRBool
 nsCocoaIMEHandler::IsIMEOpened()
 {
--- a/widget/tests/test_imestate.html
+++ b/widget/tests/test_imestate.html
@@ -856,16 +856,89 @@ function runEditorFlagChangeTests()
 }
 
 function runEditableSubframeTests()
 {
   window.open("window_imestate_iframes.html", "_blank",
               "width=600,height=600");
 }
 
+function runTestPasswordFieldOnDialog()
+{
+  if (!kIMEEnabledSupported) {
+    return;
+  }
+
+  if (document.activeElement) {
+    document.activeElement.blur();
+  }
+
+  var dialog;
+
+  function WindowObserver()
+  {
+    Components.classes["@mozilla.org/observer-service;1"].
+               getService(Components.interfaces.nsIObserverService).
+               addObserver(this, "domwindowopened", false);
+  }
+
+  WindowObserver.prototype = {
+    QueryInterface: function (iid)
+    {
+      if (iid.equals(Components.interfaces.nsIObserver) ||
+          iid.equals(Components.interfaces.nsISupports)) {
+        return this;
+      }
+    },
+
+    observe: function (subject, topic, data)
+    {
+      if (topic === "domwindowopened") {
+        ok(true, "dialog window is created");
+        dialog = subject.QueryInterface(Components.interfaces.nsIDOMWindow);
+        dialog.addEventListener("load", onPasswordDialogLoad, false);
+      }
+    }
+  };
+
+  var observer = new WindowObserver();
+  var arg1 = new Object(), arg2 = new Object();
+  Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+             getService(Components.interfaces.nsIPromptService).
+             promptPassword(window, "title", "text", arg1, "msg", arg2);
+
+  ok(true, "password dialog was closed");
+
+  Components.classes["@mozilla.org/observer-service;1"].
+             getService(Components.interfaces.nsIObserverService).
+             removeObserver(observer, "domwindowopened");
+
+  var passwordField;
+
+  function onPasswordDialogLoad()
+  {
+    ok(true, "onPasswordDialogLoad is called");
+    dialog.removeEventListener("load", onPasswordDialogLoad, false);
+    passwordField = dialog.document.getElementById("password1Textbox");
+    passwordField.addEventListener("focus", onPasswordFieldFocus, false);
+  }
+
+  function onPasswordFieldFocus()
+  {
+    ok(true, "onPasswordFieldFocus is called");
+    passwordField.removeEventListener("focus", onPasswordFieldFocus, false);
+    var utils = dialog.
+      QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+      getInterface(Components.interfaces.nsIDOMWindowUtils);
+    is(utils.IMEStatus, utils.IME_STATUS_PASSWORD,
+       "IME isn't disabled on a password field of password dialog");
+    synthesizeKey("VK_ESCAPE", { }, dialog);
+  }
+}
+
 function runTests()
 {
   if (!kIMEEnabledSupported && !kIMEOpenSupported)
     return;
 
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 
   // test for normal contents.
@@ -903,16 +976,19 @@ function runTests()
   runReadonlyChangingTest();
 
   // complex contenteditable editor's tests
   runComplexContenteditableTests();
 
   // test whether the IME state and composition are not changed unexpectedly
   runEditorFlagChangeTests();
 
+  // test password field on dialog
+  runTestPasswordFieldOnDialog();
+
   runASyncTests();
 }
 
 function runASyncTests()
 {
   // The tests must call onFinish() method.
   runEditableSubframeTests();
 }