widget/windows/nsIMM32Handler.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sts=2 sw=2 et cin: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #ifdef MOZ_LOGGING
     8 #define FORCE_PR_LOG /* Allow logging in the release build */
     9 #endif // MOZ_LOGGING
    10 #include "prlog.h"
    12 #include "nsIMM32Handler.h"
    13 #include "nsWindow.h"
    14 #include "nsWindowDefs.h"
    15 #include "WinUtils.h"
    16 #include "KeyboardLayout.h"
    17 #include <algorithm>
    19 #include "mozilla/MiscEvents.h"
    20 #include "mozilla/TextEvents.h"
    22 using namespace mozilla;
    23 using namespace mozilla::widget;
    25 static nsIMM32Handler* gIMM32Handler = nullptr;
    27 #ifdef PR_LOGGING
    28 PRLogModuleInfo* gIMM32Log = nullptr;
    29 #endif
    31 static UINT sWM_MSIME_MOUSE = 0; // mouse message for MSIME 98/2000
    33 //-------------------------------------------------------------------------
    34 //
    35 // from http://download.microsoft.com/download/6/0/9/60908e9e-d2c1-47db-98f6-216af76a235f/msime.h
    36 // The document for this has been removed from MSDN...
    37 //
    38 //-------------------------------------------------------------------------
    40 #define RWM_MOUSE           TEXT("MSIMEMouseOperation")
    42 #define IMEMOUSE_NONE       0x00    // no mouse button was pushed
    43 #define IMEMOUSE_LDOWN      0x01
    44 #define IMEMOUSE_RDOWN      0x02
    45 #define IMEMOUSE_MDOWN      0x04
    46 #define IMEMOUSE_WUP        0x10    // wheel up
    47 #define IMEMOUSE_WDOWN      0x20    // wheel down
    49 UINT nsIMM32Handler::sCodePage = 0;
    50 DWORD nsIMM32Handler::sIMEProperty = 0;
    52 /* static */ void
    53 nsIMM32Handler::EnsureHandlerInstance()
    54 {
    55   if (!gIMM32Handler) {
    56     gIMM32Handler = new nsIMM32Handler();
    57   }
    58 }
    60 /* static */ void
    61 nsIMM32Handler::Initialize()
    62 {
    63 #ifdef PR_LOGGING
    64   if (!gIMM32Log)
    65     gIMM32Log = PR_NewLogModule("nsIMM32HandlerWidgets");
    66 #endif
    68   if (!sWM_MSIME_MOUSE) {
    69     sWM_MSIME_MOUSE = ::RegisterWindowMessage(RWM_MOUSE);
    70   }
    71   InitKeyboardLayout(::GetKeyboardLayout(0));
    72 }
    74 /* static */ void
    75 nsIMM32Handler::Terminate()
    76 {
    77   if (!gIMM32Handler)
    78     return;
    79   delete gIMM32Handler;
    80   gIMM32Handler = nullptr;
    81 }
    83 /* static */ bool
    84 nsIMM32Handler::IsComposingOnOurEditor()
    85 {
    86   return gIMM32Handler && gIMM32Handler->mIsComposing;
    87 }
    89 /* static */ bool
    90 nsIMM32Handler::IsComposingOnPlugin()
    91 {
    92   return gIMM32Handler && gIMM32Handler->mIsComposingOnPlugin;
    93 }
    95 /* static */ bool
    96 nsIMM32Handler::IsComposingWindow(nsWindow* aWindow)
    97 {
    98   return gIMM32Handler && gIMM32Handler->mComposingWindow == aWindow;
    99 }
   101 /* static */ bool
   102 nsIMM32Handler::IsTopLevelWindowOfComposition(nsWindow* aWindow)
   103 {
   104   if (!gIMM32Handler || !gIMM32Handler->mComposingWindow) {
   105     return false;
   106   }
   107   HWND wnd = gIMM32Handler->mComposingWindow->GetWindowHandle();
   108   return WinUtils::GetTopLevelHWND(wnd, true) == aWindow->GetWindowHandle();
   109 }
   111 /* static */ bool
   112 nsIMM32Handler::ShouldDrawCompositionStringOurselves()
   113 {
   114   // If current IME has special UI or its composition window should not
   115   // positioned to caret position, we should now draw composition string
   116   // ourselves.
   117   return !(sIMEProperty & IME_PROP_SPECIAL_UI) &&
   118           (sIMEProperty & IME_PROP_AT_CARET);
   119 }
   121 /* static */ void
   122 nsIMM32Handler::InitKeyboardLayout(HKL aKeyboardLayout)
   123 {
   124   WORD langID = LOWORD(aKeyboardLayout);
   125   ::GetLocaleInfoW(MAKELCID(langID, SORT_DEFAULT),
   126                    LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
   127                    (PWSTR)&sCodePage, sizeof(sCodePage) / sizeof(WCHAR));
   128   sIMEProperty = ::ImmGetProperty(aKeyboardLayout, IGP_PROPERTY);
   129   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   130     ("IMM32: InitKeyboardLayout, aKeyboardLayout=%08x, sCodePage=%lu, "
   131      "sIMEProperty=%08x",
   132      aKeyboardLayout, sCodePage, sIMEProperty));
   133 }
   135 /* static */ UINT
   136 nsIMM32Handler::GetKeyboardCodePage()
   137 {
   138   return sCodePage;
   139 }
   141 /* static */
   142 nsIMEUpdatePreference
   143 nsIMM32Handler::GetIMEUpdatePreference()
   144 {
   145   return nsIMEUpdatePreference(nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE);
   146 }
   148 // used for checking the lParam of WM_IME_COMPOSITION
   149 #define IS_COMPOSING_LPARAM(lParam) \
   150   ((lParam) & (GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS))
   151 #define IS_COMMITTING_LPARAM(lParam) ((lParam) & GCS_RESULTSTR)
   152 // Some IMEs (e.g., the standard IME for Korean) don't have caret position,
   153 // then, we should not set caret position to text event.
   154 #define NO_IME_CARET -1
   156 nsIMM32Handler::nsIMM32Handler() :
   157   mComposingWindow(nullptr), mCursorPosition(NO_IME_CARET), mCompositionStart(0),
   158   mIsComposing(false), mIsComposingOnPlugin(false),
   159   mNativeCaretIsCreated(false)
   160 {
   161   PR_LOG(gIMM32Log, PR_LOG_ALWAYS, ("IMM32: nsIMM32Handler is created\n"));
   162 }
   164 nsIMM32Handler::~nsIMM32Handler()
   165 {
   166   if (mIsComposing) {
   167     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   168       ("IMM32: ~nsIMM32Handler, ERROR, the instance is still composing\n"));
   169   }
   170   PR_LOG(gIMM32Log, PR_LOG_ALWAYS, ("IMM32: nsIMM32Handler is destroyed\n"));
   171 }
   173 nsresult
   174 nsIMM32Handler::EnsureClauseArray(int32_t aCount)
   175 {
   176   NS_ENSURE_ARG_MIN(aCount, 0);
   177   mClauseArray.SetCapacity(aCount + 32);
   178   return NS_OK;
   179 }
   181 nsresult
   182 nsIMM32Handler::EnsureAttributeArray(int32_t aCount)
   183 {
   184   NS_ENSURE_ARG_MIN(aCount, 0);
   185   mAttributeArray.SetCapacity(aCount + 64);
   186   return NS_OK;
   187 }
   189 /* static */ void
   190 nsIMM32Handler::CommitComposition(nsWindow* aWindow, bool aForce)
   191 {
   192   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   193     ("IMM32: CommitComposition, aForce=%s, aWindow=%p, hWnd=%08x, mComposingWindow=%p%s\n",
   194      aForce ? "TRUE" : "FALSE",
   195      aWindow, aWindow->GetWindowHandle(),
   196      gIMM32Handler ? gIMM32Handler->mComposingWindow : nullptr,
   197      gIMM32Handler && gIMM32Handler->mComposingWindow ?
   198        IsComposingOnOurEditor() ? " (composing on editor)" :
   199                                   " (composing on plug-in)" : ""));
   200   if (!aForce && !IsComposingWindow(aWindow)) {
   201     return;
   202   }
   204   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   205   bool associated = IMEContext.AssociateDefaultContext();
   206   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   207     ("IMM32: CommitComposition, associated=%s\n",
   208      associated ? "YES" : "NO"));
   210   if (IMEContext.IsValid()) {
   211     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
   212     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   213   }
   215   if (associated) {
   216     IMEContext.Disassociate();
   217   }
   218 }
   220 /* static */ void
   221 nsIMM32Handler::CancelComposition(nsWindow* aWindow, bool aForce)
   222 {
   223   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   224     ("IMM32: CancelComposition, aForce=%s, aWindow=%p, hWnd=%08x, mComposingWindow=%p%s\n",
   225      aForce ? "TRUE" : "FALSE",
   226      aWindow, aWindow->GetWindowHandle(),
   227      gIMM32Handler ? gIMM32Handler->mComposingWindow : nullptr,
   228      gIMM32Handler && gIMM32Handler->mComposingWindow ?
   229        IsComposingOnOurEditor() ? " (composing on editor)" :
   230                                   " (composing on plug-in)" : ""));
   231   if (!aForce && !IsComposingWindow(aWindow)) {
   232     return;
   233   }
   235   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   236   bool associated = IMEContext.AssociateDefaultContext();
   237   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   238     ("IMM32: CancelComposition, associated=%s\n",
   239      associated ? "YES" : "NO"));
   241   if (IMEContext.IsValid()) {
   242     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   243   }
   245   if (associated) {
   246     IMEContext.Disassociate();
   247   }
   248 }
   250 // static
   251 void
   252 nsIMM32Handler::OnUpdateComposition(nsWindow* aWindow)
   253 {
   254   if (!gIMM32Handler) {
   255     return;
   256   }
   258   if (aWindow->PluginHasFocus()) {
   259     return;
   260   }
   262   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   263   gIMM32Handler->SetIMERelatedWindowsPos(aWindow, IMEContext);
   264 }
   267 /* static */ bool
   268 nsIMM32Handler::ProcessInputLangChangeMessage(nsWindow* aWindow,
   269                                               WPARAM wParam,
   270                                               LPARAM lParam,
   271                                               MSGResult& aResult)
   272 {
   273   aResult.mResult = 0;
   274   aResult.mConsumed = false;
   275   // We don't need to create the instance of the handler here.
   276   if (gIMM32Handler) {
   277     gIMM32Handler->OnInputLangChange(aWindow, wParam, lParam, aResult);
   278   }
   279   InitKeyboardLayout(reinterpret_cast<HKL>(lParam));
   280   // We can release the instance here, because the instance may be never
   281   // used. E.g., the new keyboard layout may not use IME, or it may use TSF.
   282   Terminate();
   283   // Don't return as "processed", the messages should be processed on nsWindow
   284   // too.
   285   return false;
   286 }
   288 /* static */ bool
   289 nsIMM32Handler::ProcessMessage(nsWindow* aWindow, UINT msg,
   290                                WPARAM &wParam, LPARAM &lParam,
   291                                MSGResult& aResult)
   292 {
   293   // XXX We store the composing window in mComposingWindow.  If IME messages are
   294   // sent to different window, we should commit the old transaction.  And also
   295   // if the new window handle is not focused, probably, we should not start
   296   // the composition, however, such case should not be, it's just bad scenario.
   298   // When a plug-in has focus or compsition, we should dispatch the IME events
   299   // to the plug-in.
   300   if (aWindow->PluginHasFocus() || IsComposingOnPlugin()) {
   301       return ProcessMessageForPlugin(aWindow, msg, wParam, lParam, aResult);
   302   }
   304   aResult.mResult = 0;
   305   switch (msg) {
   306     case WM_LBUTTONDOWN:
   307     case WM_MBUTTONDOWN:
   308     case WM_RBUTTONDOWN: {
   309       // We don't need to create the instance of the handler here.
   310       if (!gIMM32Handler) {
   311         return false;
   312       }
   313       return gIMM32Handler->OnMouseEvent(aWindow, lParam,
   314                               msg == WM_LBUTTONDOWN ? IMEMOUSE_LDOWN :
   315                               msg == WM_MBUTTONDOWN ? IMEMOUSE_MDOWN :
   316                                                       IMEMOUSE_RDOWN, aResult);
   317     }
   318     case WM_INPUTLANGCHANGE:
   319       return ProcessInputLangChangeMessage(aWindow, wParam, lParam, aResult);
   320     case WM_IME_STARTCOMPOSITION:
   321       EnsureHandlerInstance();
   322       return gIMM32Handler->OnIMEStartComposition(aWindow, aResult);
   323     case WM_IME_COMPOSITION:
   324       EnsureHandlerInstance();
   325       return gIMM32Handler->OnIMEComposition(aWindow, wParam, lParam, aResult);
   326     case WM_IME_ENDCOMPOSITION:
   327       EnsureHandlerInstance();
   328       return gIMM32Handler->OnIMEEndComposition(aWindow, aResult);
   329     case WM_IME_CHAR:
   330       return OnIMEChar(aWindow, wParam, lParam, aResult);
   331     case WM_IME_NOTIFY:
   332       return OnIMENotify(aWindow, wParam, lParam, aResult);
   333     case WM_IME_REQUEST:
   334       EnsureHandlerInstance();
   335       return gIMM32Handler->OnIMERequest(aWindow, wParam, lParam, aResult);
   336     case WM_IME_SELECT:
   337       return OnIMESelect(aWindow, wParam, lParam, aResult);
   338     case WM_IME_SETCONTEXT:
   339       return OnIMESetContext(aWindow, wParam, lParam, aResult);
   340     case WM_KEYDOWN:
   341       return OnKeyDownEvent(aWindow, wParam, lParam, aResult);
   342     case WM_CHAR:
   343       if (!gIMM32Handler) {
   344         return false;
   345       }
   346       return gIMM32Handler->OnChar(aWindow, wParam, lParam, aResult);
   347     default:
   348       return false;
   349   };
   350 }
   352 /* static */ bool
   353 nsIMM32Handler::ProcessMessageForPlugin(nsWindow* aWindow, UINT msg,
   354                                         WPARAM &wParam, LPARAM &lParam,
   355                                         MSGResult& aResult)
   356 {
   357   aResult.mResult = 0;
   358   aResult.mConsumed = false;
   359   switch (msg) {
   360     case WM_INPUTLANGCHANGEREQUEST:
   361     case WM_INPUTLANGCHANGE:
   362       aWindow->DispatchPluginEvent(msg, wParam, lParam, false);
   363       return ProcessInputLangChangeMessage(aWindow, wParam, lParam, aResult);
   364     case WM_IME_COMPOSITION:
   365       EnsureHandlerInstance();
   366       return gIMM32Handler->OnIMECompositionOnPlugin(aWindow, wParam, lParam,
   367                                                      aResult);
   368     case WM_IME_STARTCOMPOSITION:
   369       EnsureHandlerInstance();
   370       return gIMM32Handler->OnIMEStartCompositionOnPlugin(aWindow, wParam,
   371                                                           lParam, aResult);
   372     case WM_IME_ENDCOMPOSITION:
   373       EnsureHandlerInstance();
   374       return gIMM32Handler->OnIMEEndCompositionOnPlugin(aWindow, wParam, lParam,
   375                                                         aResult);
   376     case WM_IME_CHAR:
   377       EnsureHandlerInstance();
   378       return gIMM32Handler->OnIMECharOnPlugin(aWindow, wParam, lParam, aResult);
   379     case WM_IME_SETCONTEXT:
   380       return OnIMESetContextOnPlugin(aWindow, wParam, lParam, aResult);
   381     case WM_CHAR:
   382       if (!gIMM32Handler) {
   383         return false;
   384       }
   385       return gIMM32Handler->OnCharOnPlugin(aWindow, wParam, lParam, aResult);
   386     case WM_IME_COMPOSITIONFULL:
   387     case WM_IME_CONTROL:
   388     case WM_IME_KEYDOWN:
   389     case WM_IME_KEYUP:
   390     case WM_IME_REQUEST:
   391     case WM_IME_SELECT:
   392       aResult.mConsumed =
   393         aWindow->DispatchPluginEvent(msg, wParam, lParam, false);
   394       return true;
   395   }
   396   return false;
   397 }
   399 /****************************************************************************
   400  * message handlers
   401  ****************************************************************************/
   403 void
   404 nsIMM32Handler::OnInputLangChange(nsWindow* aWindow,
   405                                   WPARAM wParam,
   406                                   LPARAM lParam,
   407                                   MSGResult& aResult)
   408 {
   409   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   410     ("IMM32: OnInputLangChange, hWnd=%08x, wParam=%08x, lParam=%08x\n",
   411      aWindow->GetWindowHandle(), wParam, lParam));
   413   aWindow->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
   414   NS_ASSERTION(!mIsComposing, "ResetInputState failed");
   416   if (mIsComposing) {
   417     HandleEndComposition(aWindow);
   418   }
   420   aResult.mConsumed = false;
   421 }
   423 bool
   424 nsIMM32Handler::OnIMEStartComposition(nsWindow* aWindow,
   425                                       MSGResult& aResult)
   426 {
   427   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   428     ("IMM32: OnIMEStartComposition, hWnd=%08x, mIsComposing=%s\n",
   429      aWindow->GetWindowHandle(), mIsComposing ? "TRUE" : "FALSE"));
   430   aResult.mConsumed = ShouldDrawCompositionStringOurselves();
   431   if (mIsComposing) {
   432     NS_WARNING("Composition has been already started");
   433     return true;
   434   }
   436   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   437   HandleStartComposition(aWindow, IMEContext);
   438   return true;
   439 }
   441 bool
   442 nsIMM32Handler::OnIMEComposition(nsWindow* aWindow,
   443                                  WPARAM wParam,
   444                                  LPARAM lParam,
   445                                  MSGResult& aResult)
   446 {
   447   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   448     ("IMM32: OnIMEComposition, hWnd=%08x, lParam=%08x, mIsComposing=%s\n",
   449      aWindow->GetWindowHandle(), lParam, mIsComposing ? "TRUE" : "FALSE"));
   450   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   451     ("IMM32: OnIMEComposition, GCS_RESULTSTR=%s, GCS_COMPSTR=%s, GCS_COMPATTR=%s, GCS_COMPCLAUSE=%s, GCS_CURSORPOS=%s\n",
   452      lParam & GCS_RESULTSTR  ? "YES" : "no",
   453      lParam & GCS_COMPSTR    ? "YES" : "no",
   454      lParam & GCS_COMPATTR   ? "YES" : "no",
   455      lParam & GCS_COMPCLAUSE ? "YES" : "no",
   456      lParam & GCS_CURSORPOS  ? "YES" : "no"));
   458   NS_PRECONDITION(!aWindow->PluginHasFocus(),
   459     "OnIMEComposition should not be called when a plug-in has focus");
   461   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   462   aResult.mConsumed = HandleComposition(aWindow, IMEContext, lParam);
   463   return true;
   464 }
   466 bool
   467 nsIMM32Handler::OnIMEEndComposition(nsWindow* aWindow,
   468                                     MSGResult& aResult)
   469 {
   470   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   471     ("IMM32: OnIMEEndComposition, hWnd=%08x, mIsComposing=%s\n",
   472      aWindow->GetWindowHandle(), mIsComposing ? "TRUE" : "FALSE"));
   474   aResult.mConsumed = ShouldDrawCompositionStringOurselves();
   475   if (!mIsComposing) {
   476     return true;
   477   }
   479   // Korean IME posts WM_IME_ENDCOMPOSITION first when we hit space during
   480   // composition. Then, we should ignore the message and commit the composition
   481   // string at following WM_IME_COMPOSITION.
   482   MSG compositionMsg;
   483   if (WinUtils::PeekMessage(&compositionMsg, aWindow->GetWindowHandle(),
   484                             WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION,
   485                             PM_NOREMOVE) &&
   486       compositionMsg.message == WM_IME_COMPOSITION &&
   487       IS_COMMITTING_LPARAM(compositionMsg.lParam)) {
   488     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   489       ("IMM32: OnIMEEndComposition, WM_IME_ENDCOMPOSITION is followed by "
   490        "WM_IME_COMPOSITION, ignoring the message..."));
   491     return true;
   492   }
   494   // Otherwise, e.g., ChangJie doesn't post WM_IME_COMPOSITION before
   495   // WM_IME_ENDCOMPOSITION when composition string becomes empty.
   496   // Then, we should dispatch a compositionupdate event, a text event and
   497   // a compositionend event.
   498   // XXX Shouldn't we dispatch the text event with actual or latest composition
   499   //     string?
   500   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   501     ("IMM32: OnIMEEndComposition, mCompositionString=\"%s\"%s",
   502      NS_ConvertUTF16toUTF8(mCompositionString).get(),
   503      mCompositionString.IsEmpty() ? "" : ", but canceling it..."));
   505   mCompositionString.Truncate();
   507   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   508   DispatchTextEvent(aWindow, IMEContext, false);
   510   HandleEndComposition(aWindow);
   512   return true;
   513 }
   515 /* static */ bool
   516 nsIMM32Handler::OnIMEChar(nsWindow* aWindow,
   517                           WPARAM wParam,
   518                           LPARAM lParam,
   519                           MSGResult& aResult)
   520 {
   521   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   522     ("IMM32: OnIMEChar, hWnd=%08x, char=%08x\n",
   523      aWindow->GetWindowHandle(), wParam));
   525   // We don't need to fire any text events from here. This method will be
   526   // called when the composition string of the current IME is not drawn by us
   527   // and some characters are committed. In that case, the committed string was
   528   // processed in nsWindow::OnIMEComposition already.
   530   // We need to consume the message so that Windows don't send two WM_CHAR msgs
   531   aResult.mConsumed = true;
   532   return true;
   533 }
   535 /* static */ bool
   536 nsIMM32Handler::OnIMECompositionFull(nsWindow* aWindow,
   537                                      MSGResult& aResult)
   538 {
   539   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   540     ("IMM32: OnIMECompositionFull, hWnd=%08x\n",
   541      aWindow->GetWindowHandle()));
   543   // not implement yet
   544   aResult.mConsumed = false;
   545   return true;
   546 }
   548 /* static */ bool
   549 nsIMM32Handler::OnIMENotify(nsWindow* aWindow,
   550                             WPARAM wParam,
   551                             LPARAM lParam,
   552                             MSGResult& aResult)
   553 {
   554 #ifdef PR_LOGGING
   555   switch (wParam) {
   556     case IMN_CHANGECANDIDATE:
   557       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   558         ("IMM32: OnIMENotify, hWnd=%08x, IMN_CHANGECANDIDATE, lParam=%08x\n",
   559          aWindow->GetWindowHandle(), lParam));
   560       break;
   561     case IMN_CLOSECANDIDATE:
   562       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   563         ("IMM32: OnIMENotify, hWnd=%08x, IMN_CLOSECANDIDATE, lParam=%08x\n",
   564          aWindow->GetWindowHandle(), lParam));
   565       break;
   566     case IMN_CLOSESTATUSWINDOW:
   567       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   568         ("IMM32: OnIMENotify, hWnd=%08x, IMN_CLOSESTATUSWINDOW\n",
   569          aWindow->GetWindowHandle()));
   570       break;
   571     case IMN_GUIDELINE:
   572       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   573         ("IMM32: OnIMENotify, hWnd=%08x, IMN_GUIDELINE\n",
   574          aWindow->GetWindowHandle()));
   575       break;
   576     case IMN_OPENCANDIDATE:
   577       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   578         ("IMM32: OnIMENotify, hWnd=%08x, IMN_OPENCANDIDATE, lParam=%08x\n",
   579          aWindow->GetWindowHandle(), lParam));
   580       break;
   581     case IMN_OPENSTATUSWINDOW:
   582       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   583         ("IMM32: OnIMENotify, hWnd=%08x, IMN_OPENSTATUSWINDOW\n",
   584          aWindow->GetWindowHandle()));
   585       break;
   586     case IMN_SETCANDIDATEPOS:
   587       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   588         ("IMM32: OnIMENotify, hWnd=%08x, IMN_SETCANDIDATEPOS, lParam=%08x\n",
   589          aWindow->GetWindowHandle(), lParam));
   590       break;
   591     case IMN_SETCOMPOSITIONFONT:
   592       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   593         ("IMM32: OnIMENotify, hWnd=%08x, IMN_SETCOMPOSITIONFONT\n",
   594          aWindow->GetWindowHandle()));
   595       break;
   596     case IMN_SETCOMPOSITIONWINDOW:
   597       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   598         ("IMM32: OnIMENotify, hWnd=%08x, IMN_SETCOMPOSITIONWINDOW\n",
   599          aWindow->GetWindowHandle()));
   600       break;
   601     case IMN_SETCONVERSIONMODE:
   602       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   603         ("IMM32: OnIMENotify, hWnd=%08x, IMN_SETCONVERSIONMODE\n",
   604          aWindow->GetWindowHandle()));
   605       break;
   606     case IMN_SETOPENSTATUS:
   607       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   608         ("IMM32: OnIMENotify, hWnd=%08x, IMN_SETOPENSTATUS\n",
   609          aWindow->GetWindowHandle()));
   610       break;
   611     case IMN_SETSENTENCEMODE:
   612       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   613         ("IMM32: OnIMENotify, hWnd=%08x, IMN_SETSENTENCEMODE\n",
   614          aWindow->GetWindowHandle()));
   615       break;
   616     case IMN_SETSTATUSWINDOWPOS:
   617       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   618         ("IMM32: OnIMENotify, hWnd=%08x, IMN_SETSTATUSWINDOWPOS\n",
   619          aWindow->GetWindowHandle()));
   620       break;
   621     case IMN_PRIVATE:
   622       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   623         ("IMM32: OnIMENotify, hWnd=%08x, IMN_PRIVATE\n",
   624          aWindow->GetWindowHandle()));
   625       break;
   626   }
   627 #endif // PR_LOGGING
   629   // not implement yet
   630   aResult.mConsumed = false;
   631   return true;
   632 }
   634 bool
   635 nsIMM32Handler::OnIMERequest(nsWindow* aWindow,
   636                              WPARAM wParam,
   637                              LPARAM lParam,
   638                              MSGResult& aResult)
   639 {
   640   switch (wParam) {
   641     case IMR_RECONVERTSTRING:
   642       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   643         ("IMM32: OnIMERequest, hWnd=%08x, IMR_RECONVERTSTRING\n",
   644          aWindow->GetWindowHandle()));
   645       aResult.mConsumed = HandleReconvert(aWindow, lParam, &aResult.mResult);
   646       return true;
   647     case IMR_QUERYCHARPOSITION:
   648       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   649         ("IMM32: OnIMERequest, hWnd=%08x, IMR_QUERYCHARPOSITION\n",
   650          aWindow->GetWindowHandle()));
   651       aResult.mConsumed =
   652         HandleQueryCharPosition(aWindow, lParam, &aResult.mResult);
   653       return true;
   654     case IMR_DOCUMENTFEED:
   655       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   656         ("IMM32: OnIMERequest, hWnd=%08x, IMR_DOCUMENTFEED\n",
   657          aWindow->GetWindowHandle()));
   658       aResult.mConsumed = HandleDocumentFeed(aWindow, lParam, &aResult.mResult);
   659       return true;
   660     default:
   661       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   662         ("IMM32: OnIMERequest, hWnd=%08x, wParam=%08x\n",
   663          aWindow->GetWindowHandle(), wParam));
   664       aResult.mConsumed = false;
   665       return true;
   666   }
   667 }
   669 /* static */ bool
   670 nsIMM32Handler::OnIMESelect(nsWindow* aWindow,
   671                             WPARAM wParam,
   672                             LPARAM lParam,
   673                             MSGResult& aResult)
   674 {
   675   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   676     ("IMM32: OnIMESelect, hWnd=%08x, wParam=%08x, lParam=%08x\n",
   677      aWindow->GetWindowHandle(), wParam, lParam));
   679   // not implement yet
   680   aResult.mConsumed = false;
   681   return true;
   682 }
   684 /* static */ bool
   685 nsIMM32Handler::OnIMESetContext(nsWindow* aWindow,
   686                                 WPARAM wParam,
   687                                 LPARAM lParam,
   688                                 MSGResult& aResult)
   689 {
   690   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   691     ("IMM32: OnIMESetContext, hWnd=%08x, %s, lParam=%08x\n",
   692      aWindow->GetWindowHandle(), wParam ? "Active" : "Deactive", lParam));
   694   aResult.mConsumed = false;
   696   // NOTE: If the aWindow is top level window of the composing window because
   697   // when a window on deactive window gets focus, WM_IME_SETCONTEXT (wParam is
   698   // TRUE) is sent to the top level window first.  After that,
   699   // WM_IME_SETCONTEXT (wParam is FALSE) is sent to the top level window.
   700   // Finally, WM_IME_SETCONTEXT (wParam is TRUE) is sent to the focused window.
   701   // The top level window never becomes composing window, so, we can ignore
   702   // the WM_IME_SETCONTEXT on the top level window.
   703   if (IsTopLevelWindowOfComposition(aWindow)) {
   704     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   705       ("IMM32: OnIMESetContext, hWnd=%08x is top level window\n"));
   706     return true;
   707   }
   709   // When IME context is activating on another window,
   710   // we should commit the old composition on the old window.
   711   bool cancelComposition = false;
   712   if (wParam && gIMM32Handler) {
   713     cancelComposition =
   714       gIMM32Handler->CommitCompositionOnPreviousWindow(aWindow);
   715   }
   717   if (wParam && (lParam & ISC_SHOWUICOMPOSITIONWINDOW) &&
   718       ShouldDrawCompositionStringOurselves()) {
   719     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   720       ("IMM32: OnIMESetContext, ISC_SHOWUICOMPOSITIONWINDOW is removed\n"));
   721     lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
   722   }
   724   // We should sent WM_IME_SETCONTEXT to the DefWndProc here because the
   725   // ancestor windows shouldn't receive this message.  If they receive the
   726   // message, we cannot know whether which window is the target of the message.
   727   aResult.mResult = ::DefWindowProc(aWindow->GetWindowHandle(),
   728                                     WM_IME_SETCONTEXT, wParam, lParam);
   730   // Cancel composition on the new window if we committed our composition on
   731   // another window.
   732   if (cancelComposition) {
   733     CancelComposition(aWindow, true);
   734   }
   736   aResult.mConsumed = true;
   737   return true;
   738 }
   740 bool
   741 nsIMM32Handler::OnChar(nsWindow* aWindow,
   742                        WPARAM wParam,
   743                        LPARAM lParam,
   744                        MSGResult& aResult)
   745 {
   746   // The return value must be same as aResult.mConsumed because only when we
   747   // consume the message, the caller shouldn't do anything anymore but
   748   // otherwise, the caller should handle the message.
   749   aResult.mConsumed = false;
   750   if (IsIMECharRecordsEmpty()) {
   751     return aResult.mConsumed;
   752   }
   753   WPARAM recWParam;
   754   LPARAM recLParam;
   755   DequeueIMECharRecords(recWParam, recLParam);
   756   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   757     ("IMM32: OnChar, aWindow=%p, wParam=%08x, lParam=%08x,\n",
   758      aWindow->GetWindowHandle(), wParam, lParam));
   759   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   760     ("               recorded: wParam=%08x, lParam=%08x\n",
   761      recWParam, recLParam));
   762   // If an unexpected char message comes, we should reset the records,
   763   // of course, this shouldn't happen.
   764   if (recWParam != wParam || recLParam != lParam) {
   765     ResetIMECharRecords();
   766     return aResult.mConsumed;
   767   }
   768   // Eat the char message which is caused by WM_IME_CHAR because we should
   769   // have processed the IME messages, so, this message could be come from
   770   // a windowless plug-in.
   771   aResult.mConsumed = true;
   772   return aResult.mConsumed;
   773 }
   775 /****************************************************************************
   776  * message handlers for plug-in
   777  ****************************************************************************/
   779 bool
   780 nsIMM32Handler::OnIMEStartCompositionOnPlugin(nsWindow* aWindow,
   781                                               WPARAM wParam,
   782                                               LPARAM lParam,
   783                                               MSGResult& aResult)
   784 {
   785   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   786     ("IMM32: OnIMEStartCompositionOnPlugin, hWnd=%08x, mIsComposingOnPlugin=%s\n",
   787      aWindow->GetWindowHandle(), mIsComposingOnPlugin ? "TRUE" : "FALSE"));
   788   mIsComposingOnPlugin = true;
   789   mComposingWindow = aWindow;
   790   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   791   SetIMERelatedWindowsPosOnPlugin(aWindow, IMEContext);
   792   aResult.mConsumed =
   793     aWindow->DispatchPluginEvent(WM_IME_STARTCOMPOSITION, wParam, lParam,
   794                                  false);
   795   return true;
   796 }
   798 bool
   799 nsIMM32Handler::OnIMECompositionOnPlugin(nsWindow* aWindow,
   800                                          WPARAM wParam,
   801                                          LPARAM lParam,
   802                                          MSGResult& aResult)
   803 {
   804   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   805     ("IMM32: OnIMECompositionOnPlugin, hWnd=%08x, lParam=%08x, mIsComposingOnPlugin=%s\n",
   806      aWindow->GetWindowHandle(), lParam,
   807      mIsComposingOnPlugin ? "TRUE" : "FALSE"));
   808   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   809     ("IMM32: OnIMECompositionOnPlugin, GCS_RESULTSTR=%s, GCS_COMPSTR=%s, GCS_COMPATTR=%s, GCS_COMPCLAUSE=%s, GCS_CURSORPOS=%s\n",
   810      lParam & GCS_RESULTSTR  ? "YES" : "no",
   811      lParam & GCS_COMPSTR    ? "YES" : "no",
   812      lParam & GCS_COMPATTR   ? "YES" : "no",
   813      lParam & GCS_COMPCLAUSE ? "YES" : "no",
   814      lParam & GCS_CURSORPOS  ? "YES" : "no"));
   815   // We should end composition if there is a committed string.
   816   if (IS_COMMITTING_LPARAM(lParam)) {
   817     mIsComposingOnPlugin = false;
   818     mComposingWindow = nullptr;
   819   }
   820   // Continue composition if there is still a string being composed.
   821   if (IS_COMPOSING_LPARAM(lParam)) {
   822     mIsComposingOnPlugin = true;
   823     mComposingWindow = aWindow;
   824     nsIMEContext IMEContext(aWindow->GetWindowHandle());
   825     SetIMERelatedWindowsPosOnPlugin(aWindow, IMEContext);
   826   }
   827   aResult.mConsumed =
   828     aWindow->DispatchPluginEvent(WM_IME_COMPOSITION, wParam, lParam, true);
   829   return true;
   830 }
   832 bool
   833 nsIMM32Handler::OnIMEEndCompositionOnPlugin(nsWindow* aWindow,
   834                                             WPARAM wParam,
   835                                             LPARAM lParam,
   836                                             MSGResult& aResult)
   837 {
   838   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   839     ("IMM32: OnIMEEndCompositionOnPlugin, hWnd=%08x, mIsComposingOnPlugin=%s\n",
   840      aWindow->GetWindowHandle(), mIsComposingOnPlugin ? "TRUE" : "FALSE"));
   842   mIsComposingOnPlugin = false;
   843   mComposingWindow = nullptr;
   845   if (mNativeCaretIsCreated) {
   846     ::DestroyCaret();
   847     mNativeCaretIsCreated = false;
   848   }
   850   aResult.mConsumed =
   851     aWindow->DispatchPluginEvent(WM_IME_ENDCOMPOSITION, wParam, lParam,
   852                                  false);
   853   return true;
   854 }
   856 bool
   857 nsIMM32Handler::OnIMECharOnPlugin(nsWindow* aWindow,
   858                                   WPARAM wParam,
   859                                   LPARAM lParam,
   860                                   MSGResult& aResult)
   861 {
   862   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   863     ("IMM32: OnIMECharOnPlugin, hWnd=%08x, char=%08x, scancode=%08x\n",
   864      aWindow->GetWindowHandle(), wParam, lParam));
   866   aResult.mConsumed =
   867     aWindow->DispatchPluginEvent(WM_IME_CHAR, wParam, lParam, true);
   869   if (!aResult.mConsumed) {
   870     // Record the WM_CHAR messages which are going to be coming.
   871     EnsureHandlerInstance();
   872     EnqueueIMECharRecords(wParam, lParam);
   873   }
   874   return true;
   875 }
   877 /* static */ bool
   878 nsIMM32Handler::OnIMESetContextOnPlugin(nsWindow* aWindow,
   879                                         WPARAM wParam,
   880                                         LPARAM lParam,
   881                                         MSGResult& aResult)
   882 {
   883   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   884     ("IMM32: OnIMESetContextOnPlugin, hWnd=%08x, %s, lParam=%08x\n",
   885      aWindow->GetWindowHandle(), wParam ? "Active" : "Deactive", lParam));
   887   // If the IME context becomes active on a plug-in, we should commit
   888   // our composition.  And also we should cancel the composition on new
   889   // window.  Note that if IsTopLevelWindowOfComposition(aWindow) returns
   890   // true, we should ignore the message here, see the comment in
   891   // OnIMESetContext() for the detail.
   892   if (wParam && gIMM32Handler && !IsTopLevelWindowOfComposition(aWindow)) {
   893     if (gIMM32Handler->CommitCompositionOnPreviousWindow(aWindow)) {
   894       CancelComposition(aWindow);
   895     }
   896   }
   898   // Dispatch message to the plug-in.
   899   // XXX When a windowless plug-in gets focus, we should send
   900   //     WM_IME_SETCONTEXT
   901   aWindow->DispatchPluginEvent(WM_IME_SETCONTEXT, wParam, lParam, false);
   903   // We should send WM_IME_SETCONTEXT to the DefWndProc here.  It shouldn't
   904   // be received on ancestor windows, see OnIMESetContext() for the detail.
   905   aResult.mResult = ::DefWindowProc(aWindow->GetWindowHandle(),
   906                                     WM_IME_SETCONTEXT, wParam, lParam);
   908   // Don't synchronously dispatch the pending events when we receive
   909   // WM_IME_SETCONTEXT because we get it during plugin destruction.
   910   // (bug 491848)
   911   aResult.mConsumed = true;
   912   return true;
   913 }
   915 bool
   916 nsIMM32Handler::OnCharOnPlugin(nsWindow* aWindow,
   917                                WPARAM wParam,
   918                                LPARAM lParam,
   919                                MSGResult& aResult)
   920 {
   921   // We should never consume char message on windowless plugin.
   922   aResult.mConsumed = false;
   923   if (IsIMECharRecordsEmpty()) {
   924     return false;
   925   }
   927   WPARAM recWParam;
   928   LPARAM recLParam;
   929   DequeueIMECharRecords(recWParam, recLParam);
   930   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   931     ("IMM32: OnCharOnPlugin, aWindow=%p, wParam=%08x, lParam=%08x,\n",
   932      aWindow->GetWindowHandle(), wParam, lParam));
   933   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   934     ("                       recorded: wParam=%08x, lParam=%08x\n",
   935      recWParam, recLParam));
   936   // If an unexpected char message comes, we should reset the records,
   937   // of course, this shouldn't happen.
   938   if (recWParam != wParam || recLParam != lParam) {
   939     ResetIMECharRecords();
   940   }
   941   // WM_CHAR on plug-in is always handled by nsWindow.
   942   return false;
   943 }
   945 /****************************************************************************
   946  * others
   947  ****************************************************************************/
   949 void
   950 nsIMM32Handler::HandleStartComposition(nsWindow* aWindow,
   951                                        const nsIMEContext &aIMEContext)
   952 {
   953   NS_PRECONDITION(!mIsComposing,
   954     "HandleStartComposition is called but mIsComposing is TRUE");
   955   NS_PRECONDITION(!aWindow->PluginHasFocus(),
   956     "HandleStartComposition should not be called when a plug-in has focus");
   958   WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
   959   nsIntPoint point(0, 0);
   960   aWindow->InitEvent(selection, &point);
   961   aWindow->DispatchWindowEvent(&selection);
   962   if (!selection.mSucceeded) {
   963     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   964       ("IMM32: HandleStartComposition, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
   965     return;
   966   }
   968   mCompositionStart = selection.mReply.mOffset;
   969   mLastDispatchedCompositionString.Truncate();
   971   WidgetCompositionEvent event(true, NS_COMPOSITION_START, aWindow);
   972   aWindow->InitEvent(event, &point);
   973   aWindow->DispatchWindowEvent(&event);
   975   mIsComposing = true;
   976   mComposingWindow = aWindow;
   978   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
   979     ("IMM32: HandleStartComposition, START composition, mCompositionStart=%ld\n",
   980      mCompositionStart));
   981 }
   983 bool
   984 nsIMM32Handler::HandleComposition(nsWindow* aWindow,
   985                                   const nsIMEContext &aIMEContext,
   986                                   LPARAM lParam)
   987 {
   988   NS_PRECONDITION(!aWindow->PluginHasFocus(),
   989     "HandleComposition should not be called when a plug-in has focus");
   991   // for bug #60050
   992   // MS-IME 95/97/98/2000 may send WM_IME_COMPOSITION with non-conversion
   993   // mode before it send WM_IME_STARTCOMPOSITION.
   994   // However, ATOK sends a WM_IME_COMPOSITION before WM_IME_STARTCOMPOSITION,
   995   // and if we access ATOK via some APIs, ATOK will sometimes fail to
   996   // initialize its state.  If WM_IME_STARTCOMPOSITION is already in the
   997   // message queue, we should ignore the strange WM_IME_COMPOSITION message and
   998   // skip to the next.  So, we should look for next composition message
   999   // (WM_IME_STARTCOMPOSITION or WM_IME_ENDCOMPOSITION or WM_IME_COMPOSITION),
  1000   // and if it's WM_IME_STARTCOMPOSITION, and one more next composition message
  1001   // is WM_IME_COMPOSITION, current IME is ATOK, probably.  Otherwise, we
  1002   // should start composition forcibly.
  1003   if (!mIsComposing) {
  1004     MSG msg1, msg2;
  1005     HWND wnd = aWindow->GetWindowHandle();
  1006     if (WinUtils::PeekMessage(&msg1, wnd, WM_IME_STARTCOMPOSITION,
  1007                               WM_IME_COMPOSITION, PM_NOREMOVE) &&
  1008         msg1.message == WM_IME_STARTCOMPOSITION &&
  1009         WinUtils::PeekMessage(&msg2, wnd, WM_IME_ENDCOMPOSITION,
  1010                               WM_IME_COMPOSITION, PM_NOREMOVE) &&
  1011         msg2.message == WM_IME_COMPOSITION) {
  1012       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1013         ("IMM32: HandleComposition, Ignores due to find a WM_IME_STARTCOMPOSITION\n"));
  1014       return ShouldDrawCompositionStringOurselves();
  1018   bool startCompositionMessageHasBeenSent = mIsComposing;
  1020   //
  1021   // This catches a fixed result
  1022   //
  1023   if (IS_COMMITTING_LPARAM(lParam)) {
  1024     if (!mIsComposing) {
  1025       HandleStartComposition(aWindow, aIMEContext);
  1028     GetCompositionString(aIMEContext, GCS_RESULTSTR);
  1030     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1031       ("IMM32: HandleComposition, GCS_RESULTSTR\n"));
  1033     DispatchTextEvent(aWindow, aIMEContext, false);
  1034     HandleEndComposition(aWindow);
  1036     if (!IS_COMPOSING_LPARAM(lParam)) {
  1037       return ShouldDrawCompositionStringOurselves();
  1042   //
  1043   // This provides us with a composition string
  1044   //
  1045   if (!mIsComposing) {
  1046     HandleStartComposition(aWindow, aIMEContext);
  1049   //--------------------------------------------------------
  1050   // 1. Get GCS_COMPSTR
  1051   //--------------------------------------------------------
  1052   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1053     ("IMM32: HandleComposition, GCS_COMPSTR\n"));
  1055   GetCompositionString(aIMEContext, GCS_COMPSTR);
  1057   if (!IS_COMPOSING_LPARAM(lParam)) {
  1058     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1059       ("IMM32: HandleComposition, lParam doesn't indicate composing, "
  1060        "mCompositionString=\"%s\", mLastDispatchedCompositionString=\"%s\"",
  1061        NS_ConvertUTF16toUTF8(mCompositionString).get(),
  1062        NS_ConvertUTF16toUTF8(mLastDispatchedCompositionString).get()));
  1064     // If composition string isn't changed, we can trust the lParam.
  1065     // So, we need to do nothing.
  1066     if (mLastDispatchedCompositionString == mCompositionString) {
  1067       return ShouldDrawCompositionStringOurselves();
  1070     // IME may send WM_IME_COMPOSITION without composing lParam values
  1071     // when composition string becomes empty (e.g., using Backspace key).
  1072     // If composition string is empty, we should dispatch a text event with
  1073     // empty string.
  1074     if (mCompositionString.IsEmpty()) {
  1075       DispatchTextEvent(aWindow, aIMEContext, false);
  1076       return ShouldDrawCompositionStringOurselves();
  1079     // Otherwise, we cannot trust the lParam value.  We might need to
  1080     // dispatch text event with the latest composition string information.
  1083   // See https://bugzilla.mozilla.org/show_bug.cgi?id=296339
  1084   if (mCompositionString.IsEmpty() && !startCompositionMessageHasBeenSent) {
  1085     // In this case, maybe, the sender is MSPinYin. That sends *only*
  1086     // WM_IME_COMPOSITION with GCS_COMP* and GCS_RESULT* when
  1087     // user inputted the Chinese full stop. So, that doesn't send
  1088     // WM_IME_STARTCOMPOSITION and WM_IME_ENDCOMPOSITION.
  1089     // If WM_IME_STARTCOMPOSITION was not sent and the composition
  1090     // string is null (it indicates the composition transaction ended),
  1091     // WM_IME_ENDCOMPOSITION may not be sent. If so, we cannot run
  1092     // HandleEndComposition() in other place.
  1093     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1094       ("IMM32: HandleComposition, Aborting GCS_COMPSTR\n"));
  1095     HandleEndComposition(aWindow);
  1096     return IS_COMMITTING_LPARAM(lParam);
  1099   //--------------------------------------------------------
  1100   // 2. Get GCS_COMPCLAUSE
  1101   //--------------------------------------------------------
  1102   long clauseArrayLength =
  1103     ::ImmGetCompositionStringW(aIMEContext.get(), GCS_COMPCLAUSE, nullptr, 0);
  1104   clauseArrayLength /= sizeof(uint32_t);
  1106   if (clauseArrayLength > 0) {
  1107     nsresult rv = EnsureClauseArray(clauseArrayLength);
  1108     NS_ENSURE_SUCCESS(rv, false);
  1110     // Intelligent ABC IME (Simplified Chinese IME, the code page is 936)
  1111     // will crash in ImmGetCompositionStringW for GCS_COMPCLAUSE (bug 424663).
  1112     // See comment 35 of the bug for the detail. Therefore, we should use A
  1113     // API for it, however, we should not kill Unicode support on all IMEs.
  1114     bool useA_API = !(sIMEProperty & IME_PROP_UNICODE);
  1116     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1117       ("IMM32: HandleComposition, GCS_COMPCLAUSE, useA_API=%s\n",
  1118        useA_API ? "TRUE" : "FALSE"));
  1120     long clauseArrayLength2 = 
  1121       useA_API ?
  1122         ::ImmGetCompositionStringA(aIMEContext.get(), GCS_COMPCLAUSE,
  1123                                    mClauseArray.Elements(),
  1124                                    mClauseArray.Capacity() * sizeof(uint32_t)) :
  1125         ::ImmGetCompositionStringW(aIMEContext.get(), GCS_COMPCLAUSE,
  1126                                    mClauseArray.Elements(),
  1127                                    mClauseArray.Capacity() * sizeof(uint32_t));
  1128     clauseArrayLength2 /= sizeof(uint32_t);
  1130     if (clauseArrayLength != clauseArrayLength2) {
  1131       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1132         ("IMM32: HandleComposition, GCS_COMPCLAUSE, clauseArrayLength=%ld but clauseArrayLength2=%ld\n",
  1133          clauseArrayLength, clauseArrayLength2));
  1134       if (clauseArrayLength > clauseArrayLength2)
  1135         clauseArrayLength = clauseArrayLength2;
  1138     if (useA_API) {
  1139       // Convert each values of sIMECompClauseArray. The values mean offset of
  1140       // the clauses in ANSI string. But we need the values in Unicode string.
  1141       nsAutoCString compANSIStr;
  1142       if (ConvertToANSIString(mCompositionString, GetKeyboardCodePage(),
  1143                               compANSIStr)) {
  1144         uint32_t maxlen = compANSIStr.Length();
  1145         mClauseArray[0] = 0; // first value must be 0
  1146         for (int32_t i = 1; i < clauseArrayLength; i++) {
  1147           uint32_t len = std::min(mClauseArray[i], maxlen);
  1148           mClauseArray[i] = ::MultiByteToWideChar(GetKeyboardCodePage(), 
  1149                                                   MB_PRECOMPOSED,
  1150                                                   (LPCSTR)compANSIStr.get(),
  1151                                                   len, nullptr, 0);
  1156   // compClauseArrayLength may be negative. I.e., ImmGetCompositionStringW
  1157   // may return an error code.
  1158   mClauseArray.SetLength(std::max<long>(0, clauseArrayLength));
  1160   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1161     ("IMM32: HandleComposition, GCS_COMPCLAUSE, mClauseLength=%ld\n",
  1162      mClauseArray.Length()));
  1164   //--------------------------------------------------------
  1165   // 3. Get GCS_COMPATTR
  1166   //--------------------------------------------------------
  1167   // This provides us with the attribute string necessary 
  1168   // for doing hiliting
  1169   long attrArrayLength =
  1170     ::ImmGetCompositionStringW(aIMEContext.get(), GCS_COMPATTR, nullptr, 0);
  1171   attrArrayLength /= sizeof(uint8_t);
  1173   if (attrArrayLength > 0) {
  1174     nsresult rv = EnsureAttributeArray(attrArrayLength);
  1175     NS_ENSURE_SUCCESS(rv, false);
  1176     attrArrayLength =
  1177       ::ImmGetCompositionStringW(aIMEContext.get(), GCS_COMPATTR,
  1178                                  mAttributeArray.Elements(),
  1179                                  mAttributeArray.Capacity() * sizeof(uint8_t));
  1182   // attrStrLen may be negative. I.e., ImmGetCompositionStringW may return an
  1183   // error code.
  1184   mAttributeArray.SetLength(std::max<long>(0, attrArrayLength));
  1186   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1187     ("IMM32: HandleComposition, GCS_COMPATTR, mAttributeLength=%ld\n",
  1188      mAttributeArray.Length()));
  1190   //--------------------------------------------------------
  1191   // 4. Get GCS_CURSOPOS
  1192   //--------------------------------------------------------
  1193   // Some IMEs (e.g., the standard IME for Korean) don't have caret position.
  1194   if (lParam & GCS_CURSORPOS) {
  1195     mCursorPosition =
  1196       ::ImmGetCompositionStringW(aIMEContext.get(), GCS_CURSORPOS, nullptr, 0);
  1197     if (mCursorPosition < 0) {
  1198       mCursorPosition = NO_IME_CARET; // The result is error
  1200   } else {
  1201     mCursorPosition = NO_IME_CARET;
  1204   NS_ASSERTION(mCursorPosition <= (long)mCompositionString.Length(),
  1205                "illegal pos");
  1207   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1208     ("IMM32: HandleComposition, GCS_CURSORPOS, mCursorPosition=%d\n",
  1209      mCursorPosition));
  1211   //--------------------------------------------------------
  1212   // 5. Send the text event
  1213   //--------------------------------------------------------
  1214   DispatchTextEvent(aWindow, aIMEContext);
  1216   return ShouldDrawCompositionStringOurselves();
  1219 void
  1220 nsIMM32Handler::HandleEndComposition(nsWindow* aWindow)
  1222   NS_PRECONDITION(mIsComposing,
  1223     "HandleEndComposition is called but mIsComposing is FALSE");
  1224   NS_PRECONDITION(!aWindow->PluginHasFocus(),
  1225     "HandleComposition should not be called when a plug-in has focus");
  1227   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1228     ("IMM32: HandleEndComposition\n"));
  1230   WidgetCompositionEvent event(true, NS_COMPOSITION_END, aWindow);
  1231   nsIntPoint point(0, 0);
  1233   if (mNativeCaretIsCreated) {
  1234     ::DestroyCaret();
  1235     mNativeCaretIsCreated = false;
  1238   aWindow->InitEvent(event, &point);
  1239   // The last dispatched composition string must be the committed string.
  1240   event.data = mLastDispatchedCompositionString;
  1241   aWindow->DispatchWindowEvent(&event);
  1242   mIsComposing = false;
  1243   mComposingWindow = nullptr;
  1244   mLastDispatchedCompositionString.Truncate();
  1247 static void
  1248 DumpReconvertString(RECONVERTSTRING* aReconv)
  1250   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1251     ("  dwSize=%ld, dwVersion=%ld, dwStrLen=%ld, dwStrOffset=%ld\n",
  1252      aReconv->dwSize, aReconv->dwVersion,
  1253      aReconv->dwStrLen, aReconv->dwStrOffset));
  1254   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1255     ("  dwCompStrLen=%ld, dwCompStrOffset=%ld, dwTargetStrLen=%ld, dwTargetStrOffset=%ld\n",
  1256      aReconv->dwCompStrLen, aReconv->dwCompStrOffset,
  1257      aReconv->dwTargetStrLen, aReconv->dwTargetStrOffset));
  1258   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1259     ("  result str=\"%s\"\n",
  1260      NS_ConvertUTF16toUTF8(
  1261        nsAutoString((char16_t*)((char*)(aReconv) + aReconv->dwStrOffset),
  1262                     aReconv->dwStrLen)).get()));
  1265 bool
  1266 nsIMM32Handler::HandleReconvert(nsWindow* aWindow,
  1267                                 LPARAM lParam,
  1268                                 LRESULT *oResult)
  1270   *oResult = 0;
  1271   RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);
  1273   WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
  1274   nsIntPoint point(0, 0);
  1275   aWindow->InitEvent(selection, &point);
  1276   aWindow->DispatchWindowEvent(&selection);
  1277   if (!selection.mSucceeded) {
  1278     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1279       ("IMM32: HandleReconvert, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
  1280     return false;
  1283   uint32_t len = selection.mReply.mString.Length();
  1284   uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
  1286   if (!pReconv) {
  1287     // Return need size to reconvert.
  1288     if (len == 0) {
  1289       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1290         ("IMM32: HandleReconvert, There are not selected text\n"));
  1291       return false;
  1293     *oResult = needSize;
  1294     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1295       ("IMM32: HandleReconvert, SUCCEEDED result=%ld\n",
  1296        *oResult));
  1297     return true;
  1300   if (pReconv->dwSize < needSize) {
  1301     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1302       ("IMM32: HandleReconvert, FAILED pReconv->dwSize=%ld, needSize=%ld\n",
  1303        pReconv->dwSize, needSize));
  1304     return false;
  1307   *oResult = needSize;
  1309   // Fill reconvert struct
  1310   pReconv->dwVersion         = 0;
  1311   pReconv->dwStrLen          = len;
  1312   pReconv->dwStrOffset       = sizeof(RECONVERTSTRING);
  1313   pReconv->dwCompStrLen      = len;
  1314   pReconv->dwCompStrOffset   = 0;
  1315   pReconv->dwTargetStrLen    = len;
  1316   pReconv->dwTargetStrOffset = 0;
  1318   ::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
  1319                selection.mReply.mString.get(), len * sizeof(WCHAR));
  1321   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1322     ("IMM32: HandleReconvert, SUCCEEDED result=%ld\n",
  1323      *oResult));
  1324   DumpReconvertString(pReconv);
  1326   return true;
  1329 bool
  1330 nsIMM32Handler::HandleQueryCharPosition(nsWindow* aWindow,
  1331                                         LPARAM lParam,
  1332                                         LRESULT *oResult)
  1334   uint32_t len = mIsComposing ? mCompositionString.Length() : 0;
  1335   *oResult = false;
  1336   IMECHARPOSITION* pCharPosition = reinterpret_cast<IMECHARPOSITION*>(lParam);
  1337   if (!pCharPosition) {
  1338     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1339       ("IMM32: HandleQueryCharPosition, FAILED (pCharPosition is null)\n"));
  1340     return false;
  1342   if (pCharPosition->dwSize < sizeof(IMECHARPOSITION)) {
  1343     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1344       ("IMM32: HandleReconvert, FAILED, pCharPosition->dwSize=%ld, sizeof(IMECHARPOSITION)=%ld\n",
  1345        pCharPosition->dwSize, sizeof(IMECHARPOSITION)));
  1346     return false;
  1348   if (::GetFocus() != aWindow->GetWindowHandle()) {
  1349     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1350       ("IMM32: HandleReconvert, FAILED, ::GetFocus()=%08x, OurWindowHandle=%08x\n",
  1351        ::GetFocus(), aWindow->GetWindowHandle()));
  1352     return false;
  1354   if (pCharPosition->dwCharPos > len) {
  1355     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1356       ("IMM32: HandleQueryCharPosition, FAILED, pCharPosition->dwCharPos=%ld, len=%ld\n",
  1357       pCharPosition->dwCharPos, len));
  1358     return false;
  1361   nsIntRect r;
  1362   bool ret =
  1363     GetCharacterRectOfSelectedTextAt(aWindow, pCharPosition->dwCharPos, r);
  1364   NS_ENSURE_TRUE(ret, false);
  1366   nsIntRect screenRect;
  1367   // We always need top level window that is owner window of the popup window
  1368   // even if the content of the popup window has focus.
  1369   ResolveIMECaretPos(aWindow->GetTopLevelWindow(false),
  1370                      r, nullptr, screenRect);
  1371   pCharPosition->pt.x = screenRect.x;
  1372   pCharPosition->pt.y = screenRect.y;
  1374   pCharPosition->cLineHeight = r.height;
  1376   // XXX we should use NS_QUERY_EDITOR_RECT event here.
  1377   ::GetWindowRect(aWindow->GetWindowHandle(), &pCharPosition->rcDocument);
  1379   *oResult = TRUE;
  1381   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1382     ("IMM32: HandleQueryCharPosition, SUCCEEDED\n"));
  1383   return true;
  1386 bool
  1387 nsIMM32Handler::HandleDocumentFeed(nsWindow* aWindow,
  1388                                    LPARAM lParam,
  1389                                    LRESULT *oResult)
  1391   *oResult = 0;
  1392   RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);
  1394   nsIntPoint point(0, 0);
  1396   bool hasCompositionString =
  1397     mIsComposing && ShouldDrawCompositionStringOurselves();
  1399   int32_t targetOffset, targetLength;
  1400   if (!hasCompositionString) {
  1401     WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
  1402     aWindow->InitEvent(selection, &point);
  1403     aWindow->DispatchWindowEvent(&selection);
  1404     if (!selection.mSucceeded) {
  1405       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1406         ("IMM32: HandleDocumentFeed, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
  1407       return false;
  1409     targetOffset = int32_t(selection.mReply.mOffset);
  1410     targetLength = int32_t(selection.mReply.mString.Length());
  1411   } else {
  1412     targetOffset = int32_t(mCompositionStart);
  1413     targetLength = int32_t(mCompositionString.Length());
  1416   // XXX nsString::Find and nsString::RFind take int32_t for offset, so,
  1417   //     we cannot support this message when the current offset is larger than
  1418   //     INT32_MAX.
  1419   if (targetOffset < 0 || targetLength < 0 ||
  1420       targetOffset + targetLength < 0) {
  1421     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1422       ("IMM32: HandleDocumentFeed, FAILED (The selection is out of range)\n"));
  1423     return false;
  1426   // Get all contents of the focused editor.
  1427   WidgetQueryContentEvent textContent(true, NS_QUERY_TEXT_CONTENT, aWindow);
  1428   textContent.InitForQueryTextContent(0, UINT32_MAX);
  1429   aWindow->InitEvent(textContent, &point);
  1430   aWindow->DispatchWindowEvent(&textContent);
  1431   if (!textContent.mSucceeded) {
  1432     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1433       ("IMM32: HandleDocumentFeed, FAILED (NS_QUERY_TEXT_CONTENT)\n"));
  1434     return false;
  1437   nsAutoString str(textContent.mReply.mString);
  1438   if (targetOffset > int32_t(str.Length())) {
  1439     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1440       ("IMM32: HandleDocumentFeed, FAILED (The caret offset is invalid)\n"));
  1441     return false;
  1444   // Get the focused paragraph, we decide that it starts from the previous CRLF
  1445   // (or start of the editor) to the next one (or the end of the editor).
  1446   int32_t paragraphStart = str.RFind("\n", false, targetOffset, -1) + 1;
  1447   int32_t paragraphEnd =
  1448     str.Find("\r", false, targetOffset + targetLength, -1);
  1449   if (paragraphEnd < 0) {
  1450     paragraphEnd = str.Length();
  1452   nsDependentSubstring paragraph(str, paragraphStart,
  1453                                  paragraphEnd - paragraphStart);
  1455   uint32_t len = paragraph.Length();
  1456   uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
  1458   if (!pReconv) {
  1459     *oResult = needSize;
  1460     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1461       ("IMM32: HandleDocumentFeed, SUCCEEDED result=%ld\n",
  1462        *oResult));
  1463     return true;
  1466   if (pReconv->dwSize < needSize) {
  1467     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1468       ("IMM32: HandleDocumentFeed, FAILED pReconv->dwSize=%ld, needSize=%ld\n",
  1469        pReconv->dwSize, needSize));
  1470     return false;
  1473   // Fill reconvert struct
  1474   pReconv->dwVersion         = 0;
  1475   pReconv->dwStrLen          = len;
  1476   pReconv->dwStrOffset       = sizeof(RECONVERTSTRING);
  1477   if (hasCompositionString) {
  1478     pReconv->dwCompStrLen      = targetLength;
  1479     pReconv->dwCompStrOffset   =
  1480       (targetOffset - paragraphStart) * sizeof(WCHAR);
  1481     // Set composition target clause information
  1482     uint32_t offset, length;
  1483     if (!GetTargetClauseRange(&offset, &length)) {
  1484       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1485         ("IMM32: HandleDocumentFeed, FAILED, by GetTargetClauseRange\n"));
  1486       return false;
  1488     pReconv->dwTargetStrLen    = length;
  1489     pReconv->dwTargetStrOffset = (offset - paragraphStart) * sizeof(WCHAR);
  1490   } else {
  1491     pReconv->dwTargetStrLen    = targetLength;
  1492     pReconv->dwTargetStrOffset =
  1493       (targetOffset - paragraphStart) * sizeof(WCHAR);
  1494     // There is no composition string, so, the length is zero but we should
  1495     // set the cursor offset to the composition str offset.
  1496     pReconv->dwCompStrLen      = 0;
  1497     pReconv->dwCompStrOffset   = pReconv->dwTargetStrOffset;
  1500   *oResult = needSize;
  1501   ::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
  1502                paragraph.BeginReading(), len * sizeof(WCHAR));
  1504   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1505     ("IMM32: HandleDocumentFeed, SUCCEEDED result=%ld\n",
  1506      *oResult));
  1507   DumpReconvertString(pReconv);
  1509   return true;
  1512 bool
  1513 nsIMM32Handler::CommitCompositionOnPreviousWindow(nsWindow* aWindow)
  1515   if (!mComposingWindow || mComposingWindow == aWindow) {
  1516     return false;
  1519   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1520     ("IMM32: CommitCompositionOnPreviousWindow, mIsComposing=%s, mIsComposingOnPlugin=%s\n",
  1521      mIsComposing ? "TRUE" : "FALSE", mIsComposingOnPlugin ? "TRUE" : "FALSE"));
  1523   // If we have composition, we should dispatch composition events internally.
  1524   if (mIsComposing) {
  1525     nsIMEContext IMEContext(mComposingWindow->GetWindowHandle());
  1526     NS_ASSERTION(IMEContext.IsValid(), "IME context must be valid");
  1528     DispatchTextEvent(mComposingWindow, IMEContext, false);
  1529     HandleEndComposition(mComposingWindow);
  1530     return true;
  1533   // XXX When plug-in has composition, we should commit composition on the
  1534   // plug-in.  However, we need some more work for that.
  1535   return mIsComposingOnPlugin;
  1538 static uint32_t
  1539 PlatformToNSAttr(uint8_t aAttr)
  1541   switch (aAttr)
  1543     case ATTR_INPUT_ERROR:
  1544     // case ATTR_FIXEDCONVERTED:
  1545     case ATTR_INPUT:
  1546       return NS_TEXTRANGE_RAWINPUT;
  1547     case ATTR_CONVERTED:
  1548       return NS_TEXTRANGE_CONVERTEDTEXT;
  1549     case ATTR_TARGET_NOTCONVERTED:
  1550       return NS_TEXTRANGE_SELECTEDRAWTEXT;
  1551     case ATTR_TARGET_CONVERTED:
  1552       return NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
  1553     default:
  1554       NS_ASSERTION(false, "unknown attribute");
  1555       return NS_TEXTRANGE_CARETPOSITION;
  1559 #ifdef PR_LOGGING
  1560 static const char*
  1561 GetRangeTypeName(uint32_t aRangeType)
  1563   switch (aRangeType) {
  1564     case NS_TEXTRANGE_RAWINPUT:
  1565       return "NS_TEXTRANGE_RAWINPUT";
  1566     case NS_TEXTRANGE_CONVERTEDTEXT:
  1567       return "NS_TEXTRANGE_CONVERTEDTEXT";
  1568     case NS_TEXTRANGE_SELECTEDRAWTEXT:
  1569       return "NS_TEXTRANGE_SELECTEDRAWTEXT";
  1570     case NS_TEXTRANGE_SELECTEDCONVERTEDTEXT:
  1571       return "NS_TEXTRANGE_SELECTEDCONVERTEDTEXT";
  1572     case NS_TEXTRANGE_CARETPOSITION:
  1573       return "NS_TEXTRANGE_CARETPOSITION";
  1574     default:
  1575       return "UNKNOWN SELECTION TYPE!!";
  1578 #endif
  1580 void
  1581 nsIMM32Handler::DispatchTextEvent(nsWindow* aWindow,
  1582                                   const nsIMEContext &aIMEContext,
  1583                                   bool aCheckAttr)
  1585   NS_ASSERTION(mIsComposing, "conflict state");
  1586   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1587     ("IMM32: DispatchTextEvent, aCheckAttr=%s\n",
  1588      aCheckAttr ? "TRUE": "FALSE"));
  1590   // If we don't need to draw composition string ourselves and this is not
  1591   // commit event (i.e., under composing), we don't need to fire text event
  1592   // during composing.
  1593   if (aCheckAttr && !ShouldDrawCompositionStringOurselves()) {
  1594     // But we need to adjust composition window pos and native caret pos, here.
  1595     SetIMERelatedWindowsPos(aWindow, aIMEContext);
  1596     return;
  1599   nsRefPtr<nsWindow> kungFuDeathGrip(aWindow);
  1601   nsIntPoint point(0, 0);
  1603   if (mCompositionString != mLastDispatchedCompositionString) {
  1604     WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
  1605                                              aWindow);
  1606     aWindow->InitEvent(compositionUpdate, &point);
  1607     compositionUpdate.data = mCompositionString;
  1608     mLastDispatchedCompositionString = mCompositionString;
  1610     aWindow->DispatchWindowEvent(&compositionUpdate);
  1612     if (!mIsComposing || aWindow->Destroyed()) {
  1613       return;
  1615     SetIMERelatedWindowsPos(aWindow, aIMEContext);
  1618   WidgetTextEvent event(true, NS_TEXT_TEXT, aWindow);
  1620   aWindow->InitEvent(event, &point);
  1622   if (aCheckAttr) {
  1623     event.mRanges = CreateTextRangeArray();
  1626   event.theText = mCompositionString.get();
  1628   aWindow->DispatchWindowEvent(&event);
  1630   // Calling SetIMERelatedWindowsPos will be failure on e10s at this point.
  1631   // text event will notify NOTIFY_IME_OF_COMPOSITION_UPDATE, then
  1632   // it will call SetIMERelatedWindowsPos.
  1635 already_AddRefed<TextRangeArray>
  1636 nsIMM32Handler::CreateTextRangeArray()
  1638   // Sogou (Simplified Chinese IME) returns contradictory values: The cursor
  1639   // position is actual cursor position. However, other values (composition
  1640   // string and attributes) are empty. So, if you want to remove following
  1641   // assertion, be careful.
  1642   NS_ASSERTION(ShouldDrawCompositionStringOurselves(),
  1643     "CreateTextRangeArray is called when we don't need to fire text event");
  1645   nsRefPtr<TextRangeArray> textRangeArray = new TextRangeArray();
  1647   TextRange range;
  1648   if (mClauseArray.Length() == 0) {
  1649     // Some IMEs don't return clause array information, then, we assume that
  1650     // all characters in the composition string are in one clause.
  1651     range.mStartOffset = 0;
  1652     range.mEndOffset = mCompositionString.Length();
  1653     range.mRangeType = NS_TEXTRANGE_RAWINPUT;
  1654     textRangeArray->AppendElement(range);
  1656     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1657       ("IMM32: CreateTextRangeArray, mClauseLength=0\n"));
  1658   } else {
  1659     // iterate over the attributes
  1660     uint32_t lastOffset = 0;
  1661     for (uint32_t i = 0; i < mClauseArray.Length() - 1; i++) {
  1662       uint32_t current = mClauseArray[i + 1];
  1663       if (current > mCompositionString.Length()) {
  1664         PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1665           ("IMM32: CreateTextRangeArray, mClauseArray[%ld]=%lu. "
  1666            "This is larger than mCompositionString.Length()=%lu\n",
  1667            i + 1, current, mCompositionString.Length()));
  1668         current = int32_t(mCompositionString.Length());
  1671       range.mRangeType = PlatformToNSAttr(mAttributeArray[lastOffset]);
  1672       range.mStartOffset = lastOffset;
  1673       range.mEndOffset = current;
  1674       textRangeArray->AppendElement(range);
  1676       lastOffset = current;
  1678       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1679         ("IMM32: CreateTextRangeArray, index=%ld, rangeType=%s, range=[%lu-%lu]\n",
  1680          i, GetRangeTypeName(range.mRangeType), range.mStartOffset,
  1681          range.mEndOffset));
  1685   if (mCursorPosition == NO_IME_CARET) {
  1686     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1687       ("IMM32: CreateTextRangeArray, no caret\n"));
  1688     return textRangeArray.forget();
  1691   int32_t cursor = mCursorPosition;
  1692   if (uint32_t(cursor) > mCompositionString.Length()) {
  1693     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1694       ("IMM32: CreateTextRangeArray, mCursorPosition=%ld. "
  1695        "This is larger than mCompositionString.Length()=%lu\n",
  1696        mCursorPosition, mCompositionString.Length()));
  1697     cursor = mCompositionString.Length();
  1700   range.mStartOffset = range.mEndOffset = cursor;
  1701   range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
  1702   textRangeArray->AppendElement(range);
  1704   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1705     ("IMM32: CreateTextRangeArray, caret position=%ld\n",
  1706      range.mStartOffset));
  1708   return textRangeArray.forget();
  1711 void
  1712 nsIMM32Handler::GetCompositionString(const nsIMEContext &aIMEContext,
  1713                                      DWORD aIndex)
  1715   // Retrieve the size of the required output buffer.
  1716   long lRtn = ::ImmGetCompositionStringW(aIMEContext.get(), aIndex, nullptr, 0);
  1717   if (lRtn < 0 ||
  1718       !mCompositionString.SetLength((lRtn / sizeof(WCHAR)) + 1, mozilla::fallible_t())) {
  1719     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1720       ("IMM32: GetCompositionString, FAILED by OOM\n"));
  1721     return; // Error or out of memory.
  1724   // Actually retrieve the composition string information.
  1725   lRtn = ::ImmGetCompositionStringW(aIMEContext.get(), aIndex,
  1726                                     (LPVOID)mCompositionString.BeginWriting(),
  1727                                     lRtn + sizeof(WCHAR));
  1728   mCompositionString.SetLength(lRtn / sizeof(WCHAR));
  1730   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1731     ("IMM32: GetCompositionString, SUCCEEDED mCompositionString=\"%s\"\n",
  1732      NS_ConvertUTF16toUTF8(mCompositionString).get()));
  1735 bool
  1736 nsIMM32Handler::GetTargetClauseRange(uint32_t *aOffset, uint32_t *aLength)
  1738   NS_ENSURE_TRUE(aOffset, false);
  1739   NS_ENSURE_TRUE(mIsComposing, false);
  1740   NS_ENSURE_TRUE(ShouldDrawCompositionStringOurselves(), false);
  1742   bool found = false;
  1743   *aOffset = mCompositionStart;
  1744   for (uint32_t i = 0; i < mAttributeArray.Length(); i++) {
  1745     if (mAttributeArray[i] == ATTR_TARGET_NOTCONVERTED ||
  1746         mAttributeArray[i] == ATTR_TARGET_CONVERTED) {
  1747       *aOffset = mCompositionStart + i;
  1748       found = true;
  1749       break;
  1753   if (!aLength) {
  1754     return true;
  1757   if (!found) {
  1758     // The all composition string is targetted when there is no ATTR_TARGET_*
  1759     // clause. E.g., there is only ATTR_INPUT
  1760     *aLength = mCompositionString.Length();
  1761     return true;
  1764   uint32_t offsetInComposition = *aOffset - mCompositionStart;
  1765   *aLength = mCompositionString.Length() - offsetInComposition;
  1766   for (uint32_t i = offsetInComposition; i < mAttributeArray.Length(); i++) {
  1767     if (mAttributeArray[i] != ATTR_TARGET_NOTCONVERTED &&
  1768         mAttributeArray[i] != ATTR_TARGET_CONVERTED) {
  1769       *aLength = i - offsetInComposition;
  1770       break;
  1773   return true;
  1776 bool
  1777 nsIMM32Handler::ConvertToANSIString(const nsAFlatString& aStr, UINT aCodePage,
  1778                                    nsACString& aANSIStr)
  1780   int len = ::WideCharToMultiByte(aCodePage, 0,
  1781                                   (LPCWSTR)aStr.get(), aStr.Length(),
  1782                                   nullptr, 0, nullptr, nullptr);
  1783   NS_ENSURE_TRUE(len >= 0, false);
  1785   if (!aANSIStr.SetLength(len, mozilla::fallible_t())) {
  1786     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1787       ("IMM32: ConvertToANSIString, FAILED by OOM\n"));
  1788     return false;
  1790   ::WideCharToMultiByte(aCodePage, 0, (LPCWSTR)aStr.get(), aStr.Length(),
  1791                         (LPSTR)aANSIStr.BeginWriting(), len, nullptr, nullptr);
  1792   return true;
  1795 bool
  1796 nsIMM32Handler::GetCharacterRectOfSelectedTextAt(nsWindow* aWindow,
  1797                                                  uint32_t aOffset,
  1798                                                  nsIntRect &aCharRect)
  1800   nsIntPoint point(0, 0);
  1802   WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
  1803   aWindow->InitEvent(selection, &point);
  1804   aWindow->DispatchWindowEvent(&selection);
  1805   if (!selection.mSucceeded) {
  1806     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1807       ("IMM32: GetCharacterRectOfSelectedTextAt, aOffset=%lu, FAILED (NS_QUERY_SELECTED_TEXT)\n",
  1808        aOffset));
  1809     return false;
  1812   uint32_t offset = selection.mReply.mOffset + aOffset;
  1813   bool useCaretRect = selection.mReply.mString.IsEmpty();
  1814   if (useCaretRect && ShouldDrawCompositionStringOurselves() &&
  1815       mIsComposing && !mCompositionString.IsEmpty()) {
  1816     // There is not a normal selection, but we have composition string.
  1817     // XXX mnakano - Should we implement NS_QUERY_IME_SELECTED_TEXT?
  1818     useCaretRect = false;
  1819     if (mCursorPosition != NO_IME_CARET) {
  1820       uint32_t cursorPosition =
  1821         std::min<uint32_t>(mCursorPosition, mCompositionString.Length());
  1822       NS_ASSERTION(offset >= cursorPosition, "offset is less than cursorPosition!");
  1823       offset -= cursorPosition;
  1827   nsIntRect r;
  1828   if (!useCaretRect) {
  1829     WidgetQueryContentEvent charRect(true, NS_QUERY_TEXT_RECT, aWindow);
  1830     charRect.InitForQueryTextRect(offset, 1);
  1831     aWindow->InitEvent(charRect, &point);
  1832     aWindow->DispatchWindowEvent(&charRect);
  1833     if (charRect.mSucceeded) {
  1834       aCharRect = charRect.mReply.mRect;
  1835       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1836         ("IMM32: GetCharacterRectOfSelectedTextAt, aOffset=%lu, SUCCEEDED\n",
  1837          aOffset));
  1838       PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1839         ("IMM32: GetCharacterRectOfSelectedTextAt, aCharRect={ x: %ld, y: %ld, width: %ld, height: %ld }\n",
  1840          aCharRect.x, aCharRect.y, aCharRect.width, aCharRect.height));
  1841       return true;
  1845   return GetCaretRect(aWindow, aCharRect);
  1848 bool
  1849 nsIMM32Handler::GetCaretRect(nsWindow* aWindow, nsIntRect &aCaretRect)
  1851   nsIntPoint point(0, 0);
  1853   WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, aWindow);
  1854   aWindow->InitEvent(selection, &point);
  1855   aWindow->DispatchWindowEvent(&selection);
  1856   if (!selection.mSucceeded) {
  1857     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1858       ("IMM32: GetCaretRect,  FAILED (NS_QUERY_SELECTED_TEXT)\n"));
  1859     return false;
  1862   uint32_t offset = selection.mReply.mOffset;
  1864   WidgetQueryContentEvent caretRect(true, NS_QUERY_CARET_RECT, aWindow);
  1865   caretRect.InitForQueryCaretRect(offset);
  1866   aWindow->InitEvent(caretRect, &point);
  1867   aWindow->DispatchWindowEvent(&caretRect);
  1868   if (!caretRect.mSucceeded) {
  1869     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1870       ("IMM32: GetCaretRect,  FAILED (NS_QUERY_CARET_RECT)\n"));
  1871     return false;
  1873   aCaretRect = caretRect.mReply.mRect;
  1874   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1875     ("IMM32: GetCaretRect, SUCCEEDED, aCaretRect={ x: %ld, y: %ld, width: %ld, height: %ld }\n",
  1876      aCaretRect.x, aCaretRect.y, aCaretRect.width, aCaretRect.height));
  1877   return true;
  1880 bool
  1881 nsIMM32Handler::SetIMERelatedWindowsPos(nsWindow* aWindow,
  1882                                         const nsIMEContext &aIMEContext)
  1884   nsIntRect r;
  1885   // Get first character rect of current a normal selected text or a composing
  1886   // string.
  1887   bool ret = GetCharacterRectOfSelectedTextAt(aWindow, 0, r);
  1888   NS_ENSURE_TRUE(ret, false);
  1889   nsWindow* toplevelWindow = aWindow->GetTopLevelWindow(false);
  1890   nsIntRect firstSelectedCharRect;
  1891   ResolveIMECaretPos(toplevelWindow, r, aWindow, firstSelectedCharRect);
  1893   // Set native caret size/position to our caret. Some IMEs honor it. E.g.,
  1894   // "Intelligent ABC" (Simplified Chinese) and "MS PinYin 3.0" (Simplified
  1895   // Chinese) on XP.
  1896   nsIntRect caretRect(firstSelectedCharRect);
  1897   if (GetCaretRect(aWindow, r)) {
  1898     ResolveIMECaretPos(toplevelWindow, r, aWindow, caretRect);
  1899   } else {
  1900     NS_WARNING("failed to get caret rect");
  1901     caretRect.width = 1;
  1903   if (!mNativeCaretIsCreated) {
  1904     mNativeCaretIsCreated = ::CreateCaret(aWindow->GetWindowHandle(), nullptr,
  1905                                           caretRect.width, caretRect.height);
  1906     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1907       ("IMM32: SetIMERelatedWindowsPos, mNativeCaretIsCreated=%s, width=%ld height=%ld\n",
  1908        mNativeCaretIsCreated ? "TRUE" : "FALSE",
  1909        caretRect.width, caretRect.height));
  1911   ::SetCaretPos(caretRect.x, caretRect.y);
  1913   if (ShouldDrawCompositionStringOurselves()) {
  1914     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1915       ("IMM32: SetIMERelatedWindowsPos, Set candidate window\n"));
  1917     // Get a rect of first character in current target in composition string.
  1918     if (mIsComposing && !mCompositionString.IsEmpty()) {
  1919       // If there are no targetted selection, we should use it's first character
  1920       // rect instead.
  1921       uint32_t offset;
  1922       if (!GetTargetClauseRange(&offset)) {
  1923         PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1924           ("IMM32: SetIMERelatedWindowsPos, FAILED, by GetTargetClauseRange\n"));
  1925         return false;
  1927       ret = GetCharacterRectOfSelectedTextAt(aWindow,
  1928                                              offset - mCompositionStart, r);
  1929       NS_ENSURE_TRUE(ret, false);
  1930     } else {
  1931       // If there are no composition string, we should use a first character
  1932       // rect.
  1933       ret = GetCharacterRectOfSelectedTextAt(aWindow, 0, r);
  1934       NS_ENSURE_TRUE(ret, false);
  1936     nsIntRect firstTargetCharRect;
  1937     ResolveIMECaretPos(toplevelWindow, r, aWindow, firstTargetCharRect);
  1939     // Move the candidate window to first character position of the target.
  1940     CANDIDATEFORM candForm;
  1941     candForm.dwIndex = 0;
  1942     candForm.dwStyle = CFS_EXCLUDE;
  1943     candForm.ptCurrentPos.x = firstTargetCharRect.x;
  1944     candForm.ptCurrentPos.y = firstTargetCharRect.y;
  1945     candForm.rcArea.right = candForm.rcArea.left = candForm.ptCurrentPos.x;
  1946     candForm.rcArea.top = candForm.ptCurrentPos.y;
  1947     candForm.rcArea.bottom = candForm.ptCurrentPos.y +
  1948                                firstTargetCharRect.height;
  1949     ::ImmSetCandidateWindow(aIMEContext.get(), &candForm);
  1950   } else {
  1951     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1952       ("IMM32: SetIMERelatedWindowsPos, Set composition window\n"));
  1954     // Move the composition window to caret position (if selected some
  1955     // characters, we should use first character rect of them).
  1956     // And in this mode, IME adjusts the candidate window position
  1957     // automatically. So, we don't need to set it.
  1958     COMPOSITIONFORM compForm;
  1959     compForm.dwStyle = CFS_POINT;
  1960     compForm.ptCurrentPos.x = firstSelectedCharRect.x;
  1961     compForm.ptCurrentPos.y = firstSelectedCharRect.y;
  1962     ::ImmSetCompositionWindow(aIMEContext.get(), &compForm);
  1965   return true;
  1968 void
  1969 nsIMM32Handler::SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow,
  1970                                                 const nsIMEContext& aIMEContext)
  1972   WidgetQueryContentEvent editorRectEvent(true, NS_QUERY_EDITOR_RECT, aWindow);
  1973   aWindow->InitEvent(editorRectEvent);
  1974   aWindow->DispatchWindowEvent(&editorRectEvent);
  1975   if (!editorRectEvent.mSucceeded) {
  1976     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  1977       ("IMM32: SetIMERelatedWindowsPosOnPlugin, "
  1978        "FAILED (NS_QUERY_EDITOR_RECT)"));
  1979     return;
  1982   // Clip the plugin rect by the client rect of the window because composition
  1983   // window needs to be specified the position in the client area.
  1984   nsWindow* toplevelWindow = aWindow->GetTopLevelWindow(false);
  1985   nsIntRect pluginRectInScreen =
  1986     editorRectEvent.mReply.mRect + toplevelWindow->WidgetToScreenOffset();
  1987   nsIntRect winRectInScreen;
  1988   aWindow->GetClientBounds(winRectInScreen);
  1989   // composition window cannot be positioned on the edge of client area.
  1990   winRectInScreen.width--;
  1991   winRectInScreen.height--;
  1992   nsIntRect clippedPluginRect;
  1993   clippedPluginRect.x =
  1994     std::min(std::max(pluginRectInScreen.x, winRectInScreen.x),
  1995              winRectInScreen.XMost());
  1996   clippedPluginRect.y =
  1997     std::min(std::max(pluginRectInScreen.y, winRectInScreen.y),
  1998              winRectInScreen.YMost());
  1999   int32_t xMost = std::min(pluginRectInScreen.XMost(), winRectInScreen.XMost());
  2000   int32_t yMost = std::min(pluginRectInScreen.YMost(), winRectInScreen.YMost());
  2001   clippedPluginRect.width = std::max(0, xMost - clippedPluginRect.x);
  2002   clippedPluginRect.height = std::max(0, yMost - clippedPluginRect.y);
  2003   clippedPluginRect -= aWindow->WidgetToScreenOffset();
  2005   // Cover the plugin with native caret.  This prevents IME's window and plugin
  2006   // overlap.
  2007   if (mNativeCaretIsCreated) {
  2008     ::DestroyCaret();
  2010   mNativeCaretIsCreated =
  2011     ::CreateCaret(aWindow->GetWindowHandle(), nullptr,
  2012                   clippedPluginRect.width, clippedPluginRect.height);
  2013   ::SetCaretPos(clippedPluginRect.x, clippedPluginRect.y);
  2015   // Set the composition window to bottom-left of the clipped plugin.
  2016   // As far as we know, there is no IME for RTL language.  Therefore, this code
  2017   // must not need to take care of RTL environment.
  2018   COMPOSITIONFORM compForm;
  2019   compForm.dwStyle = CFS_POINT;
  2020   compForm.ptCurrentPos.x = clippedPluginRect.BottomLeft().x;
  2021   compForm.ptCurrentPos.y = clippedPluginRect.BottomLeft().y;
  2022   if (!::ImmSetCompositionWindow(aIMEContext.get(), &compForm)) {
  2023     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  2024       ("IMM32: SetIMERelatedWindowsPosOnPlugin, "
  2025        "FAILED to set composition window"));
  2026     return;
  2030 void
  2031 nsIMM32Handler::ResolveIMECaretPos(nsIWidget* aReferenceWidget,
  2032                                    nsIntRect& aCursorRect,
  2033                                    nsIWidget* aNewOriginWidget,
  2034                                    nsIntRect& aOutRect)
  2036   aOutRect = aCursorRect;
  2038   if (aReferenceWidget == aNewOriginWidget)
  2039     return;
  2041   if (aReferenceWidget)
  2042     aOutRect.MoveBy(aReferenceWidget->WidgetToScreenOffset());
  2044   if (aNewOriginWidget)
  2045     aOutRect.MoveBy(-aNewOriginWidget->WidgetToScreenOffset());
  2048 bool
  2049 nsIMM32Handler::OnMouseEvent(nsWindow* aWindow, LPARAM lParam, int aAction,
  2050                              MSGResult& aResult)
  2052   aResult.mConsumed = false; // always call next wndprc
  2054   if (!sWM_MSIME_MOUSE || !mIsComposing ||
  2055       !ShouldDrawCompositionStringOurselves()) {
  2056     return false;
  2059   nsIntPoint cursor(LOWORD(lParam), HIWORD(lParam));
  2060   WidgetQueryContentEvent charAtPt(true, NS_QUERY_CHARACTER_AT_POINT, aWindow);
  2061   aWindow->InitEvent(charAtPt, &cursor);
  2062   aWindow->DispatchWindowEvent(&charAtPt);
  2063   if (!charAtPt.mSucceeded ||
  2064       charAtPt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND ||
  2065       charAtPt.mReply.mOffset < mCompositionStart ||
  2066       charAtPt.mReply.mOffset >
  2067         mCompositionStart + mCompositionString.Length()) {
  2068     return false;
  2071   // calcurate positioning and offset
  2072   // char :            JCH1|JCH2|JCH3
  2073   // offset:           0011 1122 2233
  2074   // positioning:      2301 2301 2301
  2075   nsIntRect cursorInTopLevel, cursorRect(cursor, nsIntSize(0, 0));
  2076   ResolveIMECaretPos(aWindow, cursorRect,
  2077                      aWindow->GetTopLevelWindow(false), cursorInTopLevel);
  2078   int32_t cursorXInChar = cursorInTopLevel.x - charAtPt.mReply.mRect.x;
  2079   // The event might hit to zero-width character, see bug 694913.
  2080   // The reason might be:
  2081   // * There are some zero-width characters are actually.
  2082   // * font-size is specified zero.
  2083   // But nobody reproduced this bug actually...
  2084   // We should assume that user clicked on right most of the zero-width
  2085   // character in such case.
  2086   int positioning = 1;
  2087   if (charAtPt.mReply.mRect.width > 0) {
  2088     positioning = cursorXInChar * 4 / charAtPt.mReply.mRect.width;
  2089     positioning = (positioning + 2) % 4;
  2092   int offset = charAtPt.mReply.mOffset - mCompositionStart;
  2093   if (positioning < 2) {
  2094     offset++;
  2097   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  2098     ("IMM32: OnMouseEvent, x,y=%ld,%ld, offset=%ld, positioning=%ld\n",
  2099      cursor.x, cursor.y, offset, positioning));
  2101   // send MS_MSIME_MOUSE message to default IME window.
  2102   HWND imeWnd = ::ImmGetDefaultIMEWnd(aWindow->GetWindowHandle());
  2103   nsIMEContext IMEContext(aWindow->GetWindowHandle());
  2104   return ::SendMessageW(imeWnd, sWM_MSIME_MOUSE,
  2105                         MAKELONG(MAKEWORD(aAction, positioning), offset),
  2106                         (LPARAM) IMEContext.get()) == 1;
  2109 /* static */ bool
  2110 nsIMM32Handler::OnKeyDownEvent(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
  2111                                MSGResult& aResult)
  2113   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
  2114     ("IMM32: OnKeyDownEvent, hWnd=%08x, wParam=%08x, lParam=%08x\n",
  2115      aWindow->GetWindowHandle(), wParam, lParam));
  2116   aResult.mConsumed = false;
  2117   switch (wParam) {
  2118     case VK_TAB:
  2119     case VK_PRIOR:
  2120     case VK_NEXT:
  2121     case VK_END:
  2122     case VK_HOME:
  2123     case VK_LEFT:
  2124     case VK_UP:
  2125     case VK_RIGHT:
  2126     case VK_DOWN:
  2127       // If IME didn't process the key message (the virtual key code wasn't
  2128       // converted to VK_PROCESSKEY), and the virtual key code event causes
  2129       // to move caret, we should cancel the composition here.  Then, this
  2130       // event will be dispatched.
  2131       // XXX I think that we should dispatch all key events during composition,
  2132       //     and nsEditor should cancel/commit the composition if it *thinks*
  2133       //     it's needed.
  2134       if (IsComposingOnOurEditor()) {
  2135         // NOTE: We don't need to cancel the composition on another window.
  2136         CancelComposition(aWindow, false);
  2138       return false;
  2139     default:
  2140       return false;

mercurial