widget/windows/nsIMM32Handler.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial