Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/windows/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 271 kB image not shown  

Quellcode-Bibliothek TSFTextStore.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#define INPUTSCOPE_INIT_GUID
#define TEXTATTRS_INIT_GUID
#include "TSFTextStore.h"

#include "IMMHandler.h"
#include "KeyboardLayout.h"
#include "WinIMEHandler.h"
#include "WinUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Logging.h"
#include "mozilla/StaticPrefs_intl.h"
#include "mozilla/glean/WidgetWindowsMetrics.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEvents.h"
#include "mozilla/ToString.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/widget/WinRegistry.h"
#include "nsWindow.h"
#include "nsPrintfCString.h"

#include <algorithm>
#include <comutil.h>  // for _bstr_t
#include <oleauto.h>  // for SysAllocString
#include <olectl.h>

// For collecting other people's log, tell `MOZ_LOG=IMEHandler:4,sync`
// rather than `MOZ_LOG=IMEHandler:5,sync` since using `5` may create too
// big file.
// Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
mozilla::LazyLogModule gIMELog("IMEHandler");

// TODO: GUID_PROP_URL has not been declared in the SDK yet.  We should drop the
//       `s` prefix after it's released by a new SDK and define it with #if.
static const GUID sGUID_PROP_URL = {
    0xd5138268,
    0xa1bf,
    0x4308,
    {0xbc, 0xbf, 0x2e, 0x73, 0x93, 0x98, 0xe2, 0x34}};

namespace mozilla {
namespace widget {

/**
 * TSF related code should log its behavior even on release build especially
 * in the interface methods.
 *
 * In interface methods, use LogLevel::Info.
 * In internal methods, use LogLevel::Debug for logging normal behavior.
 * For logging error, use LogLevel::Error.
 *
 * When an instance method is called, start with following text:
 *   "0x%p TSFFoo::Bar(", the 0x%p should be the "this" of the nsFoo.
 * after that, start with:
 *   "0x%p   TSFFoo::Bar("
 * In an internal method, start with following text:
 *   "0x%p   TSFFoo::Bar("
 * When a static method is called, start with following text:
 *   "TSFFoo::Bar("
 */


enum class TextInputProcessorID {
  // Internal use only.  This won't be returned by TSFStaticSink::ActiveTIP().
  eNotComputed,

  // Not a TIP.  E.g., simple keyboard layout or IMM-IME.
  eNone,

  // Used for other TIPs, i.e., any TIPs which we don't support specifically.
  eUnknown,

  // TIP for Japanese.
  eMicrosoftIMEForJapanese,
  eMicrosoftOfficeIME2010ForJapanese,
  eGoogleJapaneseInput,
  eATOK2011,
  eATOK2012,
  eATOK2013,
  eATOK2014,
  eATOK2015,
  eATOK2016,
  eATOKUnknown,
  eJapanist10,

  // TIP for Traditional Chinese.
  eMicrosoftBopomofo,
  eMicrosoftChangJie,
  eMicrosoftPhonetic,
  eMicrosoftQuick,
  eMicrosoftNewChangJie,
  eMicrosoftNewPhonetic,
  eMicrosoftNewQuick,
  eFreeChangJie,

  // TIP for Simplified Chinese.
  eMicrosoftPinyin,
  eMicrosoftPinyinNewExperienceInputStyle,
  eMicrosoftWubi,

  // TIP for Korean.
  eMicrosoftIMEForKorean,
  eMicrosoftOldHangul,

  // Keyman Desktop, which can install various language keyboards.
  eKeymanDesktop,
};

static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }

static void HandleSeparator(nsCString& aDesc) {
  if (!aDesc.IsEmpty()) {
    aDesc.AppendLiteral(" | ");
  }
}

static const nsCString GetFindFlagName(DWORD aFindFlag) {
  nsCString description;
  if (!aFindFlag) {
    description.AppendLiteral("no flags (0)");
    return description;
  }
  if (aFindFlag & TS_ATTR_FIND_BACKWARDS) {
    description.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
  }
  if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) {
    HandleSeparator(description);
    description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
  }
  if (aFindFlag & TS_ATTR_FIND_UPDATESTART) {
    HandleSeparator(description);
    description.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
  }
  if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) {
    HandleSeparator(description);
    description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
  }
  if (aFindFlag & TS_ATTR_FIND_WANT_END) {
    HandleSeparator(description);
    description.AppendLiteral("TS_ATTR_FIND_WANT_END");
  }
  if (aFindFlag & TS_ATTR_FIND_HIDDEN) {
    HandleSeparator(description);
    description.AppendLiteral("TS_ATTR_FIND_HIDDEN");
  }
  if (description.IsEmpty()) {
    description.AppendLiteral("Unknown (");
    description.AppendInt(static_cast<uint32_t>(aFindFlag));
    description.Append(')');
  }
  return description;
}

class GetACPFromPointFlagName : public nsAutoCString {
 public:
  explicit GetACPFromPointFlagName(DWORD aFlags) {
    if (!aFlags) {
      AppendLiteral("no flags (0)");
      return;
    }
    if (aFlags & GXFPF_ROUND_NEAREST) {
      AppendLiteral("GXFPF_ROUND_NEAREST");
      aFlags &= ~GXFPF_ROUND_NEAREST;
    }
    if (aFlags & GXFPF_NEAREST) {
      HandleSeparator(*this);
      AppendLiteral("GXFPF_NEAREST");
      aFlags &= ~GXFPF_NEAREST;
    }
    if (aFlags) {
      HandleSeparator(*this);
      AppendLiteral("Unknown(");
      AppendInt(static_cast<uint32_t>(aFlags));
      Append(')');
    }
  }
  virtual ~GetACPFromPointFlagName() {}
};

static const char* GetFocusChangeName(
    InputContextAction::FocusChange aFocusChange) {
  switch (aFocusChange) {
    case InputContextAction::FOCUS_NOT_CHANGED:
      return "FOCUS_NOT_CHANGED";
    case InputContextAction::GOT_FOCUS:
      return "GOT_FOCUS";
    case InputContextAction::LOST_FOCUS:
      return "LOST_FOCUS";
    case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
      return "MENU_GOT_PSEUDO_FOCUS";
    case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
      return "MENU_LOST_PSEUDO_FOCUS";
    case InputContextAction::WIDGET_CREATED:
      return "WIDGET_CREATED";
    default:
      return "Unknown";
  }
}

static nsCString GetCLSIDNameStr(REFCLSID aCLSID) {
  LPOLESTR str = nullptr;
  HRESULT hr = ::StringFromCLSID(aCLSID, &str);
  if (FAILED(hr) || !str || !str[0]) {
    return ""_ns;
  }

  nsCString result;
  result = NS_ConvertUTF16toUTF8(str);
  ::CoTaskMemFree(str);
  return result;
}

static nsCString GetGUIDNameStr(REFGUID aGUID) {
  OLECHAR str[40];
  int len = ::StringFromGUID2(aGUID, str, std::size(str));
  if (!len || !str[0]) {
    return ""_ns;
  }

  return NS_ConvertUTF16toUTF8(str);
}

static nsCString GetGUIDNameStrWithTable(REFGUID aGUID) {
#define RETURN_GUID_NAME(aNamedGUID)      \
  if (IsEqualGUID(aGUID, aNamedGUID)) {   \
    return nsLiteralCString(#aNamedGUID); \
  }

  RETURN_GUID_NAME(GUID_PROP_INPUTSCOPE)
  RETURN_GUID_NAME(sGUID_PROP_URL)
  RETURN_GUID_NAME(TSATTRID_OTHERS)
  RETURN_GUID_NAME(TSATTRID_Font)
  RETURN_GUID_NAME(TSATTRID_Font_FaceName)
  RETURN_GUID_NAME(TSATTRID_Font_SizePts)
  RETURN_GUID_NAME(TSATTRID_Font_Style)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Bold)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Italic)
  RETURN_GUID_NAME(TSATTRID_Font_Style_SmallCaps)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Capitalize)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Uppercase)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Lowercase)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_LasVegasLights)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_BlinkingBackground)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_SparkleText)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingBlackAnts)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingRedAnts)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_Shimmer)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeDown)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeRight)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Emboss)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Engrave)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Hidden)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Kerning)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Outlined)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Position)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Protected)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Shadow)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Spacing)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Weight)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Height)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Underline)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Single)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Double)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Single)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Double)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Overline)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Single)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Double)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Blink)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Subscript)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Superscript)
  RETURN_GUID_NAME(TSATTRID_Font_Style_Color)
  RETURN_GUID_NAME(TSATTRID_Font_Style_BackgroundColor)
  RETURN_GUID_NAME(TSATTRID_Text)
  RETURN_GUID_NAME(TSATTRID_Text_VerticalWriting)
  RETURN_GUID_NAME(TSATTRID_Text_RightToLeft)
  RETURN_GUID_NAME(TSATTRID_Text_Orientation)
  RETURN_GUID_NAME(TSATTRID_Text_Language)
  RETURN_GUID_NAME(TSATTRID_Text_ReadOnly)
  RETURN_GUID_NAME(TSATTRID_Text_EmbeddedObject)
  RETURN_GUID_NAME(TSATTRID_Text_Alignment)
  RETURN_GUID_NAME(TSATTRID_Text_Alignment_Left)
  RETURN_GUID_NAME(TSATTRID_Text_Alignment_Right)
  RETURN_GUID_NAME(TSATTRID_Text_Alignment_Center)
  RETURN_GUID_NAME(TSATTRID_Text_Alignment_Justify)
  RETURN_GUID_NAME(TSATTRID_Text_Link)
  RETURN_GUID_NAME(TSATTRID_Text_Hyphenation)
  RETURN_GUID_NAME(TSATTRID_Text_Para)
  RETURN_GUID_NAME(TSATTRID_Text_Para_FirstLineIndent)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LeftIndent)
  RETURN_GUID_NAME(TSATTRID_Text_Para_RightIndent)
  RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceAfter)
  RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceBefore)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Single)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_OnePtFive)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Double)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_AtLeast)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Exactly)
  RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Multiple)
  RETURN_GUID_NAME(TSATTRID_List)
  RETURN_GUID_NAME(TSATTRID_List_LevelIndel)
  RETURN_GUID_NAME(TSATTRID_List_Type)
  RETURN_GUID_NAME(TSATTRID_List_Type_Bullet)
  RETURN_GUID_NAME(TSATTRID_List_Type_Arabic)
  RETURN_GUID_NAME(TSATTRID_List_Type_LowerLetter)
  RETURN_GUID_NAME(TSATTRID_List_Type_UpperLetter)
  RETURN_GUID_NAME(TSATTRID_List_Type_LowerRoman)
  RETURN_GUID_NAME(TSATTRID_List_Type_UpperRoman)
  RETURN_GUID_NAME(TSATTRID_App)
  RETURN_GUID_NAME(TSATTRID_App_IncorrectSpelling)
  RETURN_GUID_NAME(TSATTRID_App_IncorrectGrammar)

#undef RETURN_GUID_NAME

  return GetGUIDNameStr(aGUID);
}

static nsCString GetRIIDNameStr(REFIID aRIID) {
  LPOLESTR str = nullptr;
  HRESULT hr = ::StringFromIID(aRIID, &str);
  if (FAILED(hr) || !str || !str[0]) {
    return ""_ns;
  }

  nsAutoString key(L"Interface\\");
  key += str;

  nsCString result;
  wchar_t buf[256];
  if (WinRegistry::GetString(HKEY_CLASSES_ROOT, key, u""_ns, buf,
                             WinRegistry::kLegacyWinUtilsStringFlags)) {
    result = NS_ConvertUTF16toUTF8(buf);
  } else {
    result = NS_ConvertUTF16toUTF8(str);
  }

  ::CoTaskMemFree(str);
  return result;
}

static const char* GetCommonReturnValueName(HRESULT aResult) {
  switch (aResult) {
    case S_OK:
      return "S_OK";
    case E_ABORT:
      return "E_ABORT";
    case E_ACCESSDENIED:
      return "E_ACCESSDENIED";
    case E_FAIL:
      return "E_FAIL";
    case E_HANDLE:
      return "E_HANDLE";
    case E_INVALIDARG:
      return "E_INVALIDARG";
    case E_NOINTERFACE:
      return "E_NOINTERFACE";
    case E_NOTIMPL:
      return "E_NOTIMPL";
    case E_OUTOFMEMORY:
      return "E_OUTOFMEMORY";
    case E_POINTER:
      return "E_POINTER";
    case E_UNEXPECTED:
      return "E_UNEXPECTED";
    default:
      return SUCCEEDED(aResult) ? "Succeeded" : "Failed";
  }
}

static const char* GetTextStoreReturnValueName(HRESULT aResult) {
  switch (aResult) {
    case TS_E_FORMAT:
      return "TS_E_FORMAT";
    case TS_E_INVALIDPOINT:
      return "TS_E_INVALIDPOINT";
    case TS_E_INVALIDPOS:
      return "TS_E_INVALIDPOS";
    case TS_E_NOINTERFACE:
      return "TS_E_NOINTERFACE";
    case TS_E_NOLAYOUT:
      return "TS_E_NOLAYOUT";
    case TS_E_NOLOCK:
      return "TS_E_NOLOCK";
    case TS_E_NOOBJECT:
      return "TS_E_NOOBJECT";
    case TS_E_NOSELECTION:
      return "TS_E_NOSELECTION";
    case TS_E_NOSERVICE:
      return "TS_E_NOSERVICE";
    case TS_E_READONLY:
      return "TS_E_READONLY";
    case TS_E_SYNCHRONOUS:
      return "TS_E_SYNCHRONOUS";
    case TS_S_ASYNC:
      return "TS_S_ASYNC";
    default:
      return GetCommonReturnValueName(aResult);
  }
}

static const nsCString GetSinkMaskNameStr(DWORD aSinkMask) {
  nsCString description;
  if (aSinkMask & TS_AS_TEXT_CHANGE) {
    description.AppendLiteral("TS_AS_TEXT_CHANGE");
  }
  if (aSinkMask & TS_AS_SEL_CHANGE) {
    HandleSeparator(description);
    description.AppendLiteral("TS_AS_SEL_CHANGE");
  }
  if (aSinkMask & TS_AS_LAYOUT_CHANGE) {
    HandleSeparator(description);
    description.AppendLiteral("TS_AS_LAYOUT_CHANGE");
  }
  if (aSinkMask & TS_AS_ATTR_CHANGE) {
    HandleSeparator(description);
    description.AppendLiteral("TS_AS_ATTR_CHANGE");
  }
  if (aSinkMask & TS_AS_STATUS_CHANGE) {
    HandleSeparator(description);
    description.AppendLiteral("TS_AS_STATUS_CHANGE");
  }
  if (description.IsEmpty()) {
    description.AppendLiteral("not-specified");
  }
  return description;
}

static const nsCString GetLockFlagNameStr(DWORD aLockFlags) {
  nsCString description;
  if ((aLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE) {
    description.AppendLiteral("TS_LF_READWRITE");
  } else if (aLockFlags & TS_LF_READ) {
    description.AppendLiteral("TS_LF_READ");
  }
  if (aLockFlags & TS_LF_SYNC) {
    if (!description.IsEmpty()) {
      description.AppendLiteral(" | ");
    }
    description.AppendLiteral("TS_LF_SYNC");
  }
  if (description.IsEmpty()) {
    description.AppendLiteral("not-specified");
  }
  return description;
}

static const char* GetTextRunTypeName(TsRunType aRunType) {
  switch (aRunType) {
    case TS_RT_PLAIN:
      return "TS_RT_PLAIN";
    case TS_RT_HIDDEN:
      return "TS_RT_HIDDEN";
    case TS_RT_OPAQUE:
      return "TS_RT_OPAQUE";
    default:
      return "Unknown";
  }
}

static nsCString GetColorName(const TF_DA_COLOR& aColor) {
  switch (aColor.type) {
    case TF_CT_NONE:
      return "TF_CT_NONE"_ns;
    case TF_CT_SYSCOLOR:
      return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
                             static_cast<int32_t>(aColor.nIndex));
    case TF_CT_COLORREF:
      return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
                             static_cast<int32_t>(aColor.cr));
      break;
    default:
      return nsPrintfCString("Unknown(%08X)",
                             static_cast<int32_t>(aColor.type));
  }
}

static nsCString GetLineStyleName(TF_DA_LINESTYLE aLineStyle) {
  switch (aLineStyle) {
    case TF_LS_NONE:
      return "TF_LS_NONE"_ns;
    case TF_LS_SOLID:
      return "TF_LS_SOLID"_ns;
    case TF_LS_DOT:
      return "TF_LS_DOT"_ns;
    case TF_LS_DASH:
      return "TF_LS_DASH"_ns;
    case TF_LS_SQUIGGLE:
      return "TF_LS_SQUIGGLE"_ns;
    default: {
      return nsPrintfCString("Unknown(%08X)"static_cast<int32_t>(aLineStyle));
    }
  }
}

static nsCString GetClauseAttrName(TF_DA_ATTR_INFO aAttr) {
  switch (aAttr) {
    case TF_ATTR_INPUT:
      return "TF_ATTR_INPUT"_ns;
    case TF_ATTR_TARGET_CONVERTED:
      return "TF_ATTR_TARGET_CONVERTED"_ns;
    case TF_ATTR_CONVERTED:
      return "TF_ATTR_CONVERTED"_ns;
    case TF_ATTR_TARGET_NOTCONVERTED:
      return "TF_ATTR_TARGET_NOTCONVERTED"_ns;
    case TF_ATTR_INPUT_ERROR:
      return "TF_ATTR_INPUT_ERROR"_ns;
    case TF_ATTR_FIXEDCONVERTED:
      return "TF_ATTR_FIXEDCONVERTED"_ns;
    case TF_ATTR_OTHER:
      return "TF_ATTR_OTHER"_ns;
    default: {
      return nsPrintfCString("Unknown(%08X)"static_cast<int32_t>(aAttr));
    }
  }
}

static nsCString GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE& aDispAttr) {
  nsCString str;
  str = "crText:{ ";
  str += GetColorName(aDispAttr.crText);
  str += " }, crBk:{ ";
  str += GetColorName(aDispAttr.crBk);
  str += " }, lsStyle: ";
  str += GetLineStyleName(aDispAttr.lsStyle);
  str += ", fBoldLine: ";
  str += GetBoolName(aDispAttr.fBoldLine);
  str += ", crLine:{ ";
  str += GetColorName(aDispAttr.crLine);
  str += " }, bAttr: ";
  str += GetClauseAttrName(aDispAttr.bAttr);
  return str;
}

static const char* GetMouseButtonName(int16_t aButton) {
  switch (aButton) {
    case MouseButton::ePrimary:
      return "LeftButton";
    case MouseButton::eMiddle:
      return "MiddleButton";
    case MouseButton::eSecondary:
      return "RightButton";
    default:
      return "UnknownButton";
  }
}

#define ADD_SEPARATOR_IF_NECESSARY(aStr) \
  if (!aStr.IsEmpty()) {                 \
    aStr.AppendLiteral(", ");            \
  }

static nsCString GetMouseButtonsName(int16_t aButtons) {
  if (!aButtons) {
    return "no buttons"_ns;
  }
  nsCString names;
  if (aButtons & MouseButtonsFlag::ePrimaryFlag) {
    names = "LeftButton";
  }
  if (aButtons & MouseButtonsFlag::eSecondaryFlag) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += "RightButton";
  }
  if (aButtons & MouseButtonsFlag::eMiddleFlag) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += "MiddleButton";
  }
  if (aButtons & MouseButtonsFlag::e4thFlag) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += "4thButton";
  }
  if (aButtons & MouseButtonsFlag::e5thFlag) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += "5thButton";
  }
  return names;
}

static nsCString GetModifiersName(Modifiers aModifiers) {
  if (aModifiers == MODIFIER_NONE) {
    return "no modifiers"_ns;
  }
  nsCString names;
  if (aModifiers & MODIFIER_ALT) {
    names = NS_DOM_KEYNAME_ALT;
  }
  if (aModifiers & MODIFIER_ALTGRAPH) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_ALTGRAPH;
  }
  if (aModifiers & MODIFIER_CAPSLOCK) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_CAPSLOCK;
  }
  if (aModifiers & MODIFIER_CONTROL) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_CONTROL;
  }
  if (aModifiers & MODIFIER_FN) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_FN;
  }
  if (aModifiers & MODIFIER_FNLOCK) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_FNLOCK;
  }
  if (aModifiers & MODIFIER_META) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_META;
  }
  if (aModifiers & MODIFIER_NUMLOCK) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_NUMLOCK;
  }
  if (aModifiers & MODIFIER_SCROLLLOCK) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_SCROLLLOCK;
  }
  if (aModifiers & MODIFIER_SHIFT) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_SHIFT;
  }
  if (aModifiers & MODIFIER_SYMBOL) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_SYMBOL;
  }
  if (aModifiers & MODIFIER_SYMBOLLOCK) {
    ADD_SEPARATOR_IF_NECESSARY(names);
    names += NS_DOM_KEYNAME_SYMBOLLOCK;
  }
  return names;
}

class GetWritingModeName : public nsAutoCString {
 public:
  explicit GetWritingModeName(const WritingMode& aWritingMode) {
    if (!aWritingMode.IsVertical()) {
      AssignLiteral("Horizontal");
      return;
    }
    if (aWritingMode.IsVerticalLR()) {
      AssignLiteral("Vertical (LR)");
      return;
    }
    AssignLiteral("Vertical (RL)");
  }
  virtual ~GetWritingModeName() {}
};

class GetEscapedUTF8String final : public NS_ConvertUTF16toUTF8 {
 public:
  explicit GetEscapedUTF8String(const nsAString& aString)
      : NS_ConvertUTF16toUTF8(aString) {
    Escape();
  }
  explicit GetEscapedUTF8String(const char16ptr_t aString)
      : NS_ConvertUTF16toUTF8(aString) {
    Escape();
  }
  GetEscapedUTF8String(const char16ptr_t aString, uint32_t aLength)
      : NS_ConvertUTF16toUTF8(aString, aLength) {
    Escape();
  }

 private:
  void Escape() {
    ReplaceSubstring("\r""\\r");
    ReplaceSubstring("\n""\\n");
    ReplaceSubstring("\t""\\t");
  }
};

class GetInputScopeString : public nsAutoCString {
 public:
  explicit GetInputScopeString(const nsTArray<InputScope>& aList) {
    for (InputScope inputScope : aList) {
      if (!IsEmpty()) {
        AppendLiteral(", ");
      }
      switch (inputScope) {
        case IS_DEFAULT:
          AppendLiteral("IS_DEFAULT");
          break;
        case IS_URL:
          AppendLiteral("IS_URL");
          break;
        case IS_FILE_FULLFILEPATH:
          AppendLiteral("IS_FILE_FULLFILEPATH");
          break;
        case IS_FILE_FILENAME:
          AppendLiteral("IS_FILE_FILENAME");
          break;
        case IS_EMAIL_USERNAME:
          AppendLiteral("IS_EMAIL_USERNAME");
          break;
        case IS_EMAIL_SMTPEMAILADDRESS:
          AppendLiteral("IS_EMAIL_SMTPEMAILADDRESS");
          break;
        case IS_LOGINNAME:
          AppendLiteral("IS_LOGINNAME");
          break;
        case IS_PERSONALNAME_FULLNAME:
          AppendLiteral("IS_PERSONALNAME_FULLNAME");
          break;
        case IS_PERSONALNAME_PREFIX:
          AppendLiteral("IS_PERSONALNAME_PREFIX");
          break;
        case IS_PERSONALNAME_GIVENNAME:
          AppendLiteral("IS_PERSONALNAME_GIVENNAME");
          break;
        case IS_PERSONALNAME_MIDDLENAME:
          AppendLiteral("IS_PERSONALNAME_MIDDLENAME");
          break;
        case IS_PERSONALNAME_SURNAME:
          AppendLiteral("IS_PERSONALNAME_SURNAME");
          break;
        case IS_PERSONALNAME_SUFFIX:
          AppendLiteral("IS_PERSONALNAME_SUFFIX");
          break;
        case IS_ADDRESS_FULLPOSTALADDRESS:
          AppendLiteral("IS_ADDRESS_FULLPOSTALADDRESS");
          break;
        case IS_ADDRESS_POSTALCODE:
          AppendLiteral("IS_ADDRESS_POSTALCODE");
          break;
        case IS_ADDRESS_STREET:
          AppendLiteral("IS_ADDRESS_STREET");
          break;
        case IS_ADDRESS_STATEORPROVINCE:
          AppendLiteral("IS_ADDRESS_STATEORPROVINCE");
          break;
        case IS_ADDRESS_CITY:
          AppendLiteral("IS_ADDRESS_CITY");
          break;
        case IS_ADDRESS_COUNTRYNAME:
          AppendLiteral("IS_ADDRESS_COUNTRYNAME");
          break;
        case IS_ADDRESS_COUNTRYSHORTNAME:
          AppendLiteral("IS_ADDRESS_COUNTRYSHORTNAME");
          break;
        case IS_CURRENCY_AMOUNTANDSYMBOL:
          AppendLiteral("IS_CURRENCY_AMOUNTANDSYMBOL");
          break;
        case IS_CURRENCY_AMOUNT:
          AppendLiteral("IS_CURRENCY_AMOUNT");
          break;
        case IS_DATE_FULLDATE:
          AppendLiteral("IS_DATE_FULLDATE");
          break;
        case IS_DATE_MONTH:
          AppendLiteral("IS_DATE_MONTH");
          break;
        case IS_DATE_DAY:
          AppendLiteral("IS_DATE_DAY");
          break;
        case IS_DATE_YEAR:
          AppendLiteral("IS_DATE_YEAR");
          break;
        case IS_DATE_MONTHNAME:
          AppendLiteral("IS_DATE_MONTHNAME");
          break;
        case IS_DATE_DAYNAME:
          AppendLiteral("IS_DATE_DAYNAME");
          break;
        case IS_DIGITS:
          AppendLiteral("IS_DIGITS");
          break;
        case IS_NUMBER:
          AppendLiteral("IS_NUMBER");
          break;
        case IS_ONECHAR:
          AppendLiteral("IS_ONECHAR");
          break;
        case IS_PASSWORD:
          AppendLiteral("IS_PASSWORD");
          break;
        case IS_TELEPHONE_FULLTELEPHONENUMBER:
          AppendLiteral("IS_TELEPHONE_FULLTELEPHONENUMBER");
          break;
        case IS_TELEPHONE_COUNTRYCODE:
          AppendLiteral("IS_TELEPHONE_COUNTRYCODE");
          break;
        case IS_TELEPHONE_AREACODE:
          AppendLiteral("IS_TELEPHONE_AREACODE");
          break;
        case IS_TELEPHONE_LOCALNUMBER:
          AppendLiteral("IS_TELEPHONE_LOCALNUMBER");
          break;
        case IS_TIME_FULLTIME:
          AppendLiteral("IS_TIME_FULLTIME");
          break;
        case IS_TIME_HOUR:
          AppendLiteral("IS_TIME_HOUR");
          break;
        case IS_TIME_MINORSEC:
          AppendLiteral("IS_TIME_MINORSEC");
          break;
        case IS_NUMBER_FULLWIDTH:
          AppendLiteral("IS_NUMBER_FULLWIDTH");
          break;
        case IS_ALPHANUMERIC_HALFWIDTH:
          AppendLiteral("IS_ALPHANUMERIC_HALFWIDTH");
          break;
        case IS_ALPHANUMERIC_FULLWIDTH:
          AppendLiteral("IS_ALPHANUMERIC_FULLWIDTH");
          break;
        case IS_CURRENCY_CHINESE:
          AppendLiteral("IS_CURRENCY_CHINESE");
          break;
        case IS_BOPOMOFO:
          AppendLiteral("IS_BOPOMOFO");
          break;
        case IS_HIRAGANA:
          AppendLiteral("IS_HIRAGANA");
          break;
        case IS_KATAKANA_HALFWIDTH:
          AppendLiteral("IS_KATAKANA_HALFWIDTH");
          break;
        case IS_KATAKANA_FULLWIDTH:
          AppendLiteral("IS_KATAKANA_FULLWIDTH");
          break;
        case IS_HANJA:
          AppendLiteral("IS_HANJA");
          break;
        case IS_PHRASELIST:
          AppendLiteral("IS_PHRASELIST");
          break;
        case IS_REGULAREXPRESSION:
          AppendLiteral("IS_REGULAREXPRESSION");
          break;
        case IS_SRGS:
          AppendLiteral("IS_SRGS");
          break;
        case IS_XML:
          AppendLiteral("IS_XML");
          break;
        case IS_PRIVATE:
          AppendLiteral("IS_PRIVATE");
          break;
        default:
          AppendPrintf("Unknown Value(%d)", inputScope);
          break;
      }
    }
  }
};

/******************************************************************/
/* InputScopeImpl                                                 */
/******************************************************************/

class InputScopeImpl final : public ITfInputScope {
  ~InputScopeImpl() {}

 public:
  explicit InputScopeImpl(const nsTArray<InputScope>& aList)
      : mInputScopes(aList.Clone()) {
    MOZ_LOG(
        gIMELog, LogLevel::Info,
        ("0x%p InputScopeImpl(%s)"this, GetInputScopeString(aList).get()));
  }

  NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(InputScopeImpl)

  STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
    *ppv = nullptr;
    if ((IID_IUnknown == riid) || (IID_ITfInputScope == riid)) {
      *ppv = static_cast<ITfInputScope*>(this);
    }
    if (*ppv) {
      AddRef();
      return S_OK;
    }
    return E_NOINTERFACE;
  }

  STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount) {
    uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length());

    InputScope* pScope =
        (InputScope*)CoTaskMemAlloc(sizeof(InputScope) * count);
    NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY);

    if (mInputScopes.IsEmpty()) {
      *pScope = IS_DEFAULT;
      *pcCount = 1;
      *pprgInputScopes = pScope;
      return S_OK;
    }

    *pcCount = 0;

    for (uint32_t idx = 0; idx < count; idx++) {
      *(pScope + idx) = mInputScopes[idx];
      (*pcCount)++;
    }

    *pprgInputScopes = pScope;
    return S_OK;
  }

  STDMETHODIMP GetPhrase(BSTR** ppbstrPhrases, UINT* pcCount) {
    return E_NOTIMPL;
  }
  STDMETHODIMP GetRegularExpression(BSTR* pbstrRegExp) { return E_NOTIMPL; }
  STDMETHODIMP GetSRGS(BSTR* pbstrSRGS) { return E_NOTIMPL; }
  STDMETHODIMP GetXML(BSTR* pbstrXML) { return E_NOTIMPL; }

 private:
  nsTArray<InputScope> mInputScopes;
};

/******************************************************************/
/* TSFStaticSink                                                  */
/******************************************************************/

class TSFStaticSink final : public ITfInputProcessorProfileActivationSink {
 public:
  static TSFStaticSink* GetInstance() {
    if (!sInstance) {
      RefPtr<ITfThreadMgr> threadMgr = TSFTextStore::GetThreadMgr();
      if (NS_WARN_IF(!threadMgr)) {
        MOZ_LOG(
            gIMELog, LogLevel::Error,
            ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
             "instance due to no ThreadMgr instance"));
        return nullptr;
      }
      RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
          TSFTextStore::GetInputProcessorProfiles();
      if (NS_WARN_IF(!inputProcessorProfiles)) {
        MOZ_LOG(
            gIMELog, LogLevel::Error,
            ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
             "instance due to no InputProcessorProfiles instance"));
        return nullptr;
      }
      RefPtr<TSFStaticSink> staticSink = new TSFStaticSink();
      if (NS_WARN_IF(!staticSink->Init(threadMgr, inputProcessorProfiles))) {
        staticSink->Destroy();
        MOZ_LOG(
            gIMELog, LogLevel::Error,
            ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
             "instance"));
        return nullptr;
      }
      sInstance = staticSink.forget();
    }
    return sInstance;
  }

  static void Shutdown() {
    if (sInstance) {
      sInstance->Destroy();
      sInstance = nullptr;
    }
  }

  bool Init(ITfThreadMgr* aThreadMgr,
            ITfInputProcessorProfiles* aInputProcessorProfiles);
  STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
    *ppv = nullptr;
    if (IID_IUnknown == riid ||
        IID_ITfInputProcessorProfileActivationSink == riid) {
      *ppv = static_cast<ITfInputProcessorProfileActivationSink*>(this);
    }
    if (*ppv) {
      AddRef();
      return S_OK;
    }
    return E_NOINTERFACE;
  }

  NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFStaticSink)

  const nsString& GetActiveTIPKeyboardDescription() const {
    return mActiveTIPKeyboardDescription;
  }

  static bool IsIMM_IMEActive() {
    // Use IMM API until TSFStaticSink starts to work.
    if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
      return IsIMM_IME(::GetKeyboardLayout(0));
    }
    return sInstance->mIsIMM_IME;
  }

  static bool IsIMM_IME(HKL aHKL) {
    return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0);
  }

  static bool IsTraditionalChinese() {
    EnsureInstance();
    return sInstance && sInstance->IsTraditionalChineseInternal();
  }
  static bool IsSimplifiedChinese() {
    EnsureInstance();
    return sInstance && sInstance->IsSimplifiedChineseInternal();
  }
  static bool IsJapanese() {
    EnsureInstance();
    return sInstance && sInstance->IsJapaneseInternal();
  }
  static bool IsKorean() {
    EnsureInstance();
    return sInstance && sInstance->IsKoreanInternal();
  }

  /**
   * ActiveTIP() returns an ID for currently active TIP.
   * Please note that this method is expensive due to needs a lot of GUID
   * comparations if active language ID is one of CJKT.  If you need to
   * check TIPs for a specific language, you should check current language
   * first.
   */

  static TextInputProcessorID ActiveTIP() {
    EnsureInstance();
    if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
      return TextInputProcessorID::eUnknown;
    }
    sInstance->ComputeActiveTextInputProcessor();
    if (NS_WARN_IF(sInstance->mActiveTIP ==
                   TextInputProcessorID::eNotComputed)) {
      return TextInputProcessorID::eUnknown;
    }
    return sInstance->mActiveTIP;
  }

  static bool GetActiveTIPNameForTelemetry(nsAString& aName) {
    if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
      return false;
    }
    if (sInstance->mActiveTIPGUID == GUID_NULL) {
      aName.Truncate();
      aName.AppendPrintf("0x%04X", sInstance->mLangID);
      return true;
    }
    // key should be "LocaleID|Description".  Although GUID of the
    // profile is unique key since description may be localized for system
    // language, unfortunately, it's too long to record as key with its
    // description.  Therefore, we should record only the description with
    // LocaleID because Microsoft IME may not include language information.
    // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
    aName.Truncate();
    aName.AppendPrintf("0x%04X|", sInstance->mLangID);
    nsAutoString description;
    description.Assign(sInstance->mActiveTIPKeyboardDescription);
    static const uint32_t kMaxDescriptionLength = 72 - aName.Length();
    if (description.Length() > kMaxDescriptionLength) {
      if (NS_IS_LOW_SURROGATE(description[kMaxDescriptionLength - 1]) &&
          NS_IS_HIGH_SURROGATE(description[kMaxDescriptionLength - 2])) {
        description.Truncate(kMaxDescriptionLength - 2);
      } else {
        description.Truncate(kMaxDescriptionLength - 1);
      }
      // U+2026 is "..."
      description.Append(char16_t(0x2026));
    }
    aName.Append(description);
    return true;
  }

  static bool IsMSChangJieOrMSQuickActive() {
    // ActiveTIP() is expensive if it hasn't computed active TIP yet.
    // For avoiding unnecessary computation, we should check if the language
    // for current TIP is Traditional Chinese.
    if (!IsTraditionalChinese()) {
      return false;
    }
    switch (ActiveTIP()) {
      case TextInputProcessorID::eMicrosoftChangJie:
      case TextInputProcessorID::eMicrosoftQuick:
        return true;
      default:
        return false;
    }
  }

  static bool IsMSPinyinOrMSWubiActive() {
    // ActiveTIP() is expensive if it hasn't computed active TIP yet.
    // For avoiding unnecessary computation, we should check if the language
    // for current TIP is Simplified Chinese.
    if (!IsSimplifiedChinese()) {
      return false;
    }
    switch (ActiveTIP()) {
      case TextInputProcessorID::eMicrosoftPinyin:
      case TextInputProcessorID::eMicrosoftWubi:
        return true;
      default:
        return false;
    }
  }

  static bool IsMSJapaneseIMEActive() {
    // ActiveTIP() is expensive if it hasn't computed active TIP yet.
    // For avoiding unnecessary computation, we should check if the language
    // for current TIP is Japanese.
    if (!IsJapanese()) {
      return false;
    }
    return ActiveTIP() == TextInputProcessorID::eMicrosoftIMEForJapanese;
  }

  static bool IsGoogleJapaneseInputActive() {
    // ActiveTIP() is expensive if it hasn't computed active TIP yet.
    // For avoiding unnecessary computation, we should check if the language
    // for current TIP is Japanese.
    if (!IsJapanese()) {
      return false;
    }
    return ActiveTIP() == TextInputProcessorID::eGoogleJapaneseInput;
  }

  static bool IsATOKActive() {
    // ActiveTIP() is expensive if it hasn't computed active TIP yet.
    // For avoiding unnecessary computation, we should check if active TIP is
    // ATOK first since it's cheaper.
    return IsJapanese() && sInstance->IsATOKActiveInternal();
  }

  // Note that ATOK 2011 - 2016 refers native caret position for deciding its
  // popup window position.
  static bool IsATOKReferringNativeCaretActive() {
    // ActiveTIP() is expensive if it hasn't computed active TIP yet.
    // For avoiding unnecessary computation, we should check if active TIP is
    // ATOK first since it's cheaper.
    if (!IsJapanese() || !sInstance->IsATOKActiveInternal()) {
      return false;
    }
    switch (ActiveTIP()) {
      case TextInputProcessorID::eATOK2011:
      case TextInputProcessorID::eATOK2012:
      case TextInputProcessorID::eATOK2013:
      case TextInputProcessorID::eATOK2014:
      case TextInputProcessorID::eATOK2015:
        return true;
      default:
        return false;
    }
  }

 private:
  static void EnsureInstance() {
    if (!sInstance) {
      RefPtr<TSFStaticSink> staticSink = GetInstance();
      Unused << staticSink;
    }
  }

  bool IsTraditionalChineseInternal() const { return mLangID == 0x0404; }
  bool IsSimplifiedChineseInternal() const { return mLangID == 0x0804; }
  bool IsJapaneseInternal() const { return mLangID == 0x0411; }
  bool IsKoreanInternal() const { return mLangID == 0x0412; }

  bool IsATOKActiveInternal() {
    EnsureInitActiveTIPKeyboard();
    // FYI: Name of packaged ATOK includes the release year like "ATOK 2015".
    //      Name of ATOK Passport (subscription) equals "ATOK".
    return StringBeginsWith(mActiveTIPKeyboardDescription, u"ATOK "_ns) ||
           mActiveTIPKeyboardDescription.EqualsLiteral("ATOK");
  }

  void ComputeActiveTextInputProcessor() {
    if (mActiveTIP != TextInputProcessorID::eNotComputed) {
      return;
    }

    if (mActiveTIPGUID == GUID_NULL) {
      mActiveTIP = TextInputProcessorID::eNone;
      return;
    }

    // Comparing GUID is slow. So, we should use language information to
    // reduce the comparing cost for TIP which is not we do not support
    // specifically since they are always compared with all supported TIPs.
    switch (mLangID) {
      case 0x0404:
        mActiveTIP = ComputeActiveTIPAsTraditionalChinese();
        break;
      case 0x0411:
        mActiveTIP = ComputeActiveTIPAsJapanese();
        break;
      case 0x0412:
        mActiveTIP = ComputeActiveTIPAsKorean();
        break;
      case 0x0804:
        mActiveTIP = ComputeActiveTIPAsSimplifiedChinese();
        break;
      default:
        mActiveTIP = TextInputProcessorID::eUnknown;
        break;
    }
    // Special case for Keyman Desktop, it is available for any languages.
    // Therefore, we need to check it only if we don't know the active TIP.
    if (mActiveTIP != TextInputProcessorID::eUnknown) {
      return;
    }

    // Note that keyboard layouts for Keyman assign its GUID on install
    // randomly, but CLSID is constant in any environments.
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1670834#c7
    // https://github.com/keymanapp/keyman/blob/318c73a9e1d571d942837ff9964590626e5bd5aa/windows/src/engine/kmtip/globals.cpp#L37
    // {FE0420F1-38D1-4B4C-96BF-E7E20A74CFB7}
    static constexpr CLSID kKeymanDesktop_CLSID = {
        0xFE0420F1,
        0x38D1,
        0x4B4C,
        {0x96, 0xBF, 0xE7, 0xE2, 0x0A, 0x74, 0xCF, 0xB7}};
    if (mActiveTIPCLSID == kKeymanDesktop_CLSID) {
      mActiveTIP = TextInputProcessorID::eKeymanDesktop;
    }
  }

  TextInputProcessorID ComputeActiveTIPAsJapanese() {
    // {A76C93D9-5523-4E90-AAFA-4DB112F9AC76} (Win7, Win8.1, Win10)
    static constexpr GUID kMicrosoftIMEForJapaneseGUID = {
        0xA76C93D9,
        0x5523,
        0x4E90,
        {0xAA, 0xFA, 0x4D, 0xB1, 0x12, 0xF9, 0xAC, 0x76}};
    if (mActiveTIPGUID == kMicrosoftIMEForJapaneseGUID) {
      return TextInputProcessorID::eMicrosoftIMEForJapanese;
    }
    // {54EDCC94-1524-4BB1-9FB7-7BABE4F4CA64}
    static constexpr GUID kMicrosoftOfficeIME2010ForJapaneseGUID = {
        0x54EDCC94,
        0x1524,
        0x4BB1,
        {0x9F, 0xB7, 0x7B, 0xAB, 0xE4, 0xF4, 0xCA, 0x64}};
    if (mActiveTIPGUID == kMicrosoftOfficeIME2010ForJapaneseGUID) {
      return TextInputProcessorID::eMicrosoftOfficeIME2010ForJapanese;
    }
    // {773EB24E-CA1D-4B1B-B420-FA985BB0B80D}
    static constexpr GUID kGoogleJapaneseInputGUID = {
        0x773EB24E,
        0xCA1D,
        0x4B1B,
        {0xB4, 0x20, 0xFA, 0x98, 0x5B, 0xB0, 0xB8, 0x0D}};
    if (mActiveTIPGUID == kGoogleJapaneseInputGUID) {
      return TextInputProcessorID::eGoogleJapaneseInput;
    }
    // {F9C24A5C-8A53-499D-9572-93B2FF582115}
    static const GUID kATOK2011GUID = {
        0xF9C24A5C,
        0x8A53,
        0x499D,
        {0x95, 0x72, 0x93, 0xB2, 0xFF, 0x58, 0x21, 0x15}};
    if (mActiveTIPGUID == kATOK2011GUID) {
      return TextInputProcessorID::eATOK2011;
    }
    // {1DE01562-F445-401B-B6C3-E5B18DB79461}
    static constexpr GUID kATOK2012GUID = {
        0x1DE01562,
        0xF445,
        0x401B,
        {0xB6, 0xC3, 0xE5, 0xB1, 0x8D, 0xB7, 0x94, 0x61}};
    if (mActiveTIPGUID == kATOK2012GUID) {
      return TextInputProcessorID::eATOK2012;
    }
    // {3C4DB511-189A-4168-B6EA-BFD0B4C85615}
    static constexpr GUID kATOK2013GUID = {
        0x3C4DB511,
        0x189A,
        0x4168,
        {0xB6, 0xEA, 0xBF, 0xD0, 0xB4, 0xC8, 0x56, 0x15}};
    if (mActiveTIPGUID == kATOK2013GUID) {
      return TextInputProcessorID::eATOK2013;
    }
    // {4EF33B79-6AA9-4271-B4BF-9321C279381B}
    static constexpr GUID kATOK2014GUID = {
        0x4EF33B79,
        0x6AA9,
        0x4271,
        {0xB4, 0xBF, 0x93, 0x21, 0xC2, 0x79, 0x38, 0x1B}};
    if (mActiveTIPGUID == kATOK2014GUID) {
      return TextInputProcessorID::eATOK2014;
    }
    // {EAB4DC00-CE2E-483D-A86A-E6B99DA9599A}
    static constexpr GUID kATOK2015GUID = {
        0xEAB4DC00,
        0xCE2E,
        0x483D,
        {0xA8, 0x6A, 0xE6, 0xB9, 0x9D, 0xA9, 0x59, 0x9A}};
    if (mActiveTIPGUID == kATOK2015GUID) {
      return TextInputProcessorID::eATOK2015;
    }
    // {0B557B4C-5740-4110-A60A-1493FA10BF2B}
    static constexpr GUID kATOK2016GUID = {
        0x0B557B4C,
        0x5740,
        0x4110,
        {0xA6, 0x0A, 0x14, 0x93, 0xFA, 0x10, 0xBF, 0x2B}};
    if (mActiveTIPGUID == kATOK2016GUID) {
      return TextInputProcessorID::eATOK2016;
    }

    // * ATOK 2017
    //   - {6DBFD8F5-701D-11E6-920F-782BCBA6348F}
    // * ATOK Passport (confirmed with version 31.1.2)
    //   - {A38F2FD9-7199-45E1-841C-BE0313D8052F}

    if (IsATOKActiveInternal()) {
      return TextInputProcessorID::eATOKUnknown;
    }

    // {E6D66705-1EDA-4373-8D01-1D0CB2D054C7}
    static constexpr GUID kJapanist10GUID = {
        0xE6D66705,
        0x1EDA,
        0x4373,
        {0x8D, 0x01, 0x1D, 0x0C, 0xB2, 0xD0, 0x54, 0xC7}};
    if (mActiveTIPGUID == kJapanist10GUID) {
      return TextInputProcessorID::eJapanist10;
    }

    return TextInputProcessorID::eUnknown;
  }

  TextInputProcessorID ComputeActiveTIPAsTraditionalChinese() {
    // {B2F9C502-1742-11D4-9790-0080C882687E} (Win8.1, Win10)
    static constexpr GUID kMicrosoftBopomofoGUID = {
        0xB2F9C502,
        0x1742,
        0x11D4,
        {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftBopomofoGUID) {
      return TextInputProcessorID::eMicrosoftBopomofo;
    }
    // {4BDF9F03-C7D3-11D4-B2AB-0080C882687E} (Win7, Win8.1, Win10)
    static const GUID kMicrosoftChangJieGUID = {
        0x4BDF9F03,
        0xC7D3,
        0x11D4,
        {0xB2, 0xAB, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftChangJieGUID) {
      return TextInputProcessorID::eMicrosoftChangJie;
    }
    // {761309DE-317A-11D4-9B5D-0080C882687E} (Win7)
    static constexpr GUID kMicrosoftPhoneticGUID = {
        0x761309DE,
        0x317A,
        0x11D4,
        {0x9B, 0x5D, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftPhoneticGUID) {
      return TextInputProcessorID::eMicrosoftPhonetic;
    }
    // {6024B45F-5C54-11D4-B921-0080C882687E} (Win7, Win8.1, Win10)
    static constexpr GUID kMicrosoftQuickGUID = {
        0x6024B45F,
        0x5C54,
        0x11D4,
        {0xB9, 0x21, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftQuickGUID) {
      return TextInputProcessorID::eMicrosoftQuick;
    }
    // {F3BA907A-6C7E-11D4-97FA-0080C882687E} (Win7)
    static constexpr GUID kMicrosoftNewChangJieGUID = {
        0xF3BA907A,
        0x6C7E,
        0x11D4,
        {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftNewChangJieGUID) {
      return TextInputProcessorID::eMicrosoftNewChangJie;
    }
    // {B2F9C502-1742-11D4-9790-0080C882687E} (Win7)
    static constexpr GUID kMicrosoftNewPhoneticGUID = {
        0xB2F9C502,
        0x1742,
        0x11D4,
        {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftNewPhoneticGUID) {
      return TextInputProcessorID::eMicrosoftNewPhonetic;
    }
    // {0B883BA0-C1C7-11D4-87F9-0080C882687E} (Win7)
    static constexpr GUID kMicrosoftNewQuickGUID = {
        0x0B883BA0,
        0xC1C7,
        0x11D4,
        {0x87, 0xF9, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftNewQuickGUID) {
      return TextInputProcessorID::eMicrosoftNewQuick;
    }

    // NOTE: There are some other Traditional Chinese TIPs installed in Windows:
    // * Chinese Traditional Array (version 6.0)
    //   - {D38EFF65-AA46-4FD5-91A7-67845FB02F5B} (Win7, Win8.1)
    // * Chinese Traditional DaYi (version 6.0)
    //   - {037B2C25-480C-4D7F-B027-D6CA6B69788A} (Win7, Win8.1)

    // {B58630B5-0ED3-4335-BBC9-E77BBCB43CAD}
    static const GUID kFreeChangJieGUID = {
        0xB58630B5,
        0x0ED3,
        0x4335,
        {0xBB, 0xC9, 0xE7, 0x7B, 0xBC, 0xB4, 0x3C, 0xAD}};
    if (mActiveTIPGUID == kFreeChangJieGUID) {
      return TextInputProcessorID::eFreeChangJie;
    }

    return TextInputProcessorID::eUnknown;
  }

  TextInputProcessorID ComputeActiveTIPAsSimplifiedChinese() {
    // FYI: This matches with neither "Microsoft Pinyin ABC Input Style" nor
    //      "Microsoft Pinyin New Experience Input Style" on Win7.
    // {FA550B04-5AD7-411F-A5AC-CA038EC515D7} (Win8.1, Win10)
    static constexpr GUID kMicrosoftPinyinGUID = {
        0xFA550B04,
        0x5AD7,
        0x411F,
        {0xA5, 0xAC, 0xCA, 0x03, 0x8E, 0xC5, 0x15, 0xD7}};
    if (mActiveTIPGUID == kMicrosoftPinyinGUID) {
      return TextInputProcessorID::eMicrosoftPinyin;
    }

    // {F3BA9077-6C7E-11D4-97FA-0080C882687E} (Win7)
    static constexpr GUID kMicrosoftPinyinNewExperienceInputStyleGUID = {
        0xF3BA9077,
        0x6C7E,
        0x11D4,
        {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
    if (mActiveTIPGUID == kMicrosoftPinyinNewExperienceInputStyleGUID) {
      return TextInputProcessorID::eMicrosoftPinyinNewExperienceInputStyle;
    }
    // {82590C13-F4DD-44F4-BA1D-8667246FDF8E} (Win8.1, Win10)
    static constexpr GUID kMicrosoftWubiGUID = {
        0x82590C13,
        0xF4DD,
        0x44F4,
        {0xBA, 0x1D, 0x86, 0x67, 0x24, 0x6F, 0xDF, 0x8E}};
    if (mActiveTIPGUID == kMicrosoftWubiGUID) {
      return TextInputProcessorID::eMicrosoftWubi;
    }
    // NOTE: There are some other Simplified Chinese TIPs installed in Windows:
    // * Chinese Simplified QuanPin (version 6.0)
    //   - {54FC610E-6ABD-4685-9DDD-A130BDF1B170} (Win8.1)
    // * Chinese Simplified ZhengMa (version 6.0)
    //   - {733B4D81-3BC3-4132-B91A-E9CDD5E2BFC9} (Win8.1)
    // * Chinese Simplified ShuangPin (version 6.0)
    //   - {EF63706D-31C4-490E-9DBB-BD150ADC454B} (Win8.1)
    // * Microsoft Pinyin ABC Input Style
    //   - {FCA121D2-8C6D-41FB-B2DE-A2AD110D4820} (Win7)
    return TextInputProcessorID::eUnknown;
  }

  TextInputProcessorID ComputeActiveTIPAsKorean() {
    // {B5FE1F02-D5F2-4445-9C03-C568F23C99A1} (Win7, Win8.1, Win10)
    static constexpr GUID kMicrosoftIMEForKoreanGUID = {
        0xB5FE1F02,
        0xD5F2,
        0x4445,
        {0x9C, 0x03, 0xC5, 0x68, 0xF2, 0x3C, 0x99, 0xA1}};
    if (mActiveTIPGUID == kMicrosoftIMEForKoreanGUID) {
      return TextInputProcessorID::eMicrosoftIMEForKorean;
    }
    // {B60AF051-257A-46BC-B9D3-84DAD819BAFB} (Win8.1, Win10)
    static constexpr GUID kMicrosoftOldHangulGUID = {
        0xB60AF051,
        0x257A,
        0x46BC,
        {0xB9, 0xD3, 0x84, 0xDA, 0xD8, 0x19, 0xBA, 0xFB}};
    if (mActiveTIPGUID == kMicrosoftOldHangulGUID) {
      return TextInputProcessorID::eMicrosoftOldHangul;
    }

    // NOTE: There is the other Korean TIP installed in Windows:
    // * Microsoft IME 2010
    //   - {48878C45-93F9-4aaf-A6A1-272CD863C4F5} (Win7)

    return TextInputProcessorID::eUnknown;
  }

 public:  // ITfInputProcessorProfileActivationSink
  STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID, HKL,
                           DWORD);

 private:
  TSFStaticSink();
  virtual ~TSFStaticSink() {}

  bool EnsureInitActiveTIPKeyboard();

  void Destroy();

  void GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
                         REFGUID aProfile, nsAString& aDescription);
  bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
                             REFGUID aProfile);

  TextInputProcessorID mActiveTIP;

  // Cookie of installing ITfInputProcessorProfileActivationSink
  DWORD mIPProfileCookie;

  LANGID mLangID;

  // True if current IME is implemented with IMM.
  bool mIsIMM_IME;
  // True if OnActivated() is already called
  bool mOnActivatedCalled;

  RefPtr<ITfThreadMgr> mThreadMgr;
  RefPtr<ITfInputProcessorProfiles> mInputProcessorProfiles;

  // Active TIP keyboard's description.  If active language profile isn't TIP,
  // i.e., IMM-IME or just a keyboard layout, this is empty.
  nsString mActiveTIPKeyboardDescription;

  // Active TIP's GUID and CLSID
  GUID mActiveTIPGUID;
  CLSID mActiveTIPCLSID;

  static StaticRefPtr<TSFStaticSink> sInstance;
};

StaticRefPtr<TSFStaticSink> TSFStaticSink::sInstance;

TSFStaticSink::TSFStaticSink()
    : mActiveTIP(TextInputProcessorID::eNotComputed),
      mIPProfileCookie(TF_INVALID_COOKIE),
      mLangID(0),
      mIsIMM_IME(false),
      mOnActivatedCalled(false),
      mActiveTIPGUID(GUID_NULL) {}

bool TSFStaticSink::Init(ITfThreadMgr* aThreadMgr,
                         ITfInputProcessorProfiles* aInputProcessorProfiles) {
  MOZ_ASSERT(!mThreadMgr && !mInputProcessorProfiles,
             "TSFStaticSink::Init() must be called only once");

  mThreadMgr = aThreadMgr;
  mInputProcessorProfiles = aInputProcessorProfiles;

  RefPtr<ITfSource> source;
  HRESULT hr =
      mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
  if (FAILED(hr)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFStaticSink::Init() FAILED to get ITfSource "
             "instance (0x%08lX)",
             this, hr));
    return false;
  }

  // NOTE: On Vista or later, Windows let us know activate IME changed only
  //       with ITfInputProcessorProfileActivationSink.
  hr = source->AdviseSink(
      IID_ITfInputProcessorProfileActivationSink,
      static_cast<ITfInputProcessorProfileActivationSink*>(this),
      &mIPProfileCookie);
  if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFStaticSink::Init() FAILED to install "
             "ITfInputProcessorProfileActivationSink (0x%08lX)",
             this, hr));
    return false;
  }

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFStaticSink::Init(), "
           "mIPProfileCookie=0x%08lX",
           this, mIPProfileCookie));
  return true;
}

void TSFStaticSink::Destroy() {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFStaticSink::Shutdown() "
           "mIPProfileCookie=0x%08lX",
           this, mIPProfileCookie));

  if (mIPProfileCookie != TF_INVALID_COOKIE) {
    RefPtr<ITfSource> source;
    HRESULT hr =
        mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
    if (FAILED(hr)) {
      MOZ_LOG(gIMELog, LogLevel::Error,
              ("0x%p TSFStaticSink::Shutdown() FAILED to get "
               "ITfSource instance (0x%08lX)",
               this, hr));
    } else {
      hr = source->UnadviseSink(mIPProfileCookie);
      if (FAILED(hr)) {
        MOZ_LOG(gIMELog, LogLevel::Error,
                ("0x%p TSFTextStore::Shutdown() FAILED to uninstall "
                 "ITfInputProcessorProfileActivationSink (0x%08lX)",
                 this, hr));
      }
    }
  }

  mThreadMgr = nullptr;
  mInputProcessorProfiles = nullptr;
}

STDMETHODIMP
TSFStaticSink::OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID rclsid,
                           REFGUID catid, REFGUID guidProfile, HKL hkl,
                           DWORD dwFlags) {
  if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) &&
      (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ||
       catid == GUID_TFCAT_TIP_KEYBOARD)) {
    mOnActivatedCalled = true;
    mActiveTIP = TextInputProcessorID::eNotComputed;
    mActiveTIPGUID = guidProfile;
    mActiveTIPCLSID = rclsid;
    mLangID = langid & 0xFFFF;
    mIsIMM_IME = IsIMM_IME(hkl);
    GetTIPDescription(rclsid, langid, guidProfile,
                      mActiveTIPKeyboardDescription);
    if (mActiveTIPGUID != GUID_NULL) {
      // key should be "LocaleID|Description".  Although GUID of the
      // profile is unique key since description may be localized for system
      // language, unfortunately, it's too long to record as key with its
      // description.  Therefore, we should record only the description with
      // LocaleID because Microsoft IME may not include language information.
      // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
      nsAutoString key;
      TSFStaticSink::GetActiveTIPNameForTelemetry(key);
      glean::widget::ime_name_on_windows.Get(NS_ConvertUTF16toUTF8(key))
          .Set(true);
    }
    // Notify IMEHandler of changing active keyboard layout.
    IMEHandler::OnKeyboardLayoutChanged();
  }
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08lX), "
           "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%p, "
           "dwFlags=0x%08lX (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
           "mActiveTIPDescription=\"%s\"",
           this,
           dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR
               ? "TF_PROFILETYPE_INPUTPROCESSOR"
           : dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT
               ? "TF_PROFILETYPE_KEYBOARDLAYOUT"
               : "Unknown",
           dwProfileType, langid, GetCLSIDNameStr(rclsid).get(),
           GetGUIDNameStr(catid).get(), GetGUIDNameStr(guidProfile).get(), hkl,
           dwFlags, GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE),
           GetBoolName(mIsIMM_IME),
           NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
  return S_OK;
}

bool TSFStaticSink::EnsureInitActiveTIPKeyboard() {
  if (mOnActivatedCalled) {
    return true;
  }

  RefPtr<ITfInputProcessorProfileMgr> profileMgr;
  HRESULT hr = mInputProcessorProfiles->QueryInterface(
      IID_ITfInputProcessorProfileMgr, getter_AddRefs(profileMgr));
  if (FAILED(hr) || !profileMgr) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
             "to get input processor profile manager, hr=0x%08lX",
             this, hr));
    return false;
  }

  TF_INPUTPROCESSORPROFILE profile;
  hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
  if (hr == S_FALSE) {
    MOZ_LOG(gIMELog, LogLevel::Info,
            ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
             "to get active keyboard layout profile due to no active profile, "
             "hr=0x%08lX",
             this, hr));
    // XXX Should we call OnActivated() with arguments like non-TIP in this
    //     case?
    return false;
  }
  if (FAILED(hr)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
             "to get active TIP keyboard, hr=0x%08lX",
             this, hr));
    return false;
  }

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), "
           "calling OnActivated() manually...",
           this));
  OnActivated(profile.dwProfileType, profile.langid, profile.clsid,
              profile.catid, profile.guidProfile, ::GetKeyboardLayout(0),
              TF_IPSINK_FLAG_ACTIVE);
  return true;
}

void TSFStaticSink::GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
                                      REFGUID aProfile,
                                      nsAString& aDescription) {
  aDescription.Truncate();

  if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
    return;
  }

  BSTR description = nullptr;
  HRESULT hr = mInputProcessorProfiles->GetLanguageProfileDescription(
      aTextService, aLangID, aProfile, &description);
  if (FAILED(hr)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFStaticSink::InitActiveTIPDescription() FAILED "
             "due to GetLanguageProfileDescription() failure, hr=0x%08lX",
             this, hr));
    return;
  }

  if (description && description[0]) {
    aDescription.Assign(description);
  }
  ::SysFreeString(description);
}

bool TSFStaticSink::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
                                          REFGUID aProfile) {
  if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
    return false;
  }

  RefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
  HRESULT hr = mInputProcessorProfiles->EnumLanguageProfiles(
      aLangID, getter_AddRefs(enumLangProfiles));
  if (FAILED(hr) || !enumLangProfiles) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFStaticSink::IsTIPCategoryKeyboard(), FAILED "
             "to get language profiles enumerator, hr=0x%08lX",
             this, hr));
    return false;
  }

  TF_LANGUAGEPROFILE profile;
  ULONG fetch = 0;
  while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
    // XXX We're not sure a profile is registered with two or more categories.
    if (profile.clsid == aTextService && profile.guidProfile == aProfile &&
        profile.catid == GUID_TFCAT_TIP_KEYBOARD) {
      return true;
    }
  }
  return false;
}

/******************************************************************/
/* TSFTextStore                                                   */
/******************************************************************/

StaticRefPtr<ITfThreadMgr> TSFTextStore::sThreadMgr;
StaticRefPtr<ITfMessagePump> TSFTextStore::sMessagePump;
StaticRefPtr<ITfKeystrokeMgr> TSFTextStore::sKeystrokeMgr;
StaticRefPtr<ITfDisplayAttributeMgr> TSFTextStore::sDisplayAttrMgr;
StaticRefPtr<ITfCategoryMgr> TSFTextStore::sCategoryMgr;
StaticRefPtr<ITfCompartment> TSFTextStore::sCompartmentForOpenClose;
StaticRefPtr<ITfDocumentMgr> TSFTextStore::sDisabledDocumentMgr;
StaticRefPtr<ITfContext> TSFTextStore::sDisabledContext;
StaticRefPtr<ITfInputProcessorProfiles> TSFTextStore::sInputProcessorProfiles;
StaticRefPtr<TSFTextStore> TSFTextStore::sEnabledTextStore;
const MSG* TSFTextStore::sHandlingKeyMsg = nullptr;
DWORD TSFTextStore::sClientId = 0;
bool TSFTextStore::sIsKeyboardEventDispatched = false;

#define TEXTSTORE_DEFAULT_VIEW (1)

TSFTextStore::TSFTextStore()
    : mEditCookie(0),
      mSinkMask(0),
      mLock(0),
      mLockQueued(0),
      mHandlingKeyMessage(0) {
  // We hope that 5 or more actions don't occur at once.
  mPendingActions.SetCapacity(5);

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFTextStore::TSFTextStore() SUCCEEDED"this));
}

TSFTextStore::~TSFTextStore() {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFTextStore instance is destroyed"this));
}

bool TSFTextStore::Init(nsWindow* aWidget, const InputContext& aContext) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFTextStore::Init(aWidget=0x%p)"this, aWidget));

  if (NS_WARN_IF(!aWidget) || NS_WARN_IF(aWidget->Destroyed())) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED due to being initialized with "
             "destroyed widget",
             this));
    return false;
  }

  if (mDocumentMgr) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED due to already initialized",
             this));
    return false;
  }

  mWidget = aWidget;
  if (NS_WARN_IF(!mWidget)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED "
             "due to aWidget is nullptr ",
             this));
    return false;
  }
  mDispatcher = mWidget->GetTextEventDispatcher();
  if (NS_WARN_IF(!mDispatcher)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED "
             "due to aWidget->GetTextEventDispatcher() failure",
             this));
    return false;
  }

  mInPrivateBrowsing = aContext.mInPrivateBrowsing;
  SetInputScope(aContext.mHTMLInputType, aContext.mHTMLInputMode);

  if (aContext.mURI) {
    // We don't need the document URL if it fails, let's ignore the error.
    nsAutoCString spec;
    if (NS_SUCCEEDED(aContext.mURI->GetSpec(spec))) {
      CopyUTF8toUTF16(spec, mDocumentURL);
    }
  }

  // Create document manager
  RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
  RefPtr<ITfDocumentMgr> documentMgr;
  HRESULT hr = threadMgr->CreateDocumentMgr(getter_AddRefs(documentMgr));
  if (NS_WARN_IF(FAILED(hr))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr "
             "(0x%08lX)",
             this, hr));
    return false;
  }
  if (NS_WARN_IF(mDestroyed)) {
    MOZ_LOG(
        gIMELog, LogLevel::Error,
        ("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr due to "
         "TextStore being destroyed during calling "
         "ITfThreadMgr::CreateDocumentMgr()",
         this));
    return false;
  }
  // Create context and add it to document manager
  RefPtr<ITfContext> context;
  hr = documentMgr->CreateContext(sClientId, 0,
                                  static_cast<ITextStoreACP*>(this),
                                  getter_AddRefs(context), &mEditCookie);
  if (NS_WARN_IF(FAILED(hr))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED to create the context "
             "(0x%08lX)",
             this, hr));
    return false;
  }
  if (NS_WARN_IF(mDestroyed)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
             "TextStore being destroyed during calling "
             "ITfDocumentMgr::CreateContext()",
             this));
    return false;
  }

  hr = documentMgr->Push(context);
  if (NS_WARN_IF(FAILED(hr))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED to push the context (0x%08lX)",
             this, hr));
    return false;
  }
  if (NS_WARN_IF(mDestroyed)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
             "TextStore being destroyed during calling ITfDocumentMgr::Push()",
             this));
    documentMgr->Pop(TF_POPF_ALL);
    return false;
  }

  mDocumentMgr = documentMgr;
  mContext = context;

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFTextStore::Init() succeeded: "
           "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08lX",
           this, mDocumentMgr.get(), mContext.get(), mEditCookie));

  return true;
}

void TSFTextStore::Destroy() {
  if (mBeingDestroyed) {
    return;
  }

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFTextStore::Destroy(), mLock=%s, "
           "mComposition=%s, mHandlingKeyMessage=%u",
           this, GetLockFlagNameStr(mLock).get(),
           ToString(mComposition).c_str(), mHandlingKeyMessage));

  mDestroyed = true;

  // Destroy native caret first because it's not directly related to TSF and
  // there may be another textstore which gets focus.  So, we should avoid
  // to destroy caret after the new one recreates caret.
  IMEHandler::MaybeDestroyNativeCaret();

  if (mLock) {
    mPendingDestroy = true;
    return;
  }

  AutoRestore<bool> savedBeingDestroyed(mBeingDestroyed);
  mBeingDestroyed = true;

  // If there is composition, TSF keeps the composition even after the text
  // store destroyed.  So, we should clear the composition here.
  if (mComposition.isSome()) {
    CommitCompositionInternal(false);
  }

  if (mSink) {
    MOZ_LOG(gIMELog, LogLevel::Debug,
            ("0x%p TSFTextStore::Destroy(), calling "
             "ITextStoreACPSink::OnLayoutChange(TS_LC_DESTROY)...",
             this));
    RefPtr<ITextStoreACPSink> sink = mSink;
    sink->OnLayoutChange(TS_LC_DESTROY, TEXTSTORE_DEFAULT_VIEW);
  }

  // If this is called during handling a keydown or keyup message, we should
  // put off to release TSF objects until it completely finishes since
  // MS-IME for Japanese refers some objects without grabbing them.
  if (!mHandlingKeyMessage) {
    ReleaseTSFObjects();
  }

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFTextStore::Destroy() succeeded"this));
}

void TSFTextStore::ReleaseTSFObjects() {
  MOZ_ASSERT(!mHandlingKeyMessage);

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("0x%p TSFTextStore::ReleaseTSFObjects()"this));

  mDocumentURL.Truncate();
  mContext = nullptr;
  if (mDocumentMgr) {
    RefPtr<ITfDocumentMgr> documentMgr = mDocumentMgr.forget();
    documentMgr->Pop(TF_POPF_ALL);
  }
  mSink = nullptr;
  mWidget = nullptr;
  mDispatcher = nullptr;

  if (!mMouseTrackers.IsEmpty()) {
    MOZ_LOG(gIMELog, LogLevel::Debug,
            ("0x%p TSFTextStore::ReleaseTSFObjects(), "
             "removing a mouse tracker...",
             this));
    mMouseTrackers.Clear();
  }

  MOZ_LOG(gIMELog, LogLevel::Debug,
          ("0x%p TSFTextStore::ReleaseTSFObjects() completed"this));
}

STDMETHODIMP
TSFTextStore::QueryInterface(REFIID riid, void** ppv) {
  *ppv = nullptr;
  if ((IID_IUnknown == riid) || (IID_ITextStoreACP == riid)) {
    *ppv = static_cast<ITextStoreACP*>(this);
  } else if (IID_ITfContextOwnerCompositionSink == riid) {
    *ppv = static_cast<ITfContextOwnerCompositionSink*>(this);
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.71Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.