1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "WinIMEHandler.h"
7 
8 #include "IMMHandler.h"
9 #include "mozilla/Preferences.h"
10 #include "mozilla/TextEvents.h"
11 #include "mozilla/WindowsVersion.h"
12 #include "nsWindowDefs.h"
13 #include "WinTextEventDispatcherListener.h"
14 
15 #include "TSFTextStore.h"
16 
17 #include "OSKInputPaneManager.h"
18 #include "OSKTabTipManager.h"
19 #include "OSKVRManager.h"
20 #include "nsLookAndFeel.h"
21 #include "nsWindow.h"
22 #include "WinUtils.h"
23 #include "nsIWindowsRegKey.h"
24 #include "nsIWindowsUIUtils.h"
25 
26 #ifdef ACCESSIBILITY
27 #  include "nsAccessibilityService.h"
28 #endif  // #ifdef ACCESSIBILITY
29 
30 #include "shellapi.h"
31 #include "shlobj.h"
32 #include "powrprof.h"
33 #include "setupapi.h"
34 #include "cfgmgr32.h"
35 
36 #include "FxRWindowManager.h"
37 #include "moz_external_vr.h"
38 
39 const char* kOskEnabled = "ui.osk.enabled";
40 const char* kOskDetectPhysicalKeyboard = "ui.osk.detect_physical_keyboard";
41 const char* kOskRequireWin10 = "ui.osk.require_win10";
42 const char* kOskDebugReason = "ui.osk.debug.keyboardDisplayReason";
43 
44 namespace mozilla {
45 namespace widget {
46 
47 /******************************************************************************
48  * IMEHandler
49  ******************************************************************************/
50 
51 nsWindow* IMEHandler::sFocusedWindow = nullptr;
52 InputContextAction::Cause IMEHandler::sLastContextActionCause =
53     InputContextAction::CAUSE_UNKNOWN;
54 bool IMEHandler::sMaybeEditable = false;
55 bool IMEHandler::sForceDisableCurrentIMM_IME = false;
56 bool IMEHandler::sNativeCaretIsCreated = false;
57 bool IMEHandler::sHasNativeCaretBeenRequested = false;
58 
59 bool IMEHandler::sIsInTSFMode = false;
60 bool IMEHandler::sIsIMMEnabled = true;
61 bool IMEHandler::sAssociateIMCOnlyWhenIMM_IMEActive = false;
62 decltype(SetInputScopes)* IMEHandler::sSetInputScopes = nullptr;
63 
64 static POWER_PLATFORM_ROLE sPowerPlatformRole = PlatformRoleUnspecified;
65 static bool sDeterminedPowerPlatformRole = false;
66 
67 // static
Initialize()68 void IMEHandler::Initialize() {
69   TSFTextStore::Initialize();
70   sIsInTSFMode = TSFTextStore::IsInTSFMode();
71   sIsIMMEnabled =
72       !sIsInTSFMode || Preferences::GetBool("intl.tsf.support_imm", true);
73   sAssociateIMCOnlyWhenIMM_IMEActive =
74       sIsIMMEnabled &&
75       Preferences::GetBool("intl.tsf.associate_imc_only_when_imm_ime_is_active",
76                            false);
77   if (!sIsInTSFMode) {
78     // When full TSFTextStore is not available, try to use SetInputScopes API
79     // to enable at least InputScope. Use GET_MODULE_HANDLE_EX_FLAG_PIN to
80     // ensure that msctf.dll will not be unloaded.
81     HMODULE module = nullptr;
82     if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"msctf.dll",
83                            &module)) {
84       sSetInputScopes = reinterpret_cast<decltype(SetInputScopes)*>(
85           GetProcAddress(module, "SetInputScopes"));
86     }
87   }
88 
89   IMMHandler::Initialize();
90 
91   sForceDisableCurrentIMM_IME = IMMHandler::IsActiveIMEInBlockList();
92 }
93 
94 // static
Terminate()95 void IMEHandler::Terminate() {
96   if (sIsInTSFMode) {
97     TSFTextStore::Terminate();
98     sIsInTSFMode = false;
99   }
100 
101   IMMHandler::Terminate();
102   WinTextEventDispatcherListener::Shutdown();
103 }
104 
105 // static
GetNativeData(nsWindow * aWindow,uint32_t aDataType)106 void* IMEHandler::GetNativeData(nsWindow* aWindow, uint32_t aDataType) {
107   if (aDataType == NS_RAW_NATIVE_IME_CONTEXT) {
108     if (IsTSFAvailable()) {
109       return TSFTextStore::GetThreadManager();
110     }
111     IMEContext context(aWindow);
112     if (context.IsValid()) {
113       return context.get();
114     }
115     // If IMC isn't associated with the window, IME is disabled on the window
116     // now.  In such case, we should return default IMC instead.
117     const IMEContext& defaultIMC = aWindow->DefaultIMC();
118     if (defaultIMC.IsValid()) {
119       return defaultIMC.get();
120     }
121     // If there is no default IMC, we should return the pointer to the window
122     // since if we return nullptr, IMEStateManager cannot manage composition
123     // with TextComposition instance.  This is possible if no IME is installed,
124     // but composition may occur with dead key sequence.
125     return aWindow;
126   }
127 
128   void* result = TSFTextStore::GetNativeData(aDataType);
129   if (!result || !(*(static_cast<void**>(result)))) {
130     return nullptr;
131   }
132   // XXX During the TSF module test, sIsInTSFMode must be true.  After that,
133   //     the value should be restored but currently, there is no way for that.
134   //     When the TSF test is enabled again, we need to fix this.  Perhaps,
135   //     sending a message can fix this.
136   sIsInTSFMode = true;
137   return result;
138 }
139 
140 // static
ProcessRawKeyMessage(const MSG & aMsg)141 bool IMEHandler::ProcessRawKeyMessage(const MSG& aMsg) {
142   if (IsTSFAvailable()) {
143     return TSFTextStore::ProcessRawKeyMessage(aMsg);
144   }
145   return false;  // noting to do in IMM mode.
146 }
147 
148 // static
ProcessMessage(nsWindow * aWindow,UINT aMessage,WPARAM & aWParam,LPARAM & aLParam,MSGResult & aResult)149 bool IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage,
150                                 WPARAM& aWParam, LPARAM& aLParam,
151                                 MSGResult& aResult) {
152   // If we're putting native caret over our caret, Windows dispatches
153   // EVENT_OBJECT_LOCATIONCHANGE event on other applications which hook
154   // the event with ::SetWinEventHook() and handles WM_GETOBJECT for
155   // OBJID_CARET (this is request of caret from such applications) instead
156   // of us.  If a11y module is active, it observes every our caret change
157   // and put native caret over it automatically.  However, if other
158   // applications require only caret information, activating a11y module is
159   // overwork and such applications may requires carets only in editors.
160   // Therefore, if it'd be possible, IMEHandler should put native caret over
161   // our caret, but there is a problem.  Some versions of ATOK (Japanese TIP)
162   // refer native caret and if there is, the behavior is worse than the
163   // behavior without native caret.  Therefore, we shouldn't put native caret
164   // as far as possible.
165   if (!sHasNativeCaretBeenRequested && aMessage == WM_GETOBJECT &&
166       static_cast<DWORD>(aLParam) == OBJID_CARET) {
167     // So, when we receive first WM_GETOBJECT for OBJID_CARET, let's start to
168     // create native caret for such applications.
169     sHasNativeCaretBeenRequested = true;
170     // If an editable element has focus, we can put native caret now.
171     // XXX Should we avoid doing this if there is composition?
172     MaybeCreateNativeCaret(aWindow);
173   }
174 
175   if (IsTSFAvailable()) {
176     TSFTextStore::ProcessMessage(aWindow, aMessage, aWParam, aLParam, aResult);
177     if (aResult.mConsumed) {
178       return true;
179     }
180     // If we don't support IMM in TSF mode, we don't use IMMHandler.
181     if (!sIsIMMEnabled) {
182       return false;
183     }
184     // IME isn't implemented with IMM, IMMHandler shouldn't handle any
185     // messages.
186     if (!IsIMMActive()) {
187       return false;
188     }
189   }
190 
191   bool keepGoing =
192       IMMHandler::ProcessMessage(aWindow, aMessage, aWParam, aLParam, aResult);
193 
194   // If user changes active IME to an IME which is listed in our block list,
195   // we should disassociate IMC from the window for preventing the IME to work
196   // and crash.
197   if (aMessage == WM_INPUTLANGCHANGE) {
198     bool disableIME = IMMHandler::IsActiveIMEInBlockList();
199     if (disableIME != sForceDisableCurrentIMM_IME) {
200       bool enable =
201           !disableIME && WinUtils::IsIMEEnabled(aWindow->InputContextRef());
202       AssociateIMEContext(aWindow, enable);
203       sForceDisableCurrentIMM_IME = disableIME;
204     }
205   }
206 
207   return keepGoing;
208 }
209 
210 // static
IsA11yHandlingNativeCaret()211 bool IMEHandler::IsA11yHandlingNativeCaret() {
212 #ifndef ACCESSIBILITY
213   return false;
214 #else   // #ifndef ACCESSIBILITY
215   // Let's assume that when there is the service, it handles native caret.
216   return GetAccService() != nullptr;
217 #endif  // #ifndef ACCESSIBILITY #else
218 }
219 
220 // static
IsIMMActive()221 bool IMEHandler::IsIMMActive() { return TSFTextStore::IsIMM_IMEActive(); }
222 
223 // static
IsComposing()224 bool IMEHandler::IsComposing() {
225   if (IsTSFAvailable()) {
226     return TSFTextStore::IsComposing() || IMMHandler::IsComposing();
227   }
228 
229   return IMMHandler::IsComposing();
230 }
231 
232 // static
IsComposingOn(nsWindow * aWindow)233 bool IMEHandler::IsComposingOn(nsWindow* aWindow) {
234   if (IsTSFAvailable()) {
235     return TSFTextStore::IsComposingOn(aWindow) ||
236            IMMHandler::IsComposingOn(aWindow);
237   }
238 
239   return IMMHandler::IsComposingOn(aWindow);
240 }
241 
242 // static
NotifyIME(nsWindow * aWindow,const IMENotification & aIMENotification)243 nsresult IMEHandler::NotifyIME(nsWindow* aWindow,
244                                const IMENotification& aIMENotification) {
245   if (IsTSFAvailable()) {
246     switch (aIMENotification.mMessage) {
247       case NOTIFY_IME_OF_SELECTION_CHANGE: {
248         nsresult rv = TSFTextStore::OnSelectionChange(aIMENotification);
249         // If IMM IME is active, we need to notify IMMHandler of updating
250         // composition change.  It will adjust candidate window position or
251         // composition window position.
252         bool isIMMActive = IsIMMActive();
253         if (isIMMActive) {
254           IMMHandler::OnUpdateComposition(aWindow);
255         }
256         IMMHandler::OnSelectionChange(aWindow, aIMENotification, isIMMActive);
257         return rv;
258       }
259       case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
260         // If IMM IME is active, we need to notify IMMHandler of updating
261         // composition change.  It will adjust candidate window position or
262         // composition window position.
263         if (IsIMMActive()) {
264           IMMHandler::OnUpdateComposition(aWindow);
265         } else {
266           TSFTextStore::OnUpdateComposition();
267         }
268         return NS_OK;
269       case NOTIFY_IME_OF_TEXT_CHANGE:
270         return TSFTextStore::OnTextChange(aIMENotification);
271       case NOTIFY_IME_OF_FOCUS: {
272         sFocusedWindow = aWindow;
273         IMMHandler::OnFocusChange(true, aWindow);
274         nsresult rv = TSFTextStore::OnFocusChange(true, aWindow,
275                                                   aWindow->GetInputContext());
276         MaybeCreateNativeCaret(aWindow);
277         IMEHandler::MaybeShowOnScreenKeyboard(aWindow,
278                                               aWindow->GetInputContext());
279         return rv;
280       }
281       case NOTIFY_IME_OF_BLUR:
282         sFocusedWindow = nullptr;
283         IMEHandler::MaybeDismissOnScreenKeyboard(aWindow);
284         IMMHandler::OnFocusChange(false, aWindow);
285         return TSFTextStore::OnFocusChange(false, aWindow,
286                                            aWindow->GetInputContext());
287       case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
288         // If IMM IME is active, we should send a mouse button event via IMM.
289         if (IsIMMActive()) {
290           return IMMHandler::OnMouseButtonEvent(aWindow, aIMENotification);
291         }
292         return TSFTextStore::OnMouseButtonEvent(aIMENotification);
293       case REQUEST_TO_COMMIT_COMPOSITION:
294         if (TSFTextStore::IsComposingOn(aWindow)) {
295           TSFTextStore::CommitComposition(false);
296         } else if (IsIMMActive()) {
297           IMMHandler::CommitComposition(aWindow);
298         }
299         return NS_OK;
300       case REQUEST_TO_CANCEL_COMPOSITION:
301         if (TSFTextStore::IsComposingOn(aWindow)) {
302           TSFTextStore::CommitComposition(true);
303         } else if (IsIMMActive()) {
304           IMMHandler::CancelComposition(aWindow);
305         }
306         return NS_OK;
307       case NOTIFY_IME_OF_POSITION_CHANGE:
308         return TSFTextStore::OnLayoutChange();
309       default:
310         return NS_ERROR_NOT_IMPLEMENTED;
311     }
312   }
313 
314   switch (aIMENotification.mMessage) {
315     case REQUEST_TO_COMMIT_COMPOSITION:
316       IMMHandler::CommitComposition(aWindow);
317       return NS_OK;
318     case REQUEST_TO_CANCEL_COMPOSITION:
319       IMMHandler::CancelComposition(aWindow);
320       return NS_OK;
321     case NOTIFY_IME_OF_POSITION_CHANGE:
322     case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
323       IMMHandler::OnUpdateComposition(aWindow);
324       return NS_OK;
325     case NOTIFY_IME_OF_SELECTION_CHANGE:
326       IMMHandler::OnSelectionChange(aWindow, aIMENotification, true);
327       // IMMHandler::OnSelectionChange() cannot work without its singleton
328       // instance.  Therefore, IMEHandler needs to create native caret instead
329       // if it's necessary.
330       MaybeCreateNativeCaret(aWindow);
331       return NS_OK;
332     case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
333       return IMMHandler::OnMouseButtonEvent(aWindow, aIMENotification);
334     case NOTIFY_IME_OF_FOCUS:
335       sFocusedWindow = aWindow;
336       IMMHandler::OnFocusChange(true, aWindow);
337       IMEHandler::MaybeShowOnScreenKeyboard(aWindow,
338                                             aWindow->GetInputContext());
339       MaybeCreateNativeCaret(aWindow);
340       return NS_OK;
341     case NOTIFY_IME_OF_BLUR:
342       sFocusedWindow = nullptr;
343       IMEHandler::MaybeDismissOnScreenKeyboard(aWindow);
344       IMMHandler::OnFocusChange(false, aWindow);
345       // If a plugin gets focus while TSF has focus, we need to notify TSF of
346       // the blur.
347       if (TSFTextStore::ThinksHavingFocus()) {
348         return TSFTextStore::OnFocusChange(false, aWindow,
349                                            aWindow->GetInputContext());
350       }
351       return NS_OK;
352     default:
353       return NS_ERROR_NOT_IMPLEMENTED;
354   }
355 }
356 
357 // static
GetIMENotificationRequests()358 IMENotificationRequests IMEHandler::GetIMENotificationRequests() {
359   if (IsTSFAvailable()) {
360     if (!sIsIMMEnabled) {
361       return TSFTextStore::GetIMENotificationRequests();
362     }
363     // Even if TSF is available, the active IME may be an IMM-IME.
364     // Unfortunately, changing the result of GetIMENotificationRequests() while
365     // an editor has focus isn't supported by IMEContentObserver nor
366     // ContentCacheInParent.  Therefore, we need to request whole notifications
367     // which are necessary either IMMHandler or TSFTextStore.
368     return IMMHandler::GetIMENotificationRequests() |
369            TSFTextStore::GetIMENotificationRequests();
370   }
371 
372   return IMMHandler::GetIMENotificationRequests();
373 }
374 
375 // static
376 TextEventDispatcherListener*
GetNativeTextEventDispatcherListener()377 IMEHandler::GetNativeTextEventDispatcherListener() {
378   return WinTextEventDispatcherListener::GetInstance();
379 }
380 
381 // static
GetOpenState(nsWindow * aWindow)382 bool IMEHandler::GetOpenState(nsWindow* aWindow) {
383   if (IsTSFAvailable() && !IsIMMActive()) {
384     return TSFTextStore::GetIMEOpenState();
385   }
386 
387   IMEContext context(aWindow);
388   return context.GetOpenState();
389 }
390 
391 // static
OnDestroyWindow(nsWindow * aWindow)392 void IMEHandler::OnDestroyWindow(nsWindow* aWindow) {
393   // When focus is in remote process, but the window is being destroyed, we
394   // need to clean up TSFTextStore here since NOTIFY_IME_OF_BLUR won't reach
395   // here because BrowserParent already lost the reference to the nsWindow when
396   // it receives from the remote process.
397   if (sFocusedWindow == aWindow) {
398     MOZ_ASSERT(aWindow->GetInputContext().IsOriginContentProcess(),
399                "input context of focused widget should've been set by a remote "
400                "process "
401                "if IME focus isn't cleared before destroying the widget");
402     NotifyIME(aWindow, IMENotification(NOTIFY_IME_OF_BLUR));
403   }
404 
405   // We need to do nothing here for TSF. Just restore the default context
406   // if it's been disassociated.
407   if (!sIsInTSFMode) {
408     // MSDN says we need to set IS_DEFAULT to avoid memory leak when we use
409     // SetInputScopes API. Use an empty string to do this.
410     SetInputScopeForIMM32(aWindow, u""_ns, u""_ns, false);
411   }
412   AssociateIMEContext(aWindow, true);
413 }
414 
415 // static
NeedsToAssociateIMC()416 bool IMEHandler::NeedsToAssociateIMC() {
417   return !sForceDisableCurrentIMM_IME &&
418          (!sAssociateIMCOnlyWhenIMM_IMEActive || !IsIMMActive());
419 }
420 
421 // static
SetInputContext(nsWindow * aWindow,InputContext & aInputContext,const InputContextAction & aAction)422 void IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext,
423                                  const InputContextAction& aAction) {
424   sLastContextActionCause = aAction.mCause;
425   // FYI: If there is no composition, this call will do nothing.
426   NotifyIME(aWindow, IMENotification(REQUEST_TO_COMMIT_COMPOSITION));
427 
428   if (aInputContext.mHTMLInputInputmode.EqualsLiteral("none")) {
429     IMEHandler::MaybeDismissOnScreenKeyboard(aWindow, Sync::Yes);
430   } else if (aAction.UserMightRequestOpenVKB()) {
431     IMEHandler::MaybeShowOnScreenKeyboard(aWindow, aInputContext);
432   }
433 
434   bool enable = WinUtils::IsIMEEnabled(aInputContext);
435   bool adjustOpenState = (enable && aInputContext.mIMEState.mOpen !=
436                                         IMEState::DONT_CHANGE_OPEN_STATE);
437   bool open =
438       (adjustOpenState && aInputContext.mIMEState.mOpen == IMEState::OPEN);
439 
440   // Note that even while a plugin has focus, we need to notify TSF of that.
441   if (sIsInTSFMode) {
442     TSFTextStore::SetInputContext(aWindow, aInputContext, aAction);
443     if (IsTSFAvailable()) {
444       if (sIsIMMEnabled) {
445         // Associate IMC with aWindow only when it's necessary.
446         AssociateIMEContext(aWindow, enable && NeedsToAssociateIMC());
447       }
448       if (adjustOpenState) {
449         TSFTextStore::SetIMEOpenState(open);
450       }
451       return;
452     }
453   } else {
454     // Set at least InputScope even when TextStore is not available.
455     SetInputScopeForIMM32(aWindow, aInputContext.mHTMLInputType,
456                           aInputContext.mHTMLInputInputmode,
457                           aInputContext.mInPrivateBrowsing);
458   }
459 
460   AssociateIMEContext(aWindow, enable);
461 
462   IMEContext context(aWindow);
463   if (adjustOpenState) {
464     context.SetOpenState(open);
465   }
466 }
467 
468 // static
AssociateIMEContext(nsWindowBase * aWindowBase,bool aEnable)469 void IMEHandler::AssociateIMEContext(nsWindowBase* aWindowBase, bool aEnable) {
470   IMEContext context(aWindowBase);
471   if (aEnable) {
472     context.AssociateDefaultContext();
473     return;
474   }
475   // Don't disassociate the context after the window is destroyed.
476   if (aWindowBase->Destroyed()) {
477     return;
478   }
479   context.Disassociate();
480 }
481 
482 // static
InitInputContext(nsWindow * aWindow,InputContext & aInputContext)483 void IMEHandler::InitInputContext(nsWindow* aWindow,
484                                   InputContext& aInputContext) {
485   MOZ_ASSERT(aWindow);
486   MOZ_ASSERT(aWindow->GetWindowHandle(),
487              "IMEHandler::SetInputContext() requires non-nullptr HWND");
488 
489   static bool sInitialized = false;
490   if (!sInitialized) {
491     sInitialized = true;
492     // Some TIPs like QQ Input (Simplified Chinese) may need normal window
493     // (i.e., windows except message window) when initializing themselves.
494     // Therefore, we need to initialize TSF/IMM modules after first normal
495     // window is created.  InitInputContext() should be called immediately
496     // after creating each normal window, so, here is a good place to
497     // initialize these modules.
498     Initialize();
499   }
500 
501   // For a11y, the default enabled state should be 'enabled'.
502   aInputContext.mIMEState.mEnabled = IMEEnabled::Enabled;
503 
504   if (sIsInTSFMode) {
505     TSFTextStore::SetInputContext(
506         aWindow, aInputContext,
507         InputContextAction(InputContextAction::CAUSE_UNKNOWN,
508                            InputContextAction::WIDGET_CREATED));
509     // IME context isn't necessary in pure TSF mode.
510     if (!sIsIMMEnabled) {
511       AssociateIMEContext(aWindow, false);
512     }
513     return;
514   }
515 
516 #ifdef DEBUG
517   // NOTE: IMC may be null if IMM module isn't installed.
518   IMEContext context(aWindow);
519   MOZ_ASSERT(context.IsValid() || !CurrentKeyboardLayoutHasIME());
520 #endif  // #ifdef DEBUG
521 }
522 
523 #ifdef DEBUG
524 // static
CurrentKeyboardLayoutHasIME()525 bool IMEHandler::CurrentKeyboardLayoutHasIME() {
526   if (sIsInTSFMode) {
527     return TSFTextStore::CurrentKeyboardLayoutHasIME();
528   }
529 
530   return IMMHandler::IsIMEAvailable();
531 }
532 #endif  // #ifdef DEBUG
533 
534 // static
OnKeyboardLayoutChanged()535 void IMEHandler::OnKeyboardLayoutChanged() {
536   // Be aware, this method won't be called until TSFStaticSink starts to
537   // observe active TIP change.  If you need to be notified of this, you
538   // need to create TSFStaticSink::Observe() or something and call it
539   // TSFStaticSink::EnsureInitActiveTIPKeyboard() forcibly.
540 
541   if (!sIsIMMEnabled || !IsTSFAvailable()) {
542     return;
543   }
544 
545   // We don't need to do anything when sAssociateIMCOnlyWhenIMM_IMEActive is
546   // false because IMContext won't be associated/disassociated when changing
547   // active keyboard layout/IME.
548   if (!sAssociateIMCOnlyWhenIMM_IMEActive) {
549     return;
550   }
551 
552   // If there is no TSFTextStore which has focus, i.e., no editor has focus,
553   // nothing to do here.
554   nsWindowBase* windowBase = TSFTextStore::GetEnabledWindowBase();
555   if (!windowBase) {
556     return;
557   }
558 
559   // If IME isn't available, nothing to do here.
560   InputContext inputContext = windowBase->GetInputContext();
561   if (!WinUtils::IsIMEEnabled(inputContext)) {
562     return;
563   }
564 
565   // Associate or Disassociate IMC if it's necessary.
566   // Note that this does nothing if the window has already associated with or
567   // disassociated from the window.
568   AssociateIMEContext(windowBase, NeedsToAssociateIMC());
569 }
570 
571 // static
SetInputScopeForIMM32(nsWindow * aWindow,const nsAString & aHTMLInputType,const nsAString & aHTMLInputInputmode,bool aInPrivateBrowsing)572 void IMEHandler::SetInputScopeForIMM32(nsWindow* aWindow,
573                                        const nsAString& aHTMLInputType,
574                                        const nsAString& aHTMLInputInputmode,
575                                        bool aInPrivateBrowsing) {
576   if (sIsInTSFMode || !sSetInputScopes || aWindow->Destroyed()) {
577     return;
578   }
579   AutoTArray<InputScope, 3> scopes;
580 
581   // IME may refer only first input scope, but we will append inputmode's
582   // input scopes since IME may refer it like Chrome.
583   AppendInputScopeFromType(aHTMLInputType, scopes);
584   AppendInputScopeFromInputmode(aHTMLInputInputmode, scopes);
585 
586   if (aInPrivateBrowsing) {
587     scopes.AppendElement(IS_PRIVATE);
588   }
589 
590   if (scopes.IsEmpty()) {
591     // At least, 1 item is necessary.
592     scopes.AppendElement(IS_DEFAULT);
593   }
594 
595   sSetInputScopes(aWindow->GetWindowHandle(), scopes.Elements(),
596                   scopes.Length(), nullptr, 0, nullptr, nullptr);
597 }
598 
599 // static
AppendInputScopeFromInputmode(const nsAString & aInputmode,nsTArray<InputScope> & aScopes)600 void IMEHandler::AppendInputScopeFromInputmode(const nsAString& aInputmode,
601                                                nsTArray<InputScope>& aScopes) {
602   if (aInputmode.EqualsLiteral("mozAwesomebar")) {
603     // Even if Awesomebar has focus, user may not input URL directly.
604     // However, on-screen keyboard for URL should be shown because it has
605     // some useful additional keys like ".com" and they are not hindrances
606     // even when inputting non-URL text, e.g., words to search something in
607     // the web.  On the other hand, a lot of Microsoft's IMEs and Google
608     // Japanese Input make their open state "closed" automatically if we
609     // notify them of URL as the input scope.  However, this is very annoying
610     // for the users when they try to input some words to search the web or
611     // bookmark/history items.  Therefore, if they are active, we need to
612     // notify them of the default input scope for avoiding this issue.
613     // FYI: We cannot check active TIP without TSF.  Therefore, if it's
614     //      not in TSF mode, this will check only if active IMM-IME is Google
615     //      Japanese Input.  Google Japanese Input is a TIP of TSF basically.
616     //      However, if the OS is Win7 or it's installed on Win7 but has not
617     //      been updated yet even after the OS is upgraded to Win8 or later,
618     //      it's installed as IMM-IME.
619     if (TSFTextStore::ShouldSetInputScopeOfURLBarToDefault()) {
620       return;
621     }
622     // Don't append IS_SEARCH here for showing on-screen keyboard for URL.
623     if (!aScopes.Contains(IS_URL)) {
624       aScopes.AppendElement(IS_URL);
625     }
626     return;
627   }
628 
629   // https://html.spec.whatwg.org/dev/interaction.html#attr-inputmode
630   if (aInputmode.EqualsLiteral("url")) {
631     if (!aScopes.Contains(IS_SEARCH)) {
632       aScopes.AppendElement(IS_URL);
633     }
634     return;
635   }
636   if (aInputmode.EqualsLiteral("email")) {
637     if (!aScopes.Contains(IS_EMAIL_SMTPEMAILADDRESS)) {
638       aScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
639     }
640     return;
641   }
642   if (aInputmode.EqualsLiteral("tel")) {
643     if (!aScopes.Contains(IS_TELEPHONE_FULLTELEPHONENUMBER)) {
644       aScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
645     }
646     if (!aScopes.Contains(IS_TELEPHONE_LOCALNUMBER)) {
647       aScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
648     }
649     return;
650   }
651   if (aInputmode.EqualsLiteral("numeric")) {
652     if (!aScopes.Contains(IS_DIGITS)) {
653       aScopes.AppendElement(IS_DIGITS);
654     }
655     return;
656   }
657   if (aInputmode.EqualsLiteral("decimal")) {
658     if (!aScopes.Contains(IS_NUMBER)) {
659       aScopes.AppendElement(IS_NUMBER);
660     }
661     return;
662   }
663   if (aInputmode.EqualsLiteral("search")) {
664     if (!aScopes.Contains(IS_SEARCH)) {
665       aScopes.AppendElement(IS_SEARCH);
666     }
667     return;
668   }
669 }
670 
671 // static
AppendInputScopeFromType(const nsAString & aHTMLInputType,nsTArray<InputScope> & aScopes)672 void IMEHandler::AppendInputScopeFromType(const nsAString& aHTMLInputType,
673                                           nsTArray<InputScope>& aScopes) {
674   // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
675   if (aHTMLInputType.EqualsLiteral("url")) {
676     aScopes.AppendElement(IS_URL);
677     return;
678   }
679   if (aHTMLInputType.EqualsLiteral("search")) {
680     aScopes.AppendElement(IS_SEARCH);
681     return;
682   }
683   if (aHTMLInputType.EqualsLiteral("email")) {
684     aScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
685     return;
686   }
687   if (aHTMLInputType.EqualsLiteral("password")) {
688     aScopes.AppendElement(IS_PASSWORD);
689     return;
690   }
691   if (aHTMLInputType.EqualsLiteral("datetime") ||
692       aHTMLInputType.EqualsLiteral("datetime-local")) {
693     aScopes.AppendElement(IS_DATE_FULLDATE);
694     aScopes.AppendElement(IS_TIME_FULLTIME);
695     return;
696   }
697   if (aHTMLInputType.EqualsLiteral("date") ||
698       aHTMLInputType.EqualsLiteral("month") ||
699       aHTMLInputType.EqualsLiteral("week")) {
700     aScopes.AppendElement(IS_DATE_FULLDATE);
701     return;
702   }
703   if (aHTMLInputType.EqualsLiteral("time")) {
704     aScopes.AppendElement(IS_TIME_FULLTIME);
705     return;
706   }
707   if (aHTMLInputType.EqualsLiteral("tel")) {
708     aScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
709     aScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
710     return;
711   }
712   if (aHTMLInputType.EqualsLiteral("number")) {
713     aScopes.AppendElement(IS_NUMBER);
714     return;
715   }
716 }
717 
718 // static
IsOnScreenKeyboardSupported()719 bool IMEHandler::IsOnScreenKeyboardSupported() {
720 #ifdef NIGHTLY_BUILD
721   if (FxRWindowManager::GetInstance()->IsFxRWindow(sFocusedWindow)) {
722     return true;
723   }
724 #endif  // NIGHTLY_BUILD
725   if (!IsWin8OrLater() || !Preferences::GetBool(kOskEnabled, true) ||
726       !IMEHandler::NeedOnScreenKeyboard()) {
727     return false;
728   }
729 
730   // On Windows 10 we require tablet mode, unless the user has set the relevant
731   // Windows setting to enable the on-screen keyboard in desktop mode.
732   // We might be disabled specifically on Win8(.1), so we check that afterwards.
733   if (IsWin10OrLater()) {
734     if (!IsInTabletMode() && !AutoInvokeOnScreenKeyboardInDesktopMode()) {
735       return false;
736     }
737   } else if (Preferences::GetBool(kOskRequireWin10, true)) {
738     return false;
739   }
740 
741   return true;
742 }
743 
744 // static
MaybeShowOnScreenKeyboard(nsWindow * aWindow,const InputContext & aInputContext)745 void IMEHandler::MaybeShowOnScreenKeyboard(nsWindow* aWindow,
746                                            const InputContext& aInputContext) {
747   if (aInputContext.mHTMLInputInputmode.EqualsLiteral("none")) {
748     return;
749   }
750 
751   if (!IsOnScreenKeyboardSupported()) {
752     return;
753   }
754 
755   IMEHandler::ShowOnScreenKeyboard(aWindow);
756 }
757 
758 // static
MaybeDismissOnScreenKeyboard(nsWindow * aWindow,Sync aSync)759 void IMEHandler::MaybeDismissOnScreenKeyboard(nsWindow* aWindow, Sync aSync) {
760 #ifdef NIGHTLY_BUILD
761   if (FxRWindowManager::GetInstance()->IsFxRWindow(aWindow)) {
762     OSKVRManager::DismissOnScreenKeyboard();
763   }
764 #endif  // NIGHTLY_BUILD
765   if (!IsWin8OrLater()) {
766     return;
767   }
768 
769   if (aSync == Sync::Yes) {
770     DismissOnScreenKeyboard(aWindow);
771     return;
772   }
773 
774   RefPtr<nsWindow> window(aWindow);
775   NS_DispatchToCurrentThreadQueue(
776       NS_NewRunnableFunction("IMEHandler::MaybeDismissOnScreenKeyboard",
777                              [window]() {
778                                if (window->Destroyed()) {
779                                  return;
780                                }
781                                if (!sFocusedWindow) {
782                                  DismissOnScreenKeyboard(window);
783                                }
784                              }),
785       EventQueuePriority::Idle);
786 }
787 
788 // static
WStringStartsWithCaseInsensitive(const std::wstring & aHaystack,const std::wstring & aNeedle)789 bool IMEHandler::WStringStartsWithCaseInsensitive(const std::wstring& aHaystack,
790                                                   const std::wstring& aNeedle) {
791   std::wstring lowerCaseHaystack(aHaystack);
792   std::wstring lowerCaseNeedle(aNeedle);
793   std::transform(lowerCaseHaystack.begin(), lowerCaseHaystack.end(),
794                  lowerCaseHaystack.begin(), ::tolower);
795   std::transform(lowerCaseNeedle.begin(), lowerCaseNeedle.end(),
796                  lowerCaseNeedle.begin(), ::tolower);
797   return wcsstr(lowerCaseHaystack.c_str(), lowerCaseNeedle.c_str()) ==
798          lowerCaseHaystack.c_str();
799 }
800 
801 // Returns false if a physical keyboard is detected on Windows 8 and up,
802 // or there is some other reason why an onscreen keyboard is not necessary.
803 // Returns true if no keyboard is found and this device looks like it needs
804 // an on-screen keyboard for text input.
805 // static
NeedOnScreenKeyboard()806 bool IMEHandler::NeedOnScreenKeyboard() {
807   // This function is only supported for Windows 8 and up.
808   if (!IsWin8OrLater()) {
809     Preferences::SetString(kOskDebugReason, L"IKPOS: Requires Win8+.");
810     return false;
811   }
812 
813   if (!Preferences::GetBool(kOskDetectPhysicalKeyboard, true)) {
814     Preferences::SetString(kOskDebugReason, L"IKPOS: Detection disabled.");
815     return true;
816   }
817 
818   // If the last focus cause was not user-initiated (ie a result of code
819   // setting focus to an element) then don't auto-show a keyboard. This
820   // avoids cases where the keyboard would pop up "just" because e.g. a
821   // web page chooses to focus a search field on the page, even when that
822   // really isn't what the user is trying to do at that moment.
823   if (!InputContextAction::IsHandlingUserInput(sLastContextActionCause)) {
824     return false;
825   }
826 
827   // This function should be only invoked for machines with touch screens.
828   if ((::GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH) !=
829       NID_INTEGRATED_TOUCH) {
830     Preferences::SetString(kOskDebugReason, L"IKPOS: Touch screen not found.");
831     return false;
832   }
833 
834   // If the device is docked, the user is treating the device as a PC.
835   if (::GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
836     Preferences::SetString(kOskDebugReason, L"IKPOS: System docked.");
837     return false;
838   }
839 
840   // To determine whether a keyboard is present on the device, we do the
841   // following:-
842   // 1. If the platform role is that of a mobile or slate device, check the
843   //    system metric SM_CONVERTIBLESLATEMODE to see if it is being used
844   //    in slate mode. If it is, also check that the last input was a touch.
845   //    If all of this is true, then we should show the on-screen keyboard.
846 
847   // 2. If step 1 didn't determine we should show the keyboard, we check if
848   //    this device has keyboards attached to it.
849 
850   // Check if the device is being used as a laptop or a tablet. This can be
851   // checked by first checking the role of the device and then the
852   // corresponding system metric (SM_CONVERTIBLESLATEMODE). If it is being
853   // used as a tablet then we want the OSK to show up.
854   if (!sDeterminedPowerPlatformRole) {
855     sDeterminedPowerPlatformRole = true;
856     sPowerPlatformRole = WinUtils::GetPowerPlatformRole();
857   }
858 
859   // If this a mobile or slate (tablet) device, check if it is in slate mode.
860   // If the last input was touch, ignore whether or not a keyboard is present.
861   if ((sPowerPlatformRole == PlatformRoleMobile ||
862        sPowerPlatformRole == PlatformRoleSlate) &&
863       ::GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0 &&
864       sLastContextActionCause == InputContextAction::CAUSE_TOUCH) {
865     Preferences::SetString(
866         kOskDebugReason,
867         L"IKPOS: Mobile/Slate Platform role, in slate mode with touch event.");
868     return true;
869   }
870 
871   return !IMEHandler::IsKeyboardPresentOnSlate();
872 }
873 
874 // Uses the Setup APIs to enumerate the attached keyboards and returns true
875 // if the keyboard count is 1 or more. While this will work in most cases
876 // it won't work if there are devices which expose keyboard interfaces which
877 // are attached to the machine.
878 // Based on IsKeyboardPresentOnSlate() in Chromium's base/win/win_util.cc.
879 // static
IsKeyboardPresentOnSlate()880 bool IMEHandler::IsKeyboardPresentOnSlate() {
881   const GUID KEYBOARD_CLASS_GUID = {
882       0x4D36E96B,
883       0xE325,
884       0x11CE,
885       {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}};
886 
887   // Query for all the keyboard devices.
888   HDEVINFO device_info = ::SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, nullptr,
889                                                nullptr, DIGCF_PRESENT);
890   if (device_info == INVALID_HANDLE_VALUE) {
891     Preferences::SetString(kOskDebugReason, L"IKPOS: No keyboard info.");
892     return false;
893   }
894 
895   // Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
896   // the count is more than 1 we assume that a keyboard is present. This is
897   // under the assumption that there will always be one keyboard device.
898   for (DWORD i = 0;; ++i) {
899     SP_DEVINFO_DATA device_info_data = {0};
900     device_info_data.cbSize = sizeof(device_info_data);
901     if (!::SetupDiEnumDeviceInfo(device_info, i, &device_info_data)) {
902       break;
903     }
904 
905     // Get the device ID.
906     wchar_t device_id[MAX_DEVICE_ID_LEN];
907     CONFIGRET status = ::CM_Get_Device_ID(device_info_data.DevInst, device_id,
908                                           MAX_DEVICE_ID_LEN, 0);
909     if (status == CR_SUCCESS) {
910       static const std::wstring BT_HID_DEVICE = L"HID\\{00001124";
911       static const std::wstring BT_HOGP_DEVICE = L"HID\\{00001812";
912       // To reduce the scope of the hack we only look for ACPI and HID\\VID
913       // prefixes in the keyboard device ids.
914       if (IMEHandler::WStringStartsWithCaseInsensitive(device_id, L"ACPI") ||
915           IMEHandler::WStringStartsWithCaseInsensitive(device_id,
916                                                        L"HID\\VID") ||
917           IMEHandler::WStringStartsWithCaseInsensitive(device_id,
918                                                        BT_HID_DEVICE) ||
919           IMEHandler::WStringStartsWithCaseInsensitive(device_id,
920                                                        BT_HOGP_DEVICE)) {
921         // The heuristic we are using is to check the count of keyboards and
922         // return true if the API's report one or more keyboards. Please note
923         // that this will break for non keyboard devices which expose a
924         // keyboard PDO.
925         Preferences::SetString(kOskDebugReason,
926                                L"IKPOS: Keyboard presence confirmed.");
927         return true;
928       }
929     }
930   }
931   Preferences::SetString(kOskDebugReason,
932                          L"IKPOS: Lack of keyboard confirmed.");
933   return false;
934 }
935 
936 // static
IsInTabletMode()937 bool IMEHandler::IsInTabletMode() {
938   nsCOMPtr<nsIWindowsUIUtils> uiUtils(
939       do_GetService("@mozilla.org/windows-ui-utils;1"));
940   if (NS_WARN_IF(!uiUtils)) {
941     Preferences::SetString(kOskDebugReason,
942                            L"IITM: nsIWindowsUIUtils not available.");
943     return false;
944   }
945   bool isInTabletMode = false;
946   uiUtils->GetInTabletMode(&isInTabletMode);
947   if (isInTabletMode) {
948     Preferences::SetString(kOskDebugReason, L"IITM: GetInTabletMode=true.");
949   } else {
950     Preferences::SetString(kOskDebugReason, L"IITM: GetInTabletMode=false.");
951   }
952   return isInTabletMode;
953 }
954 
ReadEnableDesktopModeAutoInvoke(uint32_t aRoot,nsIWindowsRegKey * aRegKey,uint32_t & aValue)955 static bool ReadEnableDesktopModeAutoInvoke(uint32_t aRoot,
956                                             nsIWindowsRegKey* aRegKey,
957                                             uint32_t& aValue) {
958   nsresult rv;
959   rv = aRegKey->Open(aRoot, u"SOFTWARE\\Microsoft\\TabletTip\\1.7"_ns,
960                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
961   if (NS_FAILED(rv)) {
962     Preferences::SetString(kOskDebugReason,
963                            L"AIOSKIDM: failed opening regkey.");
964     return false;
965   }
966   // EnableDesktopModeAutoInvoke is an opt-in option from the Windows
967   // Settings to "Automatically show the touch keyboard in windowed apps
968   // when there's no keyboard attached to your device." If the user has
969   // opted-in to this behavior, the tablet-mode requirement is skipped.
970   rv = aRegKey->ReadIntValue(u"EnableDesktopModeAutoInvoke"_ns, &aValue);
971   if (NS_FAILED(rv)) {
972     Preferences::SetString(kOskDebugReason,
973                            L"AIOSKIDM: failed reading value of regkey.");
974     return false;
975   }
976   return true;
977 }
978 
979 // static
AutoInvokeOnScreenKeyboardInDesktopMode()980 bool IMEHandler::AutoInvokeOnScreenKeyboardInDesktopMode() {
981   nsresult rv;
982   nsCOMPtr<nsIWindowsRegKey> regKey(
983       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
984   if (NS_WARN_IF(NS_FAILED(rv))) {
985     Preferences::SetString(kOskDebugReason,
986                            L"AIOSKIDM: "
987                            L"nsIWindowsRegKey not available");
988     return false;
989   }
990 
991   uint32_t value;
992   if (!ReadEnableDesktopModeAutoInvoke(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
993                                        regKey, value) &&
994       !ReadEnableDesktopModeAutoInvoke(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
995                                        regKey, value)) {
996     return false;
997   }
998   if (!!value) {
999     Preferences::SetString(kOskDebugReason, L"AIOSKIDM: regkey value=true.");
1000   } else {
1001     Preferences::SetString(kOskDebugReason, L"AIOSKIDM: regkey value=false.");
1002   }
1003   return !!value;
1004 }
1005 
1006 // Based on DisplayVirtualKeyboard() in Chromium's base/win/win_util.cc.
1007 // static
ShowOnScreenKeyboard(nsWindow * aWindow)1008 void IMEHandler::ShowOnScreenKeyboard(nsWindow* aWindow) {
1009 #ifdef NIGHTLY_BUILD
1010   if (FxRWindowManager::GetInstance()->IsFxRWindow(sFocusedWindow)) {
1011     OSKVRManager::ShowOnScreenKeyboard();
1012     return;
1013   }
1014 #endif  // NIGHTLY_BUILD
1015 
1016   if (IsWin10AnniversaryUpdateOrLater()) {
1017     OSKInputPaneManager::ShowOnScreenKeyboard(aWindow->GetWindowHandle());
1018     return;
1019   }
1020 
1021   OSKTabTipManager::ShowOnScreenKeyboard();
1022 }
1023 
1024 // Based on DismissVirtualKeyboard() in Chromium's base/win/win_util.cc.
1025 // static
DismissOnScreenKeyboard(nsWindow * aWindow)1026 void IMEHandler::DismissOnScreenKeyboard(nsWindow* aWindow) {
1027   // Dismiss the virtual keyboard if it's open
1028   if (IsWin10AnniversaryUpdateOrLater()) {
1029     OSKInputPaneManager::DismissOnScreenKeyboard(aWindow->GetWindowHandle());
1030     return;
1031   }
1032 
1033   OSKTabTipManager::DismissOnScreenKeyboard();
1034 }
1035 
MaybeCreateNativeCaret(nsWindow * aWindow)1036 bool IMEHandler::MaybeCreateNativeCaret(nsWindow* aWindow) {
1037   MOZ_ASSERT(aWindow);
1038 
1039   if (IsA11yHandlingNativeCaret()) {
1040     return false;
1041   }
1042 
1043   if (!sHasNativeCaretBeenRequested) {
1044     // If we have not received WM_GETOBJECT for OBJID_CARET, there may be new
1045     // application which requires our caret information.  For kicking its
1046     // window event proc, we should fire a window event here.
1047     // (If there is such application, sHasNativeCaretBeenRequested will be set
1048     // to true later.)
1049     // FYI: If we create native caret and move its position, native caret
1050     //      causes EVENT_OBJECT_LOCATIONCHANGE event with OBJID_CARET and
1051     //      OBJID_CLIENT.
1052     ::NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, aWindow->GetWindowHandle(),
1053                      OBJID_CARET, OBJID_CLIENT);
1054     return false;
1055   }
1056 
1057   MaybeDestroyNativeCaret();
1058 
1059   // If focused content is not text editable, we don't support caret
1060   // caret information without a11y module.
1061   if (!aWindow->GetInputContext().mIMEState.IsEditable()) {
1062     return false;
1063   }
1064 
1065   WidgetQueryContentEvent queryCaretRectEvent(true, eQueryCaretRect, aWindow);
1066   aWindow->InitEvent(queryCaretRectEvent);
1067 
1068   WidgetQueryContentEvent::Options options;
1069   options.mRelativeToInsertionPoint = true;
1070   queryCaretRectEvent.InitForQueryCaretRect(0, options);
1071 
1072   aWindow->DispatchWindowEvent(&queryCaretRectEvent);
1073   if (NS_WARN_IF(queryCaretRectEvent.Failed())) {
1074     return false;
1075   }
1076 
1077   return CreateNativeCaret(aWindow, queryCaretRectEvent.mReply->mRect);
1078 }
1079 
CreateNativeCaret(nsWindow * aWindow,const LayoutDeviceIntRect & aCaretRect)1080 bool IMEHandler::CreateNativeCaret(nsWindow* aWindow,
1081                                    const LayoutDeviceIntRect& aCaretRect) {
1082   MOZ_ASSERT(aWindow);
1083 
1084   MOZ_ASSERT(!IsA11yHandlingNativeCaret());
1085 
1086   sNativeCaretIsCreated =
1087       ::CreateCaret(aWindow->GetWindowHandle(), nullptr, aCaretRect.Width(),
1088                     aCaretRect.Height());
1089   if (!sNativeCaretIsCreated) {
1090     return false;
1091   }
1092   nsWindow* toplevelWindow = aWindow->GetTopLevelWindow(false);
1093   if (NS_WARN_IF(!toplevelWindow)) {
1094     MaybeDestroyNativeCaret();
1095     return false;
1096   }
1097 
1098   LayoutDeviceIntPoint caretPosition(aCaretRect.TopLeft());
1099   if (toplevelWindow != aWindow) {
1100     caretPosition += toplevelWindow->WidgetToScreenOffset();
1101     caretPosition -= aWindow->WidgetToScreenOffset();
1102   }
1103 
1104   ::SetCaretPos(caretPosition.x, caretPosition.y);
1105   return true;
1106 }
1107 
MaybeDestroyNativeCaret()1108 void IMEHandler::MaybeDestroyNativeCaret() {
1109   if (!sNativeCaretIsCreated) {
1110     return;
1111   }
1112   ::DestroyCaret();
1113   sNativeCaretIsCreated = false;
1114 }
1115 
1116 }  // namespace widget
1117 }  // namespace mozilla
1118