# Global Metrics

path: .metrics.cognitive.sum
old: 67.0
new: 207.0
path: .metrics.cognitive.average
old: 3.526315789473684
new: 5.447368421052632
path: .metrics.cyclomatic.sum
old: 96.0
new: 217.0
path: .metrics.cyclomatic.average
old: 3.692307692307693
new: 5.425
path: .metrics.mi.mi_original
old: -7.483102362334762
new: -47.443012290569655
path: .metrics.mi.mi_sei
old: -53.39525263954303
new: -90.00368340888042
path: .metrics.nexits.sum
old: 39.0
new: 116.0
path: .metrics.nexits.average
old: 2.0526315789473686
new: 3.0526315789473686
path: .metrics.loc.blank
old: 100.0
new: 108.0
path: .metrics.loc.ploc
old: 488.0
new: 834.0
path: .metrics.loc.sloc
old: 652.0
new: 1180.0
path: .metrics.loc.lloc
old: 213.0
new: 414.0
path: .metrics.loc.cloc
old: 64.0
new: 238.0
path: .metrics.nargs.average
old: 2.0526315789473686
new: 1.0526315789473684
path: .metrics.nargs.sum
old: 39.0
new: 40.0
path: .metrics.nom.functions
old: 19.0
new: 37.0
path: .metrics.nom.closures
old: 0.0
new: 1.0
path: .metrics.nom.total
old: 19.0
new: 38.0
path: .metrics.halstead.N1
old: 1406.0
new: 2259.0
path: .metrics.halstead.length
old: 2393.0
new: 3587.0
path: .metrics.halstead.effort
old: 875426.1889837642
new: 1495482.7858421402
path: .metrics.halstead.purity_ratio
old: 0.991709997058714
new: 1.1646288944700742
path: .metrics.halstead.n1
old: 25.0
new: 32.0
path: .metrics.halstead.bugs
old: 3.050411538800909
new: 4.359128694077723
path: .metrics.halstead.N2
old: 987.0
new: 1328.0
path: .metrics.halstead.difficulty
old: 44.3794964028777
new: 46.698901098901096
path: .metrics.halstead.n2
old: 278.0
new: 455.0
path: .metrics.halstead.vocabulary
old: 303.0
new: 487.0
path: .metrics.halstead.time
old: 48634.788276875785
new: 83082.37699123
path: .metrics.halstead.volume
old: 19725.915342450775
new: 32023.93954998936
path: .metrics.halstead.estimated_program_length
old: 2373.1620229615028
new: 4177.523844464156
path: .metrics.halstead.level
old: 0.02253292806484296
new: 0.02141378012048193

# Spaces Data

## Minimal test - lines (44, 1180)

path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 40.0
path: .spaces[0].metrics.nargs.average
old: null
new: 1.0526315789473684
path: .spaces[0].metrics.mi.mi_original
old: null
new: -46.482773494791715
path: .spaces[0].metrics.mi.mi_sei
old: null
new: -88.56527473953608
path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 0.0
path: .spaces[0].metrics.nexits.average
old: null
new: 3.0526315789473686
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 116.0
path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 1137.0
path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 804.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 101.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 232.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 414.0
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 37.0
path: .spaces[0].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 38.0
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 207.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 5.447368421052632
path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 2239.0
path: .spaces[0].metrics.halstead.time
old: 0.0
new: 84152.1535252367
path: .spaces[0].metrics.halstead.length
old: 1.0
new: 3533.0
path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 1294.0
path: .spaces[0].metrics.halstead.level
old: null
new: 0.02062403400309119
path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 32.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 3891.165394857949
path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 31240.02376328098
path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 48.48711943793911
path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 427.0
path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 4.39646788357633
path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.101377128462482
path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 459.0
path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 1514738.7634542608
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 216.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 5.538461538461538

### Code

    namespace mozilla {
    namespace widget {

    /******************************************************************************
     * IMEHandler
     ******************************************************************************/

    nsWindow* IMEHandler::sFocusedWindow = nullptr;
    InputContextAction::Cause IMEHandler::sLastContextActionCause =
        InputContextAction::CAUSE_UNKNOWN;
    bool IMEHandler::sMaybeEditable = false;
    bool IMEHandler::sForceDisableCurrentIMM_IME = false;
    bool IMEHandler::sNativeCaretIsCreated = false;
    bool IMEHandler::sHasNativeCaretBeenRequested = false;

    bool IMEHandler::sIsInTSFMode = false;
    bool IMEHandler::sIsIMMEnabled = true;
    bool IMEHandler::sAssociateIMCOnlyWhenIMM_IMEActive = false;
    decltype(SetInputScopes)* IMEHandler::sSetInputScopes = nullptr;

    static POWER_PLATFORM_ROLE sPowerPlatformRole = PlatformRoleUnspecified;
    static bool sDeterminedPowerPlatformRole = false;

    // static
    void IMEHandler::Initialize() {
      TSFTextStore::Initialize();
      sIsInTSFMode = TSFTextStore::IsInTSFMode();
      sIsIMMEnabled =
          !sIsInTSFMode || Preferences::GetBool("intl.tsf.support_imm", true);
      sAssociateIMCOnlyWhenIMM_IMEActive =
          sIsIMMEnabled &&
          Preferences::GetBool("intl.tsf.associate_imc_only_when_imm_ime_is_active",
                               false);
      if (!sIsInTSFMode) {
        // When full TSFTextStore is not available, try to use SetInputScopes API
        // to enable at least InputScope. Use GET_MODULE_HANDLE_EX_FLAG_PIN to
        // ensure that msctf.dll will not be unloaded.
        HMODULE module = nullptr;
        if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"msctf.dll",
                               &module)) {
          sSetInputScopes = reinterpret_cast(
              GetProcAddress(module, "SetInputScopes"));
        }
      }

      IMMHandler::Initialize();

      sForceDisableCurrentIMM_IME = IMMHandler::IsActiveIMEInBlockList();
    }

    // static
    void IMEHandler::Terminate() {
      if (sIsInTSFMode) {
        TSFTextStore::Terminate();
        sIsInTSFMode = false;
      }

      IMMHandler::Terminate();
      WinTextEventDispatcherListener::Shutdown();
    }

    // static
    void* IMEHandler::GetNativeData(nsWindow* aWindow, uint32_t aDataType) {
      if (aDataType == NS_RAW_NATIVE_IME_CONTEXT) {
        if (IsTSFAvailable()) {
          return TSFTextStore::GetThreadManager();
        }
        IMEContext context(aWindow);
        if (context.IsValid()) {
          return context.get();
        }
        // If IMC isn't associated with the window, IME is disabled on the window
        // now.  In such case, we should return default IMC instead.
        const IMEContext& defaultIMC = aWindow->DefaultIMC();
        if (defaultIMC.IsValid()) {
          return defaultIMC.get();
        }
        // If there is no default IMC, we should return the pointer to the window
        // since if we return nullptr, IMEStateManager cannot manage composition
        // with TextComposition instance.  This is possible if no IME is installed,
        // but composition may occur with dead key sequence.
        return aWindow;
      }

      void* result = TSFTextStore::GetNativeData(aDataType);
      if (!result || !(*(static_cast(result)))) {
        return nullptr;
      }
      // XXX During the TSF module test, sIsInTSFMode must be true.  After that,
      //     the value should be restored but currently, there is no way for that.
      //     When the TSF test is enabled again, we need to fix this.  Perhaps,
      //     sending a message can fix this.
      sIsInTSFMode = true;
      return result;
    }

    // static
    bool IMEHandler::ProcessRawKeyMessage(const MSG& aMsg) {
      if (IsTSFAvailable()) {
        return TSFTextStore::ProcessRawKeyMessage(aMsg);
      }
      return false;  // noting to do in IMM mode.
    }

    // static
    bool IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage,
                                    WPARAM& aWParam, LPARAM& aLParam,
                                    MSGResult& aResult) {
      // If we're putting native caret over our caret, Windows dispatches
      // EVENT_OBJECT_LOCATIONCHANGE event on other applications which hook
      // the event with ::SetWinEventHook() and handles WM_GETOBJECT for
      // OBJID_CARET (this is request of caret from such applications) instead
      // of us.  If a11y module is active, it observes every our caret change
      // and put native caret over it automatically.  However, if other
      // applications require only caret information, activating a11y module is
      // overwork and such applications may requires carets only in editors.
      // Therefore, if it'd be possible, IMEHandler should put native caret over
      // our caret, but there is a problem.  Some versions of ATOK (Japanese TIP)
      // refer native caret and if there is, the behavior is worse than the
      // behavior without native caret.  Therefore, we shouldn't put native caret
      // as far as possible.
      if (!sHasNativeCaretBeenRequested && aMessage == WM_GETOBJECT &&
          static_cast(aLParam) == OBJID_CARET) {
        // So, when we receive first WM_GETOBJECT for OBJID_CARET, let's start to
        // create native caret for such applications.
        sHasNativeCaretBeenRequested = true;
        // If an editable element has focus, we can put native caret now.
        // XXX Should we avoid doing this if there is composition?
        MaybeCreateNativeCaret(aWindow);
      }

      if (IsTSFAvailable()) {
        TSFTextStore::ProcessMessage(aWindow, aMessage, aWParam, aLParam, aResult);
        if (aResult.mConsumed) {
          return true;
        }
        // If we don't support IMM in TSF mode, we don't use IMMHandler.
        if (!sIsIMMEnabled) {
          return false;
        }
        // IME isn't implemented with IMM, IMMHandler shouldn't handle any
        // messages.
        if (!IsIMMActive()) {
          return false;
        }
      }

      bool keepGoing =
          IMMHandler::ProcessMessage(aWindow, aMessage, aWParam, aLParam, aResult);

      // If user changes active IME to an IME which is listed in our block list,
      // we should disassociate IMC from the window for preventing the IME to work
      // and crash.
      if (aMessage == WM_INPUTLANGCHANGE) {
        bool disableIME = IMMHandler::IsActiveIMEInBlockList();
        if (disableIME != sForceDisableCurrentIMM_IME) {
          bool enable =
              !disableIME && WinUtils::IsIMEEnabled(aWindow->InputContextRef());
          AssociateIMEContext(aWindow, enable);
          sForceDisableCurrentIMM_IME = disableIME;
        }
      }

      return keepGoing;
    }

    // static
    bool IMEHandler::IsA11yHandlingNativeCaret() {
    #ifndef ACCESSIBILITY
      return false;
    #else   // #ifndef ACCESSIBILITY
      // Let's assume that when there is the service, it handles native caret.
      return GetAccService() != nullptr;
    #endif  // #ifndef ACCESSIBILITY #else
    }

    // static
    bool IMEHandler::IsIMMActive() { return TSFTextStore::IsIMM_IMEActive(); }

    // static
    bool IMEHandler::IsComposing() {
      if (IsTSFAvailable()) {
        return TSFTextStore::IsComposing() || IMMHandler::IsComposing();
      }

      return IMMHandler::IsComposing();
    }

    // static
    bool IMEHandler::IsComposingOn(nsWindow* aWindow) {
      if (IsTSFAvailable()) {
        return TSFTextStore::IsComposingOn(aWindow) ||
               IMMHandler::IsComposingOn(aWindow);
      }

      return IMMHandler::IsComposingOn(aWindow);
    }

    // static
    nsresult IMEHandler::NotifyIME(nsWindow* aWindow,
                                   const IMENotification& aIMENotification) {
      if (IsTSFAvailable()) {
        switch (aIMENotification.mMessage) {
          case NOTIFY_IME_OF_SELECTION_CHANGE: {
            nsresult rv = TSFTextStore::OnSelectionChange(aIMENotification);
            // If IMM IME is active, we need to notify IMMHandler of updating
            // composition change.  It will adjust candidate window position or
            // composition window position.
            bool isIMMActive = IsIMMActive();
            if (isIMMActive) {
              IMMHandler::OnUpdateComposition(aWindow);
            }
            IMMHandler::OnSelectionChange(aWindow, aIMENotification, isIMMActive);
            return rv;
          }
          case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
            // If IMM IME is active, we need to notify IMMHandler of updating
            // composition change.  It will adjust candidate window position or
            // composition window position.
            if (IsIMMActive()) {
              IMMHandler::OnUpdateComposition(aWindow);
            } else {
              TSFTextStore::OnUpdateComposition();
            }
            return NS_OK;
          case NOTIFY_IME_OF_TEXT_CHANGE:
            return TSFTextStore::OnTextChange(aIMENotification);
          case NOTIFY_IME_OF_FOCUS: {
            sFocusedWindow = aWindow;
            IMMHandler::OnFocusChange(true, aWindow);
            nsresult rv = TSFTextStore::OnFocusChange(true, aWindow,
                                                      aWindow->GetInputContext());
            MaybeCreateNativeCaret(aWindow);
            IMEHandler::MaybeShowOnScreenKeyboard(aWindow,
                                                  aWindow->GetInputContext());
            return rv;
          }
          case NOTIFY_IME_OF_BLUR:
            sFocusedWindow = nullptr;
            IMEHandler::MaybeDismissOnScreenKeyboard(aWindow);
            IMMHandler::OnFocusChange(false, aWindow);
            return TSFTextStore::OnFocusChange(false, aWindow,
                                               aWindow->GetInputContext());
          case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
            // If IMM IME is active, we should send a mouse button event via IMM.
            if (IsIMMActive()) {
              return IMMHandler::OnMouseButtonEvent(aWindow, aIMENotification);
            }
            return TSFTextStore::OnMouseButtonEvent(aIMENotification);
          case REQUEST_TO_COMMIT_COMPOSITION:
            if (TSFTextStore::IsComposingOn(aWindow)) {
              TSFTextStore::CommitComposition(false);
            } else if (IsIMMActive()) {
              IMMHandler::CommitComposition(aWindow);
            }
            return NS_OK;
          case REQUEST_TO_CANCEL_COMPOSITION:
            if (TSFTextStore::IsComposingOn(aWindow)) {
              TSFTextStore::CommitComposition(true);
            } else if (IsIMMActive()) {
              IMMHandler::CancelComposition(aWindow);
            }
            return NS_OK;
          case NOTIFY_IME_OF_POSITION_CHANGE:
            return TSFTextStore::OnLayoutChange();
          default:
            return NS_ERROR_NOT_IMPLEMENTED;
        }
      }

      switch (aIMENotification.mMessage) {
        case REQUEST_TO_COMMIT_COMPOSITION:
          IMMHandler::CommitComposition(aWindow);
          return NS_OK;
        case REQUEST_TO_CANCEL_COMPOSITION:
          IMMHandler::CancelComposition(aWindow);
          return NS_OK;
        case NOTIFY_IME_OF_POSITION_CHANGE:
        case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
          IMMHandler::OnUpdateComposition(aWindow);
          return NS_OK;
        case NOTIFY_IME_OF_SELECTION_CHANGE:
          IMMHandler::OnSelectionChange(aWindow, aIMENotification, true);
          // IMMHandler::OnSelectionChange() cannot work without its singleton
          // instance.  Therefore, IMEHandler needs to create native caret instead
          // if it's necessary.
          MaybeCreateNativeCaret(aWindow);
          return NS_OK;
        case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
          return IMMHandler::OnMouseButtonEvent(aWindow, aIMENotification);
        case NOTIFY_IME_OF_FOCUS:
          sFocusedWindow = aWindow;
          IMMHandler::OnFocusChange(true, aWindow);
          IMEHandler::MaybeShowOnScreenKeyboard(aWindow,
                                                aWindow->GetInputContext());
          MaybeCreateNativeCaret(aWindow);
          return NS_OK;
        case NOTIFY_IME_OF_BLUR:
          sFocusedWindow = nullptr;
          IMEHandler::MaybeDismissOnScreenKeyboard(aWindow);
          IMMHandler::OnFocusChange(false, aWindow);
          // If a plugin gets focus while TSF has focus, we need to notify TSF of
          // the blur.
          if (TSFTextStore::ThinksHavingFocus()) {
            return TSFTextStore::OnFocusChange(false, aWindow,
                                               aWindow->GetInputContext());
          }
          return NS_OK;
        default:
          return NS_ERROR_NOT_IMPLEMENTED;
      }
    }

    // static
    IMENotificationRequests IMEHandler::GetIMENotificationRequests() {
      if (IsTSFAvailable()) {
        if (!sIsIMMEnabled) {
          return TSFTextStore::GetIMENotificationRequests();
        }
        // Even if TSF is available, the active IME may be an IMM-IME.
        // Unfortunately, changing the result of GetIMENotificationRequests() while
        // an editor has focus isn't supported by IMEContentObserver nor
        // ContentCacheInParent.  Therefore, we need to request whole notifications
        // which are necessary either IMMHandler or TSFTextStore.
        return IMMHandler::GetIMENotificationRequests() |
               TSFTextStore::GetIMENotificationRequests();
      }

      return IMMHandler::GetIMENotificationRequests();
    }

    // static
    TextEventDispatcherListener*
    IMEHandler::GetNativeTextEventDispatcherListener() {
      return WinTextEventDispatcherListener::GetInstance();
    }

    // static
    bool IMEHandler::GetOpenState(nsWindow* aWindow) {
      if (IsTSFAvailable() && !IsIMMActive()) {
        return TSFTextStore::GetIMEOpenState();
      }

      IMEContext context(aWindow);
      return context.GetOpenState();
    }

    // static
    void IMEHandler::OnDestroyWindow(nsWindow* aWindow) {
      // When focus is in remote process, but the window is being destroyed, we
      // need to clean up TSFTextStore here since NOTIFY_IME_OF_BLUR won't reach
      // here because BrowserParent already lost the reference to the nsWindow when
      // it receives from the remote process.
      if (sFocusedWindow == aWindow) {
        MOZ_ASSERT(aWindow->GetInputContext().IsOriginContentProcess(),
                   "input context of focused widget should've been set by a remote "
                   "process "
                   "if IME focus isn't cleared before destroying the widget");
        NotifyIME(aWindow, IMENotification(NOTIFY_IME_OF_BLUR));
      }

      // We need to do nothing here for TSF. Just restore the default context
      // if it's been disassociated.
      if (!sIsInTSFMode) {
        // MSDN says we need to set IS_DEFAULT to avoid memory leak when we use
        // SetInputScopes API. Use an empty string to do this.
        SetInputScopeForIMM32(aWindow, u""_ns, u""_ns, false);
      }
      AssociateIMEContext(aWindow, true);
    }

    // static
    bool IMEHandler::NeedsToAssociateIMC() {
      return !sForceDisableCurrentIMM_IME &&
             (!sAssociateIMCOnlyWhenIMM_IMEActive || !IsIMMActive());
    }

    // static
    void IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext,
                                     const InputContextAction& aAction) {
      sLastContextActionCause = aAction.mCause;
      // FYI: If there is no composition, this call will do nothing.
      NotifyIME(aWindow, IMENotification(REQUEST_TO_COMMIT_COMPOSITION));

      if (aInputContext.mHTMLInputInputmode.EqualsLiteral("none")) {
        IMEHandler::MaybeDismissOnScreenKeyboard(aWindow, Sync::Yes);
      } else if (aAction.UserMightRequestOpenVKB()) {
        IMEHandler::MaybeShowOnScreenKeyboard(aWindow, aInputContext);
      }

      bool enable = WinUtils::IsIMEEnabled(aInputContext);
      bool adjustOpenState = (enable && aInputContext.mIMEState.mOpen !=
                                            IMEState::DONT_CHANGE_OPEN_STATE);
      bool open =
          (adjustOpenState && aInputContext.mIMEState.mOpen == IMEState::OPEN);

      // Note that even while a plugin has focus, we need to notify TSF of that.
      if (sIsInTSFMode) {
        TSFTextStore::SetInputContext(aWindow, aInputContext, aAction);
        if (IsTSFAvailable()) {
          if (sIsIMMEnabled) {
            // Associate IMC with aWindow only when it's necessary.
            AssociateIMEContext(aWindow, enable && NeedsToAssociateIMC());
          }
          if (adjustOpenState) {
            TSFTextStore::SetIMEOpenState(open);
          }
          return;
        }
      } else {
        // Set at least InputScope even when TextStore is not available.
        SetInputScopeForIMM32(aWindow, aInputContext.mHTMLInputType,
                              aInputContext.mHTMLInputInputmode,
                              aInputContext.mInPrivateBrowsing);
      }

      AssociateIMEContext(aWindow, enable);

      IMEContext context(aWindow);
      if (adjustOpenState) {
        context.SetOpenState(open);
      }
    }

    // static
    void IMEHandler::AssociateIMEContext(nsWindowBase* aWindowBase, bool aEnable) {
      IMEContext context(aWindowBase);
      if (aEnable) {
        context.AssociateDefaultContext();
        return;
      }
      // Don't disassociate the context after the window is destroyed.
      if (aWindowBase->Destroyed()) {
        return;
      }
      context.Disassociate();
    }

    // static
    void IMEHandler::InitInputContext(nsWindow* aWindow,
                                      InputContext& aInputContext) {
      MOZ_ASSERT(aWindow);
      MOZ_ASSERT(aWindow->GetWindowHandle(),
                 "IMEHandler::SetInputContext() requires non-nullptr HWND");

      static bool sInitialized = false;
      if (!sInitialized) {
        sInitialized = true;
        // Some TIPs like QQ Input (Simplified Chinese) may need normal window
        // (i.e., windows except message window) when initializing themselves.
        // Therefore, we need to initialize TSF/IMM modules after first normal
        // window is created.  InitInputContext() should be called immediately
        // after creating each normal window, so, here is a good place to
        // initialize these modules.
        Initialize();
      }

      // For a11y, the default enabled state should be 'enabled'.
      aInputContext.mIMEState.mEnabled = IMEEnabled::Enabled;

      if (sIsInTSFMode) {
        TSFTextStore::SetInputContext(
            aWindow, aInputContext,
            InputContextAction(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::WIDGET_CREATED));
        // IME context isn't necessary in pure TSF mode.
        if (!sIsIMMEnabled) {
          AssociateIMEContext(aWindow, false);
        }
        return;
      }

    #ifdef DEBUG
      // NOTE: IMC may be null if IMM module isn't installed.
      IMEContext context(aWindow);
      MOZ_ASSERT(context.IsValid() || !CurrentKeyboardLayoutHasIME());
    #endif  // #ifdef DEBUG
    }

    #ifdef DEBUG
    // static
    bool IMEHandler::CurrentKeyboardLayoutHasIME() {
      if (sIsInTSFMode) {
        return TSFTextStore::CurrentKeyboardLayoutHasIME();
      }

      return IMMHandler::IsIMEAvailable();
    }
    #endif  // #ifdef DEBUG

    // static
    void IMEHandler::OnKeyboardLayoutChanged() {
      // Be aware, this method won't be called until TSFStaticSink starts to
      // observe active TIP change.  If you need to be notified of this, you
      // need to create TSFStaticSink::Observe() or something and call it
      // TSFStaticSink::EnsureInitActiveTIPKeyboard() forcibly.

      if (!sIsIMMEnabled || !IsTSFAvailable()) {
        return;
      }

      // We don't need to do anything when sAssociateIMCOnlyWhenIMM_IMEActive is
      // false because IMContext won't be associated/disassociated when changing
      // active keyboard layout/IME.
      if (!sAssociateIMCOnlyWhenIMM_IMEActive) {
        return;
      }

      // If there is no TSFTextStore which has focus, i.e., no editor has focus,
      // nothing to do here.
      nsWindowBase* windowBase = TSFTextStore::GetEnabledWindowBase();
      if (!windowBase) {
        return;
      }

      // If IME isn't available, nothing to do here.
      InputContext inputContext = windowBase->GetInputContext();
      if (!WinUtils::IsIMEEnabled(inputContext)) {
        return;
      }

      // Associate or Disassociate IMC if it's necessary.
      // Note that this does nothing if the window has already associated with or
      // disassociated from the window.
      AssociateIMEContext(windowBase, NeedsToAssociateIMC());
    }

    // static
    void IMEHandler::SetInputScopeForIMM32(nsWindow* aWindow,
                                           const nsAString& aHTMLInputType,
                                           const nsAString& aHTMLInputInputmode,
                                           bool aInPrivateBrowsing) {
      if (sIsInTSFMode || !sSetInputScopes || aWindow->Destroyed()) {
        return;
      }
      AutoTArray scopes;

      // IME may refer only first input scope, but we will append inputmode's
      // input scopes since IME may refer it like Chrome.
      AppendInputScopeFromType(aHTMLInputType, scopes);
      AppendInputScopeFromInputmode(aHTMLInputInputmode, scopes);

      if (aInPrivateBrowsing) {
        scopes.AppendElement(IS_PRIVATE);
      }

      if (scopes.IsEmpty()) {
        // At least, 1 item is necessary.
        scopes.AppendElement(IS_DEFAULT);
      }

      sSetInputScopes(aWindow->GetWindowHandle(), scopes.Elements(),
                      scopes.Length(), nullptr, 0, nullptr, nullptr);
    }

    // static
    void IMEHandler::AppendInputScopeFromInputmode(const nsAString& aInputmode,
                                                   nsTArray& aScopes) {
      if (aInputmode.EqualsLiteral("mozAwesomebar")) {
        // Even if Awesomebar has focus, user may not input URL directly.
        // However, on-screen keyboard for URL should be shown because it has
        // some useful additional keys like ".com" and they are not hindrances
        // even when inputting non-URL text, e.g., words to search something in
        // the web.  On the other hand, a lot of Microsoft's IMEs and Google
        // Japanese Input make their open state "closed" automatically if we
        // notify them of URL as the input scope.  However, this is very annoying
        // for the users when they try to input some words to search the web or
        // bookmark/history items.  Therefore, if they are active, we need to
        // notify them of the default input scope for avoiding this issue.
        // FYI: We cannot check active TIP without TSF.  Therefore, if it's
        //      not in TSF mode, this will check only if active IMM-IME is Google
        //      Japanese Input.  Google Japanese Input is a TIP of TSF basically.
        //      However, if the OS is Win7 or it's installed on Win7 but has not
        //      been updated yet even after the OS is upgraded to Win8 or later,
        //      it's installed as IMM-IME.
        if (TSFTextStore::ShouldSetInputScopeOfURLBarToDefault()) {
          return;
        }
        // Don't append IS_SEARCH here for showing on-screen keyboard for URL.
        if (!aScopes.Contains(IS_URL)) {
          aScopes.AppendElement(IS_URL);
        }
        return;
      }

      // https://html.spec.whatwg.org/dev/interaction.html#attr-inputmode
      if (aInputmode.EqualsLiteral("url")) {
        if (!aScopes.Contains(IS_SEARCH)) {
          aScopes.AppendElement(IS_URL);
        }
        return;
      }
      if (aInputmode.EqualsLiteral("email")) {
        if (!aScopes.Contains(IS_EMAIL_SMTPEMAILADDRESS)) {
          aScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
        }
        return;
      }
      if (aInputmode.EqualsLiteral("tel")) {
        if (!aScopes.Contains(IS_TELEPHONE_FULLTELEPHONENUMBER)) {
          aScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
        }
        if (!aScopes.Contains(IS_TELEPHONE_LOCALNUMBER)) {
          aScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
        }
        return;
      }
      if (aInputmode.EqualsLiteral("numeric")) {
        if (!aScopes.Contains(IS_DIGITS)) {
          aScopes.AppendElement(IS_DIGITS);
        }
        return;
      }
      if (aInputmode.EqualsLiteral("decimal")) {
        if (!aScopes.Contains(IS_NUMBER)) {
          aScopes.AppendElement(IS_NUMBER);
        }
        return;
      }
      if (aInputmode.EqualsLiteral("search")) {
        if (!aScopes.Contains(IS_SEARCH)) {
          aScopes.AppendElement(IS_SEARCH);
        }
        return;
      }
    }

    // static
    void IMEHandler::AppendInputScopeFromType(const nsAString& aHTMLInputType,
                                              nsTArray& aScopes) {
      // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
      if (aHTMLInputType.EqualsLiteral("url")) {
        aScopes.AppendElement(IS_URL);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("search")) {
        aScopes.AppendElement(IS_SEARCH);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("email")) {
        aScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("password")) {
        aScopes.AppendElement(IS_PASSWORD);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("datetime") ||
          aHTMLInputType.EqualsLiteral("datetime-local")) {
        aScopes.AppendElement(IS_DATE_FULLDATE);
        aScopes.AppendElement(IS_TIME_FULLTIME);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("date") ||
          aHTMLInputType.EqualsLiteral("month") ||
          aHTMLInputType.EqualsLiteral("week")) {
        aScopes.AppendElement(IS_DATE_FULLDATE);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("time")) {
        aScopes.AppendElement(IS_TIME_FULLTIME);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("tel")) {
        aScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
        aScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
        return;
      }
      if (aHTMLInputType.EqualsLiteral("number")) {
        aScopes.AppendElement(IS_NUMBER);
        return;
      }
    }

    // static
    void IMEHandler::MaybeShowOnScreenKeyboard(nsWindow* aWindow,
                                               const InputContext& aInputContext) {
      if (aInputContext.mHTMLInputInputmode.EqualsLiteral("none")) {
        return;
      }
    #ifdef NIGHTLY_BUILD
      if (FxRWindowManager::GetInstance()->IsFxRWindow(sFocusedWindow)) {
        mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
        shmem.SendIMEState(FxRWindowManager::GetInstance()->GetWindowID(),
                           mozilla::gfx::VRFxEventState::FOCUS);
        return;
      }
    #endif  // NIGHTLY_BUILD
      if (!IsWin8OrLater() || !Preferences::GetBool(kOskEnabled, true) ||
          GetOnScreenKeyboardWindow() || !IMEHandler::NeedOnScreenKeyboard()) {
        return;
      }

      // On Windows 10 we require tablet mode, unless the user has set the relevant
      // Windows setting to enable the on-screen keyboard in desktop mode.
      // We might be disabled specifically on Win8(.1), so we check that afterwards.
      if (IsWin10OrLater()) {
        if (!IsInTabletMode() && !AutoInvokeOnScreenKeyboardInDesktopMode()) {
          return;
        }
      } else if (Preferences::GetBool(kOskRequireWin10, true)) {
        return;
      }

      IMEHandler::ShowOnScreenKeyboard(aWindow);
    }

    // static
    void IMEHandler::MaybeDismissOnScreenKeyboard(nsWindow* aWindow, Sync aSync) {
    #ifdef NIGHTLY_BUILD
      if (FxRWindowManager::GetInstance()->IsFxRWindow(aWindow)) {
        mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
        shmem.SendIMEState(FxRWindowManager::GetInstance()->GetWindowID(),
                           mozilla::gfx::VRFxEventState::BLUR);
      }
    #endif  // NIGHTLY_BUILD
      if (!IsWin8OrLater()) {
        return;
      }

      if (aSync == Sync::Yes) {
        DismissOnScreenKeyboard(aWindow);
        return;
      }

      RefPtr window(aWindow);
      NS_DispatchToCurrentThreadQueue(
          NS_NewRunnableFunction("IMEHandler::MaybeDismissOnScreenKeyboard",
                                 [window]() {
                                   if (window->Destroyed()) {
                                     return;
                                   }
                                   if (!sFocusedWindow) {
                                     DismissOnScreenKeyboard(window);
                                   }
                                 }),
          EventQueuePriority::Idle);
    }

    // static
    bool IMEHandler::WStringStartsWithCaseInsensitive(const std::wstring& aHaystack,
                                                      const std::wstring& aNeedle) {
      std::wstring lowerCaseHaystack(aHaystack);
      std::wstring lowerCaseNeedle(aNeedle);
      std::transform(lowerCaseHaystack.begin(), lowerCaseHaystack.end(),
                     lowerCaseHaystack.begin(), ::tolower);
      std::transform(lowerCaseNeedle.begin(), lowerCaseNeedle.end(),
                     lowerCaseNeedle.begin(), ::tolower);
      return wcsstr(lowerCaseHaystack.c_str(), lowerCaseNeedle.c_str()) ==
             lowerCaseHaystack.c_str();
    }

    // Returns false if a physical keyboard is detected on Windows 8 and up,
    // or there is some other reason why an onscreen keyboard is not necessary.
    // Returns true if no keyboard is found and this device looks like it needs
    // an on-screen keyboard for text input.
    // static
    bool IMEHandler::NeedOnScreenKeyboard() {
      // This function is only supported for Windows 8 and up.
      if (!IsWin8OrLater()) {
        Preferences::SetString(kOskDebugReason, L"IKPOS: Requires Win8+.");
        return false;
      }

      if (!Preferences::GetBool(kOskDetectPhysicalKeyboard, true)) {
        Preferences::SetString(kOskDebugReason, L"IKPOS: Detection disabled.");
        return true;
      }

      // If the last focus cause was not user-initiated (ie a result of code
      // setting focus to an element) then don't auto-show a keyboard. This
      // avoids cases where the keyboard would pop up "just" because e.g. a
      // web page chooses to focus a search field on the page, even when that
      // really isn't what the user is trying to do at that moment.
      if (!InputContextAction::IsHandlingUserInput(sLastContextActionCause)) {
        return false;
      }

      // This function should be only invoked for machines with touch screens.
      if ((::GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH) !=
          NID_INTEGRATED_TOUCH) {
        Preferences::SetString(kOskDebugReason, L"IKPOS: Touch screen not found.");
        return false;
      }

      // If the device is docked, the user is treating the device as a PC.
      if (::GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
        Preferences::SetString(kOskDebugReason, L"IKPOS: System docked.");
        return false;
      }

      // To determine whether a keyboard is present on the device, we do the
      // following:-
      // 1. If the platform role is that of a mobile or slate device, check the
      //    system metric SM_CONVERTIBLESLATEMODE to see if it is being used
      //    in slate mode. If it is, also check that the last input was a touch.
      //    If all of this is true, then we should show the on-screen keyboard.

      // 2. If step 1 didn't determine we should show the keyboard, we check if
      //    this device has keyboards attached to it.

      // Check if the device is being used as a laptop or a tablet. This can be
      // checked by first checking the role of the device and then the
      // corresponding system metric (SM_CONVERTIBLESLATEMODE). If it is being
      // used as a tablet then we want the OSK to show up.
      if (!sDeterminedPowerPlatformRole) {
        sDeterminedPowerPlatformRole = true;
        sPowerPlatformRole = WinUtils::GetPowerPlatformRole();
      }

      // If this a mobile or slate (tablet) device, check if it is in slate mode.
      // If the last input was touch, ignore whether or not a keyboard is present.
      if ((sPowerPlatformRole == PlatformRoleMobile ||
           sPowerPlatformRole == PlatformRoleSlate) &&
          ::GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0 &&
          sLastContextActionCause == InputContextAction::CAUSE_TOUCH) {
        Preferences::SetString(
            kOskDebugReason,
            L"IKPOS: Mobile/Slate Platform role, in slate mode with touch event.");
        return true;
      }

      return !IMEHandler::IsKeyboardPresentOnSlate();
    }

    // Uses the Setup APIs to enumerate the attached keyboards and returns true
    // if the keyboard count is 1 or more. While this will work in most cases
    // it won't work if there are devices which expose keyboard interfaces which
    // are attached to the machine.
    // Based on IsKeyboardPresentOnSlate() in Chromium's base/win/win_util.cc.
    // static
    bool IMEHandler::IsKeyboardPresentOnSlate() {
      const GUID KEYBOARD_CLASS_GUID = {
          0x4D36E96B,
          0xE325,
          0x11CE,
          {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}};

      // Query for all the keyboard devices.
      HDEVINFO device_info = ::SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, nullptr,
                                                   nullptr, DIGCF_PRESENT);
      if (device_info == INVALID_HANDLE_VALUE) {
        Preferences::SetString(kOskDebugReason, L"IKPOS: No keyboard info.");
        return false;
      }

      // Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
      // the count is more than 1 we assume that a keyboard is present. This is
      // under the assumption that there will always be one keyboard device.
      for (DWORD i = 0;; ++i) {
        SP_DEVINFO_DATA device_info_data = {0};
        device_info_data.cbSize = sizeof(device_info_data);
        if (!::SetupDiEnumDeviceInfo(device_info, i, &device_info_data)) {
          break;
        }

        // Get the device ID.
        wchar_t device_id[MAX_DEVICE_ID_LEN];
        CONFIGRET status = ::CM_Get_Device_ID(device_info_data.DevInst, device_id,
                                              MAX_DEVICE_ID_LEN, 0);
        if (status == CR_SUCCESS) {
          static const std::wstring BT_HID_DEVICE = L"HID\\{00001124";
          static const std::wstring BT_HOGP_DEVICE = L"HID\\{00001812";
          // To reduce the scope of the hack we only look for ACPI and HID\\VID
          // prefixes in the keyboard device ids.
          if (IMEHandler::WStringStartsWithCaseInsensitive(device_id, L"ACPI") ||
              IMEHandler::WStringStartsWithCaseInsensitive(device_id,
                                                           L"HID\\VID") ||
              IMEHandler::WStringStartsWithCaseInsensitive(device_id,
                                                           BT_HID_DEVICE) ||
              IMEHandler::WStringStartsWithCaseInsensitive(device_id,
                                                           BT_HOGP_DEVICE)) {
            // The heuristic we are using is to check the count of keyboards and
            // return true if the API's report one or more keyboards. Please note
            // that this will break for non keyboard devices which expose a
            // keyboard PDO.
            Preferences::SetString(kOskDebugReason,
                                   L"IKPOS: Keyboard presence confirmed.");
            return true;
          }
        }
      }
      Preferences::SetString(kOskDebugReason,
                             L"IKPOS: Lack of keyboard confirmed.");
      return false;
    }

    // static
    bool IMEHandler::IsInTabletMode() {
      nsCOMPtr uiUtils(
          do_GetService("@mozilla.org/windows-ui-utils;1"));
      if (NS_WARN_IF(!uiUtils)) {
        Preferences::SetString(kOskDebugReason,
                               L"IITM: nsIWindowsUIUtils not available.");
        return false;
      }
      bool isInTabletMode = false;
      uiUtils->GetInTabletMode(&isInTabletMode);
      if (isInTabletMode) {
        Preferences::SetString(kOskDebugReason, L"IITM: GetInTabletMode=true.");
      } else {
        Preferences::SetString(kOskDebugReason, L"IITM: GetInTabletMode=false.");
      }
      return isInTabletMode;
    }

    static bool ReadEnableDesktopModeAutoInvoke(uint32_t aRoot,
                                                nsIWindowsRegKey* aRegKey,
                                                uint32_t& aValue) {
      nsresult rv;
      rv = aRegKey->Open(aRoot, u"SOFTWARE\\Microsoft\\TabletTip\\1.7"_ns,
                         nsIWindowsRegKey::ACCESS_QUERY_VALUE);
      if (NS_FAILED(rv)) {
        Preferences::SetString(kOskDebugReason,
                               L"AIOSKIDM: failed opening regkey.");
        return false;
      }
      // EnableDesktopModeAutoInvoke is an opt-in option from the Windows
      // Settings to "Automatically show the touch keyboard in windowed apps
      // when there's no keyboard attached to your device." If the user has
      // opted-in to this behavior, the tablet-mode requirement is skipped.
      rv = aRegKey->ReadIntValue(u"EnableDesktopModeAutoInvoke"_ns, &aValue);
      if (NS_FAILED(rv)) {
        Preferences::SetString(kOskDebugReason,
                               L"AIOSKIDM: failed reading value of regkey.");
        return false;
      }
      return true;
    }

    // static
    bool IMEHandler::AutoInvokeOnScreenKeyboardInDesktopMode() {
      nsresult rv;
      nsCOMPtr regKey(
          do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
      if (NS_WARN_IF(NS_FAILED(rv))) {
        Preferences::SetString(kOskDebugReason,
                               L"AIOSKIDM: "
                               L"nsIWindowsRegKey not available");
        return false;
      }

      uint32_t value;
      if (!ReadEnableDesktopModeAutoInvoke(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
                                           regKey, value) &&
          !ReadEnableDesktopModeAutoInvoke(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
                                           regKey, value)) {
        return false;
      }
      if (!!value) {
        Preferences::SetString(kOskDebugReason, L"AIOSKIDM: regkey value=true.");
      } else {
        Preferences::SetString(kOskDebugReason, L"AIOSKIDM: regkey value=false.");
      }
      return !!value;
    }

    // Based on DisplayVirtualKeyboard() in Chromium's base/win/win_util.cc.
    // static
    void IMEHandler::ShowOnScreenKeyboard(nsWindow* aWindow) {
      if (IsWin10AnniversaryUpdateOrLater()) {
        OSKInputPaneManager::ShowOnScreenKeyboard(aWindow->GetWindowHandle());
        return;
      }

      nsAutoString cachedPath;
      nsresult result = Preferences::GetString(kOskPathPrefName, cachedPath);
      if (NS_FAILED(result) || cachedPath.IsEmpty()) {
        wchar_t path[MAX_PATH];
        // The path to TabTip.exe is defined at the following registry key.
        // This is pulled out of the 64-bit registry hive directly.
        const wchar_t kRegKeyName[] =
            L"Software\\Classes\\CLSID\\"
            L"{054AAE20-4BEA-4347-8A35-64A533254A9D}\\LocalServer32";
        if (!WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, kRegKeyName, nullptr,
                                      path, sizeof path)) {
          return;
        }

        std::wstring wstrpath(path);
        // The path provided by the registry will often contain
        // %CommonProgramFiles%, which will need to be replaced if it is present.
        size_t commonProgramFilesOffset = wstrpath.find(L"%CommonProgramFiles%");
        if (commonProgramFilesOffset != std::wstring::npos) {
          // The path read from the registry contains the %CommonProgramFiles%
          // environment variable prefix. On 64 bit Windows the
          // SHGetKnownFolderPath function returns the common program files path
          // with the X86 suffix for the FOLDERID_ProgramFilesCommon value.
          // To get the correct path to TabTip.exe we first read the environment
          // variable CommonProgramW6432 which points to the desired common
          // files path. Failing that we fallback to the SHGetKnownFolderPath API.

          // We then replace the %CommonProgramFiles% value with the actual common
          // files path found in the process.
          std::wstring commonProgramFilesPath;
          std::vector commonProgramFilesPathW6432;
          DWORD bufferSize =
              ::GetEnvironmentVariableW(L"CommonProgramW6432", nullptr, 0);
          if (bufferSize) {
            commonProgramFilesPathW6432.resize(bufferSize);
            ::GetEnvironmentVariableW(L"CommonProgramW6432",
                                      commonProgramFilesPathW6432.data(),
                                      bufferSize);
            commonProgramFilesPath =
                std::wstring(commonProgramFilesPathW6432.data());
          } else {
            PWSTR path = nullptr;
            HRESULT hres = SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0,
                                                nullptr, &path);
            if (FAILED(hres) || !path) {
              return;
            }
            commonProgramFilesPath =
                static_cast(nsDependentString(path).get());
            ::CoTaskMemFree(path);
          }
          wstrpath.replace(commonProgramFilesOffset,
                           wcslen(L"%CommonProgramFiles%"), commonProgramFilesPath);
        }

        cachedPath.Assign(wstrpath.data());
        Preferences::SetString(kOskPathPrefName, cachedPath);
      }

      const char16_t* cachedPathPtr;
      cachedPath.GetData(&cachedPathPtr);
      ShellExecuteW(nullptr, L"", char16ptr_t(cachedPathPtr), nullptr, nullptr,
                    SW_SHOW);
    }

    // Based on DismissVirtualKeyboard() in Chromium's base/win/win_util.cc.
    // static
    void IMEHandler::DismissOnScreenKeyboard(nsWindow* aWindow) {
      // Dismiss the virtual keyboard if it's open
      if (IsWin10AnniversaryUpdateOrLater()) {
        OSKInputPaneManager::DismissOnScreenKeyboard(aWindow->GetWindowHandle());
        return;
      }

      HWND osk = GetOnScreenKeyboardWindow();
      if (osk) {
        ::PostMessage(osk, WM_SYSCOMMAND, SC_CLOSE, 0);
      }
    }

    // static
    HWND IMEHandler::GetOnScreenKeyboardWindow() {
      const wchar_t kOSKClassName[] = L"IPTip_Main_Window";
      HWND osk = ::FindWindowW(kOSKClassName, nullptr);
      if (::IsWindow(osk) && ::IsWindowEnabled(osk) && ::IsWindowVisible(osk)) {
        return osk;
      }
      return nullptr;
    }

    bool IMEHandler::MaybeCreateNativeCaret(nsWindow* aWindow) {
      MOZ_ASSERT(aWindow);

      if (IsA11yHandlingNativeCaret()) {
        return false;
      }

      if (!sHasNativeCaretBeenRequested) {
        // If we have not received WM_GETOBJECT for OBJID_CARET, there may be new
        // application which requires our caret information.  For kicking its
        // window event proc, we should fire a window event here.
        // (If there is such application, sHasNativeCaretBeenRequested will be set
        // to true later.)
        // FYI: If we create native caret and move its position, native caret
        //      causes EVENT_OBJECT_LOCATIONCHANGE event with OBJID_CARET and
        //      OBJID_CLIENT.
        ::NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, aWindow->GetWindowHandle(),
                         OBJID_CARET, OBJID_CLIENT);
        return false;
      }

      MaybeDestroyNativeCaret();

      // If focused content is not text editable, we don't support caret
      // caret information without a11y module.
      if (!aWindow->GetInputContext().mIMEState.IsEditable()) {
        return false;
      }

      WidgetQueryContentEvent queryCaretRectEvent(true, eQueryCaretRect, aWindow);
      aWindow->InitEvent(queryCaretRectEvent);

      WidgetQueryContentEvent::Options options;
      options.mRelativeToInsertionPoint = true;
      queryCaretRectEvent.InitForQueryCaretRect(0, options);

      aWindow->DispatchWindowEvent(&queryCaretRectEvent);
      if (NS_WARN_IF(queryCaretRectEvent.Failed())) {
        return false;
      }

      return CreateNativeCaret(aWindow, queryCaretRectEvent.mReply->mRect);
    }

    bool IMEHandler::CreateNativeCaret(nsWindow* aWindow,
                                       const LayoutDeviceIntRect& aCaretRect) {
      MOZ_ASSERT(aWindow);

      MOZ_ASSERT(!IsA11yHandlingNativeCaret());

      sNativeCaretIsCreated =
          ::CreateCaret(aWindow->GetWindowHandle(), nullptr, aCaretRect.Width(),
                        aCaretRect.Height());
      if (!sNativeCaretIsCreated) {
        return false;
      }
      nsWindow* toplevelWindow = aWindow->GetTopLevelWindow(false);
      if (NS_WARN_IF(!toplevelWindow)) {
        MaybeDestroyNativeCaret();
        return false;
      }

      LayoutDeviceIntPoint caretPosition(aCaretRect.TopLeft());
      if (toplevelWindow != aWindow) {
        caretPosition += toplevelWindow->WidgetToScreenOffset();
        caretPosition -= aWindow->WidgetToScreenOffset();
      }

      ::SetCaretPos(caretPosition.x, caretPosition.y);
      return true;
    }

    void IMEHandler::MaybeDestroyNativeCaret() {
      if (!sNativeCaretIsCreated) {
        return;
      }
      ::DestroyCaret();
      sNativeCaretIsCreated = false;
    }

    }  // namespace widget
    }  // namespace mozilla