diff --git a/widget/src/cocoa/nsCocoaTextInputHandler.h b/widget/src/cocoa/nsCocoaTextInputHandler.h index 98f1e65f3f6..5ffb5be9289 100644 --- a/widget/src/cocoa/nsCocoaTextInputHandler.h +++ b/widget/src/cocoa/nsCocoaTextInputHandler.h @@ -259,6 +259,10 @@ public: 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. diff --git a/widget/src/cocoa/nsCocoaTextInputHandler.mm b/widget/src/cocoa/nsCocoaTextInputHandler.mm index cbb9b3c2af8..3fa779bd07d 100644 --- a/widget/src/cocoa/nsCocoaTextInputHandler.mm +++ b/widget/src/cocoa/nsCocoaTextInputHandler.mm @@ -682,6 +682,17 @@ nsCocoaIMEHandler::DebugPrintAllIMEModes(PRLogModuleInfo* aLogModuleInfo) ::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 - @@ -706,8 +717,8 @@ nsCocoaIMEHandler::ResetIMEWindowLevel() #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) @@ -719,7 +730,7 @@ nsCocoaIMEHandler::ResetIMEWindowLevel() return; } - TSMDocumentID doc = ::TSMGetActiveDocument(); + TSMDocumentID doc = GetCurrentTSMDocumentID(); if (!doc) { // retry mPendingMethods |= kResetIMEWindowLevel; @@ -752,7 +763,7 @@ nsCocoaIMEHandler::ResetIMEWindowLevel() if (windowLevel == NSNormalWindowLevel) windowLevel++; - ::TSMSetDocumentProperty(::TSMGetActiveDocument(), + ::TSMSetDocumentProperty(GetCurrentTSMDocumentID(), kTSMDocumentWindowLevelPropertyTag, sizeof(windowLevel), &windowLevel); @@ -803,8 +814,8 @@ nsCocoaIMEHandler::SyncASCIICapableOnly() #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) @@ -816,7 +827,7 @@ nsCocoaIMEHandler::SyncASCIICapableOnly() return; } - TSMDocumentID doc = ::TSMGetActiveDocument(); + TSMDocumentID doc = GetCurrentTSMDocumentID(); if (!doc) { // retry mPendingMethods |= kSyncASCIICapableOnly; @@ -847,12 +858,9 @@ nsCocoaIMEHandler::ResetTimer() "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); @@ -863,6 +871,11 @@ 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 @@ -873,10 +886,6 @@ nsCocoaIMEHandler::ExecutePendingMethods() // 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(); @@ -1164,7 +1173,8 @@ nsCocoaIMEHandler::IsFocused() 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); diff --git a/widget/tests/test_imestate.html b/widget/tests/test_imestate.html index 44a02f3af16..4a1e212961c 100644 --- a/widget/tests/test_imestate.html +++ b/widget/tests/test_imestate.html @@ -861,6 +861,79 @@ function runEditableSubframeTests() "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) @@ -908,6 +981,9 @@ function runTests() // test whether the IME state and composition are not changed unexpectedly runEditorFlagChangeTests(); + // test password field on dialog + runTestPasswordFieldOnDialog(); + runASyncTests(); }