Bug 358899 [Cocoa] Improve nsIKBStateControl implementation r=josh+ere+masaki.katakai+roc, sr=roc
authormasayuki@d-toybox.com
Sun, 15 Apr 2007 06:43:55 -0700
changeset 539 af6c1f09fe550b7e6d8f47eeffc7d17cbae0997d
parent 538 18b3441146f1b5a1bbcd5a3c0016fd06b1e5faa8
child 540 e0dd2dcf4a92ccb2ab076dcc95221b82a83d9f8c
push idunknown
push userunknown
push dateunknown
reviewersjosh, roc
bugs358899
milestone1.9a4pre
Bug 358899 [Cocoa] Improve nsIKBStateControl implementation r=josh+ere+masaki.katakai+roc, sr=roc
content/base/public/nsContentUtils.h
content/base/public/nsIContent.h
content/base/src/nsContentUtils.cpp
content/events/src/nsIMEStateManager.cpp
editor/libeditor/base/nsEditor.cpp
widget/public/nsIKBStateControl.h
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
widget/src/gtk/nsWidget.cpp
widget/src/gtk/nsWidget.h
widget/src/gtk2/nsWindow.cpp
widget/src/gtk2/nsWindow.h
widget/src/mac/nsWindow.cpp
widget/src/mac/nsWindow.h
widget/src/photon/nsWidget.cpp
widget/src/photon/nsWidget.h
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1014,16 +1014,20 @@ public:
         mObject = aObject;
       }
       return rv;
     }
     PRUint32 mLangID;
     void *mObject;
   };
 
+  /**
+   * Convert nsIContent::IME_STATUS_* to nsIKBStateControll::IME_STATUS_*
+   */
+  static PRUint32 GetKBStateControlStatusFromIMEStatus(PRUint32 aState);
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult doReparentContentWrapper(nsIContent *aChild,
                                            JSContext *cx,
                                            JSObject *aOldGlobal,
                                            JSObject *aNewGlobal,
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -519,26 +519,31 @@ public:
    *         IME_STATUS_ENABLE and IME_STATUS_DISABLE must not be set
    *         together; likewise IME_STATUS_OPEN and IME_STATUS_CLOSE must
    *         not be set together.
    *         If you return IME_STATUS_DISABLE, you should not set the
    *         OPEN or CLOSE flag; that way, when IME is next enabled,
    *         the previous OPEN/CLOSE state will be restored (unless the newly
    *         focused content specifies the OPEN/CLOSE state by setting the OPEN
    *         or CLOSE flag with the ENABLE flag).
+   *         IME_STATUS_PASSWORD should be returned only from password editor,
+   *         this value has a special meaning. It is used as alternative of
+   *         IME_STATUS_DISABLED.
    */
   enum {
-    IME_STATUS_NONE    = 0x0000,
-    IME_STATUS_ENABLE  = 0x0001,
-    IME_STATUS_DISABLE = 0x0002,
-    IME_STATUS_OPEN    = 0x0004,
-    IME_STATUS_CLOSE   = 0x0008
+    IME_STATUS_NONE     = 0x0000,
+    IME_STATUS_ENABLE   = 0x0001,
+    IME_STATUS_DISABLE  = 0x0002,
+    IME_STATUS_PASSWORD = 0x0004,
+    IME_STATUS_OPEN     = 0x0008,
+    IME_STATUS_CLOSE    = 0x0010
   };
   enum {
-    IME_STATUS_MASK_ENABLED = IME_STATUS_ENABLE | IME_STATUS_DISABLE,
+    IME_STATUS_MASK_ENABLED = IME_STATUS_ENABLE | IME_STATUS_DISABLE |
+                              IME_STATUS_PASSWORD,
     IME_STATUS_MASK_OPENED  = IME_STATUS_OPEN | IME_STATUS_CLOSE
   };
   virtual PRUint32 GetDesiredIMEState()
   {
     return IME_STATUS_DISABLE;
   }
 
   /**
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -131,16 +131,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsIEventListenerManager.h"
 #include "nsAttrName.h"
 #include "nsIDOMUserDataHandler.h"
 #include "nsIFragmentContentSink.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsTPtrArray.h"
 #include "nsGUIEvent.h"
 #include "nsMutationEvent.h"
+#include "nsIKBStateControl.h"
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #endif
 #include "nsCycleCollectionParticipant.h"
 
 // for ReportToConsole
 #include "nsIStringBundle.h"
@@ -3568,8 +3569,25 @@ nsContentUtils::DropScriptObject(PRUint3
 {
   PRUint32 langIndex = NS_STID_INDEX(aLangID);
   nsresult rv = sScriptRuntimes[langIndex]->DropScriptObject(aObject);
   if (--sScriptRootCount[langIndex] == 0) {
     NS_RELEASE(sScriptRuntimes[langIndex]);
   }
   return rv;
 }
+
+/* static */
+PRUint32
+nsContentUtils::GetKBStateControlStatusFromIMEStatus(PRUint32 aState)
+{
+  switch (aState & nsIContent::IME_STATUS_MASK_ENABLED) {
+    case nsIContent::IME_STATUS_DISABLE:
+      return nsIKBStateControl::IME_STATUS_DISABLED;
+    case nsIContent::IME_STATUS_ENABLE:
+      return nsIKBStateControl::IME_STATUS_ENABLED;
+    case nsIContent::IME_STATUS_PASSWORD:
+      return nsIKBStateControl::IME_STATUS_PASSWORD;
+    default:
+      NS_ERROR("The given state doesn't have valid enable state");
+      return nsIKBStateControl::IME_STATUS_ENABLED;
+  }
+}
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -48,16 +48,17 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIEditorDocShell.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsIKBStateControl.h"
 #include "nsIFocusController.h"
 #include "nsIDOMWindow.h"
+#include "nsContentUtils.h"
 
 /******************************************************************/
 /* nsIMEStateManager                                              */
 /******************************************************************/
 
 nsIContent*    nsIMEStateManager::sContent      = nsnull;
 nsPresContext* nsIMEStateManager::sPresContext  = nsnull;
 nsPIDOMWindow* nsIMEStateManager::sActiveWindow = nsnull;
@@ -118,23 +119,23 @@ nsIMEStateManager::OnChangeFocus(nsPresC
   if (aPresContext == sPresContext && aContent == sContent) {
     // actual focus isn't changing, but if IME enabled state is changing,
     // we should do it.
     PRUint32 newEnabledState = newState & nsIContent::IME_STATUS_MASK_ENABLED;
     if (newEnabledState == 0) {
       // the enabled state isn't changing, we should do nothing.
       return NS_OK;
     }
-    PRBool enabled;
+    PRUint32 enabled;
     if (NS_FAILED(kb->GetIMEEnabled(&enabled))) {
       // this platform doesn't support IME controlling
       return NS_OK;
     }
-    if ((enabled && newEnabledState == nsIContent::IME_STATUS_ENABLE) ||
-        (!enabled && newEnabledState == nsIContent::IME_STATUS_DISABLE)) {
+    if (enabled ==
+        nsContentUtils::GetKBStateControlStatusFromIMEStatus(newEnabledState)) {
       // the enabled state isn't changing.
       return NS_OK;
     }
   }
 
   // Current IME transaction should commit
   if (sPresContext) {
     nsIKBStateControl* oldKB;
@@ -167,19 +168,30 @@ nsIMEStateManager::OnActivate(nsPresCont
   return NS_OK;
 }
 
 nsresult
 nsIMEStateManager::OnDeactivate(nsPresContext* aPresContext)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
   NS_ENSURE_TRUE(aPresContext->Document()->GetWindow(), NS_ERROR_FAILURE);
-  if (sActiveWindow ==
+  if (sActiveWindow !=
       aPresContext->Document()->GetWindow()->GetPrivateRoot())
-    sActiveWindow = nsnull;
+    return NS_OK;
+
+  sActiveWindow = nsnull;
+#ifdef NS_KBSC_USE_SHARED_CONTEXT
+  // Reset the latest content. When the window is activated, the IME state
+  // may be changed on other applications.
+  sContent = nsnull;
+  // We should enable the IME state for other applications.
+  nsIKBStateControl* kb = GetKBStateControl(aPresContext);
+  if (kb)
+    SetIMEState(aPresContext, nsIContent::IME_STATUS_ENABLE, kb);
+#endif // NS_KBSC_USE_SHARED_CONTEXT
   return NS_OK;
 }
 
 PRBool
 nsIMEStateManager::IsActive(nsPresContext* aPresContext)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
   nsPIDOMWindow* window = aPresContext->Document()->GetWindow();
@@ -233,18 +245,19 @@ nsIMEStateManager::GetNewIMEState(nsPres
 }
 
 void
 nsIMEStateManager::SetIMEState(nsPresContext*     aPresContext,
                                PRUint32           aState,
                                nsIKBStateControl* aKB)
 {
   if (aState & nsIContent::IME_STATUS_MASK_ENABLED) {
-    PRBool enable = (aState & nsIContent::IME_STATUS_ENABLE);
-    aKB->SetIMEEnabled(enable);
+    PRUint32 state =
+      nsContentUtils::GetKBStateControlStatusFromIMEStatus(aState);
+    aKB->SetIMEEnabled(state);
   }
   if (aState & nsIContent::IME_STATUS_MASK_OPENED) {
     PRBool open = (aState & nsIContent::IME_STATUS_OPEN);
     aKB->SetIMEOpenState(open);
   }
 }
 
 nsIKBStateControl*
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -2203,20 +2203,21 @@ nsEditor::NotifyIMEOnBlur()
 
 NS_IMETHODIMP
 nsEditor::GetPreferredIMEState(PRUint32 *aState)
 {
   NS_ENSURE_ARG_POINTER(aState);
 
   PRUint32 flags;
   if (NS_SUCCEEDED(GetFlags(&flags)) &&
-      flags & (nsIPlaintextEditor::eEditorPasswordMask |
-               nsIPlaintextEditor::eEditorReadonlyMask |
+      flags & (nsIPlaintextEditor::eEditorReadonlyMask |
                nsIPlaintextEditor::eEditorDisabledMask))
     *aState = nsIContent::IME_STATUS_DISABLE;
+  else if (flags & nsIPlaintextEditor::eEditorPasswordMask)
+    *aState = nsIContent::IME_STATUS_PASSWORD;
   else
     *aState = nsIContent::IME_STATUS_ENABLE;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEditor::GetReconversionString(nsReconversionEventReply* aReply)
 {
--- a/widget/public/nsIKBStateControl.h
+++ b/widget/public/nsIKBStateControl.h
@@ -36,21 +36,30 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsIKBStateControl_h__
 #define nsIKBStateControl_h__
 
 #include "nsISupports.h"
 
-// {8C636698-8075-4547-80AD-B032F08EF2D3}
+// {BC33E975-C433-4df5-B4BA-041CDE6D1A17}
 #define NS_IKBSTATECONTROL_IID \
-{ 0x8c636698, 0x8075, 0x4547, \
-{ 0x80, 0xad, 0xb0, 0x32, 0xf0, 0x8e, 0xf2, 0xd3 } }
+{ 0xbc33e975, 0xc433, 0x4df5, \
+{ 0xb4, 0xba, 0x04, 0x1c, 0xde, 0x6d, 0x1a, 0x17 } }
+
 
+#if defined(XP_MACOSX)
+/*
+ * If the all applications use same context for IME, i.e., When gecko changes
+ * the state of IME, the same changes can be on other processes.
+ * Then, NS_KBSC_USE_SHARED_CONTEXT should be defined.
+ */
+#define NS_KBSC_USE_SHARED_CONTEXT 1
+#endif
 
 /**
  * interface to control keyboard input state
  */
 class nsIKBStateControl : public nsISupports {
 
   public:
 
@@ -81,28 +90,47 @@ class nsIKBStateControl : public nsISupp
     /*
      * Get IME is 'Opened' or 'Closed'.
      * If IME is 'Opened', aState is set PR_TRUE.
      * If IME is 'Closed', aState is set PR_FALSE.
      */
     NS_IMETHOD GetIMEOpenState(PRBool* aState) = 0;
 
     /*
-     * Set the state to 'Enabled' or 'Disabled'.
-     * If aState is TRUE, IME enable state is set to 'Enabled'.
-     * If aState is FALSE, set to 'Disabled'.
+     * IME enabled states, the aState value of SetIMEEnabled/GetIMEEnabled
+     * should be one value of following values.
      */
-    NS_IMETHOD SetIMEEnabled(PRBool aState) = 0;
+    enum {
+      /*
+       * 'Disabled' means the user cannot use IME. So, the open state should be
+       * 'closed' during 'disabled'.
+       */
+      IME_STATUS_DISABLED = 0,
+      /*
+       * 'Enabled' means the user can use IME.
+       */
+      IME_STATUS_ENABLED = 1,
+      /*
+       * 'Password' state is a special case for the password editors.
+       * E.g., on mac, the password editors should disable the non-Roman
+       * keyboard layouts at getting focus. Thus, the password editor may have
+       * special rules on some platforms.
+       */
+      IME_STATUS_PASSWORD = 2
+    };
 
     /*
-     * Get IME is 'Enabled' or 'Disabled'.
-     * If IME is 'Enabled', aState is set PR_TRUE.
-     * If IME is 'Disabled', aState is set PR_FALSE.
+     * Set the state to 'Enabled' or 'Disabled' or 'Password'.
      */
-    NS_IMETHOD GetIMEEnabled(PRBool* aState) = 0;
+    NS_IMETHOD SetIMEEnabled(PRUint32 aState) = 0;
+
+    /*
+     * Get IME is 'Enabled' or 'Disabled' or 'Password'.
+     */
+    NS_IMETHOD GetIMEEnabled(PRUint32* aState) = 0;
 
     /*
      * Destruct and don't commit the IME composition string.
      */
     NS_IMETHOD CancelIMEComposition() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIKBStateControl, NS_IKBSTATECONTROL_IID)
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -93,17 +93,16 @@ union nsPluginPort;
   BOOL mIsPluginView;
 
   NSEvent* mCurKeyEvent;   // only valid during a keyDown
   PRBool  mKeyHandled;
   
   // needed for NSTextInput implementation
   NSRange mMarkedRange;
   NSRange mSelectedRange;
-  BOOL mInComposition;
   BOOL mIgnoreDoCommand;
 
   BOOL mInHandScroll; // true for as long as we are hand scrolling
   // hand scroll locations
   NSPoint mHandScrollStartMouseLoc;
   nscoord mHandScrollStartScrollX, mHandScrollStartScrollY;
   
   // when menuForEvent: is called, we store its event here (strong)
@@ -121,24 +120,53 @@ union nsPluginPort;
   nsIDragService* mDragService;
   
   PRUint32 mLastModifierState;
 }
 
 // these are sent to the first responder when the window key status changes
 - (void)viewsWindowDidBecomeKey;
 - (void)viewsWindowDidResignKey;
-
-- (BOOL)isComposing;
 @end
 
 
 
 //-------------------------------------------------------------------------
 //
+// nsTSMManager
+//
+//-------------------------------------------------------------------------
+
+class nsTSMManager {
+public:
+  static PRBool IsComposing() { return sComposingView ? PR_TRUE : PR_FALSE; }
+  static PRBool IsIMEEnabled() { return sIsIMEEnabled; }
+
+  // Note that we cannot get the actual state in TSM. But we can trust this
+  // value. Because nsIMEStateManager reset this at every focus changing.
+  static PRBool IsRomanKeyboardsOnly() { return sIsRomanKeyboardsOnly; }
+
+  static PRBool GetIMEOpenState();
+
+  static void StartComposing(NSView<mozView>* aComposingView);
+  static void EndComposing();
+  static void EnableIME(PRBool aEnable);
+  static void SetIMEOpenState(PRBool aOpen);
+  static void SetRomanKeyboardsOnly(PRBool aRomanOnly);
+
+  static void CommitIME();
+  static void CancelIME();
+private:
+  static PRBool sIsIMEEnabled;
+  static PRBool sIsRomanKeyboardsOnly;
+  static NSView<mozView>* sComposingView;
+};
+
+//-------------------------------------------------------------------------
+//
 // nsChildView
 //
 //-------------------------------------------------------------------------
 
 class nsChildView : public nsBaseWidget,
                     public nsIPluginWidget,
                     public nsIKBStateControl,
                     public nsIEventSink
@@ -152,18 +180,18 @@ public:
   
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIEVENTSINK 
 
   // nsIKBStateControl interface
   NS_IMETHOD              ResetInputState();
   NS_IMETHOD              SetIMEOpenState(PRBool aState);
   NS_IMETHOD              GetIMEOpenState(PRBool* aState);
-  NS_IMETHOD              SetIMEEnabled(PRBool aState);
-  NS_IMETHOD              GetIMEEnabled(PRBool* aState);
+  NS_IMETHOD              SetIMEEnabled(PRUint32 aState);
+  NS_IMETHOD              GetIMEEnabled(PRUint32* aState);
   NS_IMETHOD              CancelIMEComposition();
  
   // nsIWidget interface
   NS_IMETHOD              Create(nsIWidget *aParent,
                                  const nsRect &aRect,
                                  EVENT_CALLBACK aHandleEventFunction,
                                  nsIDeviceContext *aContext,
                                  nsIAppShell *aAppShell = nsnull,
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -73,16 +73,20 @@
 #undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated
 
 extern "C" {
   CG_EXTERN void CGContextResetCTM(CGContextRef);
   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
   CG_EXTERN void CGContextResetClip(CGContextRef);
 }
 
+PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
+PRBool nsTSMManager::sIsRomanKeyboardsOnly = PR_FALSE;
+NSView<mozView>* nsTSMManager::sComposingView = nsnull;
+
 static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
 static NSView* sLastViewEntered = nil;
 #ifdef INVALIDATE_DEBUGGING
 static void blinkRect(Rect* r);
 static void blinkRgn(RgnHandle rgn);
 #endif
 
 nsIRollupListener * gRollupListener = nsnull;
@@ -1570,70 +1574,95 @@ NS_IMETHODIMP nsChildView::GetAnimatedRe
 // Note that this and other nsIKBStateControl methods don't necessarily
 // get called on the same ChildView that input is going through.
 NS_IMETHODIMP nsChildView::ResetInputState()
 {
 #ifdef DEBUG_IME
   NSLog(@"**** ResetInputState");
 #endif
 
-  if (![mView isComposing])
-    return NS_OK;
-
-  NSInputManager *currentIM = [NSInputManager currentInputManager];
-  
-  // commit the current text
-  [currentIM unmarkText];
-
-  // and clear the input manager's string
-  [currentIM markedTextAbandoned:mView];
-  
+  nsTSMManager::CommitIME();
   return NS_OK;
 }
 
 
 // 'open' means that it can take non-ASCII chars
 NS_IMETHODIMP nsChildView::SetIMEOpenState(PRBool aState)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+#ifdef DEBUG_IME
+  NSLog(@"**** SetIMEOpenState aState = %d", aState);
+#endif
+
+  nsTSMManager::SetIMEOpenState(aState);
+  return NS_OK;
 }
 
 
 // 'open' means that it can take non-ASCII chars
 NS_IMETHODIMP nsChildView::GetIMEOpenState(PRBool* aState)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+#ifdef DEBUG_IME
+  NSLog(@"**** GetIMEOpenState");
+#endif
+
+  *aState = nsTSMManager::GetIMEOpenState();
+  return NS_OK;
 }
 
 
-NS_IMETHODIMP nsChildView::SetIMEEnabled(PRBool aState)
+NS_IMETHODIMP nsChildView::SetIMEEnabled(PRUint32 aState)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+#ifdef DEBUG_IME
+  NSLog(@"**** SetIMEEnabled aState = %d", aState);
+#endif
+
+  switch (aState) {
+    case nsIKBStateControl::IME_STATUS_ENABLED:
+      nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
+      nsTSMManager::EnableIME(PR_TRUE);
+      break;
+    case nsIKBStateControl::IME_STATUS_DISABLED:
+      nsTSMManager::SetRomanKeyboardsOnly(PR_FALSE);
+      nsTSMManager::EnableIME(PR_FALSE);
+      break;
+    case nsIKBStateControl::IME_STATUS_PASSWORD:
+      nsTSMManager::SetRomanKeyboardsOnly(PR_TRUE);
+      nsTSMManager::EnableIME(PR_FALSE);
+      break;
+    default:
+      NS_ERROR("not implemented!");
+  }
+  return NS_OK;
 }
 
 
-NS_IMETHODIMP nsChildView::GetIMEEnabled(PRBool* aState)
+NS_IMETHODIMP nsChildView::GetIMEEnabled(PRUint32* aState)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+#ifdef DEBUG_IME
+  NSLog(@"**** GetIMEEnabled");
+#endif
+
+  if (nsTSMManager::IsIMEEnabled())
+    *aState = nsIKBStateControl::IME_STATUS_ENABLED;
+  else if (nsTSMManager::IsRomanKeyboardsOnly())
+    *aState = nsIKBStateControl::IME_STATUS_PASSWORD;
+  else
+    *aState = nsIKBStateControl::IME_STATUS_DISABLED;
+  return NS_OK;
 }
 
 
 // Destruct and don't commit the IME composition string.
 NS_IMETHODIMP nsChildView::CancelIMEComposition()
 {
 #ifdef DEBUG_IME
   NSLog(@"**** CancelIMEComposition");
 #endif
 
-  if (![mView isComposing])
-    return NS_OK;
-
-  NSInputManager *currentIM = [NSInputManager currentInputManager];
-  [currentIM markedTextAbandoned:mView];
-  
+  nsTSMManager::CancelIME();
   return NS_OK;
 }
 
 
 GrafPtr
 nsChildView::GetChildViewQuickDrawPort()
 {
   if ([mView isKindOfClass:[ChildView class]])
@@ -1780,17 +1809,16 @@ NSEvent* globalDragEvent = nil;
     mCurKeyEvent = nil;
     mKeyHandled = PR_FALSE;
     
     // initialization for NSTextInput
     mMarkedRange.location = NSNotFound;
     mMarkedRange.length = 0;
     mSelectedRange.location = NSNotFound;
     mSelectedRange.length = 0;
-    mInComposition = NO;
     mLastMenuForEventEvent = nil;
     mDragService = nsnull;
   }
   
   // register for things we'll take from other applications
   [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
                                                           NSStringPboardType,
                                                           NSURLPboardType,
@@ -2910,17 +2938,17 @@ static void ConvertCocoaKeyEventToMacEve
 
   NSString *tmpStr = [insertString string];
   unsigned int len = [tmpStr length];
   PRUnichar buffer[MAX_BUFFER_SIZE];
   PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
   [tmpStr getCharacters: bufPtr];
   bufPtr[len] = (PRUnichar)'\0';
 
-  if (len == 1 && !mInComposition) {
+  if (len == 1 && !nsTSMManager::IsComposing()) {
     // dispatch keypress event with char instead of textEvent
     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
     geckoEvent.time      = PR_IntervalNow();
     geckoEvent.charCode  = bufPtr[0]; // gecko expects OS-translated unicode
     geckoEvent.isChar    = PR_TRUE;
     geckoEvent.isShift   = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
     if (mKeyHandled)
       geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
@@ -2933,31 +2961,31 @@ static void ConvertCocoaKeyEventToMacEve
     if (mCurKeyEvent) {
       ConvertCocoaKeyEventToMacEvent(mCurKeyEvent, macEvent);
       geckoEvent.nativeMsg = &macEvent;
     }
 
     mGeckoChild->DispatchWindowEvent(geckoEvent);
   }
   else {
-    if (!mInComposition) {
+    if (!nsTSMManager::IsComposing()) {
       // send start composition event to gecko
       [self sendCompositionEvent: NS_COMPOSITION_START];
-      mInComposition = YES;
+      nsTSMManager::StartComposing(self);
     }
 
     // dispatch textevent (is this redundant?)
     [self sendTextEvent:bufPtr attributedString:insertString
                                selectedRange:NSMakeRange(0, len)
                                markedRange:mMarkedRange
                                doCommit:YES];
 
     // send end composition event to gecko
     [self sendCompositionEvent: NS_COMPOSITION_END];
-    mInComposition = NO;
+    nsTSMManager::EndComposing();
     mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
   }
 
   if (bufPtr != buffer)
     delete[] bufPtr;
 }
 
 
@@ -3004,46 +3032,47 @@ static void ConvertCocoaKeyEventToMacEve
   PRUint32 maxlen = len > 12 ? 12 : len;
   for (PRUnichar *a = bufPtr; (*a != (PRUnichar)'\0') && n<maxlen; a++, n++) printf((*a&0xff80) ? "\\u%4X" : "%c", *a); 
   printf("\n");
 #endif
 
   mMarkedRange.location = 0;
   mMarkedRange.length = len;
 
-  if (!mInComposition) {
+  if (!nsTSMManager::IsComposing()) {
     [self sendCompositionEvent:NS_COMPOSITION_START];
-    mInComposition = YES;
+    nsTSMManager::StartComposing(self);
   }
 
   [self sendTextEvent:bufPtr attributedString:aString
                              selectedRange:selRange
                              markedRange:mMarkedRange
                              doCommit:NO];
 
-  if (mInComposition && len == 0)
+  if (nsTSMManager::IsComposing() && len == 0)
     [self unmarkText];
   
   if (bufPtr != buffer)
     delete[] bufPtr;
 }
 
 
 - (void) unmarkText
 {
 #if DEBUG_IME
   NSLog(@"****in unmarkText");
   NSLog(@" markedRange   = %d, %d", mMarkedRange.location, mMarkedRange.length);
   NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
 #endif
 
   mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
-  if (mInComposition) {
+  if (nsTSMManager::IsComposing()) {
     [self sendCompositionEvent: NS_COMPOSITION_END];
-    mInComposition = NO;  // brade: do we need to send an end composition event?
+    // brade: do we need to send an end composition event?
+    nsTSMManager::EndComposing();
   }
 }
 
 
 - (BOOL) hasMarkedText
 {
   return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
 }
@@ -3186,38 +3215,48 @@ static void ConvertCocoaKeyEventToMacEve
   // The key down event may have shifted the focus, in which
   // case we should not fire the key press.
   NSResponder* resp = [[self window] firstResponder];
   if (resp != (NSResponder*)self) {
     mCurKeyEvent = nil;
     return;
   }
 
+  PRBool dispatchedKeyPress = PR_FALSE;
   if (nonDeadKeyPress) {
     nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, nsnull);
     [self convertKeyEvent:theEvent toGeckoEvent:&geckoEvent];
 
     if (mKeyHandled)
       geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
 
     // if this is a non-letter keypress, or the control key is down,
     // dispatch the keydown to gecko, so that we trap delete,
     // control-letter combinations etc before Cocoa tries to use
     // them for keybindings.
-    if ((!geckoEvent.isChar || geckoEvent.isControl) && !mInComposition) {
+    if ((!geckoEvent.isChar || geckoEvent.isControl) &&
+        !nsTSMManager::IsComposing()) {
       // plugins need a native event, it will either be keyDown or autoKey
       EventRecord macEvent;
       ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
       geckoEvent.nativeMsg = &macEvent;
 
       mIgnoreDoCommand = mGeckoChild->DispatchWindowEvent(geckoEvent);
+      dispatchedKeyPress = PR_TRUE;
     }
   }
 
-  [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
+  // We should send this event to the superclass if IME is enabled.
+  // Otherwise, we need to suppress IME composition. We can do it by
+  // not sending this event to the superclass. But in that case,
+  // we need to call insertText ourselves.
+  if (nsTSMManager::IsIMEEnabled())
+    [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
+  else if (nonDeadKeyPress && !dispatchedKeyPress)
+    [self insertText:[theEvent characters]];
 
   mIgnoreDoCommand = NO;
   mCurKeyEvent = nil;
   mKeyHandled = PR_FALSE;
 }
 
 
 - (void)keyUp:(NSEvent*)theEvent
@@ -3237,17 +3276,17 @@ static void ConvertCocoaKeyEventToMacEve
 
   mGeckoChild->DispatchWindowEvent(geckoEvent);
 }
 
 
 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
 {
   // don't bother if we're in composition
-  if (mInComposition)
+  if (nsTSMManager::IsComposing())
     return NO;
 
   // see if the menu system will handle the event
   if ([[NSApp mainMenu] performKeyEquivalent:theEvent])
     return YES;
 
   // don't handle this if certain modifiers are down - those should
   // be sent as normal key up/down events and cocoa will do so automatically
@@ -3367,22 +3406,16 @@ static void ConvertCocoaKeyEventToMacEve
   nsFocusEvent deactivateEvent(PR_TRUE, NS_DEACTIVATE, mGeckoChild);
   mGeckoChild->DispatchWindowEvent(deactivateEvent);
 
   nsFocusEvent unfocusEvent(PR_TRUE, NS_LOSTFOCUS, mGeckoChild);
   mGeckoChild->DispatchWindowEvent(unfocusEvent);
 }
 
 
-- (BOOL)isComposing
-{
-  return mInComposition;
-}
-
-
 // Key code constants
 enum
 {
   kEscapeKeyCode      = 0x35,
   kCommandKeyCode     = 0x37,
   kShiftKeyCode       = 0x38,
   kCapsLockKeyCode    = 0x39,
   kControlKeyCode     = 0x3B,
@@ -3920,8 +3953,99 @@ static PRBool IsSpecialGeckoKey(UInt32 m
   }
 
   return [accessible accessibilityAttributeValue:attribute];
 }
 
 #endif /* ACCESSIBILITY */
 
 @end
+
+
+#pragma mark -
+
+
+PRBool
+nsTSMManager::GetIMEOpenState()
+{
+  return GetScriptManagerVariable(smKeyScript) != smRoman ? PR_TRUE : PR_FALSE;
+}
+
+
+void
+nsTSMManager::StartComposing(NSView<mozView>* aComposingView)
+{
+  if (sComposingView && sComposingView != sComposingView)
+    CommitIME();
+  sComposingView = aComposingView;
+}
+
+
+void
+nsTSMManager::EndComposing()
+{
+  sComposingView = nsnull;
+}
+
+
+void
+nsTSMManager::EnableIME(PRBool aEnable)
+{
+  if (aEnable == sIsIMEEnabled)
+    return;
+  CommitIME();
+  sIsIMEEnabled = aEnable;
+}
+
+
+void
+nsTSMManager::SetIMEOpenState(PRBool aOpen)
+{
+  if (aOpen == GetIMEOpenState())
+    return;
+  CommitIME();
+  KeyScript(aOpen ? smKeySwapScript : smKeyRoman);
+}
+
+
+#define ENABLE_ROMAN_KYBDS_ONLY -23
+void
+nsTSMManager::SetRomanKeyboardsOnly(PRBool aRomanOnly)
+{
+  if (aRomanOnly == sIsRomanKeyboardsOnly)
+    return;
+  CommitIME();
+  KeyScript(aRomanOnly ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds);
+  sIsRomanKeyboardsOnly = aRomanOnly;
+}
+
+
+void
+nsTSMManager::CommitIME()
+{
+  if (!sComposingView)
+    return;
+
+  NSInputManager *currentIM = [NSInputManager currentInputManager];
+
+  // commit the current text
+  [currentIM unmarkText];
+
+  // and clear the input manager's string
+  [currentIM markedTextAbandoned:sComposingView];
+
+  EndComposing();
+}
+
+
+void
+nsTSMManager::CancelIME()
+{
+  if (!sComposingView)
+    return;
+
+  NSInputManager *currentIM = [NSInputManager currentInputManager];
+
+  // clear the input manager's string
+  [currentIM markedTextAbandoned:sComposingView];
+
+  EndComposing();
+}
--- a/widget/src/gtk/nsWidget.cpp
+++ b/widget/src/gtk/nsWidget.cpp
@@ -2369,21 +2369,21 @@ NS_IMETHODIMP nsWidget::ResetInputState(
 NS_IMETHODIMP nsWidget::SetIMEOpenState(PRBool aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsWidget::GetIMEOpenState(PRBool* aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsWidget::SetIMEEnabled(PRBool aState) {
+NS_IMETHODIMP nsWidget::SetIMEEnabled(PRUint32 aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsWidget::GetIMEEnabled(PRBool* aState) {
+NS_IMETHODIMP nsWidget::GetIMEEnabled(PRUint32* aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsWidget::CancelIMEComposition() {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* virtual */
--- a/widget/src/gtk/nsWidget.h
+++ b/widget/src/gtk/nsWidget.h
@@ -169,18 +169,18 @@ public:
   NS_IMETHOD Update(void);
   NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus);
 
 
   // nsIKBStateControl
   NS_IMETHOD ResetInputState();
   NS_IMETHOD SetIMEOpenState(PRBool aState);
   NS_IMETHOD GetIMEOpenState(PRBool* aState);
-  NS_IMETHOD SetIMEEnabled(PRBool aState);
-  NS_IMETHOD GetIMEEnabled(PRBool* aState);
+  NS_IMETHOD SetIMEEnabled(PRUint32 aState);
+  NS_IMETHOD GetIMEEnabled(PRUint32* aState);
   NS_IMETHOD CancelIMEComposition();
 
   void InitEvent(nsGUIEvent& event, nsPoint* aPoint = nsnull);
     
   // Utility functions
 
   PRBool     ConvertStatus(nsEventStatus aStatus)
                           { return aStatus == nsEventStatus_eConsumeNoDefault; }
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -5063,17 +5063,17 @@ nsWindow::IMEDestroyContext(void)
     // to unset the focus on this window before we destroy the window.
     GtkIMContext *im = IMEGetContext();
     if (im && gIMEFocusWindow && gIMEFocusWindow->IMEGetContext() == im) {
         gIMEFocusWindow->IMELoseFocus();
         gIMEFocusWindow = nsnull;
     }
 
     mIMEData->mOwner   = nsnull;
-    mIMEData->mEnabled = PR_FALSE;
+    mIMEData->mEnabled = nsIKBStateControl::IME_STATUS_DISABLED;
 
     if (mIMEData->mContext) {
         gtk_im_context_set_client_window(mIMEData->mContext, nsnull);
         g_object_unref(G_OBJECT(mIMEData->mContext));
         mIMEData->mContext = nsnull;
     }
 
     if (mIMEData->mDummyContext) {
@@ -5226,20 +5226,26 @@ nsWindow::IMEComposeEnd(void)
 }
 
 GtkIMContext*
 nsWindow::IMEGetContext()
 {
     return IM_get_input_context(this);
 }
 
+static PRBool
+IsIMEEnabledState(PRUint32 aState)
+{
+    return aState == nsIKBStateControl::IME_STATUS_ENABLED ? PR_TRUE : PR_FALSE;
+}
+
 PRBool
 nsWindow::IMEIsEnabled(void)
 {
-    return mIMEData ? mIMEData->mEnabled : PR_FALSE;
+    return mIMEData ? IsIMEEnabledState(mIMEData->mEnabled) : PR_FALSE;
 }
 
 nsWindow*
 nsWindow::IMEComposingWindow(void)
 {
     return mIMEData ? mIMEData->mComposingWindow : nsnull;
 }
 
@@ -5349,58 +5355,65 @@ nsWindow::SetIMEOpenState(PRBool aState)
 
 NS_IMETHODIMP
 nsWindow::GetIMEOpenState(PRBool* aState)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsWindow::SetIMEEnabled(PRBool aState)
+nsWindow::SetIMEEnabled(PRUint32 aState)
 {
     IMEInitData();
-
-    if (!mIMEData || mIMEData->mEnabled == aState)
+    if (!mIMEData)
         return NS_OK;
 
+    PRBool newState = IsIMEEnabledState(aState);
+    PRBool oldState = IsIMEEnabledState(mIMEData->mEnabled);
+    if (newState == oldState) {
+        mIMEData->mEnabled = aState;
+        return NS_OK;
+    }
+
     GtkIMContext *focusedIm = nsnull;
     // XXX Don't we need to check gFocusWindow?
     nsWindow *focusedWin = gIMEFocusWindow;
     if (focusedWin && focusedWin->mIMEData)
         focusedIm = focusedWin->mIMEData->mContext;
 
     if (focusedIm && focusedIm == mIMEData->mContext) {
         // Release current IME focus if IME is enabled.
-        if (mIMEData->mEnabled) {
+        if (oldState) {
             focusedWin->ResetInputState();
             focusedWin->IMELoseFocus();
         }
 
         mIMEData->mEnabled = aState;
 
         // Even when aState is not PR_TRUE, we need to set IME focus.
         // Because some IMs are updating the status bar of them in this time.
         focusedWin->IMESetFocus();
     } else {
-        if (mIMEData->mEnabled)
+        if (oldState)
             ResetInputState();
         mIMEData->mEnabled = aState;
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWindow::GetIMEEnabled(PRBool* aState)
+nsWindow::GetIMEEnabled(PRUint32* aState)
 {
     NS_ENSURE_ARG_POINTER(aState);
 
     IMEInitData();
 
-    *aState = IMEIsEnabled();
+    *aState =
+      mIMEData ? mIMEData->mEnabled : nsIKBStateControl::IME_STATUS_DISABLED;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::CancelIMEComposition()
 {
     IMEInitData();
 
--- a/widget/src/gtk2/nsWindow.h
+++ b/widget/src/gtk2/nsWindow.h
@@ -313,34 +313,34 @@ public:
         nsWindow           *mComposingWindow;
         // Owner of this struct.
         // The owner window must release the contexts at destroying.
         nsWindow           *mOwner;
         // The reference counter. When this will be zero by the decrement,
         // the decrementer must free the instance.
         PRUint32           mRefCount;
         // IME enabled state in this window.
-        PRPackedBool       mEnabled;
+        PRUint32           mEnabled;
         nsIMEData(nsWindow* aOwner) {
             mContext         = nsnull;
             mDummyContext    = nsnull;
             mComposingWindow = nsnull;
             mOwner           = aOwner;
             mRefCount        = 1;
-            mEnabled         = PR_TRUE;
+            mEnabled         = nsIKBStateControl::IME_STATUS_ENABLED;
         }
     };
     nsIMEData          *mIMEData;
 
     // nsIKBStateControl interface
     NS_IMETHOD ResetInputState();
     NS_IMETHOD SetIMEOpenState(PRBool aState);
     NS_IMETHOD GetIMEOpenState(PRBool* aState);
-    NS_IMETHOD SetIMEEnabled(PRBool aState);
-    NS_IMETHOD GetIMEEnabled(PRBool* aState);
+    NS_IMETHOD SetIMEEnabled(PRUint32 aState);
+    NS_IMETHOD GetIMEEnabled(PRUint32* aState);
     NS_IMETHOD CancelIMEComposition();
 
 #endif
 
    void                ResizeTransparencyBitmap(PRInt32 aNewWidth, PRInt32 aNewHeight);
    void                ApplyTransparencyBitmap();
 #ifdef MOZ_XUL
    NS_IMETHOD          SetWindowTranslucency(PRBool aTransparent);
--- a/widget/src/mac/nsWindow.cpp
+++ b/widget/src/mac/nsWindow.cpp
@@ -470,17 +470,17 @@ void* nsWindow::GetNativeData(PRUint32 a
       retVal = (void*)mWindowPtr;
     	break;
 
     case NS_NATIVE_REGION:
 		retVal = (void*)mVisRegion;
     	break;
 
     case NS_NATIVE_COLORMAP:
-    	//•TODO
+    	// TODO
     	break;
 
     case NS_NATIVE_OFFSETX:
     	point.MoveTo(mBounds.x, mBounds.y);
     	LocalToWindowCoordinate(point);
     	retVal = (void*)point.x;
      	break;
 
@@ -633,17 +633,17 @@ NS_IMETHODIMP nsWindow::SetFont(const ns
 
 //-------------------------------------------------------------------------
 //
 // Set the colormap of the window
 //
 //-------------------------------------------------------------------------
 NS_IMETHODIMP nsWindow::SetColorMap(nsColorMap *aColorMap)
 {
-	//•TODO
+	// TODO
 	// We may need to move this to nsMacWindow:
 	// I'm not sure all the individual widgets
 	// can have each their own colorMap on Mac.
 	return NS_OK;
 }
 
 //-------------------------------------------------------------------------
 //
@@ -1102,17 +1102,17 @@ inline PRUint16 COLOR8TOCOLOR16(PRUint8 
 //
 //-------------------------------------------------------------------------
 void nsWindow::StartDraw(nsIRenderingContext* aRenderingContext)
 {
 	if (mDrawing || mOnDestroyCalled)
 		return;
 	mDrawing = PR_TRUE;
 
-	CalcWindowRegions();	//•REVISIT
+	CalcWindowRegions();	// REVISIT
 
 	if (aRenderingContext == nsnull)
 	{
 		// make sure we have a rendering context
 		mTempRenderingContext = GetRenderingContext();
 		mTempRenderingContextMadeHere = PR_TRUE;
 	}
 	else
@@ -2362,20 +2362,20 @@ NS_IMETHODIMP nsWindow::ResetInputState(
 NS_IMETHODIMP nsWindow::SetIMEOpenState(PRBool aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsWindow::GetIMEOpenState(PRBool* aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsWindow::SetIMEEnabled(PRBool aState) {
+NS_IMETHODIMP nsWindow::SetIMEEnabled(PRUint32 aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsWindow::GetIMEEnabled(PRBool* aState) {
+NS_IMETHODIMP nsWindow::GetIMEEnabled(PRUint32* aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsWindow::CancelIMEComposition() {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
--- a/widget/src/mac/nsWindow.h
+++ b/widget/src/mac/nsWindow.h
@@ -214,18 +214,18 @@ public:
     NS_IMETHOD              GetPluginClipRect(nsRect& outClipRect, nsPoint& outOrigin, PRBool& outWidgetVisible);
     NS_IMETHOD              StartDrawPlugin(void);
     NS_IMETHOD              EndDrawPlugin(void);
 
     // nsIKBStateControl interface
     NS_IMETHOD ResetInputState();
     NS_IMETHOD SetIMEOpenState(PRBool aState);
     NS_IMETHOD GetIMEOpenState(PRBool* aState);
-    NS_IMETHOD SetIMEEnabled(PRBool aState);
-    NS_IMETHOD GetIMEEnabled(PRBool* aState);
+    NS_IMETHOD SetIMEEnabled(PRUint32 aState);
+    NS_IMETHOD GetIMEEnabled(PRUint32* aState);
     NS_IMETHOD CancelIMEComposition();
 
 protected:
 
   PRBool          ReportDestroyEvent();
   PRBool          ReportMoveEvent();
   PRBool          ReportSizeEvent();
 
--- a/widget/src/photon/nsWidget.cpp
+++ b/widget/src/photon/nsWidget.cpp
@@ -234,21 +234,21 @@ NS_IMETHODIMP nsWidget::ResetInputState(
 NS_IMETHODIMP nsWidget::SetIMEOpenState(PRBool aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 	}
 
 NS_IMETHODIMP nsWidget::GetIMEOpenState(PRBool* aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 	}
 
-NS_IMETHODIMP nsWidget::SetIMEEnabled(PRBool aState) {
+NS_IMETHODIMP nsWidget::SetIMEEnabled(PRUint32 aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 	}
 
-NS_IMETHODIMP nsWidget::GetIMEEnabled(PRBool* aState) {
+NS_IMETHODIMP nsWidget::GetIMEEnabled(PRUint32* aState) {
   return NS_ERROR_NOT_IMPLEMENTED;
 	}
 
 NS_IMETHODIMP nsWidget::CancelIMEComposition() {
   return NS_ERROR_NOT_IMPLEMENTED;
 	}
 
 //-------------------------------------------------------------------------
--- a/widget/src/photon/nsWidget.h
+++ b/widget/src/photon/nsWidget.h
@@ -211,18 +211,18 @@ public:
 
   NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus);
 
 
   // nsIKBStateControl
   NS_IMETHOD ResetInputState();
   NS_IMETHOD SetIMEOpenState(PRBool aState);
   NS_IMETHOD GetIMEOpenState(PRBool* aState);
-  NS_IMETHOD SetIMEEnabled(PRBool aState);
-  NS_IMETHOD GetIMEEnabled(PRBool* aState);
+  NS_IMETHOD SetIMEEnabled(PRUint32 aState);
+  NS_IMETHOD GetIMEEnabled(PRUint32* aState);
   NS_IMETHOD CancelIMEComposition();
 
   inline void InitEvent(nsGUIEvent& event, PRUint32 aEventType, nsPoint* aPoint = nsnull)
 		{
 		if( aPoint == nsnull ) {
 		  event.refPoint.x = 0;
 		  event.refPoint.y = 0;
 		  }
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -813,16 +813,17 @@ nsWindow::nsWindow() : nsBaseWidget()
   mIsInMouseCapture   = PR_FALSE;
   mIsInMouseWheelProcessing = PR_FALSE;
   mLastSize.width     = 0;
   mLastSize.height    = 0;
   mOldStyle           = 0;
   mOldExStyle         = 0;
   mPainting           = 0;
   mOldIMC             = NULL;
+  mIMEEnabled         = nsIKBStateControl::IME_STATUS_ENABLED;
 
   mLeadByte = '\0';
   mBlurEventSuppressionLevel = 0;
 
   static BOOL gbInitGlobalValue = FALSE;
   if (! gbInitGlobalValue) {
     gbInitGlobalValue = TRUE;
     gKeyboardLayout = GetKeyboardLayout(0);
@@ -7468,32 +7469,34 @@ NS_IMETHODIMP nsWindow::GetIMEOpenState(
     *aState = isOpen ? PR_TRUE : PR_FALSE;
     ::ImmReleaseContext(mWnd, hIMC);
   } else 
     *aState = PR_FALSE;
   return NS_OK;
 }
 
 //==========================================================================
-NS_IMETHODIMP nsWindow::SetIMEEnabled(PRBool aState)
+NS_IMETHODIMP nsWindow::SetIMEEnabled(PRUint32 aState)
 {
   if (sIMEIsComposing)
     ResetInputState();
-  if (!aState != !mOldIMC)
+  mIMEEnabled = aState;
+  PRBool enable = (aState == nsIKBStateControl::IME_STATUS_ENABLED);
+  if (!enable != !mOldIMC)
     return NS_OK;
-  mOldIMC = ::ImmAssociateContext(mWnd, aState ? mOldIMC : NULL);
-  NS_ASSERTION(!aState || !mOldIMC, "Another IMC was associated");
+  mOldIMC = ::ImmAssociateContext(mWnd, enable ? mOldIMC : NULL);
+  NS_ASSERTION(!enable || !mOldIMC, "Another IMC was associated");
 
   return NS_OK;
 }
 
 //==========================================================================
-NS_IMETHODIMP nsWindow::GetIMEEnabled(PRBool* aState)
-{
-  *aState = !mOldIMC;
+NS_IMETHODIMP nsWindow::GetIMEEnabled(PRUint32* aState)
+{
+  *aState = mIMEEnabled;
   return NS_OK;
 }
 
 //==========================================================================
 NS_IMETHODIMP nsWindow::CancelIMEComposition()
 {
 #ifdef DEBUG_KBSTATE
   printf("CancelIMEComposition\n");
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -225,18 +225,18 @@ private:
 public:
 #endif
 
   // nsIKBStateControl interface
 
   NS_IMETHOD ResetInputState();
   NS_IMETHOD SetIMEOpenState(PRBool aState);
   NS_IMETHOD GetIMEOpenState(PRBool* aState);
-  NS_IMETHOD SetIMEEnabled(PRBool aState);
-  NS_IMETHOD GetIMEEnabled(PRBool* aState);
+  NS_IMETHOD SetIMEEnabled(PRUint32 aState);
+  NS_IMETHOD GetIMEEnabled(PRUint32* aState);
   NS_IMETHOD CancelIMEComposition();
 
   PRBool IMEMouseHandling(PRInt32 aAction, LPARAM lParam);
   PRBool IMECompositionHitTest(POINT * ptPos);
   PRBool HandleMouseActionOfIME(PRInt32 aAction, POINT* ptPos);
   void GetCompositionWindowPos(HIMC hIMC, PRUint32 aEventType, COMPOSITIONFORM *cpForm);
 
   // nsSwitchToUIThread interface
@@ -446,16 +446,17 @@ protected:
   PRInt32       mMenuCmdId;
 
   // Window styles used by this window before chrome was hidden
   DWORD         mOldStyle;
   DWORD         mOldExStyle;
 
   // To enable/disable IME
   HIMC          mOldIMC;
+  PRUint32      mIMEEnabled;
 
   static HKL    gKeyboardLayout;
   static PRBool gSwitchKeyboardLayout;
 
   HKL           mLastKeyboardLayout;
 
   // Drag & Drop
   nsNativeDragTarget * mNativeDragTarget;