Bug 1184449 part.2 nsIMM32Handler should store selection range as far as possible r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 22 Jul 2015 12:40:32 +0900
changeset 285711 4f684751728862e83dff92bd4eee5282f9a3f78e
parent 285710 56319240dff61410b6dffc7ae80ba2cd41f9e1eb
child 285712 d82e6e3d419eca2168aca7b261164c9e1405efeb
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1184449
milestone42.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 1184449 part.2 nsIMM32Handler should store selection range as far as possible r=m_kato
widget/windows/WinIMEHandler.cpp
widget/windows/nsIMM32Handler.cpp
widget/windows/nsIMM32Handler.h
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -173,38 +173,42 @@ IMEHandler::NotifyIME(nsWindow* aWindow,
 #ifdef NS_ENABLE_TSF
   if (IsTSFAvailable()) {
     switch (aIMENotification.mMessage) {
       case NOTIFY_IME_OF_SELECTION_CHANGE: {
         nsresult rv = nsTextStore::OnSelectionChange(aIMENotification);
         // If IMM IME is active, we need to notify nsIMM32Handler of updating
         // composition change.  It will adjust candidate window position or
         // composition window position.
-        if (IsIMMActive()) {
+        bool isIMMActive = IsIMMActive();
+        if (isIMMActive) {
           nsIMM32Handler::OnUpdateComposition(aWindow);
-          nsIMM32Handler::OnSelectionChange(aWindow, aIMENotification);
         }
+        nsIMM32Handler::OnSelectionChange(aWindow, aIMENotification,
+                                          isIMMActive);
         return rv;
       }
       case NOTIFY_IME_OF_COMPOSITION_UPDATE:
         // If IMM IME is active, we need to notify nsIMM32Handler of updating
         // composition change.  It will adjust candidate window position or
         // composition window position.
         if (IsIMMActive()) {
           nsIMM32Handler::OnUpdateComposition(aWindow);
         } else {
           nsTextStore::OnUpdateComposition();
         }
         return NS_OK;
       case NOTIFY_IME_OF_TEXT_CHANGE:
         return nsTextStore::OnTextChange(aIMENotification);
       case NOTIFY_IME_OF_FOCUS:
+        nsIMM32Handler::OnFocusChange(true, aWindow);
         return nsTextStore::OnFocusChange(true, aWindow,
                                           aWindow->GetInputContext());
       case NOTIFY_IME_OF_BLUR:
+        nsIMM32Handler::OnFocusChange(false, aWindow);
         return nsTextStore::OnFocusChange(false, aWindow,
                                           aWindow->GetInputContext());
       case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
         // If IMM IME is active, we should send a mouse button event via IMM.
         if (IsIMMActive()) {
           return nsIMM32Handler::OnMouseButtonEvent(aWindow, aIMENotification);
         }
         return nsTextStore::OnMouseButtonEvent(aIMENotification);
@@ -237,30 +241,34 @@ IMEHandler::NotifyIME(nsWindow* aWindow,
     case REQUEST_TO_CANCEL_COMPOSITION:
       nsIMM32Handler::CancelComposition(aWindow);
       return NS_OK;
     case NOTIFY_IME_OF_POSITION_CHANGE:
     case NOTIFY_IME_OF_COMPOSITION_UPDATE:
       nsIMM32Handler::OnUpdateComposition(aWindow);
       return NS_OK;
     case NOTIFY_IME_OF_SELECTION_CHANGE:
-      nsIMM32Handler::OnSelectionChange(aWindow, aIMENotification);
+      nsIMM32Handler::OnSelectionChange(aWindow, aIMENotification, true);
       return NS_OK;
     case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
       return nsIMM32Handler::OnMouseButtonEvent(aWindow, aIMENotification);
+    case NOTIFY_IME_OF_FOCUS:
+      nsIMM32Handler::OnFocusChange(true, aWindow);
+      return NS_OK;
+    case NOTIFY_IME_OF_BLUR:
+      nsIMM32Handler::OnFocusChange(false, aWindow);
 #ifdef NS_ENABLE_TSF
-    case NOTIFY_IME_OF_BLUR:
       // If a plugin gets focus while TSF has focus, we need to notify TSF of
       // the blur.
       if (nsTextStore::ThinksHavingFocus()) {
         return nsTextStore::OnFocusChange(false, aWindow,
                                           aWindow->GetInputContext());
       }
-      return NS_ERROR_NOT_IMPLEMENTED;
 #endif //NS_ENABLE_TSF
+      return NS_OK;
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 // static
 nsIMEUpdatePreference
 IMEHandler::GetUpdatePreference()
--- a/widget/windows/nsIMM32Handler.cpp
+++ b/widget/windows/nsIMM32Handler.cpp
@@ -8,30 +8,37 @@
 
 #include "nsIMM32Handler.h"
 #include "nsWindow.h"
 #include "nsWindowDefs.h"
 #include "WinUtils.h"
 #include "KeyboardLayout.h"
 #include <algorithm>
 
+#include "mozilla/CheckedInt.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/TextEvents.h"
 
 #ifndef IME_PROP_ACCEPT_WIDE_VKEY
 #define IME_PROP_ACCEPT_WIDE_VKEY 0x20
 #endif
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
 static nsIMM32Handler* gIMM32Handler = nullptr;
 
 PRLogModuleInfo* gIMM32Log = nullptr;
 
+static const char*
+GetBoolName(bool aBool)
+{
+  return aBool ? "true" : "false";
+}
+
 static void
 HandleSeparator(nsACString& aDesc)
 {
   if (!aDesc.IsEmpty()) {
     aDesc.AppendLiteral(" | ");
   }
 }
 
@@ -132,16 +139,17 @@ static UINT sWM_MSIME_MOUSE = 0; // mous
 #define IMEMOUSE_WDOWN      0x20    // wheel down
 
 WritingMode nsIMM32Handler::sWritingModeOfCompositionFont;
 nsString nsIMM32Handler::sIMEName;
 UINT nsIMM32Handler::sCodePage = 0;
 DWORD nsIMM32Handler::sIMEProperty = 0;
 DWORD nsIMM32Handler::sIMEUIProperty = 0;
 bool nsIMM32Handler::sAssumeVerticalWritingModeNotSupported = false;
+bool nsIMM32Handler::sHasFocus = false;
 
 /* static */ void
 nsIMM32Handler::EnsureHandlerInstance()
 {
   if (!gIMM32Handler) {
     gIMM32Handler = new nsIMM32Handler();
   }
 }
@@ -402,16 +410,30 @@ nsIMM32Handler::CancelComposition(nsWind
 
   if (associated) {
     IMEContext.Disassociate();
   }
 }
 
 // static
 void
+nsIMM32Handler::OnFocusChange(bool aFocus, nsWindow* aWindow)
+{
+  MOZ_LOG(gIMM32Log, LogLevel::Info,
+    ("IMM32: OnFocusChange(aFocus=%s, aWindow=%p), sHasFocus=%s",
+     GetBoolName(aFocus), aWindow, GetBoolName(sHasFocus)));
+
+  if (gIMM32Handler) {
+    gIMM32Handler->mSelection.Clear();
+  }
+  sHasFocus = aFocus;
+}
+
+// static
+void
 nsIMM32Handler::OnUpdateComposition(nsWindow* aWindow)
 {
   if (!gIMM32Handler) {
     return;
   }
  
   if (aWindow->PluginHasFocus()) {
     return;
@@ -419,23 +441,29 @@ nsIMM32Handler::OnUpdateComposition(nsWi
 
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   gIMM32Handler->SetIMERelatedWindowsPos(aWindow, IMEContext);
 }
 
 // static
 void
 nsIMM32Handler::OnSelectionChange(nsWindow* aWindow,
-                                  const IMENotification& aIMENotification)
+                                  const IMENotification& aIMENotification,
+                                  bool aIsIMMActive)
 {
-  if (aIMENotification.mSelectionChangeData.mCausedByComposition) {
-    return;
+  if (!aIMENotification.mSelectionChangeData.mCausedByComposition &&
+      aIsIMMActive) {
+    MaybeAdjustCompositionFont(aWindow,
+      aIMENotification.mSelectionChangeData.GetWritingMode());
   }
-  MaybeAdjustCompositionFont(aWindow,
-    aIMENotification.mSelectionChangeData.GetWritingMode());
+  // MaybeAdjustCompositionFont() may create gIMM32Handler.  So, check it
+  // after a call of MaybeAdjustCompositionFont().
+  if (gIMM32Handler) {
+    gIMM32Handler->mSelection.Update(aIMENotification);
+  }
 }
 
 // static
 void
 nsIMM32Handler::MaybeAdjustCompositionFont(nsWindow* aWindow,
                                            const WritingMode& aWritingMode,
                                            bool aForceUpdate)
 {
@@ -1129,31 +1157,30 @@ void
 nsIMM32Handler::HandleStartComposition(nsWindow* aWindow,
                                        const nsIMEContext &aIMEContext)
 {
   NS_PRECONDITION(!mIsComposing,
     "HandleStartComposition is called but mIsComposing is TRUE");
   NS_PRECONDITION(!aWindow->PluginHasFocus(),
     "HandleStartComposition should not be called when a plug-in has focus");
 
-  WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
-  nsIntPoint point(0, 0);
-  aWindow->InitEvent(selection, &point);
-  aWindow->DispatchWindowEvent(&selection);
-  if (!selection.mSucceeded) {
-    MOZ_LOG(gIMM32Log, LogLevel::Info,
-      ("IMM32: HandleStartComposition, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
+  Selection& selection = GetSelection();
+  if (!selection.EnsureValidSelection(aWindow)) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: HandleStartComposition, FAILED, due to "
+       "Selection::EnsureValidSelection() failure"));
     return;
   }
 
-  AdjustCompositionFont(aIMEContext, selection.GetWritingMode());
+  AdjustCompositionFont(aIMEContext, selection.mWritingMode);
 
-  mCompositionStart = selection.mReply.mOffset;
+  mCompositionStart = selection.mOffset;
 
   WidgetCompositionEvent event(true, NS_COMPOSITION_START, aWindow);
+  nsIntPoint point(0, 0);
   aWindow->InitEvent(event, &point);
   aWindow->DispatchWindowEvent(&event);
 
   mIsComposing = true;
   mComposingWindow = aWindow;
 
   MOZ_LOG(gIMM32Log, LogLevel::Info,
     ("IMM32: HandleStartComposition, START composition, mCompositionStart=%ld\n",
@@ -1453,27 +1480,25 @@ DumpReconvertString(RECONVERTSTRING* aRe
 bool
 nsIMM32Handler::HandleReconvert(nsWindow* aWindow,
                                 LPARAM lParam,
                                 LRESULT *oResult)
 {
   *oResult = 0;
   RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);
 
-  WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
-  nsIntPoint point(0, 0);
-  aWindow->InitEvent(selection, &point);
-  aWindow->DispatchWindowEvent(&selection);
-  if (!selection.mSucceeded) {
-    MOZ_LOG(gIMM32Log, LogLevel::Info,
-      ("IMM32: HandleReconvert, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
+  Selection& selection = GetSelection();
+  if (!selection.EnsureValidSelection(aWindow)) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: HandleReconvert, FAILED, due to "
+       "Selection::EnsureValidSelection() failure"));
     return false;
   }
 
-  uint32_t len = selection.mReply.mString.Length();
+  uint32_t len = selection.Length();
   uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
 
   if (!pReconv) {
     // Return need size to reconvert.
     if (len == 0) {
       MOZ_LOG(gIMM32Log, LogLevel::Info,
         ("IMM32: HandleReconvert, There are not selected text\n"));
       return false;
@@ -1499,17 +1524,17 @@ nsIMM32Handler::HandleReconvert(nsWindow
   pReconv->dwStrLen          = len;
   pReconv->dwStrOffset       = sizeof(RECONVERTSTRING);
   pReconv->dwCompStrLen      = len;
   pReconv->dwCompStrOffset   = 0;
   pReconv->dwTargetStrLen    = len;
   pReconv->dwTargetStrOffset = 0;
 
   ::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
-               selection.mReply.mString.get(), len * sizeof(WCHAR));
+               selection.mString.get(), len * sizeof(WCHAR));
 
   MOZ_LOG(gIMM32Log, LogLevel::Info,
     ("IMM32: HandleReconvert, SUCCEEDED result=%ld\n",
      *oResult));
   DumpReconvertString(pReconv);
 
   return true;
 }
@@ -1609,26 +1634,25 @@ nsIMM32Handler::HandleDocumentFeed(nsWin
 
   nsIntPoint point(0, 0);
 
   bool hasCompositionString =
     mIsComposing && ShouldDrawCompositionStringOurselves();
 
   int32_t targetOffset, targetLength;
   if (!hasCompositionString) {
-    WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
-    aWindow->InitEvent(selection, &point);
-    aWindow->DispatchWindowEvent(&selection);
-    if (!selection.mSucceeded) {
-      MOZ_LOG(gIMM32Log, LogLevel::Info,
-        ("IMM32: HandleDocumentFeed, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
+    Selection& selection = GetSelection();
+    if (!selection.EnsureValidSelection(aWindow)) {
+      MOZ_LOG(gIMM32Log, LogLevel::Error,
+        ("IMM32: HandleDocumentFeed, FAILED, due to "
+         "Selection::EnsureValidSelection() failure"));
       return false;
     }
-    targetOffset = int32_t(selection.mReply.mOffset);
-    targetLength = int32_t(selection.mReply.mString.Length());
+    targetOffset = int32_t(selection.mOffset);
+    targetLength = int32_t(selection.Length());
   } else {
     targetOffset = int32_t(mCompositionStart);
     targetLength = int32_t(mCompositionString.Length());
   }
 
   // XXX nsString::Find and nsString::RFind take int32_t for offset, so,
   //     we cannot support this message when the current offset is larger than
   //     INT32_MAX.
@@ -1994,28 +2018,26 @@ nsIMM32Handler::ConvertToANSIString(cons
 bool
 nsIMM32Handler::GetCharacterRectOfSelectedTextAt(nsWindow* aWindow,
                                                  uint32_t aOffset,
                                                  nsIntRect& aCharRect,
                                                  WritingMode* aWritingMode)
 {
   nsIntPoint point(0, 0);
 
-  WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
-  aWindow->InitEvent(selection, &point);
-  aWindow->DispatchWindowEvent(&selection);
-  if (!selection.mSucceeded) {
-    MOZ_LOG(gIMM32Log, LogLevel::Info,
-      ("IMM32: GetCharacterRectOfSelectedTextAt, aOffset=%lu, FAILED (NS_QUERY_SELECTED_TEXT)\n",
-       aOffset));
+  Selection& selection = GetSelection();
+  if (!selection.EnsureValidSelection(aWindow)) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: GetCharacterRectOfSelectedTextAt, FAILED, due to "
+       "Selection::EnsureValidSelection() failure"));
     return false;
   }
 
-  uint32_t offset = selection.mReply.mOffset + aOffset;
-  bool useCaretRect = selection.mReply.mString.IsEmpty();
+  uint32_t offset = selection.mOffset + aOffset;
+  bool useCaretRect = selection.mString.IsEmpty();
   if (useCaretRect && ShouldDrawCompositionStringOurselves() &&
       mIsComposing && !mCompositionString.IsEmpty()) {
     // There is not a normal selection, but we have composition string.
     // XXX mnakano - Should we implement NS_QUERY_IME_SELECTED_TEXT?
     useCaretRect = false;
     if (mCursorPosition != NO_IME_CARET) {
       uint32_t cursorPosition =
         std::min<uint32_t>(mCursorPosition, mCompositionString.Length());
@@ -2053,29 +2075,26 @@ nsIMM32Handler::GetCharacterRectOfSelect
 
 bool
 nsIMM32Handler::GetCaretRect(nsWindow* aWindow,
                              nsIntRect& aCaretRect,
                              WritingMode* aWritingMode)
 {
   nsIntPoint point(0, 0);
 
-  WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
-  aWindow->InitEvent(selection, &point);
-  aWindow->DispatchWindowEvent(&selection);
-  if (!selection.mSucceeded) {
-    MOZ_LOG(gIMM32Log, LogLevel::Info,
-      ("IMM32: GetCaretRect,  FAILED (NS_QUERY_SELECTED_TEXT)\n"));
+  Selection& selection = GetSelection();
+  if (!selection.EnsureValidSelection(aWindow)) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: GetCaretRect, FAILED, due to "
+       "Selection::EnsureValidSelection() failure"));
     return false;
   }
 
-  uint32_t offset = selection.mReply.mOffset;
-
   WidgetQueryContentEvent caretRect(true, NS_QUERY_CARET_RECT, aWindow);
-  caretRect.InitForQueryCaretRect(offset);
+  caretRect.InitForQueryCaretRect(selection.mOffset);
   aWindow->InitEvent(caretRect, &point);
   aWindow->DispatchWindowEvent(&caretRect);
   if (!caretRect.mSucceeded) {
     MOZ_LOG(gIMM32Log, LogLevel::Info,
       ("IMM32: GetCaretRect,  FAILED (NS_QUERY_CARET_RECT)\n"));
     return false;
   }
   aCaretRect = LayoutDevicePixel::ToUntyped(caretRect.mReply.mRect);
@@ -2567,8 +2586,96 @@ nsIMM32Handler::OnKeyDownEvent(nsWindow*
         // NOTE: We don't need to cancel the composition on another window.
         CancelComposition(aWindow, false);
       }
       return false;
     default:
       return false;
   }
 }
+
+/******************************************************************************
+ * nsIMM32Handler::Selection
+ ******************************************************************************/
+
+bool
+nsIMM32Handler::Selection::IsValid() const
+{
+  if (!mIsValid || NS_WARN_IF(mOffset == UINT32_MAX)) {
+    return false;
+  }
+  CheckedInt<uint32_t> endOffset =
+    CheckedInt<uint32_t>(mOffset) + Length();
+  return endOffset.isValid();
+}
+
+bool
+nsIMM32Handler::Selection::Update(const IMENotification& aIMENotification)
+{
+  mOffset = aIMENotification.mSelectionChangeData.mOffset;
+  mString = aIMENotification.mSelectionChangeData.String();
+  mWritingMode = aIMENotification.mSelectionChangeData.GetWritingMode();
+  mIsValid = true;
+
+  MOZ_LOG(gIMM32Log, LogLevel::Info,
+    ("IMM32: Selection::Update, aIMENotification={ mSelectionChangeData={ "
+     "mOffset=%u, mLength=%u, GetWritingMode()=%s } }",
+     mOffset, mString.Length(), GetWritingModeName(mWritingMode).get()));
+
+  if (!IsValid()) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: Selection::Update, FAILED, due to invalid range"));
+    Clear();
+    return false;
+  }
+  return true;
+}
+
+bool
+nsIMM32Handler::Selection::Init(nsWindow* aWindow)
+{
+  Clear();
+
+  WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
+  nsIntPoint point(0, 0);
+  aWindow->InitEvent(selection, &point);
+  aWindow->DispatchWindowEvent(&selection);
+  if (NS_WARN_IF(!selection.mSucceeded)) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: Selection::Init, FAILED, due to NS_QUERY_SELECTED_TEXT "
+       "failure"));
+    return false;
+  }
+  // If the window is destroyed during querying selected text, we shouldn't
+  // do anymore.
+  if (aWindow->Destroyed()) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: Selection::Init, FAILED, due to the widget destroyed"));
+    return false;
+  }
+
+  mOffset = selection.mReply.mOffset;
+  mString = selection.mReply.mString;
+  mWritingMode = selection.GetWritingMode();
+  mIsValid = true;
+
+  MOZ_LOG(gIMM32Log, LogLevel::Info,
+    ("IMM32: Selection::Init, selection={ mReply={ mOffset=%u, "
+     "mString.Length()=%u, mWritingMode=%s } }",
+     mOffset, mString.Length(), GetWritingModeName(mWritingMode).get()));
+
+  if (!IsValid()) {
+    MOZ_LOG(gIMM32Log, LogLevel::Error,
+      ("IMM32: Selection::Init, FAILED, due to invalid range"));
+    Clear();
+    return false;
+  }
+  return true;
+}
+
+bool
+nsIMM32Handler::Selection::EnsureValidSelection(nsWindow* aWindow)
+{
+  if (IsValid()) {
+    return true;
+  }
+  return Init(aWindow);
+}
--- a/widget/windows/nsIMM32Handler.h
+++ b/widget/windows/nsIMM32Handler.h
@@ -138,19 +138,21 @@ public:
   static bool IsIMEAvailable() { return !!::ImmIsIME(::GetKeyboardLayout(0)); }
 #endif
 
   // If aForce is TRUE, these methods doesn't check whether we have composition
   // or not.  If you don't set it to TRUE, these method doesn't commit/cancel
   // the composition on uexpected window.
   static void CommitComposition(nsWindow* aWindow, bool aForce = false);
   static void CancelComposition(nsWindow* aWindow, bool aForce = false);
+  static void OnFocusChange(bool aFocus, nsWindow* aWindow);
   static void OnUpdateComposition(nsWindow* aWindow);
   static void OnSelectionChange(nsWindow* aWindow,
-                                const IMENotification& aIMENotification);
+                                const IMENotification& aIMENotification,
+                                bool aIsIMMActive);
 
   static nsIMEUpdatePreference GetIMEUpdatePreference();
 
   // Returns NS_SUCCESS_EVENT_CONSUMED if the mouse button event is consumed by
   // IME.  Otherwise, NS_OK.
   static nsresult OnMouseButtonEvent(nsWindow* aWindow,
                                      const IMENotification& aIMENotification);
 
@@ -382,21 +384,52 @@ protected:
   nsWindow* mComposingWindow;
   nsString  mCompositionString;
   InfallibleTArray<uint32_t> mClauseArray;
   InfallibleTArray<uint8_t> mAttributeArray;
 
   int32_t mCursorPosition;
   uint32_t mCompositionStart;
 
+  struct Selection
+  {
+    nsString mString;
+    uint32_t mOffset;
+    mozilla::WritingMode mWritingMode;
+    bool mIsValid;
+
+    Selection()
+      : mOffset(UINT32_MAX)
+      , mIsValid(false)
+    {
+    }
+
+    void Clear()
+    {
+      mOffset = UINT32_MAX;
+      mIsValid = false;
+    }
+    uint32_t Length() const { return mString.Length(); }
+
+    bool IsValid() const;
+    bool Update(const IMENotification& aIMENotification);
+    bool Init(nsWindow* aWindow);
+    bool EnsureValidSelection(nsWindow* aWindow);
+  };
+  Selection mSelection;
+  // mSelection stores the latest selection data only when sHasFocus it true.
+  // Therefore, if sHasFocus is false, temporary instance should be used.
+  Selection GetSelection() { return sHasFocus ? mSelection : Selection(); }
+
   bool mIsComposing;
   bool mIsComposingOnPlugin;
   bool mNativeCaretIsCreated;
 
   static mozilla::WritingMode sWritingModeOfCompositionFont;
   static nsString sIMEName;
   static UINT sCodePage;
   static DWORD sIMEProperty;
   static DWORD sIMEUIProperty;
   static bool sAssumeVerticalWritingModeNotSupported;
+  static bool sHasFocus;
 };
 
 #endif // nsIMM32Handler_h__