widget/windows/nsTextStore.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/windows/nsTextStore.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4124 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include <olectl.h>
    1.10 +#include <algorithm>
    1.11 +
    1.12 +#ifdef MOZ_LOGGING
    1.13 +#define FORCE_PR_LOG /* Allow logging in the release build */
    1.14 +#endif // MOZ_LOGGING
    1.15 +#include "prlog.h"
    1.16 +
    1.17 +#include "nscore.h"
    1.18 +#include "nsWindow.h"
    1.19 +#ifdef MOZ_METRO
    1.20 +#include "winrt/MetroWidget.h"
    1.21 +#endif
    1.22 +#include "nsPrintfCString.h"
    1.23 +#include "WinUtils.h"
    1.24 +#include "mozilla/Preferences.h"
    1.25 +#include "mozilla/TextEvents.h"
    1.26 +#include "mozilla/WindowsVersion.h"
    1.27 +
    1.28 +#define INPUTSCOPE_INIT_GUID
    1.29 +#include "nsTextStore.h"
    1.30 +
    1.31 +using namespace mozilla;
    1.32 +using namespace mozilla::widget;
    1.33 +
    1.34 +static const char* kPrefNameTSFEnabled = "intl.tsf.enable";
    1.35 +
    1.36 +static const char* kLegacyPrefNameTSFEnabled = "intl.enable_tsf_support";
    1.37 +
    1.38 +#ifdef PR_LOGGING
    1.39 +/**
    1.40 + * TSF related code should log its behavior even on release build especially
    1.41 + * in the interface methods.
    1.42 + *
    1.43 + * In interface methods, use PR_LOG_ALWAYS.
    1.44 + * In internal methods, use PR_LOG_DEBUG for logging normal behavior.
    1.45 + * For logging error, use PR_LOG_ERROR.
    1.46 + *
    1.47 + * When an instance method is called, start with following text:
    1.48 + *   "TSF: 0x%p nsFoo::Bar(", the 0x%p should be the "this" of the nsFoo.
    1.49 + * after that, start with:
    1.50 + *   "TSF: 0x%p   nsFoo::Bar("
    1.51 + * In an internal method, start with following text:
    1.52 + *   "TSF: 0x%p   nsFoo::Bar("
    1.53 + * When a static method is called, start with following text:
    1.54 + *   "TSF: nsFoo::Bar("
    1.55 + */
    1.56 +
    1.57 +PRLogModuleInfo* sTextStoreLog = nullptr;
    1.58 +#endif // #ifdef PR_LOGGING
    1.59 +
    1.60 +/******************************************************************/
    1.61 +/* InputScopeImpl                                                 */
    1.62 +/******************************************************************/
    1.63 +
    1.64 +class InputScopeImpl MOZ_FINAL : public ITfInputScope
    1.65 +{
    1.66 +public:
    1.67 +  InputScopeImpl(const nsTArray<InputScope>& aList) :
    1.68 +    mRefCnt(1),
    1.69 +    mInputScopes(aList)
    1.70 +  {
    1.71 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
    1.72 +      ("TSF: 0x%p InputScopeImpl()", this));
    1.73 +  }
    1.74 +
    1.75 +  STDMETHODIMP_(ULONG) AddRef(void) { return ++mRefCnt; }
    1.76 +
    1.77 +  STDMETHODIMP_(ULONG) Release(void)
    1.78 +  {
    1.79 +    --mRefCnt;
    1.80 +    if (mRefCnt) {
    1.81 +      return mRefCnt;
    1.82 +    }
    1.83 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
    1.84 +      ("TSF: 0x%p InputScopeImpl::Release() final", this));
    1.85 +    delete this;
    1.86 +    return 0;
    1.87 +  }
    1.88 +
    1.89 +  STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    1.90 +  {
    1.91 +    *ppv=nullptr;
    1.92 +    if ( (IID_IUnknown == riid) || (IID_ITfInputScope == riid) ) {
    1.93 +      *ppv = static_cast<ITfInputScope*>(this);
    1.94 +    }
    1.95 +    if (*ppv) {
    1.96 +      AddRef();
    1.97 +      return S_OK;
    1.98 +    }
    1.99 +    return E_NOINTERFACE;
   1.100 +  }
   1.101 +
   1.102 +  STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount)
   1.103 +  {
   1.104 +    uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length());
   1.105 +
   1.106 +    InputScope* pScope = (InputScope*) CoTaskMemAlloc(sizeof(InputScope) * count);
   1.107 +    NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY);
   1.108 +
   1.109 +    if (mInputScopes.IsEmpty()) {
   1.110 +      *pScope = IS_DEFAULT;
   1.111 +      *pcCount = 1;
   1.112 +      *pprgInputScopes = pScope;
   1.113 +      return S_OK;
   1.114 +    }
   1.115 +
   1.116 +    *pcCount = 0;
   1.117 +
   1.118 +    for (uint32_t idx = 0; idx < count; idx++) {
   1.119 +      *(pScope + idx) = mInputScopes[idx];
   1.120 +      (*pcCount)++;
   1.121 +    }
   1.122 +
   1.123 +    *pprgInputScopes = pScope;
   1.124 +    return S_OK;
   1.125 +  }
   1.126 +
   1.127 +  STDMETHODIMP GetPhrase(BSTR **ppbstrPhrases, UINT *pcCount) { return E_NOTIMPL; }
   1.128 +  STDMETHODIMP GetRegularExpression(BSTR *pbstrRegExp) { return E_NOTIMPL; }
   1.129 +  STDMETHODIMP GetSRGS(BSTR *pbstrSRGS) { return E_NOTIMPL; }
   1.130 +  STDMETHODIMP GetXML(BSTR *pbstrXML) { return E_NOTIMPL; }
   1.131 +
   1.132 +private:
   1.133 +  DWORD mRefCnt;
   1.134 +  nsTArray<InputScope> mInputScopes;
   1.135 +};
   1.136 +
   1.137 +/******************************************************************/
   1.138 +/* nsTextStore                                                    */
   1.139 +/******************************************************************/
   1.140 +
   1.141 +ITfThreadMgr*           nsTextStore::sTsfThreadMgr   = nullptr;
   1.142 +ITfMessagePump*         nsTextStore::sMessagePump    = nullptr;
   1.143 +ITfKeystrokeMgr*        nsTextStore::sKeystrokeMgr   = nullptr;
   1.144 +ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = nullptr;
   1.145 +ITfCategoryMgr*         nsTextStore::sCategoryMgr    = nullptr;
   1.146 +ITfDocumentMgr*         nsTextStore::sTsfDisabledDocumentMgr = nullptr;
   1.147 +ITfContext*             nsTextStore::sTsfDisabledContext = nullptr;
   1.148 +ITfInputProcessorProfiles* nsTextStore::sInputProcessorProfiles = nullptr;
   1.149 +DWORD         nsTextStore::sTsfClientId  = 0;
   1.150 +nsTextStore*  nsTextStore::sTsfTextStore = nullptr;
   1.151 +
   1.152 +bool nsTextStore::sCreateNativeCaretForATOK = false;
   1.153 +
   1.154 +UINT nsTextStore::sFlushTIPInputMessage  = 0;
   1.155 +
   1.156 +#define TEXTSTORE_DEFAULT_VIEW (1)
   1.157 +
   1.158 +#ifdef PR_LOGGING
   1.159 +
   1.160 +static const char*
   1.161 +GetBoolName(bool aBool)
   1.162 +{
   1.163 +  return aBool ? "true" : "false";
   1.164 +}
   1.165 +
   1.166 +static void
   1.167 +HandleSeparator(nsCString& aDesc)
   1.168 +{
   1.169 +  if (!aDesc.IsEmpty()) {
   1.170 +    aDesc.AppendLiteral(" | ");
   1.171 +  }
   1.172 +}
   1.173 +
   1.174 +static const nsCString
   1.175 +GetFindFlagName(DWORD aFindFlag)
   1.176 +{
   1.177 +  nsAutoCString description;
   1.178 +  if (!aFindFlag) {
   1.179 +    description.AppendLiteral("no flags (0)");
   1.180 +    return description;
   1.181 +  }
   1.182 +  if (aFindFlag & TS_ATTR_FIND_BACKWARDS) {
   1.183 +    description.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
   1.184 +  }
   1.185 +  if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) {
   1.186 +    HandleSeparator(description);
   1.187 +    description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
   1.188 +  }
   1.189 +  if (aFindFlag & TS_ATTR_FIND_UPDATESTART) {
   1.190 +    HandleSeparator(description);
   1.191 +    description.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
   1.192 +  }
   1.193 +  if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) {
   1.194 +    HandleSeparator(description);
   1.195 +    description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
   1.196 +  }
   1.197 +  if (aFindFlag & TS_ATTR_FIND_WANT_END) {
   1.198 +    HandleSeparator(description);
   1.199 +    description.AppendLiteral("TS_ATTR_FIND_WANT_END");
   1.200 +  }
   1.201 +  if (aFindFlag & TS_ATTR_FIND_HIDDEN) {
   1.202 +    HandleSeparator(description);
   1.203 +    description.AppendLiteral("TS_ATTR_FIND_HIDDEN");
   1.204 +  }
   1.205 +  if (description.IsEmpty()) {
   1.206 +    description.AppendLiteral("Unknown (");
   1.207 +    description.AppendInt(static_cast<uint32_t>(aFindFlag));
   1.208 +    description.AppendLiteral(")");
   1.209 +  }
   1.210 +  return description;
   1.211 +}
   1.212 +
   1.213 +static const char*
   1.214 +GetIMEEnabledName(IMEState::Enabled aIMEEnabled)
   1.215 +{
   1.216 +  switch (aIMEEnabled) {
   1.217 +    case IMEState::DISABLED:
   1.218 +      return "DISABLED";
   1.219 +    case IMEState::ENABLED:
   1.220 +      return "ENABLED";
   1.221 +    case IMEState::PASSWORD:
   1.222 +      return "PASSWORD";
   1.223 +    case IMEState::PLUGIN:
   1.224 +      return "PLUGIN";
   1.225 +    default:
   1.226 +      return "Invalid";
   1.227 +  }
   1.228 +}
   1.229 +
   1.230 +static const char*
   1.231 +GetFocusChangeName(InputContextAction::FocusChange aFocusChange)
   1.232 +{
   1.233 +  switch (aFocusChange) {
   1.234 +    case InputContextAction::FOCUS_NOT_CHANGED:
   1.235 +      return "FOCUS_NOT_CHANGED";
   1.236 +    case InputContextAction::GOT_FOCUS:
   1.237 +      return "GOT_FOCUS";
   1.238 +    case InputContextAction::LOST_FOCUS:
   1.239 +      return "LOST_FOCUS";
   1.240 +    case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
   1.241 +      return "MENU_GOT_PSEUDO_FOCUS";
   1.242 +    case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
   1.243 +      return "MENU_LOST_PSEUDO_FOCUS";
   1.244 +    default:
   1.245 +      return "Unknown";
   1.246 +  }
   1.247 +}
   1.248 +
   1.249 +static nsCString
   1.250 +GetCLSIDNameStr(REFCLSID aCLSID)
   1.251 +{
   1.252 +  LPOLESTR str = nullptr;
   1.253 +  HRESULT hr = ::StringFromCLSID(aCLSID, &str);
   1.254 +  if (FAILED(hr) || !str || !str[0]) {
   1.255 +    return EmptyCString();
   1.256 +  }
   1.257 +
   1.258 +  nsAutoCString result;
   1.259 +  result = NS_ConvertUTF16toUTF8(str);
   1.260 +  ::CoTaskMemFree(str);
   1.261 +  return result;
   1.262 +}
   1.263 +
   1.264 +static nsCString
   1.265 +GetGUIDNameStr(REFGUID aGUID)
   1.266 +{
   1.267 +  OLECHAR str[40];
   1.268 +  int len = ::StringFromGUID2(aGUID, str, ArrayLength(str));
   1.269 +  if (!len || !str[0]) {
   1.270 +    return EmptyCString();
   1.271 +  }
   1.272 +
   1.273 +  return NS_ConvertUTF16toUTF8(str);
   1.274 +}
   1.275 +
   1.276 +static nsCString
   1.277 +GetRIIDNameStr(REFIID aRIID)
   1.278 +{
   1.279 +  LPOLESTR str = nullptr;
   1.280 +  HRESULT hr = ::StringFromIID(aRIID, &str);
   1.281 +  if (FAILED(hr) || !str || !str[0]) {
   1.282 +    return EmptyCString();
   1.283 +  }
   1.284 +
   1.285 +  nsAutoString key(L"Interface\\");
   1.286 +  key += str;
   1.287 +
   1.288 +  nsAutoCString result;
   1.289 +  wchar_t buf[256];
   1.290 +  if (WinUtils::GetRegistryKey(HKEY_CLASSES_ROOT, key.get(), nullptr,
   1.291 +                               buf, sizeof(buf))) {
   1.292 +    result = NS_ConvertUTF16toUTF8(buf);
   1.293 +  } else {
   1.294 +    result = NS_ConvertUTF16toUTF8(str);
   1.295 +  }
   1.296 +
   1.297 +  ::CoTaskMemFree(str);
   1.298 +  return result;
   1.299 +}
   1.300 +
   1.301 +static const char*
   1.302 +GetCommonReturnValueName(HRESULT aResult)
   1.303 +{
   1.304 +  switch (aResult) {
   1.305 +    case S_OK:
   1.306 +      return "S_OK";
   1.307 +    case E_ABORT:
   1.308 +      return "E_ABORT";
   1.309 +    case E_ACCESSDENIED:
   1.310 +      return "E_ACCESSDENIED";
   1.311 +    case E_FAIL:
   1.312 +      return "E_FAIL";
   1.313 +    case E_HANDLE:
   1.314 +      return "E_HANDLE";
   1.315 +    case E_INVALIDARG:
   1.316 +      return "E_INVALIDARG";
   1.317 +    case E_NOINTERFACE:
   1.318 +      return "E_NOINTERFACE";
   1.319 +    case E_NOTIMPL:
   1.320 +      return "E_NOTIMPL";
   1.321 +    case E_OUTOFMEMORY:
   1.322 +      return "E_OUTOFMEMORY";
   1.323 +    case E_POINTER:
   1.324 +      return "E_POINTER";
   1.325 +    case E_UNEXPECTED:
   1.326 +      return "E_UNEXPECTED";
   1.327 +    default:
   1.328 +      return SUCCEEDED(aResult) ? "Succeeded" : "Failed";
   1.329 +  }
   1.330 +}
   1.331 +
   1.332 +static const char*
   1.333 +GetTextStoreReturnValueName(HRESULT aResult)
   1.334 +{
   1.335 +  switch (aResult) {
   1.336 +    case TS_E_FORMAT:
   1.337 +      return "TS_E_FORMAT";
   1.338 +    case TS_E_INVALIDPOINT:
   1.339 +      return "TS_E_INVALIDPOINT";
   1.340 +    case TS_E_INVALIDPOS:
   1.341 +      return "TS_E_INVALIDPOS";
   1.342 +    case TS_E_NOINTERFACE:
   1.343 +      return "TS_E_NOINTERFACE";
   1.344 +    case TS_E_NOLAYOUT:
   1.345 +      return "TS_E_NOLAYOUT";
   1.346 +    case TS_E_NOLOCK:
   1.347 +      return "TS_E_NOLOCK";
   1.348 +    case TS_E_NOOBJECT:
   1.349 +      return "TS_E_NOOBJECT";
   1.350 +    case TS_E_NOSELECTION:
   1.351 +      return "TS_E_NOSELECTION";
   1.352 +    case TS_E_NOSERVICE:
   1.353 +      return "TS_E_NOSERVICE";
   1.354 +    case TS_E_READONLY:
   1.355 +      return "TS_E_READONLY";
   1.356 +    case TS_E_SYNCHRONOUS:
   1.357 +      return "TS_E_SYNCHRONOUS";
   1.358 +    case TS_S_ASYNC:
   1.359 +      return "TS_S_ASYNC";
   1.360 +    default:
   1.361 +      return GetCommonReturnValueName(aResult);
   1.362 +  }
   1.363 +}
   1.364 +
   1.365 +static const nsCString
   1.366 +GetSinkMaskNameStr(DWORD aSinkMask)
   1.367 +{
   1.368 +  nsAutoCString description;
   1.369 +  if (aSinkMask & TS_AS_TEXT_CHANGE) {
   1.370 +    description.AppendLiteral("TS_AS_TEXT_CHANGE");
   1.371 +  }
   1.372 +  if (aSinkMask & TS_AS_SEL_CHANGE) {
   1.373 +    HandleSeparator(description);
   1.374 +    description.AppendLiteral("TS_AS_SEL_CHANGE");
   1.375 +  }
   1.376 +  if (aSinkMask & TS_AS_LAYOUT_CHANGE) {
   1.377 +    HandleSeparator(description);
   1.378 +    description.AppendLiteral("TS_AS_LAYOUT_CHANGE");
   1.379 +  }
   1.380 +  if (aSinkMask & TS_AS_ATTR_CHANGE) {
   1.381 +    HandleSeparator(description);
   1.382 +    description.AppendLiteral("TS_AS_ATTR_CHANGE");
   1.383 +  }
   1.384 +  if (aSinkMask & TS_AS_STATUS_CHANGE) {
   1.385 +    HandleSeparator(description);
   1.386 +    description.AppendLiteral("TS_AS_STATUS_CHANGE");
   1.387 +  }
   1.388 +  if (description.IsEmpty()) {
   1.389 +    description.AppendLiteral("not-specified");
   1.390 +  }
   1.391 +  return description;
   1.392 +}
   1.393 +
   1.394 +static const char*
   1.395 +GetActiveSelEndName(TsActiveSelEnd aSelEnd)
   1.396 +{
   1.397 +  return aSelEnd == TS_AE_NONE  ? "TS_AE_NONE" :
   1.398 +         aSelEnd == TS_AE_START ? "TS_AE_START" :
   1.399 +         aSelEnd == TS_AE_END   ? "TS_AE_END" : "Unknown";
   1.400 +}
   1.401 +
   1.402 +static const nsCString
   1.403 +GetLockFlagNameStr(DWORD aLockFlags)
   1.404 +{
   1.405 +  nsAutoCString description;
   1.406 +  if ((aLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE) {
   1.407 +    description.AppendLiteral("TS_LF_READWRITE");
   1.408 +  } else if (aLockFlags & TS_LF_READ) {
   1.409 +    description.AppendLiteral("TS_LF_READ");
   1.410 +  }
   1.411 +  if (aLockFlags & TS_LF_SYNC) {
   1.412 +    if (!description.IsEmpty()) {
   1.413 +      description.AppendLiteral(" | ");
   1.414 +    }
   1.415 +    description.AppendLiteral("TS_LF_SYNC");
   1.416 +  }
   1.417 +  if (description.IsEmpty()) {
   1.418 +    description.AppendLiteral("not-specified");
   1.419 +  }
   1.420 +  return description;
   1.421 +}
   1.422 +
   1.423 +static const char*
   1.424 +GetTextRunTypeName(TsRunType aRunType)
   1.425 +{
   1.426 +  switch (aRunType) {
   1.427 +    case TS_RT_PLAIN:
   1.428 +      return "TS_RT_PLAIN";
   1.429 +    case TS_RT_HIDDEN:
   1.430 +      return "TS_RT_HIDDEN";
   1.431 +    case  TS_RT_OPAQUE:
   1.432 +      return "TS_RT_OPAQUE";
   1.433 +    default:
   1.434 +      return "Unknown";
   1.435 +  }
   1.436 +}
   1.437 +
   1.438 +static nsCString
   1.439 +GetColorName(const TF_DA_COLOR &aColor)
   1.440 +{
   1.441 +  switch (aColor.type) {
   1.442 +    case TF_CT_NONE:
   1.443 +      return NS_LITERAL_CSTRING("TF_CT_NONE");
   1.444 +    case TF_CT_SYSCOLOR:
   1.445 +      return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
   1.446 +                             static_cast<int32_t>(aColor.nIndex));
   1.447 +    case TF_CT_COLORREF:
   1.448 +      return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
   1.449 +                             static_cast<int32_t>(aColor.cr));
   1.450 +      break;
   1.451 +    default:
   1.452 +      return nsPrintfCString("Unknown(%08X)",
   1.453 +                             static_cast<int32_t>(aColor.type));
   1.454 +  }
   1.455 +}
   1.456 +
   1.457 +static nsCString
   1.458 +GetLineStyleName(TF_DA_LINESTYLE aLineStyle)
   1.459 +{
   1.460 +  switch (aLineStyle) {
   1.461 +    case TF_LS_NONE:
   1.462 +      return NS_LITERAL_CSTRING("TF_LS_NONE");
   1.463 +    case TF_LS_SOLID:
   1.464 +      return NS_LITERAL_CSTRING("TF_LS_SOLID");
   1.465 +    case TF_LS_DOT:
   1.466 +      return NS_LITERAL_CSTRING("TF_LS_DOT");
   1.467 +    case TF_LS_DASH:
   1.468 +      return NS_LITERAL_CSTRING("TF_LS_DASH");
   1.469 +    case TF_LS_SQUIGGLE:
   1.470 +      return NS_LITERAL_CSTRING("TF_LS_SQUIGGLE");
   1.471 +    default: {
   1.472 +      return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aLineStyle));
   1.473 +    }
   1.474 +  }
   1.475 +}
   1.476 +
   1.477 +static nsCString
   1.478 +GetClauseAttrName(TF_DA_ATTR_INFO aAttr)
   1.479 +{
   1.480 +  switch (aAttr) {
   1.481 +    case TF_ATTR_INPUT:
   1.482 +      return NS_LITERAL_CSTRING("TF_ATTR_INPUT");
   1.483 +    case TF_ATTR_TARGET_CONVERTED:
   1.484 +      return NS_LITERAL_CSTRING("TF_ATTR_TARGET_CONVERTED");
   1.485 +    case TF_ATTR_CONVERTED:
   1.486 +      return NS_LITERAL_CSTRING("TF_ATTR_CONVERTED");
   1.487 +    case TF_ATTR_TARGET_NOTCONVERTED:
   1.488 +      return NS_LITERAL_CSTRING("TF_ATTR_TARGET_NOTCONVERTED");
   1.489 +    case TF_ATTR_INPUT_ERROR:
   1.490 +      return NS_LITERAL_CSTRING("TF_ATTR_INPUT_ERROR");
   1.491 +    case TF_ATTR_FIXEDCONVERTED:
   1.492 +      return NS_LITERAL_CSTRING("TF_ATTR_FIXEDCONVERTED");
   1.493 +    case TF_ATTR_OTHER:
   1.494 +      return NS_LITERAL_CSTRING("TF_ATTR_OTHER");
   1.495 +    default: {
   1.496 +      return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aAttr));
   1.497 +    }
   1.498 +  }
   1.499 +}
   1.500 +
   1.501 +static nsCString
   1.502 +GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE &aDispAttr)
   1.503 +{
   1.504 +  nsAutoCString str;
   1.505 +  str = "crText:{ ";
   1.506 +  str += GetColorName(aDispAttr.crText);
   1.507 +  str += " }, crBk:{ ";
   1.508 +  str += GetColorName(aDispAttr.crBk);
   1.509 +  str += " }, lsStyle: ";
   1.510 +  str += GetLineStyleName(aDispAttr.lsStyle);
   1.511 +  str += ", fBoldLine: ";
   1.512 +  str += GetBoolName(aDispAttr.fBoldLine);
   1.513 +  str += ", crLine:{ ";
   1.514 +  str += GetColorName(aDispAttr.crLine);
   1.515 +  str += " }, bAttr: ";
   1.516 +  str += GetClauseAttrName(aDispAttr.bAttr);
   1.517 +  return str;
   1.518 +}
   1.519 +
   1.520 +#endif // #ifdef PR_LOGGING
   1.521 +
   1.522 +nsTextStore::nsTextStore()
   1.523 + : mContent(mComposition, mSelection)
   1.524 +{
   1.525 +  mRefCnt = 1;
   1.526 +  mEditCookie = 0;
   1.527 +  mIPProfileCookie = TF_INVALID_COOKIE;
   1.528 +  mLangProfileCookie = TF_INVALID_COOKIE;
   1.529 +  mSinkMask = 0;
   1.530 +  mLock = 0;
   1.531 +  mLockQueued = 0;
   1.532 +  mInputScopeDetected = false;
   1.533 +  mInputScopeRequested = false;
   1.534 +  mIsRecordingActionsWithoutLock = false;
   1.535 +  mPendingOnSelectionChange = false;
   1.536 +  mPendingOnLayoutChange = false;
   1.537 +  mNativeCaretIsCreated = false;
   1.538 +  mIsIMM_IME = false;
   1.539 +  mOnActivatedCalled = false;
   1.540 +  // We hope that 5 or more actions don't occur at once.
   1.541 +  mPendingActions.SetCapacity(5);
   1.542 +
   1.543 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.544 +    ("TSF: 0x%p nsTextStore::nsTestStore() SUCCEEDED", this));
   1.545 +}
   1.546 +
   1.547 +bool
   1.548 +nsTextStore::Init(ITfThreadMgr* aThreadMgr)
   1.549 +{
   1.550 +  nsRefPtr<ITfSource> source;
   1.551 +  HRESULT hr =
   1.552 +    aThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
   1.553 +  if (FAILED(hr)) {
   1.554 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.555 +      ("TSF: 0x%p nsTextStore::Init() FAILED to get ITfSource instance "
   1.556 +       "(0x%08X)", this, hr));
   1.557 +    return false;
   1.558 +  }
   1.559 +
   1.560 +  // On Vista or later, Windows let us know activate IME changed only with
   1.561 +  // ITfInputProcessorProfileActivationSink.  However, it's not available on XP.
   1.562 +  // On XP, ITfActiveLanguageProfileNotifySink is available for it.
   1.563 +  // NOTE: Each OnActivated() should be called when TSF becomes available.
   1.564 +  if (IsVistaOrLater()) {
   1.565 +    hr = source->AdviseSink(IID_ITfInputProcessorProfileActivationSink,
   1.566 +                   static_cast<ITfInputProcessorProfileActivationSink*>(this),
   1.567 +                   &mIPProfileCookie);
   1.568 +    if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) {
   1.569 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.570 +        ("TSF: 0x%p nsTextStore::Init() FAILED to install "
   1.571 +         "ITfInputProcessorProfileActivationSink (0x%08X)", this, hr));
   1.572 +      return false;
   1.573 +    }
   1.574 +  } else {
   1.575 +    hr = source->AdviseSink(IID_ITfActiveLanguageProfileNotifySink,
   1.576 +                   static_cast<ITfActiveLanguageProfileNotifySink*>(this),
   1.577 +                   &mLangProfileCookie);
   1.578 +    if (FAILED(hr) || mLangProfileCookie == TF_INVALID_COOKIE) {
   1.579 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.580 +        ("TSF: 0x%p nsTextStore::Init() FAILED to install "
   1.581 +         "ITfActiveLanguageProfileNotifySink (0x%08X)", this, hr));
   1.582 +      return false;
   1.583 +    }
   1.584 +  }
   1.585 +
   1.586 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.587 +    ("TSF: 0x%p nsTextStore::Init(), "
   1.588 +     "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X",
   1.589 +     this, mIPProfileCookie, mLangProfileCookie));
   1.590 +
   1.591 +  return true;
   1.592 +}
   1.593 +
   1.594 +nsTextStore::~nsTextStore()
   1.595 +{
   1.596 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.597 +    ("TSF: 0x%p nsTextStore instance is destroyed, "
   1.598 +     "mWidget=0x%p, mDocumentMgr=0x%p, mContext=0x%p, mIPProfileCookie=0x%08X, "
   1.599 +     "mLangProfileCookie=0x%08X",
   1.600 +     this, mWidget.get(), mDocumentMgr.get(), mContext.get(),
   1.601 +     mIPProfileCookie, mLangProfileCookie));
   1.602 +
   1.603 +  if (mIPProfileCookie != TF_INVALID_COOKIE) {
   1.604 +    nsRefPtr<ITfSource> source;
   1.605 +    HRESULT hr =
   1.606 +      sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
   1.607 +    if (FAILED(hr)) {
   1.608 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.609 +        ("TSF: 0x%p   ~nsTextStore FAILED to get ITfSource instance "
   1.610 +         "(0x%08X)", this, hr));
   1.611 +    } else {
   1.612 +      hr = source->UnadviseSink(mIPProfileCookie);
   1.613 +      if (FAILED(hr)) {
   1.614 +        PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.615 +          ("TSF: 0x%p   ~nsTextStore FAILED to uninstall "
   1.616 +           "ITfInputProcessorProfileActivationSink (0x%08X)",
   1.617 +           this, hr));
   1.618 +      }
   1.619 +    }
   1.620 +  }
   1.621 +
   1.622 +  if (mLangProfileCookie != TF_INVALID_COOKIE) {
   1.623 +    nsRefPtr<ITfSource> source;
   1.624 +    HRESULT hr =
   1.625 +      sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
   1.626 +    if (FAILED(hr)) {
   1.627 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.628 +        ("TSF: 0x%p   ~nsTextStore FAILED to get ITfSource instance "
   1.629 +         "(0x%08X)", this, hr));
   1.630 +    } else {
   1.631 +      hr = source->UnadviseSink(mLangProfileCookie);
   1.632 +      if (FAILED(hr)) {
   1.633 +        PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.634 +          ("TSF: 0x%p   ~nsTextStore FAILED to uninstall "
   1.635 +           "ITfActiveLanguageProfileNotifySink (0x%08X)",
   1.636 +           this, hr));
   1.637 +      }
   1.638 +    }
   1.639 +  }
   1.640 +}
   1.641 +
   1.642 +bool
   1.643 +nsTextStore::Create(nsWindowBase* aWidget)
   1.644 +{
   1.645 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.646 +    ("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)",
   1.647 +     this, aWidget));
   1.648 +
   1.649 +  EnsureInitActiveTIPKeyboard();
   1.650 +
   1.651 +  if (mDocumentMgr) {
   1.652 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.653 +      ("TSF: 0x%p   nsTextStore::Create() FAILED due to already initialized",
   1.654 +       this));
   1.655 +    return false;
   1.656 +  }
   1.657 +
   1.658 +  // Create document manager
   1.659 +  HRESULT hr = sTsfThreadMgr->CreateDocumentMgr(
   1.660 +                                  getter_AddRefs(mDocumentMgr));
   1.661 +  if (FAILED(hr)) {
   1.662 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.663 +      ("TSF: 0x%p   nsTextStore::Create() FAILED to create DocumentMgr "
   1.664 +       "(0x%08X)", this, hr));
   1.665 +    return false;
   1.666 +  }
   1.667 +  mWidget = aWidget;
   1.668 +
   1.669 +  // Create context and add it to document manager
   1.670 +  hr = mDocumentMgr->CreateContext(sTsfClientId, 0,
   1.671 +                                   static_cast<ITextStoreACP*>(this),
   1.672 +                                   getter_AddRefs(mContext), &mEditCookie);
   1.673 +  if (FAILED(hr)) {
   1.674 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.675 +      ("TSF: 0x%p   nsTextStore::Create() FAILED to create the context "
   1.676 +       "(0x%08X)", this, hr));
   1.677 +    mDocumentMgr = nullptr;
   1.678 +    return false;
   1.679 +  }
   1.680 +
   1.681 +  hr = mDocumentMgr->Push(mContext);
   1.682 +  if (FAILED(hr)) {
   1.683 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.684 +      ("TSF: 0x%p   nsTextStore::Create() FAILED to push the context (0x%08X)",
   1.685 +       this, hr));
   1.686 +    // XXX Why don't we use NS_IF_RELEASE() here??
   1.687 +    mContext = nullptr;
   1.688 +    mDocumentMgr = nullptr;
   1.689 +    return false;
   1.690 +  }
   1.691 +
   1.692 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.693 +    ("TSF: 0x%p   nsTextStore::Create() succeeded: "
   1.694 +     "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
   1.695 +     this, mDocumentMgr.get(), mContext.get(), mEditCookie));
   1.696 +
   1.697 +  return true;
   1.698 +}
   1.699 +
   1.700 +bool
   1.701 +nsTextStore::Destroy(void)
   1.702 +{
   1.703 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.704 +    ("TSF: 0x%p nsTextStore::Destroy(), mComposition.IsComposing()=%s",
   1.705 +     this, GetBoolName(mComposition.IsComposing())));
   1.706 +
   1.707 +  // If there is composition, TSF keeps the composition even after the text
   1.708 +  // store destroyed.  So, we should clear the composition here.
   1.709 +  if (mComposition.IsComposing()) {
   1.710 +    NS_WARNING("Composition is still alive at destroying the text store");
   1.711 +    CommitCompositionInternal(false);
   1.712 +  }
   1.713 +
   1.714 +  mContent.Clear();
   1.715 +  mSelection.MarkDirty();
   1.716 +
   1.717 +  if (mWidget) {
   1.718 +    // When blurred, Tablet Input Panel posts "blur" messages
   1.719 +    // and try to insert text when the message is retrieved later.
   1.720 +    // But by that time the text store is already destroyed,
   1.721 +    // so try to get the message early
   1.722 +    MSG msg;
   1.723 +    if (WinUtils::PeekMessage(&msg, mWidget->GetWindowHandle(),
   1.724 +                              sFlushTIPInputMessage, sFlushTIPInputMessage,
   1.725 +                              PM_REMOVE)) {
   1.726 +      ::DispatchMessageW(&msg);
   1.727 +    }
   1.728 +  }
   1.729 +  mContext = nullptr;
   1.730 +  if (mDocumentMgr) {
   1.731 +    mDocumentMgr->Pop(TF_POPF_ALL);
   1.732 +    mDocumentMgr = nullptr;
   1.733 +  }
   1.734 +  mSink = nullptr;
   1.735 +  mWidget = nullptr;
   1.736 +
   1.737 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.738 +    ("TSF: 0x%p   nsTextStore::Destroy() succeeded", this));
   1.739 +  return true;
   1.740 +}
   1.741 +
   1.742 +STDMETHODIMP
   1.743 +nsTextStore::QueryInterface(REFIID riid,
   1.744 +                            void** ppv)
   1.745 +{
   1.746 +  *ppv=nullptr;
   1.747 +  if ( (IID_IUnknown == riid) || (IID_ITextStoreACP == riid) ) {
   1.748 +    *ppv = static_cast<ITextStoreACP*>(this);
   1.749 +  } else if (IID_ITfContextOwnerCompositionSink == riid) {
   1.750 +    *ppv = static_cast<ITfContextOwnerCompositionSink*>(this);
   1.751 +  } else if (IID_ITfActiveLanguageProfileNotifySink == riid) {
   1.752 +    *ppv = static_cast<ITfActiveLanguageProfileNotifySink*>(this);
   1.753 +  } else if (IID_ITfInputProcessorProfileActivationSink == riid) {
   1.754 +    *ppv = static_cast<ITfInputProcessorProfileActivationSink*>(this);
   1.755 +  }
   1.756 +  if (*ppv) {
   1.757 +    AddRef();
   1.758 +    return S_OK;
   1.759 +  }
   1.760 +
   1.761 +  PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.762 +    ("TSF: 0x%p nsTextStore::QueryInterface() FAILED, riid=%s",
   1.763 +     this, GetRIIDNameStr(riid).get()));
   1.764 +  return E_NOINTERFACE;
   1.765 +}
   1.766 +
   1.767 +STDMETHODIMP_(ULONG) nsTextStore::AddRef()
   1.768 +{
   1.769 +  return ++mRefCnt;
   1.770 +}
   1.771 +
   1.772 +STDMETHODIMP_(ULONG) nsTextStore::Release()
   1.773 +{
   1.774 +  --mRefCnt;
   1.775 +  if (0 != mRefCnt)
   1.776 +    return mRefCnt;
   1.777 +  delete this;
   1.778 +  return 0;
   1.779 +}
   1.780 +
   1.781 +STDMETHODIMP
   1.782 +nsTextStore::AdviseSink(REFIID riid,
   1.783 +                        IUnknown *punk,
   1.784 +                        DWORD dwMask)
   1.785 +{
   1.786 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.787 +    ("TSF: 0x%p nsTextStore::AdviseSink(riid=%s, punk=0x%p, dwMask=%s), "
   1.788 +     "mSink=0x%p, mSinkMask=%s",
   1.789 +     this, GetRIIDNameStr(riid).get(), punk, GetSinkMaskNameStr(dwMask).get(),
   1.790 +     mSink.get(), GetSinkMaskNameStr(mSinkMask).get()));
   1.791 +
   1.792 +  if (!punk) {
   1.793 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.794 +      ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to the null punk",
   1.795 +       this));
   1.796 +    return E_UNEXPECTED;
   1.797 +  }
   1.798 +
   1.799 +  if (IID_ITextStoreACPSink != riid) {
   1.800 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.801 +      ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to "
   1.802 +       "unsupported interface", this));
   1.803 +    return E_INVALIDARG; // means unsupported interface.
   1.804 +  }
   1.805 +
   1.806 +  if (!mSink) {
   1.807 +    // Install sink
   1.808 +    punk->QueryInterface(IID_ITextStoreACPSink, getter_AddRefs(mSink));
   1.809 +    if (!mSink) {
   1.810 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.811 +        ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to "
   1.812 +         "punk not having the interface", this));
   1.813 +      return E_UNEXPECTED;
   1.814 +    }
   1.815 +  } else {
   1.816 +    // If sink is already installed we check to see if they are the same
   1.817 +    // Get IUnknown from both sides for comparison
   1.818 +    nsRefPtr<IUnknown> comparison1, comparison2;
   1.819 +    punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
   1.820 +    mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
   1.821 +    if (comparison1 != comparison2) {
   1.822 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.823 +        ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to "
   1.824 +         "the sink being different from the stored sink", this));
   1.825 +      return CONNECT_E_ADVISELIMIT;
   1.826 +    }
   1.827 +  }
   1.828 +  // Update mask either for a new sink or an existing sink
   1.829 +  mSinkMask = dwMask;
   1.830 +  return S_OK;
   1.831 +}
   1.832 +
   1.833 +STDMETHODIMP
   1.834 +nsTextStore::UnadviseSink(IUnknown *punk)
   1.835 +{
   1.836 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.837 +    ("TSF: 0x%p nsTextStore::UnadviseSink(punk=0x%p), mSink=0x%p",
   1.838 +     this, punk, mSink.get()));
   1.839 +
   1.840 +  if (!punk) {
   1.841 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.842 +      ("TSF: 0x%p   nsTextStore::UnadviseSink() FAILED due to the null punk",
   1.843 +       this));
   1.844 +    return E_INVALIDARG;
   1.845 +  }
   1.846 +  if (!mSink) {
   1.847 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.848 +      ("TSF: 0x%p   nsTextStore::UnadviseSink() FAILED due to "
   1.849 +       "any sink not stored", this));
   1.850 +    return CONNECT_E_NOCONNECTION;
   1.851 +  }
   1.852 +  // Get IUnknown from both sides for comparison
   1.853 +  nsRefPtr<IUnknown> comparison1, comparison2;
   1.854 +  punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
   1.855 +  mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
   1.856 +  // Unadvise only if sinks are the same
   1.857 +  if (comparison1 != comparison2) {
   1.858 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.859 +      ("TSF: 0x%p   nsTextStore::UnadviseSink() FAILED due to "
   1.860 +       "the sink being different from the stored sink", this));
   1.861 +    return CONNECT_E_NOCONNECTION;
   1.862 +  }
   1.863 +  mSink = nullptr;
   1.864 +  mSinkMask = 0;
   1.865 +  return S_OK;
   1.866 +}
   1.867 +
   1.868 +STDMETHODIMP
   1.869 +nsTextStore::RequestLock(DWORD dwLockFlags,
   1.870 +                         HRESULT *phrSession)
   1.871 +{
   1.872 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.873 +    ("TSF: 0x%p nsTextStore::RequestLock(dwLockFlags=%s, phrSession=0x%p), "
   1.874 +     "mLock=%s", this, GetLockFlagNameStr(dwLockFlags).get(), phrSession,
   1.875 +     GetLockFlagNameStr(mLock).get()));
   1.876 +
   1.877 +  if (!mSink) {
   1.878 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.879 +      ("TSF: 0x%p   nsTextStore::RequestLock() FAILED due to "
   1.880 +       "any sink not stored", this));
   1.881 +    return E_FAIL;
   1.882 +  }
   1.883 +  if (!phrSession) {
   1.884 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   1.885 +      ("TSF: 0x%p   nsTextStore::RequestLock() FAILED due to "
   1.886 +       "null phrSession", this));
   1.887 +    return E_INVALIDARG;
   1.888 +  }
   1.889 +
   1.890 +  if (!mLock) {
   1.891 +    // put on lock
   1.892 +    mLock = dwLockFlags & (~TS_LF_SYNC);
   1.893 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.894 +      ("TSF: 0x%p   Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
   1.895 +       ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
   1.896 +       this, GetLockFlagNameStr(mLock).get()));
   1.897 +    *phrSession = mSink->OnLockGranted(mLock);
   1.898 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.899 +      ("TSF: 0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
   1.900 +       "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
   1.901 +       this, GetLockFlagNameStr(mLock).get()));
   1.902 +    DidLockGranted();
   1.903 +    while (mLockQueued) {
   1.904 +      mLock = mLockQueued;
   1.905 +      mLockQueued = 0;
   1.906 +      PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.907 +        ("TSF: 0x%p   Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
   1.908 +         ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
   1.909 +         this, GetLockFlagNameStr(mLock).get()));
   1.910 +      mSink->OnLockGranted(mLock);
   1.911 +      PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.912 +        ("TSF: 0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
   1.913 +         "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
   1.914 +         this, GetLockFlagNameStr(mLock).get()));
   1.915 +      DidLockGranted();
   1.916 +    }
   1.917 +
   1.918 +    // The document is now completely unlocked.
   1.919 +    mLock = 0;
   1.920 +
   1.921 +    if (mPendingOnLayoutChange) {
   1.922 +      mPendingOnLayoutChange = false;
   1.923 +      if (mSink) {
   1.924 +        PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.925 +               ("TSF: 0x%p   nsTextStore::RequestLock(), "
   1.926 +                "calling ITextStoreACPSink::OnLayoutChange()...", this));
   1.927 +        mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
   1.928 +      }
   1.929 +      // The layout change caused by composition string change should cause
   1.930 +      // calling ITfContextOwnerServices::OnLayoutChange() too.
   1.931 +      if (mContext) {
   1.932 +        nsRefPtr<ITfContextOwnerServices> service;
   1.933 +        mContext->QueryInterface(IID_ITfContextOwnerServices,
   1.934 +                                 getter_AddRefs(service));
   1.935 +        if (service) {
   1.936 +          PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.937 +                 ("TSF: 0x%p   nsTextStore::RequestLock(), "
   1.938 +                  "calling ITfContextOwnerServices::OnLayoutChange()...",
   1.939 +                  this));
   1.940 +          service->OnLayoutChange();
   1.941 +        }
   1.942 +      }
   1.943 +    }
   1.944 +
   1.945 +    if (mPendingOnSelectionChange) {
   1.946 +      mPendingOnSelectionChange = false;
   1.947 +      if (mSink) {
   1.948 +        PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.949 +               ("TSF: 0x%p   nsTextStore::RequestLock(), "
   1.950 +                "calling ITextStoreACPSink::OnSelectionChange()...", this));
   1.951 +        mSink->OnSelectionChange();
   1.952 +      }
   1.953 +    }
   1.954 +
   1.955 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.956 +      ("TSF: 0x%p   nsTextStore::RequestLock() succeeded: *phrSession=%s",
   1.957 +       this, GetTextStoreReturnValueName(*phrSession)));
   1.958 +    return S_OK;
   1.959 +  }
   1.960 +
   1.961 +  // only time when reentrant lock is allowed is when caller holds a
   1.962 +  // read-only lock and is requesting an async write lock
   1.963 +  if (IsReadLocked() && !IsReadWriteLocked() && IsReadWriteLock(dwLockFlags) &&
   1.964 +      !(dwLockFlags & TS_LF_SYNC)) {
   1.965 +    *phrSession = TS_S_ASYNC;
   1.966 +    mLockQueued = dwLockFlags & (~TS_LF_SYNC);
   1.967 +
   1.968 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.969 +      ("TSF: 0x%p   nsTextStore::RequestLock() stores the request in the "
   1.970 +       "queue, *phrSession=TS_S_ASYNC", this));
   1.971 +    return S_OK;
   1.972 +  }
   1.973 +
   1.974 +  // no more locks allowed
   1.975 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   1.976 +    ("TSF: 0x%p   nsTextStore::RequestLock() didn't allow to lock, "
   1.977 +     "*phrSession=TS_E_SYNCHRONOUS", this));
   1.978 +  *phrSession = TS_E_SYNCHRONOUS;
   1.979 +  return E_FAIL;
   1.980 +}
   1.981 +
   1.982 +void
   1.983 +nsTextStore::DidLockGranted()
   1.984 +{
   1.985 +  if (mNativeCaretIsCreated) {
   1.986 +    ::DestroyCaret();
   1.987 +    mNativeCaretIsCreated = false;
   1.988 +  }
   1.989 +  if (IsReadWriteLocked()) {
   1.990 +    FlushPendingActions();
   1.991 +  }
   1.992 +
   1.993 +  // If the widget has gone, we don't need to notify anything.
   1.994 +  if (!mWidget || mWidget->Destroyed()) {
   1.995 +    mPendingOnSelectionChange = false;
   1.996 +    mPendingOnLayoutChange = false;
   1.997 +  }
   1.998 +}
   1.999 +
  1.1000 +void
  1.1001 +nsTextStore::FlushPendingActions()
  1.1002 +{
  1.1003 +  if (!mWidget || mWidget->Destroyed()) {
  1.1004 +    mPendingActions.Clear();
  1.1005 +    mContent.Clear();
  1.1006 +    mPendingOnSelectionChange = false;
  1.1007 +    mPendingOnLayoutChange = false;
  1.1008 +    return;
  1.1009 +  }
  1.1010 +
  1.1011 +  mContent.Clear();
  1.1012 +
  1.1013 +  nsRefPtr<nsWindowBase> kungFuDeathGrip(mWidget);
  1.1014 +  for (uint32_t i = 0; i < mPendingActions.Length(); i++) {
  1.1015 +    PendingAction& action = mPendingActions[i];
  1.1016 +    switch (action.mType) {
  1.1017 +      case PendingAction::COMPOSITION_START: {
  1.1018 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1019 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1.1020 +                "flushing COMPOSITION_START={ mSelectionStart=%d, "
  1.1021 +                "mSelectionLength=%d }",
  1.1022 +                this, action.mSelectionStart, action.mSelectionLength));
  1.1023 +
  1.1024 +        MOZ_ASSERT(mComposition.mLastData.IsEmpty());
  1.1025 +
  1.1026 +        // Select composition range so the new composition replaces the range
  1.1027 +        WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
  1.1028 +        mWidget->InitEvent(selectionSet);
  1.1029 +        selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart);
  1.1030 +        selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength);
  1.1031 +        selectionSet.mReversed = false;
  1.1032 +        mWidget->DispatchWindowEvent(&selectionSet);
  1.1033 +        if (!selectionSet.mSucceeded) {
  1.1034 +          PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1035 +                 ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1.1036 +                  "FAILED due to NS_SELECTION_SET failure", this));
  1.1037 +          break;
  1.1038 +        }
  1.1039 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1040 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1.1041 +                "dispatching compositionstart event...", this));
  1.1042 +        WidgetCompositionEvent compositionStart(true, NS_COMPOSITION_START,
  1.1043 +                                                mWidget);
  1.1044 +        mWidget->InitEvent(compositionStart);
  1.1045 +        mWidget->DispatchWindowEvent(&compositionStart);
  1.1046 +        if (!mWidget || mWidget->Destroyed()) {
  1.1047 +          break;
  1.1048 +        }
  1.1049 +        break;
  1.1050 +      }
  1.1051 +      case PendingAction::COMPOSITION_UPDATE: {
  1.1052 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1053 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1.1054 +                "flushing COMPOSITION_UPDATE={ mData=\"%s\", "
  1.1055 +                "mRanges=0x%p, mRanges->Length()=%d }",
  1.1056 +                this, NS_ConvertUTF16toUTF8(action.mData).get(), action.mRanges.get(),
  1.1057 +                action.mRanges ? action.mRanges->Length() : 0));
  1.1058 +
  1.1059 +        if (!action.mRanges) {
  1.1060 +          NS_WARNING("How does this case occur?");
  1.1061 +          action.mRanges = new TextRangeArray();
  1.1062 +        }
  1.1063 +
  1.1064 +        // Adjust offsets in the ranges for XP linefeed character (only \n).
  1.1065 +        // XXX Following code is the safest approach.  However, it wastes
  1.1066 +        //     a little performance.  For ensuring the clauses do not
  1.1067 +        //     overlap each other, we should redesign TextRange later.
  1.1068 +        for (uint32_t i = 0; i < action.mRanges->Length(); ++i) {
  1.1069 +          TextRange& range = action.mRanges->ElementAt(i);
  1.1070 +          TextRange nativeRange = range;
  1.1071 +          if (nativeRange.mStartOffset > 0) {
  1.1072 +            nsAutoString preText(
  1.1073 +              Substring(action.mData, 0, nativeRange.mStartOffset));
  1.1074 +            preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1.1075 +                                     NS_LITERAL_STRING("\n"));
  1.1076 +            range.mStartOffset = preText.Length();
  1.1077 +          }
  1.1078 +          if (nativeRange.Length() == 0) {
  1.1079 +            range.mEndOffset = range.mStartOffset;
  1.1080 +          } else {
  1.1081 +            nsAutoString clause(
  1.1082 +              Substring(action.mData,
  1.1083 +                        nativeRange.mStartOffset, nativeRange.Length()));
  1.1084 +            clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1.1085 +                                    NS_LITERAL_STRING("\n"));
  1.1086 +            range.mEndOffset = range.mStartOffset + clause.Length();
  1.1087 +          }
  1.1088 +        }
  1.1089 +
  1.1090 +        action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1.1091 +                                      NS_LITERAL_STRING("\n"));
  1.1092 +
  1.1093 +        if (action.mData != mComposition.mLastData) {
  1.1094 +          PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1095 +                 ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1.1096 +                  "dispatching compositionupdate event...", this));
  1.1097 +          WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
  1.1098 +                                                   mWidget);
  1.1099 +          mWidget->InitEvent(compositionUpdate);
  1.1100 +          compositionUpdate.data = action.mData;
  1.1101 +          mComposition.mLastData = compositionUpdate.data;
  1.1102 +          mWidget->DispatchWindowEvent(&compositionUpdate);
  1.1103 +          if (!mWidget || mWidget->Destroyed()) {
  1.1104 +            break;
  1.1105 +          }
  1.1106 +        }
  1.1107 +
  1.1108 +        MOZ_ASSERT(action.mData == mComposition.mLastData);
  1.1109 +
  1.1110 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1111 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1.1112 +                "dispatching text event...", this));
  1.1113 +        WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
  1.1114 +        mWidget->InitEvent(textEvent);
  1.1115 +        textEvent.theText = mComposition.mLastData;
  1.1116 +        if (action.mRanges->IsEmpty()) {
  1.1117 +          TextRange wholeRange;
  1.1118 +          wholeRange.mStartOffset = 0;
  1.1119 +          wholeRange.mEndOffset = textEvent.theText.Length();
  1.1120 +          wholeRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
  1.1121 +          action.mRanges->AppendElement(wholeRange);
  1.1122 +        }
  1.1123 +        textEvent.mRanges = action.mRanges;
  1.1124 +        mWidget->DispatchWindowEvent(&textEvent);
  1.1125 +        // Be aware, the mWidget might already have been destroyed.
  1.1126 +        break;
  1.1127 +      }
  1.1128 +      case PendingAction::COMPOSITION_END: {
  1.1129 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1130 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1.1131 +                "flushing COMPOSITION_END={ mData=\"%s\" }",
  1.1132 +                this, NS_ConvertUTF16toUTF8(action.mData).get()));
  1.1133 +
  1.1134 +        action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1.1135 +                                      NS_LITERAL_STRING("\n"));
  1.1136 +        if (action.mData != mComposition.mLastData) {
  1.1137 +          PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1138 +                 ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1.1139 +                  "dispatching compositionupdate event...", this));
  1.1140 +          WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
  1.1141 +                                                   mWidget);
  1.1142 +          mWidget->InitEvent(compositionUpdate);
  1.1143 +          compositionUpdate.data = action.mData;
  1.1144 +          mComposition.mLastData = compositionUpdate.data;
  1.1145 +          mWidget->DispatchWindowEvent(&compositionUpdate);
  1.1146 +          if (!mWidget || mWidget->Destroyed()) {
  1.1147 +            break;
  1.1148 +          }
  1.1149 +        }
  1.1150 +
  1.1151 +        MOZ_ASSERT(action.mData == mComposition.mLastData);
  1.1152 +
  1.1153 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1154 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1.1155 +                "dispatching text event...", this));
  1.1156 +        WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
  1.1157 +        mWidget->InitEvent(textEvent);
  1.1158 +        textEvent.theText = mComposition.mLastData;
  1.1159 +        mWidget->DispatchWindowEvent(&textEvent);
  1.1160 +        if (!mWidget || mWidget->Destroyed()) {
  1.1161 +          break;
  1.1162 +        }
  1.1163 +
  1.1164 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1165 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1.1166 +                "dispatching compositionend event...", this));
  1.1167 +        WidgetCompositionEvent compositionEnd(true, NS_COMPOSITION_END,
  1.1168 +                                              mWidget);
  1.1169 +        compositionEnd.data = mComposition.mLastData;
  1.1170 +        mWidget->InitEvent(compositionEnd);
  1.1171 +        mWidget->DispatchWindowEvent(&compositionEnd);
  1.1172 +        if (!mWidget || mWidget->Destroyed()) {
  1.1173 +          break;
  1.1174 +        }
  1.1175 +        mComposition.mLastData.Truncate();
  1.1176 +        break;
  1.1177 +      }
  1.1178 +      case PendingAction::SELECTION_SET: {
  1.1179 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1180 +               ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1.1181 +                "flushing SELECTION_SET={ mSelectionStart=%d, "
  1.1182 +                "mSelectionLength=%d, mSelectionReversed=%s }",
  1.1183 +                this, action.mSelectionStart, action.mSelectionLength,
  1.1184 +                GetBoolName(action.mSelectionReversed)));
  1.1185 +
  1.1186 +        WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
  1.1187 +        selectionSet.mOffset = 
  1.1188 +          static_cast<uint32_t>(action.mSelectionStart);
  1.1189 +        selectionSet.mLength =
  1.1190 +          static_cast<uint32_t>(action.mSelectionLength);
  1.1191 +        selectionSet.mReversed = action.mSelectionReversed;
  1.1192 +        break;
  1.1193 +      }
  1.1194 +      default:
  1.1195 +        MOZ_CRASH("unexpected action type");
  1.1196 +    }
  1.1197 +
  1.1198 +    if (mWidget && !mWidget->Destroyed()) {
  1.1199 +      continue;
  1.1200 +    }
  1.1201 +
  1.1202 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1203 +           ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1.1204 +            "qutting since the mWidget has gone", this));
  1.1205 +    break;
  1.1206 +  }
  1.1207 +  mPendingActions.Clear();
  1.1208 +}
  1.1209 +
  1.1210 +STDMETHODIMP
  1.1211 +nsTextStore::GetStatus(TS_STATUS *pdcs)
  1.1212 +{
  1.1213 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1214 +    ("TSF: 0x%p nsTextStore::GetStatus(pdcs=0x%p)", this, pdcs));
  1.1215 +
  1.1216 +  if (!pdcs) {
  1.1217 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1218 +      ("TSF: 0x%p   nsTextStore::GetStatus() FAILED due to null pdcs", this));
  1.1219 +    return E_INVALIDARG;
  1.1220 +  }
  1.1221 +  pdcs->dwDynamicFlags = 0;
  1.1222 +  // we use a "flat" text model for TSF support so no hidden text
  1.1223 +  pdcs->dwStaticFlags = TS_SS_NOHIDDENTEXT;
  1.1224 +  return S_OK;
  1.1225 +}
  1.1226 +
  1.1227 +STDMETHODIMP
  1.1228 +nsTextStore::QueryInsert(LONG acpTestStart,
  1.1229 +                         LONG acpTestEnd,
  1.1230 +                         ULONG cch,
  1.1231 +                         LONG *pacpResultStart,
  1.1232 +                         LONG *pacpResultEnd)
  1.1233 +{
  1.1234 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1235 +         ("TSF: 0x%p nsTextStore::QueryInsert(acpTestStart=%ld, "
  1.1236 +          "acpTestEnd=%ld, cch=%lu, pacpResultStart=0x%p, pacpResultEnd=0x%p)",
  1.1237 +          this, acpTestStart, acpTestEnd, cch, acpTestStart, acpTestEnd));
  1.1238 +
  1.1239 +  if (!pacpResultStart || !pacpResultEnd) {
  1.1240 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1241 +           ("TSF: 0x%p   nsTextStore::QueryInsert() FAILED due to "
  1.1242 +            "the null argument", this));
  1.1243 +    return E_INVALIDARG;
  1.1244 +  }
  1.1245 +
  1.1246 +  if (acpTestStart < 0 || acpTestStart > acpTestEnd) {
  1.1247 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1248 +           ("TSF: 0x%p   nsTextStore::QueryInsert() FAILED due to "
  1.1249 +            "wrong argument", this));
  1.1250 +    return E_INVALIDARG;
  1.1251 +  }
  1.1252 +
  1.1253 +  // XXX need to adjust to cluster boundary
  1.1254 +  // Assume we are given good offsets for now
  1.1255 +  *pacpResultStart = acpTestStart;
  1.1256 +  *pacpResultEnd = acpTestStart + cch;
  1.1257 +
  1.1258 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1259 +         ("TSF: 0x%p  nsTextStore::QueryInsert() succeeded: "
  1.1260 +          "*pacpResultStart=%ld, *pacpResultEnd=%ld)",
  1.1261 +          this, *pacpResultStart, *pacpResultEnd));
  1.1262 +  return S_OK;
  1.1263 +}
  1.1264 +
  1.1265 +STDMETHODIMP
  1.1266 +nsTextStore::GetSelection(ULONG ulIndex,
  1.1267 +                          ULONG ulCount,
  1.1268 +                          TS_SELECTION_ACP *pSelection,
  1.1269 +                          ULONG *pcFetched)
  1.1270 +{
  1.1271 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1272 +         ("TSF: 0x%p nsTextStore::GetSelection(ulIndex=%lu, ulCount=%lu, "
  1.1273 +          "pSelection=0x%p, pcFetched=0x%p)",
  1.1274 +          this, ulIndex, ulCount, pSelection, pcFetched));
  1.1275 +
  1.1276 +  if (!IsReadLocked()) {
  1.1277 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1278 +           ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to not locked",
  1.1279 +            this));
  1.1280 +    return TS_E_NOLOCK;
  1.1281 +  }
  1.1282 +  if (!ulCount || !pSelection || !pcFetched) {
  1.1283 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1284 +           ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to "
  1.1285 +            "null argument", this));
  1.1286 +    return E_INVALIDARG;
  1.1287 +  }
  1.1288 +
  1.1289 +  *pcFetched = 0;
  1.1290 +
  1.1291 +  if (ulIndex != static_cast<ULONG>(TS_DEFAULT_SELECTION) &&
  1.1292 +      ulIndex != 0) {
  1.1293 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1294 +           ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to "
  1.1295 +            "unsupported selection", this));
  1.1296 +    return TS_E_NOSELECTION;
  1.1297 +  }
  1.1298 +
  1.1299 +  Selection& currentSel = CurrentSelection();
  1.1300 +  if (currentSel.IsDirty()) {
  1.1301 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1302 +           ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to "
  1.1303 +            "CurrentSelection() failure", this));
  1.1304 +    return E_FAIL;
  1.1305 +  }
  1.1306 +  *pSelection = currentSel.ACP();
  1.1307 +  *pcFetched = 1;
  1.1308 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1309 +         ("TSF: 0x%p   nsTextStore::GetSelection() succeeded", this));
  1.1310 +  return S_OK;
  1.1311 +}
  1.1312 +
  1.1313 +nsTextStore::Content&
  1.1314 +nsTextStore::CurrentContent()
  1.1315 +{
  1.1316 +  Selection& currentSel = CurrentSelection();
  1.1317 +  if (currentSel.IsDirty()) {
  1.1318 +    mContent.Clear();
  1.1319 +    return mContent;
  1.1320 +  }
  1.1321 +
  1.1322 +  if (!mContent.IsInitialized()) {
  1.1323 +    MOZ_ASSERT(mWidget && !mWidget->Destroyed());
  1.1324 +
  1.1325 +    WidgetQueryContentEvent queryText(true, NS_QUERY_TEXT_CONTENT, mWidget);
  1.1326 +    queryText.InitForQueryTextContent(0, UINT32_MAX);
  1.1327 +    mWidget->InitEvent(queryText);
  1.1328 +    mWidget->DispatchWindowEvent(&queryText);
  1.1329 +    NS_ENSURE_TRUE(queryText.mSucceeded, mContent);
  1.1330 +
  1.1331 +    mContent.Init(queryText.mReply.mString);
  1.1332 +  }
  1.1333 +
  1.1334 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1335 +         ("TSF: 0x%p   nsTextStore::CurrentContent(): "
  1.1336 +          "mContent={ mText.Length()=%d }",
  1.1337 +          this, mContent.Text().Length()));
  1.1338 +
  1.1339 +  return mContent;
  1.1340 +}
  1.1341 +
  1.1342 +nsTextStore::Selection&
  1.1343 +nsTextStore::CurrentSelection()
  1.1344 +{
  1.1345 +  if (mSelection.IsDirty()) {
  1.1346 +    // If the window has never been available, we should crash since working
  1.1347 +    // with broken values may make TIP confused.
  1.1348 +    if (!mWidget || mWidget->Destroyed()) {
  1.1349 +      MOZ_CRASH();
  1.1350 +    }
  1.1351 +
  1.1352 +    WidgetQueryContentEvent querySelection(true, NS_QUERY_SELECTED_TEXT,
  1.1353 +                                           mWidget);
  1.1354 +    mWidget->InitEvent(querySelection);
  1.1355 +    mWidget->DispatchWindowEvent(&querySelection);
  1.1356 +    NS_ENSURE_TRUE(querySelection.mSucceeded, mSelection);
  1.1357 +
  1.1358 +    mSelection.SetSelection(querySelection.mReply.mOffset,
  1.1359 +                            querySelection.mReply.mString.Length(),
  1.1360 +                            querySelection.mReply.mReversed);
  1.1361 +  }
  1.1362 +
  1.1363 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1364 +         ("TSF: 0x%p   nsTextStore::CurrentSelection(): "
  1.1365 +          "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
  1.1366 +          this, mSelection.StartOffset(), mSelection.EndOffset(),
  1.1367 +          mSelection.Length(),
  1.1368 +          GetBoolName(mSelection.IsReversed())));
  1.1369 +
  1.1370 +  return mSelection;
  1.1371 +}
  1.1372 +
  1.1373 +static HRESULT
  1.1374 +GetRangeExtent(ITfRange* aRange, LONG* aStart, LONG* aLength)
  1.1375 +{
  1.1376 +  nsRefPtr<ITfRangeACP> rangeACP;
  1.1377 +  aRange->QueryInterface(IID_ITfRangeACP, getter_AddRefs(rangeACP));
  1.1378 +  NS_ENSURE_TRUE(rangeACP, E_FAIL);
  1.1379 +  return rangeACP->GetExtent(aStart, aLength);
  1.1380 +}
  1.1381 +
  1.1382 +static uint32_t
  1.1383 +GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE &aDisplayAttr)
  1.1384 +{
  1.1385 +  uint32_t result;
  1.1386 +  switch (aDisplayAttr.bAttr) {
  1.1387 +    case TF_ATTR_TARGET_CONVERTED:
  1.1388 +      result = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
  1.1389 +      break;
  1.1390 +    case TF_ATTR_CONVERTED:
  1.1391 +      result = NS_TEXTRANGE_CONVERTEDTEXT;
  1.1392 +      break;
  1.1393 +    case TF_ATTR_TARGET_NOTCONVERTED:
  1.1394 +      result = NS_TEXTRANGE_SELECTEDRAWTEXT;
  1.1395 +      break;
  1.1396 +    default:
  1.1397 +      result = NS_TEXTRANGE_RAWINPUT;
  1.1398 +      break;
  1.1399 +  }
  1.1400 +  return result;
  1.1401 +}
  1.1402 +
  1.1403 +HRESULT
  1.1404 +nsTextStore::GetDisplayAttribute(ITfProperty* aAttrProperty,
  1.1405 +                                 ITfRange* aRange,
  1.1406 +                                 TF_DISPLAYATTRIBUTE* aResult)
  1.1407 +{
  1.1408 +  NS_ENSURE_TRUE(aAttrProperty, E_FAIL);
  1.1409 +  NS_ENSURE_TRUE(aRange, E_FAIL);
  1.1410 +  NS_ENSURE_TRUE(aResult, E_FAIL);
  1.1411 +
  1.1412 +  HRESULT hr;
  1.1413 +
  1.1414 +#ifdef PR_LOGGING
  1.1415 +  if (PR_LOG_TEST(sTextStoreLog, PR_LOG_DEBUG)) {
  1.1416 +    LONG start = 0, length = 0;
  1.1417 +    hr = GetRangeExtent(aRange, &start, &length);
  1.1418 +    PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1419 +           ("TSF: 0x%p   nsTextStore::GetDisplayAttribute(): "
  1.1420 +            "GetDisplayAttribute range=%ld-%ld (hr=%s)",
  1.1421 +            this, start - mComposition.mStart,
  1.1422 +            start - mComposition.mStart + length,
  1.1423 +            GetCommonReturnValueName(hr)));
  1.1424 +  }
  1.1425 +#endif
  1.1426 +
  1.1427 +  VARIANT propValue;
  1.1428 +  ::VariantInit(&propValue);
  1.1429 +  hr = aAttrProperty->GetValue(TfEditCookie(mEditCookie), aRange, &propValue);
  1.1430 +  if (FAILED(hr)) {
  1.1431 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1432 +           ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1.1433 +            "ITfProperty::GetValue() failed", this));
  1.1434 +    return hr;
  1.1435 +  }
  1.1436 +  if (VT_I4 != propValue.vt) {
  1.1437 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1438 +           ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1.1439 +            "ITfProperty::GetValue() returns non-VT_I4 value", this));
  1.1440 +    ::VariantClear(&propValue);
  1.1441 +    return E_FAIL;
  1.1442 +  }
  1.1443 +
  1.1444 +  NS_ENSURE_TRUE(sCategoryMgr, E_FAIL);
  1.1445 +  GUID guid;
  1.1446 +  hr = sCategoryMgr->GetGUID(DWORD(propValue.lVal), &guid);
  1.1447 +  ::VariantClear(&propValue);
  1.1448 +  if (FAILED(hr)) {
  1.1449 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1450 +           ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1.1451 +            "ITfCategoryMgr::GetGUID() failed", this));
  1.1452 +    return hr;
  1.1453 +  }
  1.1454 +
  1.1455 +  NS_ENSURE_TRUE(sDisplayAttrMgr, E_FAIL);
  1.1456 +  nsRefPtr<ITfDisplayAttributeInfo> info;
  1.1457 +  hr = sDisplayAttrMgr->GetDisplayAttributeInfo(guid, getter_AddRefs(info),
  1.1458 +                                                nullptr);
  1.1459 +  if (FAILED(hr) || !info) {
  1.1460 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1461 +           ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1.1462 +            "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed", this));
  1.1463 +    return hr;
  1.1464 +  }
  1.1465 +
  1.1466 +  hr = info->GetAttributeInfo(aResult);
  1.1467 +  if (FAILED(hr)) {
  1.1468 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1469 +           ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1.1470 +            "ITfDisplayAttributeInfo::GetAttributeInfo() failed", this));
  1.1471 +    return hr;
  1.1472 +  }
  1.1473 +
  1.1474 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1475 +         ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() succeeded: "
  1.1476 +          "Result={ %s }", this, GetDisplayAttrStr(*aResult).get()));
  1.1477 +  return S_OK;
  1.1478 +}
  1.1479 +
  1.1480 +HRESULT
  1.1481 +nsTextStore::RestartCompositionIfNecessary(ITfRange* aRangeNew)
  1.1482 +{
  1.1483 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1484 +         ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary("
  1.1485 +          "aRangeNew=0x%p), mComposition.mView=0x%p",
  1.1486 +          this, aRangeNew, mComposition.mView.get()));
  1.1487 +
  1.1488 +  if (!mComposition.IsComposing()) {
  1.1489 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1490 +           ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() FAILED "
  1.1491 +            "due to no composition view", this));
  1.1492 +    return E_FAIL;
  1.1493 +  }
  1.1494 +
  1.1495 +  HRESULT hr;
  1.1496 +  nsRefPtr<ITfCompositionView> pComposition(mComposition.mView);
  1.1497 +  nsRefPtr<ITfRange> composingRange(aRangeNew);
  1.1498 +  if (!composingRange) {
  1.1499 +    hr = pComposition->GetRange(getter_AddRefs(composingRange));
  1.1500 +    if (FAILED(hr)) {
  1.1501 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1502 +             ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() FAILED "
  1.1503 +              "due to pComposition->GetRange() failure", this));
  1.1504 +      return hr;
  1.1505 +    }
  1.1506 +  }
  1.1507 +
  1.1508 +  // Get starting offset of the composition
  1.1509 +  LONG compStart = 0, compLength = 0;
  1.1510 +  hr = GetRangeExtent(composingRange, &compStart, &compLength);
  1.1511 +  if (FAILED(hr)) {
  1.1512 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1513 +           ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() FAILED "
  1.1514 +            "due to GetRangeExtent() failure", this));
  1.1515 +    return hr;
  1.1516 +  }
  1.1517 +
  1.1518 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1519 +         ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary(), "
  1.1520 +          "range=%ld-%ld, mComposition={ mStart=%ld, mString.Length()=%lu }",
  1.1521 +          this, compStart, compStart + compLength, mComposition.mStart,
  1.1522 +          mComposition.mString.Length()));
  1.1523 +
  1.1524 +  if (mComposition.mStart != compStart ||
  1.1525 +      mComposition.mString.Length() != (ULONG)compLength) {
  1.1526 +    // If the queried composition length is different from the length
  1.1527 +    // of our composition string, OnUpdateComposition is being called
  1.1528 +    // because a part of the original composition was committed.
  1.1529 +    // Reflect that by committing existing composition and starting
  1.1530 +    // a new one. RecordCompositionEndAction() followed by
  1.1531 +    // RecordCompositionStartAction() will accomplish this automagically.
  1.1532 +    RecordCompositionEndAction();
  1.1533 +    RecordCompositionStartAction(pComposition, composingRange, true);
  1.1534 +  }
  1.1535 +
  1.1536 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1537 +         ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() succeeded",
  1.1538 +          this));
  1.1539 +  return S_OK;
  1.1540 +}
  1.1541 +
  1.1542 +static bool
  1.1543 +GetColor(const TF_DA_COLOR &aTSFColor, nscolor &aResult)
  1.1544 +{
  1.1545 +  switch (aTSFColor.type) {
  1.1546 +    case TF_CT_SYSCOLOR: {
  1.1547 +      DWORD sysColor = ::GetSysColor(aTSFColor.nIndex);
  1.1548 +      aResult = NS_RGB(GetRValue(sysColor), GetGValue(sysColor),
  1.1549 +                       GetBValue(sysColor));
  1.1550 +      return true;
  1.1551 +    }
  1.1552 +    case TF_CT_COLORREF:
  1.1553 +      aResult = NS_RGB(GetRValue(aTSFColor.cr), GetGValue(aTSFColor.cr),
  1.1554 +                       GetBValue(aTSFColor.cr));
  1.1555 +      return true;
  1.1556 +    case TF_CT_NONE:
  1.1557 +    default:
  1.1558 +      return false;
  1.1559 +  }
  1.1560 +}
  1.1561 +
  1.1562 +static bool
  1.1563 +GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle, uint8_t &aTextRangeLineStyle)
  1.1564 +{
  1.1565 +  switch (aTSFLineStyle) {
  1.1566 +    case TF_LS_NONE:
  1.1567 +      aTextRangeLineStyle = TextRangeStyle::LINESTYLE_NONE;
  1.1568 +      return true;
  1.1569 +    case TF_LS_SOLID:
  1.1570 +      aTextRangeLineStyle = TextRangeStyle::LINESTYLE_SOLID;
  1.1571 +      return true;
  1.1572 +    case TF_LS_DOT:
  1.1573 +      aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DOTTED;
  1.1574 +      return true;
  1.1575 +    case TF_LS_DASH:
  1.1576 +      aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DASHED;
  1.1577 +      return true;
  1.1578 +    case TF_LS_SQUIGGLE:
  1.1579 +      aTextRangeLineStyle = TextRangeStyle::LINESTYLE_WAVY;
  1.1580 +      return true;
  1.1581 +    default:
  1.1582 +      return false;
  1.1583 +  }
  1.1584 +}
  1.1585 +
  1.1586 +HRESULT
  1.1587 +nsTextStore::RecordCompositionUpdateAction()
  1.1588 +{
  1.1589 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1590 +         ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction(), "
  1.1591 +          "mComposition={ mView=0x%p, mString=\"%s\" }",
  1.1592 +          this, mComposition.mView.get(),
  1.1593 +          NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  1.1594 +
  1.1595 +  if (!mComposition.IsComposing()) {
  1.1596 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1597 +           ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1.1598 +            "due to no composition view", this));
  1.1599 +    return E_FAIL;
  1.1600 +  }
  1.1601 +
  1.1602 +  // Getting display attributes is *really* complicated!
  1.1603 +  // We first get the context and the property objects to query for
  1.1604 +  // attributes, but since a big range can have a variety of values for
  1.1605 +  // the attribute, we have to find out all the ranges that have distinct
  1.1606 +  // attribute values. Then we query for what the value represents through
  1.1607 +  // the display attribute manager and translate that to TextRange to be
  1.1608 +  // sent in NS_TEXT_TEXT
  1.1609 +
  1.1610 +  nsRefPtr<ITfProperty> attrPropetry;
  1.1611 +  HRESULT hr = mContext->GetProperty(GUID_PROP_ATTRIBUTE,
  1.1612 +                                     getter_AddRefs(attrPropetry));
  1.1613 +  if (FAILED(hr) || !attrPropetry) {
  1.1614 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1615 +           ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1.1616 +            "due to mContext->GetProperty() failure", this));
  1.1617 +    return FAILED(hr) ? hr : E_FAIL;
  1.1618 +  }
  1.1619 +
  1.1620 +  nsRefPtr<ITfRange> composingRange;
  1.1621 +  hr = mComposition.mView->GetRange(getter_AddRefs(composingRange));
  1.1622 +  if (FAILED(hr)) {
  1.1623 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1624 +           ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() "
  1.1625 +            "FAILED due to mComposition.mView->GetRange() failure", this));
  1.1626 +    return hr;
  1.1627 +  }
  1.1628 +
  1.1629 +  nsRefPtr<IEnumTfRanges> enumRanges;
  1.1630 +  hr = attrPropetry->EnumRanges(TfEditCookie(mEditCookie),
  1.1631 +                                getter_AddRefs(enumRanges), composingRange);
  1.1632 +  if (FAILED(hr) || !enumRanges) {
  1.1633 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1634 +           ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1.1635 +            "due to attrPropetry->EnumRanges() failure", this));
  1.1636 +    return FAILED(hr) ? hr : E_FAIL;
  1.1637 +  }
  1.1638 +
  1.1639 +  // First, put the log of content and selection here.
  1.1640 +  Selection& currentSel = CurrentSelection();
  1.1641 +  if (currentSel.IsDirty()) {
  1.1642 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1643 +           ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1.1644 +            "due to CurrentSelection() failure", this));
  1.1645 +    return E_FAIL;
  1.1646 +  }
  1.1647 +
  1.1648 +  PendingAction* action = GetPendingCompositionUpdate();
  1.1649 +  action->mData = mComposition.mString;
  1.1650 +  // The ranges might already have been initialized, however, if this is
  1.1651 +  // called again, that means we need to overwrite the ranges with current
  1.1652 +  // information.
  1.1653 +  action->mRanges->Clear();
  1.1654 +
  1.1655 +  TextRange newRange;
  1.1656 +  // No matter if we have display attribute info or not,
  1.1657 +  // we always pass in at least one range to NS_TEXT_TEXT
  1.1658 +  newRange.mStartOffset = 0;
  1.1659 +  newRange.mEndOffset = action->mData.Length();
  1.1660 +  newRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
  1.1661 +  action->mRanges->AppendElement(newRange);
  1.1662 +
  1.1663 +  nsRefPtr<ITfRange> range;
  1.1664 +  while (S_OK == enumRanges->Next(1, getter_AddRefs(range), nullptr) && range) {
  1.1665 +
  1.1666 +    LONG start = 0, length = 0;
  1.1667 +    if (FAILED(GetRangeExtent(range, &start, &length)))
  1.1668 +      continue;
  1.1669 +
  1.1670 +    TextRange newRange;
  1.1671 +    newRange.mStartOffset = uint32_t(start - mComposition.mStart);
  1.1672 +    // The end of the last range in the array is
  1.1673 +    // always kept at the end of composition
  1.1674 +    newRange.mEndOffset = mComposition.mString.Length();
  1.1675 +
  1.1676 +    TF_DISPLAYATTRIBUTE attr;
  1.1677 +    hr = GetDisplayAttribute(attrPropetry, range, &attr);
  1.1678 +    if (FAILED(hr)) {
  1.1679 +      newRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
  1.1680 +    } else {
  1.1681 +      newRange.mRangeType = GetGeckoSelectionValue(attr);
  1.1682 +      if (GetColor(attr.crText, newRange.mRangeStyle.mForegroundColor)) {
  1.1683 +        newRange.mRangeStyle.mDefinedStyles |=
  1.1684 +                               TextRangeStyle::DEFINED_FOREGROUND_COLOR;
  1.1685 +      }
  1.1686 +      if (GetColor(attr.crBk, newRange.mRangeStyle.mBackgroundColor)) {
  1.1687 +        newRange.mRangeStyle.mDefinedStyles |=
  1.1688 +                               TextRangeStyle::DEFINED_BACKGROUND_COLOR;
  1.1689 +      }
  1.1690 +      if (GetColor(attr.crLine, newRange.mRangeStyle.mUnderlineColor)) {
  1.1691 +        newRange.mRangeStyle.mDefinedStyles |=
  1.1692 +                               TextRangeStyle::DEFINED_UNDERLINE_COLOR;
  1.1693 +      }
  1.1694 +      if (GetLineStyle(attr.lsStyle, newRange.mRangeStyle.mLineStyle)) {
  1.1695 +        newRange.mRangeStyle.mDefinedStyles |=
  1.1696 +                               TextRangeStyle::DEFINED_LINESTYLE;
  1.1697 +        newRange.mRangeStyle.mIsBoldLine = attr.fBoldLine != 0;
  1.1698 +      }
  1.1699 +    }
  1.1700 +
  1.1701 +    TextRange& lastRange = action->mRanges->LastElement();
  1.1702 +    if (lastRange.mStartOffset == newRange.mStartOffset) {
  1.1703 +      // Replace range if last range is the same as this one
  1.1704 +      // So that ranges don't overlap and confuse the editor
  1.1705 +      lastRange = newRange;
  1.1706 +    } else {
  1.1707 +      lastRange.mEndOffset = newRange.mStartOffset;
  1.1708 +      action->mRanges->AppendElement(newRange);
  1.1709 +    }
  1.1710 +  }
  1.1711 +
  1.1712 +  // We need to hack for Korean Input System which is Korean standard TIP.
  1.1713 +  // It sets no change style to IME selection (the selection is always only
  1.1714 +  // one).  So, the composition string looks like normal (or committed) string.
  1.1715 +  // At this time, current selection range is same as the composition string
  1.1716 +  // range.  Other applications set a wide caret which covers the composition
  1.1717 +  // string,  however, Gecko doesn't support the wide caret drawing now (Gecko
  1.1718 +  // doesn't support XOR drawing), unfortunately.  For now, we should change
  1.1719 +  // the range style to undefined.
  1.1720 +  if (!currentSel.IsCollapsed() && action->mRanges->Length() == 1) {
  1.1721 +    TextRange& range = action->mRanges->ElementAt(0);
  1.1722 +    LONG start = currentSel.MinOffset();
  1.1723 +    LONG end = currentSel.MaxOffset();
  1.1724 +    if ((LONG)range.mStartOffset == start - mComposition.mStart &&
  1.1725 +        (LONG)range.mEndOffset == end - mComposition.mStart &&
  1.1726 +        range.mRangeStyle.IsNoChangeStyle()) {
  1.1727 +      range.mRangeStyle.Clear();
  1.1728 +      // The looks of selected type is better than others.
  1.1729 +      range.mRangeType = NS_TEXTRANGE_SELECTEDRAWTEXT;
  1.1730 +    }
  1.1731 +  }
  1.1732 +
  1.1733 +  // The caret position has to be collapsed.
  1.1734 +  LONG caretPosition = currentSel.MaxOffset();
  1.1735 +  caretPosition -= mComposition.mStart;
  1.1736 +  TextRange caretRange;
  1.1737 +  caretRange.mStartOffset = caretRange.mEndOffset = uint32_t(caretPosition);
  1.1738 +  caretRange.mRangeType = NS_TEXTRANGE_CARETPOSITION;
  1.1739 +  action->mRanges->AppendElement(caretRange);
  1.1740 +
  1.1741 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1742 +         ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() "
  1.1743 +          "succeeded", this));
  1.1744 +
  1.1745 +  return S_OK;
  1.1746 +}
  1.1747 +
  1.1748 +HRESULT
  1.1749 +nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
  1.1750 +                                  bool aDispatchTextEvent)
  1.1751 +{
  1.1752 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.1753 +         ("TSF: 0x%p   nsTextStore::SetSelectionInternal(pSelection={ "
  1.1754 +          "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s} }, "
  1.1755 +          "aDispatchTextEvent=%s), mComposition.IsComposing()=%s",
  1.1756 +          this, pSelection->acpStart, pSelection->acpEnd,
  1.1757 +          GetActiveSelEndName(pSelection->style.ase),
  1.1758 +          GetBoolName(pSelection->style.fInterimChar),
  1.1759 +          GetBoolName(aDispatchTextEvent),
  1.1760 +          GetBoolName(mComposition.IsComposing())));
  1.1761 +
  1.1762 +  MOZ_ASSERT(IsReadWriteLocked());
  1.1763 +
  1.1764 +  Selection& currentSel = CurrentSelection();
  1.1765 +  if (currentSel.IsDirty()) {
  1.1766 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1767 +       ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1.1768 +        "CurrentSelection() failure", this));
  1.1769 +    return E_FAIL;
  1.1770 +  }
  1.1771 +
  1.1772 +  if (mComposition.IsComposing()) {
  1.1773 +    if (aDispatchTextEvent) {
  1.1774 +      HRESULT hr = RestartCompositionIfNecessary();
  1.1775 +      if (FAILED(hr)) {
  1.1776 +        PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1777 +           ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1.1778 +            "RestartCompositionIfNecessary() failure", this));
  1.1779 +        return hr;
  1.1780 +      }
  1.1781 +    }
  1.1782 +    if (pSelection->acpStart < mComposition.mStart ||
  1.1783 +        pSelection->acpEnd > mComposition.EndOffset()) {
  1.1784 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1785 +         ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1.1786 +          "the selection being out of the composition string", this));
  1.1787 +      return TS_E_INVALIDPOS;
  1.1788 +    }
  1.1789 +    // Emulate selection during compositions
  1.1790 +    currentSel.SetSelection(*pSelection);
  1.1791 +    if (aDispatchTextEvent) {
  1.1792 +      HRESULT hr = RecordCompositionUpdateAction();
  1.1793 +      if (FAILED(hr)) {
  1.1794 +        PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1795 +           ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1.1796 +            "RecordCompositionUpdateAction() failure", this));
  1.1797 +        return hr;
  1.1798 +      }
  1.1799 +    }
  1.1800 +    return S_OK;
  1.1801 +  }
  1.1802 +
  1.1803 +  PendingAction* action = mPendingActions.AppendElement();
  1.1804 +  action->mType = PendingAction::SELECTION_SET;
  1.1805 +  action->mSelectionStart = pSelection->acpStart;
  1.1806 +  action->mSelectionLength = pSelection->acpEnd - pSelection->acpStart;
  1.1807 +  action->mSelectionReversed = (pSelection->style.ase == TS_AE_START);
  1.1808 +
  1.1809 +  currentSel.SetSelection(*pSelection);
  1.1810 +
  1.1811 +  return S_OK;
  1.1812 +}
  1.1813 +
  1.1814 +STDMETHODIMP
  1.1815 +nsTextStore::SetSelection(ULONG ulCount,
  1.1816 +                          const TS_SELECTION_ACP *pSelection)
  1.1817 +{
  1.1818 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1819 +         ("TSF: 0x%p nsTextStore::SetSelection(ulCount=%lu, pSelection=%p { "
  1.1820 +          "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s } }), "
  1.1821 +          "mComposition.IsComposing()=%s",
  1.1822 +          this, ulCount, pSelection,
  1.1823 +          pSelection ? pSelection->acpStart : 0,
  1.1824 +          pSelection ? pSelection->acpEnd : 0,
  1.1825 +          pSelection ? GetActiveSelEndName(pSelection->style.ase) : "",
  1.1826 +          pSelection ? GetBoolName(pSelection->style.fInterimChar) : "",
  1.1827 +          GetBoolName(mComposition.IsComposing())));
  1.1828 +
  1.1829 +  if (!IsReadWriteLocked()) {
  1.1830 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1831 +           ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1.1832 +            "not locked (read-write)", this));
  1.1833 +    return TS_E_NOLOCK;
  1.1834 +  }
  1.1835 +  if (ulCount != 1) {
  1.1836 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1837 +           ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1.1838 +            "trying setting multiple selection", this));
  1.1839 +    return E_INVALIDARG;
  1.1840 +  }
  1.1841 +  if (!pSelection) {
  1.1842 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1843 +           ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1.1844 +            "null argument", this));
  1.1845 +    return E_INVALIDARG;
  1.1846 +  }
  1.1847 +
  1.1848 +  HRESULT hr = SetSelectionInternal(pSelection, true);
  1.1849 +  if (FAILED(hr)) {
  1.1850 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1851 +           ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1.1852 +            "SetSelectionInternal() failure", this));
  1.1853 +  } else {
  1.1854 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1855 +           ("TSF: 0x%p   nsTextStore::SetSelection() succeeded", this));
  1.1856 +  }
  1.1857 +  return hr;
  1.1858 +}
  1.1859 +
  1.1860 +STDMETHODIMP
  1.1861 +nsTextStore::GetText(LONG acpStart,
  1.1862 +                     LONG acpEnd,
  1.1863 +                     WCHAR *pchPlain,
  1.1864 +                     ULONG cchPlainReq,
  1.1865 +                     ULONG *pcchPlainOut,
  1.1866 +                     TS_RUNINFO *prgRunInfo,
  1.1867 +                     ULONG ulRunInfoReq,
  1.1868 +                     ULONG *pulRunInfoOut,
  1.1869 +                     LONG *pacpNext)
  1.1870 +{
  1.1871 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1872 +    ("TSF: 0x%p nsTextStore::GetText(acpStart=%ld, acpEnd=%ld, pchPlain=0x%p, "
  1.1873 +     "cchPlainReq=%lu, pcchPlainOut=0x%p, prgRunInfo=0x%p, ulRunInfoReq=%lu, "
  1.1874 +     "pulRunInfoOut=0x%p, pacpNext=0x%p), mComposition={ mStart=%ld, "
  1.1875 +     "mString.Length()=%lu, IsComposing()=%s }",
  1.1876 +     this, acpStart, acpEnd, pchPlain, cchPlainReq, pcchPlainOut,
  1.1877 +     prgRunInfo, ulRunInfoReq, pulRunInfoOut, pacpNext,
  1.1878 +     mComposition.mStart, mComposition.mString.Length(),
  1.1879 +     GetBoolName(mComposition.IsComposing())));
  1.1880 +
  1.1881 +  if (!IsReadLocked()) {
  1.1882 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1883 +           ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1.1884 +            "not locked (read)", this));
  1.1885 +    return TS_E_NOLOCK;
  1.1886 +  }
  1.1887 +
  1.1888 +  if (!pcchPlainOut || (!pchPlain && !prgRunInfo) ||
  1.1889 +      !cchPlainReq != !pchPlain || !ulRunInfoReq != !prgRunInfo) {
  1.1890 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1891 +           ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1.1892 +            "invalid argument", this));
  1.1893 +    return E_INVALIDARG;
  1.1894 +  }
  1.1895 +
  1.1896 +  if (acpStart < 0 || acpEnd < -1 || (acpEnd != -1 && acpStart > acpEnd)) {
  1.1897 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1898 +           ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1.1899 +            "invalid position", this));
  1.1900 +    return TS_E_INVALIDPOS;
  1.1901 +  }
  1.1902 +
  1.1903 +  // Making sure to null-terminate string just to be on the safe side
  1.1904 +  *pcchPlainOut = 0;
  1.1905 +  if (pchPlain && cchPlainReq) *pchPlain = 0;
  1.1906 +  if (pulRunInfoOut) *pulRunInfoOut = 0;
  1.1907 +  if (pacpNext) *pacpNext = acpStart;
  1.1908 +  if (prgRunInfo && ulRunInfoReq) {
  1.1909 +    prgRunInfo->uCount = 0;
  1.1910 +    prgRunInfo->type = TS_RT_PLAIN;
  1.1911 +  }
  1.1912 +
  1.1913 +  Content& currentContent = CurrentContent();
  1.1914 +  if (!currentContent.IsInitialized()) {
  1.1915 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1916 +           ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1.1917 +            "CurrentContent() failure", this));
  1.1918 +    return E_FAIL;
  1.1919 +  }
  1.1920 +  if (currentContent.Text().Length() < static_cast<uint32_t>(acpStart)) {
  1.1921 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1922 +           ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1.1923 +            "acpStart is larger offset than the actual text length", this));
  1.1924 +    return TS_E_INVALIDPOS;
  1.1925 +  }
  1.1926 +  if (acpEnd != -1 &&
  1.1927 +      currentContent.Text().Length() < static_cast<uint32_t>(acpEnd)) {
  1.1928 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1929 +           ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1.1930 +            "acpEnd is larger offset than the actual text length", this));
  1.1931 +    return TS_E_INVALIDPOS;
  1.1932 +  }
  1.1933 +  uint32_t length = (acpEnd == -1) ?
  1.1934 +    currentContent.Text().Length() - static_cast<uint32_t>(acpStart) :
  1.1935 +    static_cast<uint32_t>(acpEnd - acpStart);
  1.1936 +  if (cchPlainReq && cchPlainReq - 1 < length) {
  1.1937 +    length = cchPlainReq - 1;
  1.1938 +  }
  1.1939 +  if (length) {
  1.1940 +    if (pchPlain && cchPlainReq) {
  1.1941 +      const char16_t* startChar =
  1.1942 +        currentContent.Text().BeginReading() + acpStart;
  1.1943 +      memcpy(pchPlain, startChar, length * sizeof(*pchPlain));
  1.1944 +      pchPlain[length] = 0;
  1.1945 +      *pcchPlainOut = length;
  1.1946 +    }
  1.1947 +    if (prgRunInfo && ulRunInfoReq) {
  1.1948 +      prgRunInfo->uCount = length;
  1.1949 +      prgRunInfo->type = TS_RT_PLAIN;
  1.1950 +      if (pulRunInfoOut) *pulRunInfoOut = 1;
  1.1951 +    }
  1.1952 +    if (pacpNext) *pacpNext = acpStart + length;
  1.1953 +  }
  1.1954 +
  1.1955 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1956 +         ("TSF: 0x%p   nsTextStore::GetText() succeeded: pcchPlainOut=0x%p, "
  1.1957 +          "*prgRunInfo={ uCount=%lu, type=%s }, *pulRunInfoOut=%lu, "
  1.1958 +          "*pacpNext=%ld)",
  1.1959 +          this, pcchPlainOut, prgRunInfo ? prgRunInfo->uCount : 0,
  1.1960 +          prgRunInfo ? GetTextRunTypeName(prgRunInfo->type) : "N/A",
  1.1961 +          pulRunInfoOut ? pulRunInfoOut : 0, pacpNext ? pacpNext : 0));
  1.1962 +  return S_OK;
  1.1963 +}
  1.1964 +
  1.1965 +STDMETHODIMP
  1.1966 +nsTextStore::SetText(DWORD dwFlags,
  1.1967 +                     LONG acpStart,
  1.1968 +                     LONG acpEnd,
  1.1969 +                     const WCHAR *pchText,
  1.1970 +                     ULONG cch,
  1.1971 +                     TS_TEXTCHANGE *pChange)
  1.1972 +{
  1.1973 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.1974 +         ("TSF: 0x%p nsTextStore::SetText(dwFlags=%s, acpStart=%ld, "
  1.1975 +          "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), "
  1.1976 +          "mComposition.IsComposing()=%s",
  1.1977 +          this, dwFlags == TS_ST_CORRECTION ? "TS_ST_CORRECTION" :
  1.1978 +                                              "not-specified",
  1.1979 +          acpStart, acpEnd, pchText,
  1.1980 +          pchText && cch ?
  1.1981 +            NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
  1.1982 +          cch, pChange, GetBoolName(mComposition.IsComposing())));
  1.1983 +
  1.1984 +  // Per SDK documentation, and since we don't have better
  1.1985 +  // ways to do this, this method acts as a helper to
  1.1986 +  // call SetSelection followed by InsertTextAtSelection
  1.1987 +  if (!IsReadWriteLocked()) {
  1.1988 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.1989 +           ("TSF: 0x%p   nsTextStore::SetText() FAILED due to "
  1.1990 +            "not locked (read)", this));
  1.1991 +    return TS_E_NOLOCK;
  1.1992 +  }
  1.1993 +
  1.1994 +  TS_SELECTION_ACP selection;
  1.1995 +  selection.acpStart = acpStart;
  1.1996 +  selection.acpEnd = acpEnd;
  1.1997 +  selection.style.ase = TS_AE_END;
  1.1998 +  selection.style.fInterimChar = 0;
  1.1999 +  // Set selection to desired range
  1.2000 +  HRESULT hr = SetSelectionInternal(&selection);
  1.2001 +  if (FAILED(hr)) {
  1.2002 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2003 +           ("TSF: 0x%p   nsTextStore::SetText() FAILED due to "
  1.2004 +            "SetSelectionInternal() failure", this));
  1.2005 +    return hr;
  1.2006 +  }
  1.2007 +  // Replace just selected text
  1.2008 +  if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
  1.2009 +                                     pChange)) {
  1.2010 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2011 +           ("TSF: 0x%p   nsTextStore::SetText() FAILED due to "
  1.2012 +            "InsertTextAtSelectionInternal() failure", this));
  1.2013 +    return E_FAIL;
  1.2014 +  }
  1.2015 +
  1.2016 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2017 +         ("TSF: 0x%p   nsTextStore::SetText() succeeded: pChange={ "
  1.2018 +          "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
  1.2019 +          this, pChange ? pChange->acpStart  : 0,
  1.2020 +          pChange ? pChange->acpOldEnd : 0, pChange ? pChange->acpNewEnd : 0));
  1.2021 +  return S_OK;
  1.2022 +}
  1.2023 +
  1.2024 +STDMETHODIMP
  1.2025 +nsTextStore::GetFormattedText(LONG acpStart,
  1.2026 +                              LONG acpEnd,
  1.2027 +                              IDataObject **ppDataObject)
  1.2028 +{
  1.2029 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2030 +         ("TSF: 0x%p nsTextStore::GetFormattedText() called "
  1.2031 +          "but not supported (E_NOTIMPL)", this));
  1.2032 +
  1.2033 +  // no support for formatted text
  1.2034 +  return E_NOTIMPL;
  1.2035 +}
  1.2036 +
  1.2037 +STDMETHODIMP
  1.2038 +nsTextStore::GetEmbedded(LONG acpPos,
  1.2039 +                         REFGUID rguidService,
  1.2040 +                         REFIID riid,
  1.2041 +                         IUnknown **ppunk)
  1.2042 +{
  1.2043 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2044 +         ("TSF: 0x%p nsTextStore::GetEmbedded() called "
  1.2045 +          "but not supported (E_NOTIMPL)", this));
  1.2046 +
  1.2047 +  // embedded objects are not supported
  1.2048 +  return E_NOTIMPL;
  1.2049 +}
  1.2050 +
  1.2051 +STDMETHODIMP
  1.2052 +nsTextStore::QueryInsertEmbedded(const GUID *pguidService,
  1.2053 +                                 const FORMATETC *pFormatEtc,
  1.2054 +                                 BOOL *pfInsertable)
  1.2055 +{
  1.2056 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2057 +         ("TSF: 0x%p nsTextStore::QueryInsertEmbedded() called "
  1.2058 +          "but not supported, *pfInsertable=FALSE (S_OK)", this));
  1.2059 +
  1.2060 +  // embedded objects are not supported
  1.2061 +  *pfInsertable = FALSE;
  1.2062 +  return S_OK;
  1.2063 +}
  1.2064 +
  1.2065 +STDMETHODIMP
  1.2066 +nsTextStore::InsertEmbedded(DWORD dwFlags,
  1.2067 +                            LONG acpStart,
  1.2068 +                            LONG acpEnd,
  1.2069 +                            IDataObject *pDataObject,
  1.2070 +                            TS_TEXTCHANGE *pChange)
  1.2071 +{
  1.2072 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2073 +         ("TSF: 0x%p nsTextStore::InsertEmbedded() called "
  1.2074 +          "but not supported (E_NOTIMPL)", this));
  1.2075 +
  1.2076 +  // embedded objects are not supported
  1.2077 +  return E_NOTIMPL;
  1.2078 +}
  1.2079 +
  1.2080 +void
  1.2081 +nsTextStore::SetInputScope(const nsString& aHTMLInputType)
  1.2082 +{
  1.2083 +  mInputScopes.Clear();
  1.2084 +  if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) {
  1.2085 +    return;
  1.2086 +  }
  1.2087 +  
  1.2088 +  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
  1.2089 +  if (aHTMLInputType.EqualsLiteral("url")) {
  1.2090 +    mInputScopes.AppendElement(IS_URL);
  1.2091 +  } else if (aHTMLInputType.EqualsLiteral("search")) {
  1.2092 +    mInputScopes.AppendElement(IS_SEARCH);
  1.2093 +  } else if (aHTMLInputType.EqualsLiteral("email")) {
  1.2094 +    mInputScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
  1.2095 +  } else if (aHTMLInputType.EqualsLiteral("password")) {
  1.2096 +    mInputScopes.AppendElement(IS_PASSWORD);
  1.2097 +  } else if (aHTMLInputType.EqualsLiteral("datetime") ||
  1.2098 +             aHTMLInputType.EqualsLiteral("datetime-local")) {
  1.2099 +    mInputScopes.AppendElement(IS_DATE_FULLDATE);
  1.2100 +    mInputScopes.AppendElement(IS_TIME_FULLTIME);
  1.2101 +  } else if (aHTMLInputType.EqualsLiteral("date") ||
  1.2102 +             aHTMLInputType.EqualsLiteral("month") ||
  1.2103 +             aHTMLInputType.EqualsLiteral("week")) {
  1.2104 +    mInputScopes.AppendElement(IS_DATE_FULLDATE);
  1.2105 +  } else if (aHTMLInputType.EqualsLiteral("time")) {
  1.2106 +    mInputScopes.AppendElement(IS_TIME_FULLTIME);
  1.2107 +  } else if (aHTMLInputType.EqualsLiteral("tel")) {
  1.2108 +    mInputScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
  1.2109 +    mInputScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
  1.2110 +  } else if (aHTMLInputType.EqualsLiteral("number")) {
  1.2111 +    mInputScopes.AppendElement(IS_NUMBER);
  1.2112 +  }
  1.2113 +}
  1.2114 +
  1.2115 +HRESULT
  1.2116 +nsTextStore::ProcessScopeRequest(DWORD dwFlags,
  1.2117 +                                 ULONG cFilterAttrs,
  1.2118 +                                 const TS_ATTRID *paFilterAttrs)
  1.2119 +{
  1.2120 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2121 +         ("TSF: 0x%p nsTextStore::ProcessScopeRequest(dwFlags=%s, "
  1.2122 +          "cFilterAttrs=%ld",
  1.2123 +          this, GetFindFlagName(dwFlags).get(), cFilterAttrs));
  1.2124 +
  1.2125 +  // This is a little weird! RequestSupportedAttrs gives us advanced notice
  1.2126 +  // of a support query via RetrieveRequestedAttrs for a specific attribute.
  1.2127 +  // RetrieveRequestedAttrs needs to return valid data for all attributes we
  1.2128 +  // support, but the text service will only want the input scope object
  1.2129 +  // returned in RetrieveRequestedAttrs if the dwFlags passed in here contains
  1.2130 +  // TS_ATTR_FIND_WANT_VALUE.
  1.2131 +  mInputScopeDetected = mInputScopeRequested = false;
  1.2132 +
  1.2133 +  // Currently we only support GUID_PROP_INPUTSCOPE
  1.2134 +  for (uint32_t idx = 0; idx < cFilterAttrs; ++idx) {
  1.2135 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2136 +           ("TSF: 0x%p   nsTextStore::ProcessScopeRequest() "
  1.2137 +            "requested attr=%s",
  1.2138 +            this, GetCLSIDNameStr(paFilterAttrs[idx]).get()));
  1.2139 +    if (IsEqualGUID(paFilterAttrs[idx], GUID_PROP_INPUTSCOPE)) {
  1.2140 +      PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2141 +             ("TSF: 0x%p   nsTextStore::ProcessScopeRequest() "
  1.2142 +              "GUID_PROP_INPUTSCOPE queried", this));
  1.2143 +      mInputScopeDetected = true;
  1.2144 +      if (dwFlags & TS_ATTR_FIND_WANT_VALUE) {
  1.2145 +        PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2146 +               ("TSF: 0x%p   nsTextStore::ProcessScopeRequest() "
  1.2147 +                "TS_ATTR_FIND_WANT_VALUE specified", this));
  1.2148 +        mInputScopeRequested = true;
  1.2149 +      }
  1.2150 +      break;
  1.2151 +    }
  1.2152 +  }
  1.2153 +  return S_OK;
  1.2154 +}
  1.2155 +
  1.2156 +STDMETHODIMP
  1.2157 +nsTextStore::RequestSupportedAttrs(DWORD dwFlags,
  1.2158 +                                   ULONG cFilterAttrs,
  1.2159 +                                   const TS_ATTRID *paFilterAttrs)
  1.2160 +{
  1.2161 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2162 +         ("TSF: 0x%p nsTextStore::RequestSupportedAttrs(dwFlags=%s, "
  1.2163 +          "cFilterAttrs=%lu)",
  1.2164 +          this, GetFindFlagName(dwFlags).get(), cFilterAttrs));
  1.2165 +
  1.2166 +  return ProcessScopeRequest(dwFlags, cFilterAttrs, paFilterAttrs);
  1.2167 +}
  1.2168 +
  1.2169 +STDMETHODIMP
  1.2170 +nsTextStore::RequestAttrsAtPosition(LONG acpPos,
  1.2171 +                                    ULONG cFilterAttrs,
  1.2172 +                                    const TS_ATTRID *paFilterAttrs,
  1.2173 +                                    DWORD dwFlags)
  1.2174 +{
  1.2175 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2176 +         ("TSF: 0x%p nsTextStore::RequestAttrsAtPosition(acpPos=%ld, "
  1.2177 +          "cFilterAttrs=%lu, dwFlags=%s)",
  1.2178 +          this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
  1.2179 +
  1.2180 +  return ProcessScopeRequest(dwFlags | TS_ATTR_FIND_WANT_VALUE,
  1.2181 +                             cFilterAttrs, paFilterAttrs);
  1.2182 +}
  1.2183 +
  1.2184 +STDMETHODIMP
  1.2185 +nsTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos,
  1.2186 +                                                 ULONG cFilterAttrs,
  1.2187 +                                                 const TS_ATTRID *paFilterAttr,
  1.2188 +                                                 DWORD dwFlags)
  1.2189 +{
  1.2190 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2191 +         ("TSF: 0x%p nsTextStore::RequestAttrsTransitioningAtPosition("
  1.2192 +          "acpPos=%ld, cFilterAttrs=%lu, dwFlags=%s) called but not supported "
  1.2193 +          "(S_OK)",
  1.2194 +          this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
  1.2195 +
  1.2196 +  // no per character attributes defined
  1.2197 +  return S_OK;
  1.2198 +}
  1.2199 +
  1.2200 +STDMETHODIMP
  1.2201 +nsTextStore::FindNextAttrTransition(LONG acpStart,
  1.2202 +                                    LONG acpHalt,
  1.2203 +                                    ULONG cFilterAttrs,
  1.2204 +                                    const TS_ATTRID *paFilterAttrs,
  1.2205 +                                    DWORD dwFlags,
  1.2206 +                                    LONG *pacpNext,
  1.2207 +                                    BOOL *pfFound,
  1.2208 +                                    LONG *plFoundOffset)
  1.2209 +{
  1.2210 +  if (!pacpNext || !pfFound || !plFoundOffset) {
  1.2211 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2212 +           ("TSF:   0x%p nsTextStore::FindNextAttrTransition() FAILED due to "
  1.2213 +            "null argument", this));
  1.2214 +    return E_INVALIDARG;
  1.2215 +  }
  1.2216 +
  1.2217 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2218 +         ("TSF: 0x%p   nsTextStore::FindNextAttrTransition() called "
  1.2219 +          "but not supported (S_OK)", this));
  1.2220 +
  1.2221 +  // no per character attributes defined
  1.2222 +  *pacpNext = *plFoundOffset = acpHalt;
  1.2223 +  *pfFound = FALSE;
  1.2224 +  return S_OK;
  1.2225 +}
  1.2226 +
  1.2227 +STDMETHODIMP
  1.2228 +nsTextStore::RetrieveRequestedAttrs(ULONG ulCount,
  1.2229 +                                    TS_ATTRVAL *paAttrVals,
  1.2230 +                                    ULONG *pcFetched)
  1.2231 +{
  1.2232 +  if (!pcFetched || !ulCount || !paAttrVals) {
  1.2233 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2234 +           ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() FAILED due to "
  1.2235 +            "null argument", this));
  1.2236 +    return E_INVALIDARG;
  1.2237 +  }
  1.2238 +
  1.2239 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2240 +         ("TSF: 0x%p   nsTextStore::RetrieveRequestedAttrs() called "
  1.2241 +          "ulCount=%d", this, ulCount));
  1.2242 +
  1.2243 +  if (mInputScopeDetected || mInputScopeRequested) {
  1.2244 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2245 +           ("TSF: 0x%p   nsTextStore::RetrieveRequestedAttrs() for "
  1.2246 +            "GUID_PROP_INPUTSCOPE: "
  1.2247 +            "mInputScopeDetected=%s mInputScopeRequested=%s",
  1.2248 +            this, GetBoolName(mInputScopeDetected),
  1.2249 +            GetBoolName(mInputScopeRequested)));
  1.2250 +
  1.2251 +    paAttrVals->idAttr = GUID_PROP_INPUTSCOPE;
  1.2252 +    paAttrVals->dwOverlapId = 0;
  1.2253 +    paAttrVals->varValue.vt = VT_EMPTY;
  1.2254 +    *pcFetched = 1;
  1.2255 +
  1.2256 +    if (mInputScopeRequested) {
  1.2257 +      paAttrVals->varValue.vt = VT_UNKNOWN;
  1.2258 +      paAttrVals->varValue.punkVal = (IUnknown*) new InputScopeImpl(mInputScopes);
  1.2259 +    }
  1.2260 +
  1.2261 +    mInputScopeDetected = mInputScopeRequested = false;
  1.2262 +    return S_OK;
  1.2263 +  }
  1.2264 +
  1.2265 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2266 +         ("TSF: 0x%p   nsTextStore::RetrieveRequestedAttrs() called "
  1.2267 +          "for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)", this));
  1.2268 +
  1.2269 +  paAttrVals->dwOverlapId = 0;
  1.2270 +  paAttrVals->varValue.vt = VT_EMPTY;
  1.2271 +  *pcFetched = 0;
  1.2272 +  return S_OK;
  1.2273 +}
  1.2274 +
  1.2275 +STDMETHODIMP
  1.2276 +nsTextStore::GetEndACP(LONG *pacp)
  1.2277 +{
  1.2278 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2279 +         ("TSF: 0x%p nsTextStore::GetEndACP(pacp=0x%p)", this, pacp));
  1.2280 +
  1.2281 +  if (!IsReadLocked()) {
  1.2282 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2283 +           ("TSF: 0x%p   nsTextStore::GetEndACP() FAILED due to "
  1.2284 +            "not locked (read)", this));
  1.2285 +    return TS_E_NOLOCK;
  1.2286 +  }
  1.2287 +
  1.2288 +  if (!pacp) {
  1.2289 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2290 +           ("TSF: 0x%p   nsTextStore::GetEndACP() FAILED due to "
  1.2291 +            "null argument", this));
  1.2292 +    return E_INVALIDARG;
  1.2293 +  }
  1.2294 +
  1.2295 +  Content& currentContent = CurrentContent();
  1.2296 +  if (!currentContent.IsInitialized()) {
  1.2297 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2298 +           ("TSF: 0x%p   nsTextStore::GetEndACP() FAILED due to "
  1.2299 +            "CurrentContent() failure", this));
  1.2300 +    return E_FAIL;
  1.2301 +  }
  1.2302 +  *pacp = static_cast<LONG>(currentContent.Text().Length());
  1.2303 +  return S_OK;
  1.2304 +}
  1.2305 +
  1.2306 +STDMETHODIMP
  1.2307 +nsTextStore::GetActiveView(TsViewCookie *pvcView)
  1.2308 +{
  1.2309 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2310 +         ("TSF: 0x%p nsTextStore::GetActiveView(pvcView=0x%p)", this, pvcView));
  1.2311 +
  1.2312 +  if (!pvcView) {
  1.2313 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2314 +           ("TSF: 0x%p   nsTextStore::GetActiveView() FAILED due to "
  1.2315 +            "null argument", this));
  1.2316 +    return E_INVALIDARG;
  1.2317 +  }
  1.2318 +
  1.2319 +  *pvcView = TEXTSTORE_DEFAULT_VIEW;
  1.2320 +
  1.2321 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2322 +         ("TSF: 0x%p   nsTextStore::GetActiveView() succeeded: *pvcView=%ld",
  1.2323 +          this, *pvcView));
  1.2324 +  return S_OK;
  1.2325 +}
  1.2326 +
  1.2327 +STDMETHODIMP
  1.2328 +nsTextStore::GetACPFromPoint(TsViewCookie vcView,
  1.2329 +                             const POINT *pt,
  1.2330 +                             DWORD dwFlags,
  1.2331 +                             LONG *pacp)
  1.2332 +{
  1.2333 +  if (!IsReadLocked()) {
  1.2334 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2335 +           ("TSF: 0x%p   nsTextStore::GetACPFromPoint() FAILED due to "
  1.2336 +            "not locked (read)", this));
  1.2337 +    return TS_E_NOLOCK;
  1.2338 +  }
  1.2339 +
  1.2340 +  if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  1.2341 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2342 +           ("TSF: 0x%p   nsTextStore::GetACPFromPoint() FAILED due to "
  1.2343 +            "called with invalid view", this));
  1.2344 +    return E_INVALIDARG;
  1.2345 +  }
  1.2346 +
  1.2347 +  if (mContent.IsLayoutChanged()) {
  1.2348 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2349 +           ("TSF: 0x%p   nsTextStore::GetACPFromPoint() FAILED due to "
  1.2350 +            "layout not recomputed", this));
  1.2351 +    mPendingOnLayoutChange = true;
  1.2352 +    return TS_E_NOLAYOUT;
  1.2353 +  }
  1.2354 +
  1.2355 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2356 +         ("TSF: 0x%p nsTextStore::GetACPFromPoint(vcView=%ld, "
  1.2357 +          "pt(0x%p)={ x=%ld, y=%ld }, dwFlags=%s, pacp=0x%p) called "
  1.2358 +          "but not supported (E_NOTIMPL)", this));
  1.2359 +
  1.2360 +  // not supported for now
  1.2361 +  return E_NOTIMPL;
  1.2362 +}
  1.2363 +
  1.2364 +STDMETHODIMP
  1.2365 +nsTextStore::GetTextExt(TsViewCookie vcView,
  1.2366 +                        LONG acpStart,
  1.2367 +                        LONG acpEnd,
  1.2368 +                        RECT *prc,
  1.2369 +                        BOOL *pfClipped)
  1.2370 +{
  1.2371 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2372 +         ("TSF: 0x%p nsTextStore::GetTextExt(vcView=%ld, "
  1.2373 +          "acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p)",
  1.2374 +          this, vcView, acpStart, acpEnd, prc, pfClipped));
  1.2375 +
  1.2376 +  if (!IsReadLocked()) {
  1.2377 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2378 +           ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2379 +            "not locked (read)", this));
  1.2380 +    return TS_E_NOLOCK;
  1.2381 +  }
  1.2382 +
  1.2383 +  if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  1.2384 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2385 +           ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2386 +            "called with invalid view", this));
  1.2387 +    return E_INVALIDARG;
  1.2388 +  }
  1.2389 +
  1.2390 +  if (!prc || !pfClipped) {
  1.2391 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2392 +           ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2393 +            "null argument", this));
  1.2394 +    return E_INVALIDARG;
  1.2395 +  }
  1.2396 +
  1.2397 +  if (acpStart < 0 || acpEnd < acpStart) {
  1.2398 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2399 +           ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2400 +            "invalid position", this));
  1.2401 +    return TS_E_INVALIDPOS;
  1.2402 +  }
  1.2403 +
  1.2404 +  if (mContent.IsLayoutChangedAfter(acpEnd)) {
  1.2405 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2406 +           ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2407 +            "layout not recomputed at %d", this, acpEnd));
  1.2408 +    mPendingOnLayoutChange = true;
  1.2409 +    return TS_E_NOLAYOUT;
  1.2410 +  }
  1.2411 +
  1.2412 +  // use NS_QUERY_TEXT_RECT to get rect in system, screen coordinates
  1.2413 +  WidgetQueryContentEvent event(true, NS_QUERY_TEXT_RECT, mWidget);
  1.2414 +  mWidget->InitEvent(event);
  1.2415 +  event.InitForQueryTextRect(acpStart, acpEnd - acpStart);
  1.2416 +  mWidget->DispatchWindowEvent(&event);
  1.2417 +  if (!event.mSucceeded) {
  1.2418 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2419 +           ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2420 +            "NS_QUERY_TEXT_RECT failure", this));
  1.2421 +    return TS_E_INVALIDPOS; // but unexpected failure, maybe.
  1.2422 +  }
  1.2423 +  // IMEs don't like empty rects, fix here
  1.2424 +  if (event.mReply.mRect.width <= 0)
  1.2425 +    event.mReply.mRect.width = 1;
  1.2426 +  if (event.mReply.mRect.height <= 0)
  1.2427 +    event.mReply.mRect.height = 1;
  1.2428 +
  1.2429 +  if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
  1.2430 +    // convert to unclipped screen rect
  1.2431 +    nsWindow* refWindow = static_cast<nsWindow*>(
  1.2432 +      event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWidget);
  1.2433 +    // Result rect is in top level widget coordinates
  1.2434 +    refWindow = refWindow->GetTopLevelWindow(false);
  1.2435 +    if (!refWindow) {
  1.2436 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2437 +             ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2438 +              "no top level window", this));
  1.2439 +      return E_FAIL;
  1.2440 +    }
  1.2441 +
  1.2442 +    event.mReply.mRect.MoveBy(refWindow->WidgetToScreenOffset());
  1.2443 +  }
  1.2444 +
  1.2445 +  // get bounding screen rect to test for clipping
  1.2446 +  if (!GetScreenExtInternal(*prc)) {
  1.2447 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2448 +           ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  1.2449 +            "GetScreenExtInternal() failure", this));
  1.2450 +    return E_FAIL;
  1.2451 +  }
  1.2452 +
  1.2453 +  // clip text rect to bounding rect
  1.2454 +  RECT textRect;
  1.2455 +  ::SetRect(&textRect, event.mReply.mRect.x, event.mReply.mRect.y,
  1.2456 +            event.mReply.mRect.XMost(), event.mReply.mRect.YMost());
  1.2457 +  if (!::IntersectRect(prc, prc, &textRect))
  1.2458 +    // Text is not visible
  1.2459 +    ::SetRectEmpty(prc);
  1.2460 +
  1.2461 +  // not equal if text rect was clipped
  1.2462 +  *pfClipped = !::EqualRect(prc, &textRect);
  1.2463 +
  1.2464 +  // ATOK refers native caret position and size on Desktop applications for
  1.2465 +  // deciding candidate window.  Therefore, we need to create native caret
  1.2466 +  // for hacking the bug.
  1.2467 +  if (sCreateNativeCaretForATOK &&
  1.2468 +      StringBeginsWith(
  1.2469 +        mActiveTIPKeyboardDescription, NS_LITERAL_STRING("ATOK ")) &&
  1.2470 +      mComposition.IsComposing() &&
  1.2471 +      mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart &&
  1.2472 +      mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) {
  1.2473 +    if (mNativeCaretIsCreated) {
  1.2474 +      ::DestroyCaret();
  1.2475 +      mNativeCaretIsCreated = false;
  1.2476 +    }
  1.2477 +    CreateNativeCaret();
  1.2478 +  }
  1.2479 +
  1.2480 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2481 +         ("TSF: 0x%p   nsTextStore::GetTextExt() succeeded: "
  1.2482 +          "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }, *pfClipped=%s",
  1.2483 +          this, prc->left, prc->top, prc->right, prc->bottom,
  1.2484 +          GetBoolName(*pfClipped)));
  1.2485 +
  1.2486 +  return S_OK;
  1.2487 +}
  1.2488 +
  1.2489 +STDMETHODIMP
  1.2490 +nsTextStore::GetScreenExt(TsViewCookie vcView,
  1.2491 +                          RECT *prc)
  1.2492 +{
  1.2493 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2494 +         ("TSF: 0x%p nsTextStore::GetScreenExt(vcView=%ld, prc=0x%p)",
  1.2495 +          this, vcView, prc));
  1.2496 +
  1.2497 +  if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  1.2498 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2499 +           ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  1.2500 +            "called with invalid view", this));
  1.2501 +    return E_INVALIDARG;
  1.2502 +  }
  1.2503 +
  1.2504 +  if (!prc) {
  1.2505 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2506 +           ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  1.2507 +            "null argument", this));
  1.2508 +    return E_INVALIDARG;
  1.2509 +  }
  1.2510 +
  1.2511 +  if (!GetScreenExtInternal(*prc)) {
  1.2512 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2513 +           ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  1.2514 +            "GetScreenExtInternal() failure", this));
  1.2515 +    return E_FAIL;
  1.2516 +  }
  1.2517 +
  1.2518 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2519 +         ("TSF: 0x%p   nsTextStore::GetScreenExt() succeeded: "
  1.2520 +          "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
  1.2521 +          this, prc->left, prc->top, prc->right, prc->bottom));
  1.2522 +  return S_OK;
  1.2523 +}
  1.2524 +
  1.2525 +bool
  1.2526 +nsTextStore::GetScreenExtInternal(RECT &aScreenExt)
  1.2527 +{
  1.2528 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.2529 +         ("TSF: 0x%p   nsTextStore::GetScreenExtInternal()", this));
  1.2530 +
  1.2531 +  // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates
  1.2532 +  WidgetQueryContentEvent event(true, NS_QUERY_EDITOR_RECT, mWidget);
  1.2533 +  mWidget->InitEvent(event);
  1.2534 +  mWidget->DispatchWindowEvent(&event);
  1.2535 +  if (!event.mSucceeded) {
  1.2536 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2537 +           ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  1.2538 +            "NS_QUERY_EDITOR_RECT failure", this));
  1.2539 +    return false;
  1.2540 +  }
  1.2541 +
  1.2542 +  if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
  1.2543 +    nsIntRect boundRect;
  1.2544 +    if (NS_FAILED(mWidget->GetClientBounds(boundRect))) {
  1.2545 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2546 +             ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  1.2547 +              "failed to get the client bounds", this));
  1.2548 +      return false;
  1.2549 +    }
  1.2550 +    ::SetRect(&aScreenExt, boundRect.x, boundRect.y,
  1.2551 +              boundRect.XMost(), boundRect.YMost());
  1.2552 +  } else {
  1.2553 +    NS_ASSERTION(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop,
  1.2554 +                 "environment isn't WindowsEnvironmentType_Desktop!");
  1.2555 +    nsWindow* refWindow = static_cast<nsWindow*>(
  1.2556 +      event.mReply.mFocusedWidget ?
  1.2557 +        event.mReply.mFocusedWidget : mWidget);
  1.2558 +    // Result rect is in top level widget coordinates
  1.2559 +    refWindow = refWindow->GetTopLevelWindow(false);
  1.2560 +    if (!refWindow) {
  1.2561 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2562 +             ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  1.2563 +              "no top level window", this));
  1.2564 +      return false;
  1.2565 +    }
  1.2566 +
  1.2567 +    nsIntRect boundRect;
  1.2568 +    if (NS_FAILED(refWindow->GetClientBounds(boundRect))) {
  1.2569 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2570 +             ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  1.2571 +              "failed to get the client bounds", this));
  1.2572 +      return false;
  1.2573 +    }
  1.2574 +
  1.2575 +    boundRect.MoveTo(0, 0);
  1.2576 +
  1.2577 +    // Clip frame rect to window rect
  1.2578 +    boundRect.IntersectRect(event.mReply.mRect, boundRect);
  1.2579 +    if (!boundRect.IsEmpty()) {
  1.2580 +      boundRect.MoveBy(refWindow->WidgetToScreenOffset());
  1.2581 +      ::SetRect(&aScreenExt, boundRect.x, boundRect.y,
  1.2582 +                boundRect.XMost(), boundRect.YMost());
  1.2583 +    } else {
  1.2584 +      ::SetRectEmpty(&aScreenExt);
  1.2585 +    }
  1.2586 +  }
  1.2587 +
  1.2588 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.2589 +         ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() succeeded: "
  1.2590 +          "aScreenExt={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
  1.2591 +          this, aScreenExt.left, aScreenExt.top,
  1.2592 +          aScreenExt.right, aScreenExt.bottom));
  1.2593 +  return true;
  1.2594 +}
  1.2595 +
  1.2596 +STDMETHODIMP
  1.2597 +nsTextStore::GetWnd(TsViewCookie vcView,
  1.2598 +                    HWND *phwnd)
  1.2599 +{
  1.2600 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2601 +         ("TSF: 0x%p nsTextStore::GetWnd(vcView=%ld, phwnd=0x%p), "
  1.2602 +          "mWidget=0x%p",
  1.2603 +          this, vcView, phwnd, mWidget.get()));
  1.2604 +
  1.2605 +  if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  1.2606 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2607 +           ("TSF: 0x%p   nsTextStore::GetWnd() FAILED due to "
  1.2608 +            "called with invalid view", this));
  1.2609 +    return E_INVALIDARG;
  1.2610 +  }
  1.2611 +
  1.2612 +  if (!phwnd) {
  1.2613 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2614 +           ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  1.2615 +            "null argument", this));
  1.2616 +    return E_INVALIDARG;
  1.2617 +  }
  1.2618 +
  1.2619 +  *phwnd = mWidget->GetWindowHandle();
  1.2620 +
  1.2621 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2622 +         ("TSF: 0x%p   nsTextStore::GetWnd() succeeded: *phwnd=0x%p",
  1.2623 +          this, static_cast<void*>(*phwnd)));
  1.2624 +  return S_OK;
  1.2625 +}
  1.2626 +
  1.2627 +STDMETHODIMP
  1.2628 +nsTextStore::InsertTextAtSelection(DWORD dwFlags,
  1.2629 +                                   const WCHAR *pchText,
  1.2630 +                                   ULONG cch,
  1.2631 +                                   LONG *pacpStart,
  1.2632 +                                   LONG *pacpEnd,
  1.2633 +                                   TS_TEXTCHANGE *pChange)
  1.2634 +{
  1.2635 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2636 +         ("TSF: 0x%p nsTextStore::InsertTextAtSelection(dwFlags=%s, "
  1.2637 +          "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, "
  1.2638 +          "pChange=0x%p), IsComposing()=%s",
  1.2639 +          this, dwFlags == 0 ? "0" :
  1.2640 +                dwFlags == TF_IAS_NOQUERY ? "TF_IAS_NOQUERY" :
  1.2641 +                dwFlags == TF_IAS_QUERYONLY ? "TF_IAS_QUERYONLY" : "Unknown",
  1.2642 +          pchText,
  1.2643 +          pchText && cch ? NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
  1.2644 +          cch, pacpStart, pacpEnd, pChange,
  1.2645 +          GetBoolName(mComposition.IsComposing())));
  1.2646 +
  1.2647 +  if (cch && !pchText) {
  1.2648 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2649 +           ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2650 +            "null pchText", this));
  1.2651 +    return E_INVALIDARG;
  1.2652 +  }
  1.2653 +
  1.2654 +  if (TS_IAS_QUERYONLY == dwFlags) {
  1.2655 +    if (!IsReadLocked()) {
  1.2656 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2657 +             ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2658 +              "not locked (read)", this));
  1.2659 +      return TS_E_NOLOCK;
  1.2660 +    }
  1.2661 +
  1.2662 +    if (!pacpStart || !pacpEnd) {
  1.2663 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2664 +             ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2665 +              "null argument", this));
  1.2666 +      return E_INVALIDARG;
  1.2667 +    }
  1.2668 +
  1.2669 +    // Get selection first
  1.2670 +    Selection& currentSel = CurrentSelection();
  1.2671 +    if (currentSel.IsDirty()) {
  1.2672 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2673 +             ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2674 +              "CurrentSelection() failure", this));
  1.2675 +      return E_FAIL;
  1.2676 +    }
  1.2677 +
  1.2678 +    // Simulate text insertion
  1.2679 +    *pacpStart = currentSel.StartOffset();
  1.2680 +    *pacpEnd = currentSel.EndOffset();
  1.2681 +    if (pChange) {
  1.2682 +      pChange->acpStart = currentSel.StartOffset();
  1.2683 +      pChange->acpOldEnd = currentSel.EndOffset();
  1.2684 +      pChange->acpNewEnd = currentSel.StartOffset() + static_cast<LONG>(cch);
  1.2685 +    }
  1.2686 +  } else {
  1.2687 +    if (!IsReadWriteLocked()) {
  1.2688 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2689 +             ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2690 +              "not locked (read-write)", this));
  1.2691 +      return TS_E_NOLOCK;
  1.2692 +    }
  1.2693 +
  1.2694 +    if (!pChange) {
  1.2695 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2696 +             ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2697 +              "null pChange", this));
  1.2698 +      return E_INVALIDARG;
  1.2699 +    }
  1.2700 +
  1.2701 +    if (TS_IAS_NOQUERY != dwFlags && (!pacpStart || !pacpEnd)) {
  1.2702 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2703 +             ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2704 +              "null argument", this));
  1.2705 +      return E_INVALIDARG;
  1.2706 +    }
  1.2707 +
  1.2708 +    if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
  1.2709 +                                       pChange)) {
  1.2710 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2711 +             ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  1.2712 +              "InsertTextAtSelectionInternal() failure", this));
  1.2713 +      return E_FAIL;
  1.2714 +    }
  1.2715 +
  1.2716 +    if (TS_IAS_NOQUERY != dwFlags) {
  1.2717 +      *pacpStart = pChange->acpStart;
  1.2718 +      *pacpEnd = pChange->acpNewEnd;
  1.2719 +    }
  1.2720 +  }
  1.2721 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2722 +         ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() succeeded: "
  1.2723 +          "*pacpStart=%ld, *pacpEnd=%ld, "
  1.2724 +          "*pChange={ acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld })",
  1.2725 +          this, pacpStart ? *pacpStart : 0, pacpEnd ? *pacpEnd : 0,
  1.2726 +          pChange ? pChange->acpStart: 0, pChange ? pChange->acpOldEnd : 0,
  1.2727 +          pChange ? pChange->acpNewEnd : 0));
  1.2728 +  return S_OK;
  1.2729 +}
  1.2730 +
  1.2731 +bool
  1.2732 +nsTextStore::InsertTextAtSelectionInternal(const nsAString &aInsertStr,
  1.2733 +                                           TS_TEXTCHANGE* aTextChange)
  1.2734 +{
  1.2735 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.2736 +         ("TSF: 0x%p   nsTextStore::InsertTextAtSelectionInternal("
  1.2737 +          "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s",
  1.2738 +          this, NS_ConvertUTF16toUTF8(aInsertStr).get(), aTextChange,
  1.2739 +          GetBoolName(mComposition.IsComposing())));
  1.2740 +
  1.2741 +  Content& currentContent = CurrentContent();
  1.2742 +  if (!currentContent.IsInitialized()) {
  1.2743 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2744 +           ("TSF: 0x%p   nsTextStore::InsertTextAtSelectionInternal() failed "
  1.2745 +            "due to CurrentContent() failure()", this));
  1.2746 +    return false;
  1.2747 +  }
  1.2748 +
  1.2749 +  TS_SELECTION_ACP oldSelection = currentContent.Selection().ACP();
  1.2750 +  if (!mComposition.IsComposing()) {
  1.2751 +    // Use a temporary composition to contain the text
  1.2752 +    PendingAction* compositionStart = mPendingActions.AppendElement();
  1.2753 +    compositionStart->mType = PendingAction::COMPOSITION_START;
  1.2754 +    compositionStart->mSelectionStart = oldSelection.acpStart;
  1.2755 +    compositionStart->mSelectionLength =
  1.2756 +      oldSelection.acpEnd - oldSelection.acpStart;
  1.2757 +
  1.2758 +    PendingAction* compositionEnd = mPendingActions.AppendElement();
  1.2759 +    compositionEnd->mType = PendingAction::COMPOSITION_END;
  1.2760 +    compositionEnd->mData = aInsertStr;
  1.2761 +  }
  1.2762 +
  1.2763 +  currentContent.ReplaceSelectedTextWith(aInsertStr);
  1.2764 +
  1.2765 +  if (aTextChange) {
  1.2766 +    aTextChange->acpStart = oldSelection.acpStart;
  1.2767 +    aTextChange->acpOldEnd = oldSelection.acpEnd;
  1.2768 +    aTextChange->acpNewEnd = currentContent.Selection().EndOffset();
  1.2769 +  }
  1.2770 +
  1.2771 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.2772 +         ("TSF: 0x%p   nsTextStore::InsertTextAtSelectionInternal() succeeded: "
  1.2773 +          "mWidget=0x%p, mWidget->Destroyed()=%s, aTextChange={ acpStart=%ld, "
  1.2774 +          "acpOldEnd=%ld, acpNewEnd=%ld }",
  1.2775 +          this, mWidget.get(),
  1.2776 +          GetBoolName(mWidget ? mWidget->Destroyed() : true),
  1.2777 +          aTextChange ? aTextChange->acpStart : 0,
  1.2778 +          aTextChange ? aTextChange->acpOldEnd : 0,
  1.2779 +          aTextChange ? aTextChange->acpNewEnd : 0));
  1.2780 +  return true;
  1.2781 +}
  1.2782 +
  1.2783 +STDMETHODIMP
  1.2784 +nsTextStore::InsertEmbeddedAtSelection(DWORD dwFlags,
  1.2785 +                                       IDataObject *pDataObject,
  1.2786 +                                       LONG *pacpStart,
  1.2787 +                                       LONG *pacpEnd,
  1.2788 +                                       TS_TEXTCHANGE *pChange)
  1.2789 +{
  1.2790 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2791 +         ("TSF: 0x%p nsTextStore::InsertEmbeddedAtSelection() called "
  1.2792 +          "but not supported (E_NOTIMPL)", this));
  1.2793 +
  1.2794 +  // embedded objects are not supported
  1.2795 +  return E_NOTIMPL;
  1.2796 +}
  1.2797 +
  1.2798 +HRESULT
  1.2799 +nsTextStore::RecordCompositionStartAction(ITfCompositionView* pComposition,
  1.2800 +                                          ITfRange* aRange,
  1.2801 +                                          bool aPreserveSelection)
  1.2802 +{
  1.2803 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.2804 +         ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction("
  1.2805 +          "pComposition=0x%p, aRange=0x%p, aPreserveSelection=%s), "
  1.2806 +          "mComposition.mView=0x%p",
  1.2807 +          this, pComposition, aRange, GetBoolName(aPreserveSelection),
  1.2808 +          mComposition.mView.get()));
  1.2809 +
  1.2810 +  LONG start = 0, length = 0;
  1.2811 +  HRESULT hr = GetRangeExtent(aRange, &start, &length);
  1.2812 +  if (FAILED(hr)) {
  1.2813 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2814 +           ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction() FAILED "
  1.2815 +            "due to GetRangeExtent() failure", this));
  1.2816 +    return hr;
  1.2817 +  }
  1.2818 +
  1.2819 +  Content& currentContent = CurrentContent();
  1.2820 +  if (!currentContent.IsInitialized()) {
  1.2821 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2822 +           ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction() FAILED "
  1.2823 +            "due to CurrentContent() failure", this));
  1.2824 +    return E_FAIL;
  1.2825 +  }
  1.2826 +
  1.2827 +  PendingAction* action = mPendingActions.AppendElement();
  1.2828 +  action->mType = PendingAction::COMPOSITION_START;
  1.2829 +  action->mSelectionStart = start;
  1.2830 +  action->mSelectionLength = length;
  1.2831 +
  1.2832 +  currentContent.StartComposition(pComposition, *action, aPreserveSelection);
  1.2833 +
  1.2834 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2835 +         ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction() succeeded: "
  1.2836 +          "mComposition={ mStart=%ld, mString.Length()=%ld, "
  1.2837 +          "mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
  1.2838 +          "style.fInterimChar=%s } }",
  1.2839 +          this, mComposition.mStart, mComposition.mString.Length(),
  1.2840 +          mSelection.StartOffset(), mSelection.EndOffset(),
  1.2841 +          GetActiveSelEndName(mSelection.ActiveSelEnd()),
  1.2842 +          GetBoolName(mSelection.IsInterimChar())));
  1.2843 +  return S_OK;
  1.2844 +}
  1.2845 +
  1.2846 +HRESULT
  1.2847 +nsTextStore::RecordCompositionEndAction()
  1.2848 +{
  1.2849 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.2850 +         ("TSF: 0x%p nsTextStore::RecordCompositionEndAction(), "
  1.2851 +          "mComposition={ mView=0x%p, mString=\"%s\" }",
  1.2852 +          this, mComposition.mView.get(),
  1.2853 +          NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  1.2854 +
  1.2855 +  MOZ_ASSERT(mComposition.IsComposing());
  1.2856 +
  1.2857 +  PendingAction* action = mPendingActions.AppendElement();
  1.2858 +  action->mType = PendingAction::COMPOSITION_END;
  1.2859 +  action->mData = mComposition.mString;
  1.2860 +
  1.2861 +  Content& currentContent = CurrentContent();
  1.2862 +  if (!currentContent.IsInitialized()) {
  1.2863 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2864 +           ("TSF: 0x%p   nsTextStore::RecordCompositionEndAction() FAILED due "
  1.2865 +            "to CurrentContent() failure", this));
  1.2866 +    return E_FAIL;
  1.2867 +  }
  1.2868 +  currentContent.EndComposition(*action);
  1.2869 +
  1.2870 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2871 +         ("TSF: 0x%p   nsTextStore::RecordCompositionEndAction(), succeeded",
  1.2872 +          this));
  1.2873 +  return S_OK;
  1.2874 +}
  1.2875 +
  1.2876 +STDMETHODIMP
  1.2877 +nsTextStore::OnStartComposition(ITfCompositionView* pComposition,
  1.2878 +                                BOOL* pfOk)
  1.2879 +{
  1.2880 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2881 +         ("TSF: 0x%p nsTextStore::OnStartComposition(pComposition=0x%p, "
  1.2882 +          "pfOk=0x%p), mComposition.mView=0x%p",
  1.2883 +          this, pComposition, pfOk, mComposition.mView.get()));
  1.2884 +
  1.2885 +  AutoPendingActionAndContentFlusher flusher(this);
  1.2886 +
  1.2887 +  *pfOk = FALSE;
  1.2888 +
  1.2889 +  // Only one composition at a time
  1.2890 +  if (mComposition.IsComposing()) {
  1.2891 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2892 +           ("TSF: 0x%p   nsTextStore::OnStartComposition() FAILED due to "
  1.2893 +            "there is another composition already (but returns S_OK)", this));
  1.2894 +    return S_OK;
  1.2895 +  }
  1.2896 +
  1.2897 +  nsRefPtr<ITfRange> range;
  1.2898 +  HRESULT hr = pComposition->GetRange(getter_AddRefs(range));
  1.2899 +  if (FAILED(hr)) {
  1.2900 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2901 +           ("TSF: 0x%p   nsTextStore::OnStartComposition() FAILED due to "
  1.2902 +            "pComposition->GetRange() failure", this));
  1.2903 +    return hr;
  1.2904 +  }
  1.2905 +  hr = RecordCompositionStartAction(pComposition, range, false);
  1.2906 +  if (FAILED(hr)) {
  1.2907 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2908 +           ("TSF: 0x%p   nsTextStore::OnStartComposition() FAILED due to "
  1.2909 +            "RecordCompositionStartAction() failure", this));
  1.2910 +    return hr;
  1.2911 +  }
  1.2912 +
  1.2913 +  *pfOk = TRUE;
  1.2914 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2915 +         ("TSF: 0x%p   nsTextStore::OnStartComposition() succeeded", this));
  1.2916 +  return S_OK;
  1.2917 +}
  1.2918 +
  1.2919 +STDMETHODIMP
  1.2920 +nsTextStore::OnUpdateComposition(ITfCompositionView* pComposition,
  1.2921 +                                 ITfRange* pRangeNew)
  1.2922 +{
  1.2923 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2924 +         ("TSF: 0x%p nsTextStore::OnUpdateComposition(pComposition=0x%p, "
  1.2925 +          "pRangeNew=0x%p), mComposition.mView=0x%p",
  1.2926 +          this, pComposition, pRangeNew, mComposition.mView.get()));
  1.2927 +
  1.2928 +  AutoPendingActionAndContentFlusher flusher(this);
  1.2929 +
  1.2930 +  if (!mDocumentMgr || !mContext) {
  1.2931 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2932 +           ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  1.2933 +            "not ready for the composition", this));
  1.2934 +    return E_UNEXPECTED;
  1.2935 +  }
  1.2936 +  if (!mComposition.IsComposing()) {
  1.2937 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2938 +           ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  1.2939 +            "no active composition", this));
  1.2940 +    return E_UNEXPECTED;
  1.2941 +  }
  1.2942 +  if (mComposition.mView != pComposition) {
  1.2943 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2944 +           ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  1.2945 +            "different composition view specified", this));
  1.2946 +    return E_UNEXPECTED;
  1.2947 +  }
  1.2948 +
  1.2949 +  // pRangeNew is null when the update is not complete
  1.2950 +  if (!pRangeNew) {
  1.2951 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2952 +           ("TSF: 0x%p   nsTextStore::OnUpdateComposition() succeeded but "
  1.2953 +            "not complete", this));
  1.2954 +    return S_OK;
  1.2955 +  }
  1.2956 +
  1.2957 +  HRESULT hr = RestartCompositionIfNecessary(pRangeNew);
  1.2958 +  if (FAILED(hr)) {
  1.2959 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2960 +           ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  1.2961 +            "RestartCompositionIfNecessary() failure", this));
  1.2962 +    return hr;
  1.2963 +  }
  1.2964 +
  1.2965 +  hr = RecordCompositionUpdateAction();
  1.2966 +  if (FAILED(hr)) {
  1.2967 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2968 +           ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  1.2969 +            "RecordCompositionUpdateAction() failure", this));
  1.2970 +    return hr;
  1.2971 +  }
  1.2972 +
  1.2973 +#ifdef PR_LOGGING
  1.2974 +  if (PR_LOG_TEST(sTextStoreLog, PR_LOG_ALWAYS)) {
  1.2975 +    Selection& currentSel = CurrentSelection();
  1.2976 +    if (currentSel.IsDirty()) {
  1.2977 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.2978 +             ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  1.2979 +              "CurrentSelection() failure", this));
  1.2980 +      return E_FAIL;
  1.2981 +    }
  1.2982 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2983 +           ("TSF: 0x%p   nsTextStore::OnUpdateComposition() succeeded: "
  1.2984 +            "mComposition={ mStart=%ld, mString=\"%s\" }, "
  1.2985 +            "CurrentSelection()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
  1.2986 +            this, mComposition.mStart,
  1.2987 +            NS_ConvertUTF16toUTF8(mComposition.mString).get(),
  1.2988 +            currentSel.StartOffset(), currentSel.EndOffset(),
  1.2989 +            GetActiveSelEndName(currentSel.ActiveSelEnd())));
  1.2990 +  }
  1.2991 +#endif // #ifdef PR_LOGGING
  1.2992 +  return S_OK;
  1.2993 +}
  1.2994 +
  1.2995 +STDMETHODIMP
  1.2996 +nsTextStore::OnEndComposition(ITfCompositionView* pComposition)
  1.2997 +{
  1.2998 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.2999 +         ("TSF: 0x%p nsTextStore::OnEndComposition(pComposition=0x%p), "
  1.3000 +          "mComposition={ mView=0x%p, mString=\"%s\" }",
  1.3001 +          this, pComposition, mComposition.mView.get(),
  1.3002 +          NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  1.3003 +
  1.3004 +  AutoPendingActionAndContentFlusher flusher(this);
  1.3005 +
  1.3006 +  if (!mComposition.IsComposing()) {
  1.3007 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3008 +           ("TSF: 0x%p   nsTextStore::OnEndComposition() FAILED due to "
  1.3009 +            "no active composition", this));
  1.3010 +    return E_UNEXPECTED;
  1.3011 +  }
  1.3012 +
  1.3013 +  if (mComposition.mView != pComposition) {
  1.3014 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3015 +           ("TSF: 0x%p   nsTextStore::OnEndComposition() FAILED due to "
  1.3016 +            "different composition view specified", this));
  1.3017 +    return E_UNEXPECTED;
  1.3018 +  }
  1.3019 +
  1.3020 +  HRESULT hr = RecordCompositionEndAction();
  1.3021 +  if (FAILED(hr)) {
  1.3022 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3023 +           ("TSF: 0x%p   nsTextStore::OnEndComposition() FAILED due to "
  1.3024 +            "RecordCompositionEndAction() failure", this));
  1.3025 +    return hr;
  1.3026 +  }
  1.3027 +
  1.3028 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3029 +         ("TSF: 0x%p   nsTextStore::OnEndComposition(), succeeded", this));
  1.3030 +  return S_OK;
  1.3031 +}
  1.3032 +
  1.3033 +STDMETHODIMP
  1.3034 +nsTextStore::OnActivated(REFCLSID clsid, REFGUID guidProfile,
  1.3035 +                         BOOL fActivated)
  1.3036 +{
  1.3037 +  // NOTE: This is installed only on XP or Server 2003.
  1.3038 +  if (fActivated) {
  1.3039 +    // TODO: We should check if the profile's category is keyboard or not.
  1.3040 +    mOnActivatedCalled = true;
  1.3041 +    mIsIMM_IME = IsIMM_IME(::GetKeyboardLayout(0));
  1.3042 +
  1.3043 +    LANGID langID;
  1.3044 +    HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID);
  1.3045 +    if (FAILED(hr)) {
  1.3046 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3047 +             ("TSF: nsTextStore::OnActivated() FAILED due to "
  1.3048 +              "GetCurrentLanguage() failure, hr=0x%08X", hr));
  1.3049 +    } else if (IsTIPCategoryKeyboard(clsid, langID, guidProfile)) {
  1.3050 +      GetTIPDescription(clsid, langID, guidProfile,
  1.3051 +                        mActiveTIPKeyboardDescription);
  1.3052 +    } else if (clsid == CLSID_NULL || guidProfile == GUID_NULL) {
  1.3053 +      // Perhaps, this case is that keyboard layout without TIP is activated.
  1.3054 +      mActiveTIPKeyboardDescription.Truncate();
  1.3055 +    }
  1.3056 +  }
  1.3057 +
  1.3058 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3059 +         ("TSF: 0x%p nsTextStore::OnActivated(rclsid=%s, guidProfile=%s, "
  1.3060 +          "fActivated=%s), mIsIMM_IME=%s, mActiveTIPDescription=\"%s\"",
  1.3061 +          this, GetCLSIDNameStr(clsid).get(),
  1.3062 +          GetGUIDNameStr(guidProfile).get(), GetBoolName(fActivated),
  1.3063 +          GetBoolName(mIsIMM_IME),
  1.3064 +          NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
  1.3065 +  return S_OK;
  1.3066 +}
  1.3067 +
  1.3068 +STDMETHODIMP
  1.3069 +nsTextStore::OnActivated(DWORD dwProfileType,
  1.3070 +                         LANGID langid,
  1.3071 +                         REFCLSID rclsid,
  1.3072 +                         REFGUID catid,
  1.3073 +                         REFGUID guidProfile,
  1.3074 +                         HKL hkl,
  1.3075 +                         DWORD dwFlags)
  1.3076 +{
  1.3077 +  // NOTE: This is installed only on Vista or later.  However, this may be
  1.3078 +  //       called by EnsureInitActiveLanguageProfile() even on XP or Server
  1.3079 +  //       2003.
  1.3080 +  if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) &&
  1.3081 +      (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ||
  1.3082 +       catid == GUID_TFCAT_TIP_KEYBOARD)) {
  1.3083 +    mOnActivatedCalled = true;
  1.3084 +    mIsIMM_IME = IsIMM_IME(hkl);
  1.3085 +    GetTIPDescription(rclsid, langid, guidProfile,
  1.3086 +                      mActiveTIPKeyboardDescription);
  1.3087 +  }
  1.3088 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3089 +         ("TSF: 0x%p nsTextStore::OnActivated(dwProfileType=%s (0x%08X), "
  1.3090 +          "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, "
  1.3091 +          "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
  1.3092 +          "mActiveTIPDescription=\"%s\"",
  1.3093 +          this, dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR ?
  1.3094 +                  "TF_PROFILETYPE_INPUTPROCESSOR" :
  1.3095 +                dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ?
  1.3096 +                  "TF_PROFILETYPE_KEYBOARDLAYOUT" : "Unknown", dwProfileType,
  1.3097 +          langid, GetCLSIDNameStr(rclsid).get(), GetGUIDNameStr(catid).get(),
  1.3098 +          GetGUIDNameStr(guidProfile).get(), hkl, dwFlags,
  1.3099 +          GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE),
  1.3100 +          GetBoolName(mIsIMM_IME),
  1.3101 +          NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
  1.3102 +  return S_OK;
  1.3103 +}
  1.3104 +
  1.3105 +// static
  1.3106 +nsresult
  1.3107 +nsTextStore::OnFocusChange(bool aGotFocus,
  1.3108 +                           nsWindowBase* aFocusedWidget,
  1.3109 +                           IMEState::Enabled aIMEEnabled)
  1.3110 +{
  1.3111 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3112 +         ("TSF:   nsTextStore::OnFocusChange(aGotFocus=%s, "
  1.3113 +          "aFocusedWidget=0x%p, aIMEEnabled=%s), sTsfThreadMgr=0x%p, "
  1.3114 +          "sTsfTextStore=0x%p",
  1.3115 +          GetBoolName(aGotFocus), aFocusedWidget,
  1.3116 +          GetIMEEnabledName(aIMEEnabled), sTsfThreadMgr, sTsfTextStore));
  1.3117 +
  1.3118 +  // no change notifications if TSF is disabled
  1.3119 +  NS_ENSURE_TRUE(sTsfThreadMgr && sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
  1.3120 +
  1.3121 +  nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
  1.3122 +  if (aGotFocus && (aIMEEnabled == IMEState::ENABLED ||
  1.3123 +                    aIMEEnabled == IMEState::PASSWORD)) {
  1.3124 +    bool bRet = sTsfTextStore->Create(aFocusedWidget);
  1.3125 +    NS_ENSURE_TRUE(bRet, NS_ERROR_FAILURE);
  1.3126 +    NS_ENSURE_TRUE(sTsfTextStore->mDocumentMgr, NS_ERROR_FAILURE);
  1.3127 +    if (aIMEEnabled == IMEState::PASSWORD) {
  1.3128 +      MarkContextAsKeyboardDisabled(sTsfTextStore->mContext);
  1.3129 +      nsRefPtr<ITfContext> topContext;
  1.3130 +      sTsfTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
  1.3131 +      if (topContext && topContext != sTsfTextStore->mContext) {
  1.3132 +        MarkContextAsKeyboardDisabled(topContext);
  1.3133 +      }
  1.3134 +    }
  1.3135 +    HRESULT hr = sTsfThreadMgr->SetFocus(sTsfTextStore->mDocumentMgr);
  1.3136 +    NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  1.3137 +    // Use AssociateFocus() for ensuring that any native focus event
  1.3138 +    // never steal focus from our documentMgr.
  1.3139 +    hr = sTsfThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
  1.3140 +                                       sTsfTextStore->mDocumentMgr,
  1.3141 +                                       getter_AddRefs(prevFocusedDocumentMgr));
  1.3142 +    NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  1.3143 +  } else {
  1.3144 +    if (ThinksHavingFocus()) {
  1.3145 +      DebugOnly<HRESULT> hr = sTsfThreadMgr->AssociateFocus(
  1.3146 +                                sTsfTextStore->mWidget->GetWindowHandle(),
  1.3147 +                                nullptr, getter_AddRefs(prevFocusedDocumentMgr));
  1.3148 +      NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
  1.3149 +      NS_ASSERTION(prevFocusedDocumentMgr == sTsfTextStore->mDocumentMgr,
  1.3150 +                   "different documentMgr has been associated with the window");
  1.3151 +      sTsfTextStore->Destroy();
  1.3152 +    }
  1.3153 +    HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr);
  1.3154 +    NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  1.3155 +  }
  1.3156 +  return NS_OK;
  1.3157 +}
  1.3158 +
  1.3159 +// static
  1.3160 +nsIMEUpdatePreference
  1.3161 +nsTextStore::GetIMEUpdatePreference()
  1.3162 +{
  1.3163 +  if (sTsfThreadMgr && sTsfTextStore && sTsfTextStore->mDocumentMgr) {
  1.3164 +    nsRefPtr<ITfDocumentMgr> docMgr;
  1.3165 +    sTsfThreadMgr->GetFocus(getter_AddRefs(docMgr));
  1.3166 +    if (docMgr == sTsfTextStore->mDocumentMgr) {
  1.3167 +      nsIMEUpdatePreference updatePreference(
  1.3168 +        nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
  1.3169 +        nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE |
  1.3170 +        nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE |
  1.3171 +        nsIMEUpdatePreference::NOTIFY_DURING_DEACTIVE);
  1.3172 +      // nsTextStore shouldn't notify TSF of selection change and text change
  1.3173 +      // which are caused by composition.
  1.3174 +      updatePreference.DontNotifyChangesCausedByComposition();
  1.3175 +      return updatePreference;
  1.3176 +    }
  1.3177 +  }
  1.3178 +  return nsIMEUpdatePreference();
  1.3179 +}
  1.3180 +
  1.3181 +nsresult
  1.3182 +nsTextStore::OnTextChangeInternal(const IMENotification& aIMENotification)
  1.3183 +{
  1.3184 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3185 +         ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(aIMENotification={ "
  1.3186 +          "mMessage=0x%08X, mTextChangeData={ mStartOffset=%lu, "
  1.3187 +          "mOldEndOffset=%lu, mNewEndOffset=%lu}), mSink=0x%p, mSinkMask=%s, "
  1.3188 +          "mComposition.IsComposing()=%s",
  1.3189 +          this, aIMENotification.mMessage,
  1.3190 +          aIMENotification.mTextChangeData.mStartOffset,
  1.3191 +          aIMENotification.mTextChangeData.mOldEndOffset,
  1.3192 +          aIMENotification.mTextChangeData.mNewEndOffset, mSink.get(),
  1.3193 +          GetSinkMaskNameStr(mSinkMask).get(),
  1.3194 +          GetBoolName(mComposition.IsComposing())));
  1.3195 +
  1.3196 +  if (IsReadLocked()) {
  1.3197 +    return NS_OK;
  1.3198 +  }
  1.3199 +
  1.3200 +  mSelection.MarkDirty();
  1.3201 +
  1.3202 +  if (!mSink || !(mSinkMask & TS_AS_TEXT_CHANGE)) {
  1.3203 +    return NS_OK;
  1.3204 +  }
  1.3205 +
  1.3206 +  if (!aIMENotification.mTextChangeData.IsInInt32Range()) {
  1.3207 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3208 +           ("TSF: 0x%p   nsTextStore::OnTextChangeInternal() FAILED due to "
  1.3209 +            "offset is too big for calling mSink->OnTextChange()...",
  1.3210 +            this));
  1.3211 +    return NS_OK;
  1.3212 +  }
  1.3213 +
  1.3214 +  // Some TIPs are confused by text change notification during composition.
  1.3215 +  // Especially, some of them stop working for composition in our process.
  1.3216 +  // For preventing it, let's commit the composition.
  1.3217 +  if (mComposition.IsComposing()) {
  1.3218 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3219 +           ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(), "
  1.3220 +            "committing the composition for avoiding making TIP confused...",
  1.3221 +            this));
  1.3222 +    CommitCompositionInternal(false);
  1.3223 +    return NS_OK;
  1.3224 +  }
  1.3225 +
  1.3226 +  TS_TEXTCHANGE textChange;
  1.3227 +  textChange.acpStart =
  1.3228 +    static_cast<LONG>(aIMENotification.mTextChangeData.mStartOffset);
  1.3229 +  textChange.acpOldEnd =
  1.3230 +    static_cast<LONG>(aIMENotification.mTextChangeData.mOldEndOffset);
  1.3231 +  textChange.acpNewEnd =
  1.3232 +    static_cast<LONG>(aIMENotification.mTextChangeData.mNewEndOffset);
  1.3233 +
  1.3234 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3235 +         ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(), calling "
  1.3236 +          "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
  1.3237 +          "acpNewEnd=%ld })...", this, textChange.acpStart,
  1.3238 +          textChange.acpOldEnd, textChange.acpNewEnd));
  1.3239 +  mSink->OnTextChange(0, &textChange);
  1.3240 +
  1.3241 +  return NS_OK;
  1.3242 +}
  1.3243 +
  1.3244 +nsresult
  1.3245 +nsTextStore::OnSelectionChangeInternal(void)
  1.3246 +{
  1.3247 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3248 +         ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), "
  1.3249 +          "mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, "
  1.3250 +          "mComposition.IsComposing()=%s",
  1.3251 +          this, mSink.get(), GetSinkMaskNameStr(mSinkMask).get(),
  1.3252 +          GetBoolName(mIsRecordingActionsWithoutLock),
  1.3253 +          GetBoolName(mComposition.IsComposing())));
  1.3254 +
  1.3255 +  if (IsReadLocked()) {
  1.3256 +    return NS_OK;
  1.3257 +  }
  1.3258 +
  1.3259 +  mSelection.MarkDirty();
  1.3260 +
  1.3261 +  if (!mSink || !(mSinkMask & TS_AS_SEL_CHANGE)) {
  1.3262 +    return NS_OK;
  1.3263 +  }
  1.3264 +
  1.3265 +  // Some TIPs are confused by selection change notification during composition.
  1.3266 +  // Especially, some of them stop working for composition in our process.
  1.3267 +  // For preventing it, let's commit the composition.
  1.3268 +  if (mComposition.IsComposing()) {
  1.3269 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3270 +           ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), "
  1.3271 +            "committing the composition for avoiding making TIP confused...",
  1.3272 +            this));
  1.3273 +    CommitCompositionInternal(false);
  1.3274 +    return NS_OK;
  1.3275 +  }
  1.3276 +
  1.3277 +  if (!mIsRecordingActionsWithoutLock) {
  1.3278 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3279 +           ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), calling "
  1.3280 +            "mSink->OnSelectionChange()...", this));
  1.3281 +    mSink->OnSelectionChange();
  1.3282 +  } else {
  1.3283 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3284 +           ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), pending "
  1.3285 +            "a call of mSink->OnSelectionChange()...", this));
  1.3286 +    mPendingOnSelectionChange = true;
  1.3287 +  }
  1.3288 +  return NS_OK;
  1.3289 +}
  1.3290 +
  1.3291 +nsresult
  1.3292 +nsTextStore::OnLayoutChangeInternal()
  1.3293 +{
  1.3294 +  NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
  1.3295 +  NS_ENSURE_TRUE(mSink, NS_ERROR_FAILURE);
  1.3296 +
  1.3297 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3298 +         ("TSF: 0x%p   nsTextStore::OnLayoutChangeInternal(), calling "
  1.3299 +          "mSink->OnLayoutChange()...", this));
  1.3300 +  HRESULT hr = mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
  1.3301 +  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  1.3302 +
  1.3303 +  return NS_OK;
  1.3304 +}
  1.3305 +
  1.3306 +void
  1.3307 +nsTextStore::CreateNativeCaret()
  1.3308 +{
  1.3309 +  // This method must work only on desktop application.
  1.3310 +  if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Desktop) {
  1.3311 +    return;
  1.3312 +  }
  1.3313 +
  1.3314 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3315 +         ("TSF: 0x%p   nsTextStore::CreateNativeCaret(), "
  1.3316 +          "mComposition.IsComposing()=%s",
  1.3317 +          this, GetBoolName(mComposition.IsComposing())));
  1.3318 +
  1.3319 +  Selection& currentSel = CurrentSelection();
  1.3320 +  if (currentSel.IsDirty()) {
  1.3321 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3322 +           ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  1.3323 +            "CurrentSelection() failure", this));
  1.3324 +    return;
  1.3325 +  }
  1.3326 +
  1.3327 +  // XXX If this is called without composition and the selection isn't
  1.3328 +  //     collapsed, is it OK?
  1.3329 +  uint32_t caretOffset = currentSel.MaxOffset();
  1.3330 +
  1.3331 +  WidgetQueryContentEvent queryCaretRect(true, NS_QUERY_CARET_RECT, mWidget);
  1.3332 +  queryCaretRect.InitForQueryCaretRect(caretOffset);
  1.3333 +  mWidget->InitEvent(queryCaretRect);
  1.3334 +  mWidget->DispatchWindowEvent(&queryCaretRect);
  1.3335 +  if (!queryCaretRect.mSucceeded) {
  1.3336 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3337 +           ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  1.3338 +            "NS_QUERY_CARET_RECT failure (offset=%d)", this, caretOffset));
  1.3339 +    return;
  1.3340 +  }
  1.3341 +
  1.3342 +  nsIntRect& caretRect = queryCaretRect.mReply.mRect;
  1.3343 +  mNativeCaretIsCreated = ::CreateCaret(mWidget->GetWindowHandle(), nullptr,
  1.3344 +                                        caretRect.width, caretRect.height);
  1.3345 +  if (!mNativeCaretIsCreated) {
  1.3346 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3347 +           ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  1.3348 +            "CreateCaret() failure", this));
  1.3349 +    return;
  1.3350 +  }
  1.3351 +
  1.3352 +  nsWindow* window = static_cast<nsWindow*>(mWidget.get());
  1.3353 +  nsWindow* toplevelWindow = window->GetTopLevelWindow(false);
  1.3354 +  if (!toplevelWindow) {
  1.3355 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3356 +           ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  1.3357 +            "no top level window", this));
  1.3358 +    return;
  1.3359 +  }
  1.3360 +
  1.3361 +  if (toplevelWindow != window) {
  1.3362 +    caretRect.MoveBy(toplevelWindow->WidgetToScreenOffset());
  1.3363 +    caretRect.MoveBy(-window->WidgetToScreenOffset());
  1.3364 +  }
  1.3365 +
  1.3366 +  ::SetCaretPos(caretRect.x, caretRect.y);
  1.3367 +}
  1.3368 +
  1.3369 +bool
  1.3370 +nsTextStore::EnsureInitActiveTIPKeyboard()
  1.3371 +{
  1.3372 +  if (mOnActivatedCalled) {
  1.3373 +    return true;
  1.3374 +  }
  1.3375 +
  1.3376 +  if (IsVistaOrLater()) {
  1.3377 +    nsRefPtr<ITfInputProcessorProfileMgr> profileMgr;
  1.3378 +    HRESULT hr =
  1.3379 +      sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
  1.3380 +                                              getter_AddRefs(profileMgr));
  1.3381 +    if (FAILED(hr) || !profileMgr) {
  1.3382 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3383 +        ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  1.3384 +         "to get input processor profile manager, hr=0x%08X", this, hr));
  1.3385 +      return false;
  1.3386 +    }
  1.3387 +
  1.3388 +    TF_INPUTPROCESSORPROFILE profile;
  1.3389 +    hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
  1.3390 +    if (hr == S_FALSE) {
  1.3391 +      PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3392 +        ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  1.3393 +         "to get active keyboard layout profile due to no active profile, "
  1.3394 +         "hr=0x%08X", this, hr));
  1.3395 +      // XXX Should we call OnActivated() with arguments like non-TIP in this
  1.3396 +      //     case?
  1.3397 +      return false;
  1.3398 +    }
  1.3399 +    if (FAILED(hr)) {
  1.3400 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3401 +        ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  1.3402 +         "to get active TIP keyboard, hr=0x%08X", this, hr));
  1.3403 +      return false;
  1.3404 +    }
  1.3405 +
  1.3406 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3407 +      ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), "
  1.3408 +       "calling OnActivated() manually...", this));
  1.3409 +    OnActivated(profile.dwProfileType, profile.langid, profile.clsid,
  1.3410 +                profile.catid, profile.guidProfile, ::GetKeyboardLayout(0),
  1.3411 +                TF_IPSINK_FLAG_ACTIVE);
  1.3412 +    return true;
  1.3413 +  }
  1.3414 +
  1.3415 +  LANGID langID;
  1.3416 +  HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID);
  1.3417 +  if (FAILED(hr)) {
  1.3418 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3419 +      ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  1.3420 +       "to get current language ID, hr=0x%08X", this, hr));
  1.3421 +    return false;
  1.3422 +  }
  1.3423 +
  1.3424 +  nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
  1.3425 +  hr = sInputProcessorProfiles->EnumLanguageProfiles(langID,
  1.3426 +                                  getter_AddRefs(enumLangProfiles));
  1.3427 +  if (FAILED(hr) || !enumLangProfiles) {
  1.3428 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3429 +      ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  1.3430 +       "to get language profiles enumerator, hr=0x%08X", this, hr));
  1.3431 +    return false;
  1.3432 +  }
  1.3433 +
  1.3434 +  TF_LANGUAGEPROFILE profile;
  1.3435 +  ULONG fetch = 0;
  1.3436 +  while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
  1.3437 +    if (!profile.fActive || profile.catid != GUID_TFCAT_TIP_KEYBOARD) {
  1.3438 +      continue;
  1.3439 +    }
  1.3440 +    PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3441 +      ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), "
  1.3442 +       "calling OnActivated() manually...", this));
  1.3443 +    bool isTIP = profile.guidProfile != GUID_NULL;
  1.3444 +    OnActivated(isTIP ? TF_PROFILETYPE_INPUTPROCESSOR :
  1.3445 +                        TF_PROFILETYPE_KEYBOARDLAYOUT,
  1.3446 +                profile.langid, profile.clsid, profile.catid,
  1.3447 +                profile.guidProfile, ::GetKeyboardLayout(0),
  1.3448 +                TF_IPSINK_FLAG_ACTIVE);
  1.3449 +    return true;
  1.3450 +  }
  1.3451 +
  1.3452 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3453 +    ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), "
  1.3454 +     "calling OnActivated() without active TIP manually...", this));
  1.3455 +  OnActivated(TF_PROFILETYPE_KEYBOARDLAYOUT,
  1.3456 +              langID, CLSID_NULL, GUID_TFCAT_TIP_KEYBOARD,
  1.3457 +              GUID_NULL, ::GetKeyboardLayout(0),
  1.3458 +              TF_IPSINK_FLAG_ACTIVE);
  1.3459 +  return true;
  1.3460 +}
  1.3461 +
  1.3462 +void
  1.3463 +nsTextStore::CommitCompositionInternal(bool aDiscard)
  1.3464 +{
  1.3465 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3466 +         ("TSF: 0x%p   nsTextStore::CommitCompositionInternal(aDiscard=%s), "
  1.3467 +          "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, "
  1.3468 +          "mComposition.mString=\"%s\"",
  1.3469 +          this, GetBoolName(aDiscard), mSink.get(), mContext.get(),
  1.3470 +          mComposition.mView.get(),
  1.3471 +          NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  1.3472 +
  1.3473 +  if (mComposition.IsComposing() && aDiscard) {
  1.3474 +    LONG endOffset = mComposition.EndOffset();
  1.3475 +    mComposition.mString.Truncate(0);
  1.3476 +    if (mSink && !mLock) {
  1.3477 +      TS_TEXTCHANGE textChange;
  1.3478 +      textChange.acpStart = mComposition.mStart;
  1.3479 +      textChange.acpOldEnd = endOffset;
  1.3480 +      textChange.acpNewEnd = mComposition.mStart;
  1.3481 +      PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3482 +             ("TSF: 0x%p   nsTextStore::CommitCompositionInternal(), calling"
  1.3483 +              "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
  1.3484 +              "acpNewEnd=%ld })...", this, textChange.acpStart,
  1.3485 +              textChange.acpOldEnd, textChange.acpNewEnd));
  1.3486 +      mSink->OnTextChange(0, &textChange);
  1.3487 +    }
  1.3488 +  }
  1.3489 +  // Terminate two contexts, the base context (mContext) and the top
  1.3490 +  // if the top context is not the same as the base context
  1.3491 +  nsRefPtr<ITfContext> context = mContext;
  1.3492 +  do {
  1.3493 +    if (context) {
  1.3494 +      nsRefPtr<ITfContextOwnerCompositionServices> services;
  1.3495 +      context->QueryInterface(IID_ITfContextOwnerCompositionServices,
  1.3496 +                              getter_AddRefs(services));
  1.3497 +      if (services) {
  1.3498 +        PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3499 +               ("TSF: 0x%p   nsTextStore::CommitCompositionInternal(), "
  1.3500 +                "requesting TerminateComposition() for the context 0x%p...",
  1.3501 +                this, context.get()));
  1.3502 +        services->TerminateComposition(nullptr);
  1.3503 +      }
  1.3504 +    }
  1.3505 +    if (context != mContext)
  1.3506 +      break;
  1.3507 +    if (mDocumentMgr)
  1.3508 +      mDocumentMgr->GetTop(getter_AddRefs(context));
  1.3509 +  } while (context != mContext);
  1.3510 +}
  1.3511 +
  1.3512 +static
  1.3513 +bool
  1.3514 +GetCompartment(IUnknown* pUnk,
  1.3515 +               const GUID& aID,
  1.3516 +               ITfCompartment** aCompartment)
  1.3517 +{
  1.3518 +  if (!pUnk) return false;
  1.3519 +
  1.3520 +  nsRefPtr<ITfCompartmentMgr> compMgr;
  1.3521 +  pUnk->QueryInterface(IID_ITfCompartmentMgr, getter_AddRefs(compMgr));
  1.3522 +  if (!compMgr) return false;
  1.3523 +
  1.3524 +  return SUCCEEDED(compMgr->GetCompartment(aID, aCompartment)) &&
  1.3525 +         (*aCompartment) != nullptr;
  1.3526 +}
  1.3527 +
  1.3528 +// static
  1.3529 +void
  1.3530 +nsTextStore::SetIMEOpenState(bool aState)
  1.3531 +{
  1.3532 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3533 +         ("TSF: nsTextStore::SetIMEOpenState(aState=%s)", GetBoolName(aState)));
  1.3534 +
  1.3535 +  nsRefPtr<ITfCompartment> comp;
  1.3536 +  if (!GetCompartment(sTsfThreadMgr,
  1.3537 +                      GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
  1.3538 +                      getter_AddRefs(comp))) {
  1.3539 +    PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3540 +           ("TSF:   nsTextStore::SetIMEOpenState() FAILED due to"
  1.3541 +            "no compartment available"));
  1.3542 +    return;
  1.3543 +  }
  1.3544 +
  1.3545 +  VARIANT variant;
  1.3546 +  variant.vt = VT_I4;
  1.3547 +  variant.lVal = aState;
  1.3548 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3549 +         ("TSF:   nsTextStore::SetIMEOpenState(), setting "
  1.3550 +          "0x%04X to GUID_COMPARTMENT_KEYBOARD_OPENCLOSE...",
  1.3551 +          variant.lVal));
  1.3552 +  comp->SetValue(sTsfClientId, &variant);
  1.3553 +}
  1.3554 +
  1.3555 +// static
  1.3556 +bool
  1.3557 +nsTextStore::GetIMEOpenState(void)
  1.3558 +{
  1.3559 +  nsRefPtr<ITfCompartment> comp;
  1.3560 +  if (!GetCompartment(sTsfThreadMgr,
  1.3561 +                      GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
  1.3562 +                      getter_AddRefs(comp)))
  1.3563 +    return false;
  1.3564 +
  1.3565 +  VARIANT variant;
  1.3566 +  ::VariantInit(&variant);
  1.3567 +  if (SUCCEEDED(comp->GetValue(&variant)) && variant.vt == VT_I4)
  1.3568 +    return variant.lVal != 0;
  1.3569 +
  1.3570 +  ::VariantClear(&variant); // clear up in case variant.vt != VT_I4
  1.3571 +  return false;
  1.3572 +}
  1.3573 +
  1.3574 +// static
  1.3575 +void
  1.3576 +nsTextStore::SetInputContext(nsWindowBase* aWidget,
  1.3577 +                             const InputContext& aContext,
  1.3578 +                             const InputContextAction& aAction)
  1.3579 +{
  1.3580 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3581 +         ("TSF: nsTextStore::SetInputContext(aWidget=%p, "
  1.3582 +          "aContext.mIMEState.mEnabled=%s, aAction.mFocusChange=%s), "
  1.3583 +          "ThinksHavingFocus()=%s",
  1.3584 +          aWidget, GetIMEEnabledName(aContext.mIMEState.mEnabled),
  1.3585 +          GetFocusChangeName(aAction.mFocusChange),
  1.3586 +          GetBoolName(ThinksHavingFocus())));
  1.3587 +
  1.3588 +  NS_ENSURE_TRUE_VOID(sTsfTextStore);
  1.3589 +  sTsfTextStore->SetInputScope(aContext.mHTMLInputType);
  1.3590 +
  1.3591 +  if (aAction.mFocusChange != InputContextAction::FOCUS_NOT_CHANGED) {
  1.3592 +    return;
  1.3593 +  }
  1.3594 +
  1.3595 +  // If focus isn't actually changed but the enabled state is changed,
  1.3596 +  // emulate the focus move.
  1.3597 +  if (!ThinksHavingFocus() &&
  1.3598 +      aContext.mIMEState.mEnabled == IMEState::ENABLED) {
  1.3599 +    OnFocusChange(true, aWidget, aContext.mIMEState.mEnabled);
  1.3600 +  } else if (ThinksHavingFocus() &&
  1.3601 +             aContext.mIMEState.mEnabled != IMEState::ENABLED) {
  1.3602 +    OnFocusChange(false, aWidget, aContext.mIMEState.mEnabled);
  1.3603 +  }
  1.3604 +}
  1.3605 +
  1.3606 +// static
  1.3607 +void
  1.3608 +nsTextStore::MarkContextAsKeyboardDisabled(ITfContext* aContext)
  1.3609 +{
  1.3610 +  VARIANT variant_int4_value1;
  1.3611 +  variant_int4_value1.vt = VT_I4;
  1.3612 +  variant_int4_value1.lVal = 1;
  1.3613 +
  1.3614 +  nsRefPtr<ITfCompartment> comp;
  1.3615 +  if (!GetCompartment(aContext,
  1.3616 +                      GUID_COMPARTMENT_KEYBOARD_DISABLED,
  1.3617 +                      getter_AddRefs(comp))) {
  1.3618 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3619 +           ("TSF: nsTextStore::MarkContextAsKeyboardDisabled() failed"
  1.3620 +            "aContext=0x%p...", aContext));
  1.3621 +    return;
  1.3622 +  }
  1.3623 +
  1.3624 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3625 +         ("TSF: nsTextStore::MarkContextAsKeyboardDisabled(), setting "
  1.3626 +          "to disable context 0x%p...",
  1.3627 +          aContext));
  1.3628 +  comp->SetValue(sTsfClientId, &variant_int4_value1);
  1.3629 +}
  1.3630 +
  1.3631 +// static
  1.3632 +void
  1.3633 +nsTextStore::MarkContextAsEmpty(ITfContext* aContext)
  1.3634 +{
  1.3635 +  VARIANT variant_int4_value1;
  1.3636 +  variant_int4_value1.vt = VT_I4;
  1.3637 +  variant_int4_value1.lVal = 1;
  1.3638 +
  1.3639 +  nsRefPtr<ITfCompartment> comp;
  1.3640 +  if (!GetCompartment(aContext,
  1.3641 +                      GUID_COMPARTMENT_EMPTYCONTEXT,
  1.3642 +                      getter_AddRefs(comp))) {
  1.3643 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3644 +           ("TSF: nsTextStore::MarkContextAsEmpty() failed"
  1.3645 +            "aContext=0x%p...", aContext));
  1.3646 +    return;
  1.3647 +  }
  1.3648 +
  1.3649 +  PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1.3650 +         ("TSF: nsTextStore::MarkContextAsEmpty(), setting "
  1.3651 +          "to mark empty context 0x%p...", aContext));
  1.3652 +  comp->SetValue(sTsfClientId, &variant_int4_value1);
  1.3653 +}
  1.3654 +
  1.3655 +// static
  1.3656 +bool
  1.3657 +nsTextStore::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
  1.3658 +                                   REFGUID aProfile)
  1.3659 +{
  1.3660 +  if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
  1.3661 +    return false;
  1.3662 +  }
  1.3663 +
  1.3664 +  nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
  1.3665 +  HRESULT hr =
  1.3666 +    sInputProcessorProfiles->EnumLanguageProfiles(aLangID,
  1.3667 +                               getter_AddRefs(enumLangProfiles));
  1.3668 +  if (FAILED(hr) || !enumLangProfiles) {
  1.3669 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3670 +      ("TSF:   nsTextStore::IsTIPCategoryKeyboard(), FAILED "
  1.3671 +       "to get language profiles enumerator, hr=0x%08X", hr));
  1.3672 +    return false;
  1.3673 +  }
  1.3674 +
  1.3675 +  TF_LANGUAGEPROFILE profile;
  1.3676 +  ULONG fetch = 0;
  1.3677 +  while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
  1.3678 +    // XXX We're not sure a profile is registered with two or more categories.
  1.3679 +    if (profile.clsid == aTextService &&
  1.3680 +        profile.guidProfile == aProfile &&
  1.3681 +        profile.catid == GUID_TFCAT_TIP_KEYBOARD) {
  1.3682 +      return true;
  1.3683 +    }
  1.3684 +  }
  1.3685 +  return false;
  1.3686 +}
  1.3687 +
  1.3688 +// static
  1.3689 +void
  1.3690 +nsTextStore::GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
  1.3691 +                               REFGUID aProfile, nsAString& aDescription)
  1.3692 +{
  1.3693 +  aDescription.Truncate();
  1.3694 +
  1.3695 +  if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
  1.3696 +    return;
  1.3697 +  }
  1.3698 +
  1.3699 +  BSTR description = nullptr;
  1.3700 +  HRESULT hr =
  1.3701 +    sInputProcessorProfiles->GetLanguageProfileDescription(aTextService,
  1.3702 +                                                           aLangID,
  1.3703 +                                                           aProfile,
  1.3704 +                                                           &description);
  1.3705 +  if (FAILED(hr)) {
  1.3706 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3707 +           ("TSF:   nsTextStore::InitActiveTIPDescription() FAILED due to "
  1.3708 +            "GetLanguageProfileDescription() failure, hr=0x%08X", hr));
  1.3709 +    return;
  1.3710 +  }
  1.3711 +
  1.3712 +  if (description && description[0]) {
  1.3713 +    aDescription.Assign(description);
  1.3714 +  }
  1.3715 +  ::SysFreeString(description);
  1.3716 +}
  1.3717 +
  1.3718 +// static
  1.3719 +void
  1.3720 +nsTextStore::Initialize()
  1.3721 +{
  1.3722 +#ifdef PR_LOGGING
  1.3723 +  if (!sTextStoreLog) {
  1.3724 +    sTextStoreLog = PR_NewLogModule("nsTextStoreWidgets");
  1.3725 +  }
  1.3726 +#endif
  1.3727 +
  1.3728 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3729 +    ("TSF: nsTextStore::Initialize() is called..."));
  1.3730 +
  1.3731 +  if (sTsfThreadMgr) {
  1.3732 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3733 +      ("TSF:   nsTextStore::Initialize() FAILED due to already initialized"));
  1.3734 +    return;
  1.3735 +  }
  1.3736 +
  1.3737 +  bool enableTsf = Preferences::GetBool(kPrefNameTSFEnabled, false);
  1.3738 +  // Migrate legacy TSF pref to new pref.  This should be removed in next
  1.3739 +  // release cycle or later.
  1.3740 +  if (!enableTsf && Preferences::GetBool(kLegacyPrefNameTSFEnabled, false)) {
  1.3741 +    enableTsf = true;
  1.3742 +    Preferences::SetBool(kPrefNameTSFEnabled, true);
  1.3743 +    Preferences::ClearUser(kLegacyPrefNameTSFEnabled);
  1.3744 +  }
  1.3745 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3746 +    ("TSF:   nsTextStore::Initialize(), TSF is %s",
  1.3747 +     enableTsf ? "enabled" : "disabled"));
  1.3748 +  if (!enableTsf) {
  1.3749 +    return;
  1.3750 +  }
  1.3751 +
  1.3752 +  // XXX MSDN documents that ITfInputProcessorProfiles is available only on
  1.3753 +  //     desktop apps.  However, there is no known way to obtain
  1.3754 +  //     ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
  1.3755 +  //     instance.
  1.3756 +  nsRefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
  1.3757 +  HRESULT hr =
  1.3758 +    ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
  1.3759 +                       CLSCTX_INPROC_SERVER,
  1.3760 +                       IID_ITfInputProcessorProfiles,
  1.3761 +                       getter_AddRefs(inputProcessorProfiles));
  1.3762 +  if (FAILED(hr) || !inputProcessorProfiles) {
  1.3763 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3764 +      ("TSF:   nsTextStore::Initialize() FAILED to create input processor "
  1.3765 +       "profiles, hr=0x%08X", hr));
  1.3766 +    return;
  1.3767 +  }
  1.3768 +
  1.3769 +  nsRefPtr<ITfThreadMgr> threadMgr;
  1.3770 +  hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr,
  1.3771 +                          CLSCTX_INPROC_SERVER, IID_ITfThreadMgr,
  1.3772 +                          getter_AddRefs(threadMgr));
  1.3773 +  if (FAILED(hr) || !threadMgr) {
  1.3774 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3775 +      ("TSF:   nsTextStore::Initialize() FAILED to "
  1.3776 +       "create the thread manager, hr=0x%08X", hr));
  1.3777 +    return;
  1.3778 +  }
  1.3779 +
  1.3780 +  nsRefPtr<ITfMessagePump> messagePump;
  1.3781 +  hr = threadMgr->QueryInterface(IID_ITfMessagePump,
  1.3782 +                                 getter_AddRefs(messagePump));
  1.3783 +  if (FAILED(hr) || !messagePump) {
  1.3784 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3785 +      ("TSF:   nsTextStore::Initialize() FAILED to "
  1.3786 +       "QI message pump from the thread manager, hr=0x%08X", hr));
  1.3787 +    return;
  1.3788 +  }
  1.3789 +
  1.3790 +  nsRefPtr<ITfKeystrokeMgr> keystrokeMgr;
  1.3791 +  hr = threadMgr->QueryInterface(IID_ITfKeystrokeMgr,
  1.3792 +                                 getter_AddRefs(keystrokeMgr));
  1.3793 +  if (FAILED(hr) || !keystrokeMgr) {
  1.3794 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3795 +      ("TSF:   nsTextStore::Initialize() FAILED to "
  1.3796 +       "QI keystroke manager from the thread manager, hr=0x%08X", hr));
  1.3797 +    return;
  1.3798 +  }
  1.3799 +
  1.3800 +  hr = threadMgr->Activate(&sTsfClientId);
  1.3801 +  if (FAILED(hr)) {
  1.3802 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3803 +      ("TSF:   nsTextStore::Initialize() FAILED to activate, hr=0x%08X", hr));
  1.3804 +    return;
  1.3805 +  }
  1.3806 +
  1.3807 +  nsRefPtr<ITfDisplayAttributeMgr> displayAttributeMgr;
  1.3808 +  hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr,
  1.3809 +                          CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr,
  1.3810 +                          getter_AddRefs(displayAttributeMgr));
  1.3811 +  if (FAILED(hr) || !displayAttributeMgr) {
  1.3812 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3813 +      ("TSF:   nsTextStore::Initialize() FAILED to create "
  1.3814 +       "a display attribute manager instance, hr=0x%08X", hr));
  1.3815 +    return;
  1.3816 +  }
  1.3817 +
  1.3818 +  nsRefPtr<ITfCategoryMgr> categoryMgr;
  1.3819 +  hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr,
  1.3820 +                          CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr,
  1.3821 +                          getter_AddRefs(categoryMgr));
  1.3822 +  if (FAILED(hr) || !categoryMgr) {
  1.3823 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3824 +      ("TSF:   nsTextStore::Initialize() FAILED to create "
  1.3825 +       "a category manager instance, hr=0x%08X", hr));
  1.3826 +    return;
  1.3827 +  }
  1.3828 +
  1.3829 +  nsRefPtr<ITfDocumentMgr> disabledDocumentMgr;
  1.3830 +  hr = threadMgr->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr));
  1.3831 +  if (FAILED(hr) || !disabledDocumentMgr) {
  1.3832 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3833 +      ("TSF:   nsTextStore::Initialize() FAILED to create "
  1.3834 +       "a document manager for disabled mode, hr=0x%08X", hr));
  1.3835 +    return;
  1.3836 +  }
  1.3837 +
  1.3838 +  nsRefPtr<ITfContext> disabledContext;
  1.3839 +  DWORD editCookie = 0;
  1.3840 +  hr = disabledDocumentMgr->CreateContext(sTsfClientId, 0, nullptr,
  1.3841 +                                          getter_AddRefs(disabledContext),
  1.3842 +                                          &editCookie);
  1.3843 +  if (FAILED(hr) || !disabledContext) {
  1.3844 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3845 +      ("TSF:   nsTextStore::Initialize() FAILED to create "
  1.3846 +       "a context for disabled mode, hr=0x%08X", hr));
  1.3847 +    return;
  1.3848 +  }
  1.3849 +
  1.3850 +  MarkContextAsKeyboardDisabled(disabledContext);
  1.3851 +  MarkContextAsEmpty(disabledContext);
  1.3852 +
  1.3853 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3854 +    ("TSF:   nsTextStore::Initialize() is creating "
  1.3855 +     "an nsTextStore instance..."));
  1.3856 +  nsRefPtr<nsTextStore> textStore = new nsTextStore();
  1.3857 +  if (!textStore->Init(threadMgr)) {
  1.3858 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.3859 +      ("TSF:   nsTextStore::Initialize() FAILED to initialize nsTextStore "
  1.3860 +       "instance"));
  1.3861 +    return;
  1.3862 +  }
  1.3863 +
  1.3864 +  inputProcessorProfiles.swap(sInputProcessorProfiles);
  1.3865 +  threadMgr.swap(sTsfThreadMgr);
  1.3866 +  messagePump.swap(sMessagePump);
  1.3867 +  keystrokeMgr.swap(sKeystrokeMgr);
  1.3868 +  displayAttributeMgr.swap(sDisplayAttrMgr);
  1.3869 +  categoryMgr.swap(sCategoryMgr);
  1.3870 +  disabledDocumentMgr.swap(sTsfDisabledDocumentMgr);
  1.3871 +  disabledContext.swap(sTsfDisabledContext);
  1.3872 +  textStore.swap(sTsfTextStore);
  1.3873 +
  1.3874 +  sCreateNativeCaretForATOK =
  1.3875 +    Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true);
  1.3876 +
  1.3877 +  MOZ_ASSERT(!sFlushTIPInputMessage);
  1.3878 +  sFlushTIPInputMessage = ::RegisterWindowMessageW(L"Flush TIP Input Message");
  1.3879 +
  1.3880 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1.3881 +    ("TSF:   nsTextStore::Initialize(), sTsfThreadMgr=0x%p, "
  1.3882 +     "sTsfClientId=0x%08X, sTsfTextStore=0x%p, sDisplayAttrMgr=0x%p, "
  1.3883 +     "sCategoryMgr=0x%p, sTsfDisabledDocumentMgr=0x%p, sTsfDisabledContext=%p, "
  1.3884 +     "sCreateNativeCaretForATOK=%s",
  1.3885 +     sTsfThreadMgr, sTsfClientId, sTsfTextStore, sDisplayAttrMgr, sCategoryMgr,
  1.3886 +     sTsfDisabledDocumentMgr, sTsfDisabledContext,
  1.3887 +     GetBoolName(sCreateNativeCaretForATOK)));
  1.3888 +}
  1.3889 +
  1.3890 +// static
  1.3891 +void
  1.3892 +nsTextStore::Terminate(void)
  1.3893 +{
  1.3894 +  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Terminate()"));
  1.3895 +
  1.3896 +  NS_IF_RELEASE(sDisplayAttrMgr);
  1.3897 +  NS_IF_RELEASE(sCategoryMgr);
  1.3898 +  NS_IF_RELEASE(sTsfTextStore);
  1.3899 +  NS_IF_RELEASE(sTsfDisabledDocumentMgr);
  1.3900 +  NS_IF_RELEASE(sTsfDisabledContext);
  1.3901 +  NS_IF_RELEASE(sInputProcessorProfiles);
  1.3902 +  sTsfClientId = 0;
  1.3903 +  if (sTsfThreadMgr) {
  1.3904 +    sTsfThreadMgr->Deactivate();
  1.3905 +    NS_RELEASE(sTsfThreadMgr);
  1.3906 +    NS_RELEASE(sMessagePump);
  1.3907 +    NS_RELEASE(sKeystrokeMgr);
  1.3908 +  }
  1.3909 +}
  1.3910 +
  1.3911 +// static
  1.3912 +bool
  1.3913 +nsTextStore::ProcessRawKeyMessage(const MSG& aMsg)
  1.3914 +{
  1.3915 +  if (!sKeystrokeMgr) {
  1.3916 +    return false; // not in TSF mode
  1.3917 +  }
  1.3918 +
  1.3919 +  if (aMsg.message == WM_KEYDOWN) {
  1.3920 +    BOOL eaten;
  1.3921 +    HRESULT hr = sKeystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
  1.3922 +    if (FAILED(hr) || !eaten) {
  1.3923 +      return false;
  1.3924 +    }
  1.3925 +    hr = sKeystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
  1.3926 +    return SUCCEEDED(hr) && eaten;
  1.3927 +  }
  1.3928 +  if (aMsg.message == WM_KEYUP) {
  1.3929 +    BOOL eaten;
  1.3930 +    HRESULT hr = sKeystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
  1.3931 +    if (FAILED(hr) || !eaten) {
  1.3932 +      return false;
  1.3933 +    }
  1.3934 +    hr = sKeystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
  1.3935 +    return SUCCEEDED(hr) && eaten;
  1.3936 +  }
  1.3937 +  return false;
  1.3938 +}
  1.3939 +
  1.3940 +// static
  1.3941 +void
  1.3942 +nsTextStore::ProcessMessage(nsWindowBase* aWindow, UINT aMessage,
  1.3943 +                            WPARAM& aWParam, LPARAM& aLParam,
  1.3944 +                            MSGResult& aResult)
  1.3945 +{
  1.3946 +  switch (aMessage) {
  1.3947 +    case WM_IME_SETCONTEXT:
  1.3948 +      // If a windowless plugin had focus and IME was handled on it, composition
  1.3949 +      // window was set the position.  After that, even in TSF mode, WinXP keeps
  1.3950 +      // to use composition window at the position if the active IME is not
  1.3951 +      // aware TSF.  For avoiding this issue, we need to hide the composition
  1.3952 +      // window here.
  1.3953 +      if (aWParam) {
  1.3954 +        aLParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  1.3955 +      }
  1.3956 +      break;
  1.3957 +  }
  1.3958 +}
  1.3959 +
  1.3960 +/******************************************************************/
  1.3961 +/* nsTextStore::Composition                                       */
  1.3962 +/******************************************************************/
  1.3963 +
  1.3964 +void
  1.3965 +nsTextStore::Composition::Start(ITfCompositionView* aCompositionView,
  1.3966 +                                LONG aCompositionStartOffset,
  1.3967 +                                const nsAString& aCompositionString)
  1.3968 +{
  1.3969 +  mView = aCompositionView;
  1.3970 +  mString = aCompositionString;
  1.3971 +  mStart = aCompositionStartOffset;
  1.3972 +}
  1.3973 +
  1.3974 +void
  1.3975 +nsTextStore::Composition::End()
  1.3976 +{
  1.3977 +  mView = nullptr;
  1.3978 +  mString.Truncate();
  1.3979 +}
  1.3980 +
  1.3981 +/******************************************************************************
  1.3982 + *  nsTextStore::Content
  1.3983 + *****************************************************************************/
  1.3984 +
  1.3985 +const nsDependentSubstring
  1.3986 +nsTextStore::Content::GetSelectedText() const
  1.3987 +{
  1.3988 +  MOZ_ASSERT(mInitialized);
  1.3989 +  return GetSubstring(static_cast<uint32_t>(mSelection.StartOffset()),
  1.3990 +                      static_cast<uint32_t>(mSelection.Length()));
  1.3991 +}
  1.3992 +
  1.3993 +const nsDependentSubstring
  1.3994 +nsTextStore::Content::GetSubstring(uint32_t aStart, uint32_t aLength) const
  1.3995 +{
  1.3996 +  MOZ_ASSERT(mInitialized);
  1.3997 +  return nsDependentSubstring(mText, aStart, aLength);
  1.3998 +}
  1.3999 +
  1.4000 +void
  1.4001 +nsTextStore::Content::ReplaceSelectedTextWith(const nsAString& aString)
  1.4002 +{
  1.4003 +  MOZ_ASSERT(mInitialized);
  1.4004 +  ReplaceTextWith(mSelection.StartOffset(), mSelection.Length(), aString);
  1.4005 +}
  1.4006 +
  1.4007 +inline uint32_t
  1.4008 +FirstDifferentCharOffset(const nsAString& aStr1, const nsAString& aStr2)
  1.4009 +{
  1.4010 +  MOZ_ASSERT(aStr1 != aStr2);
  1.4011 +  uint32_t i = 0;
  1.4012 +  uint32_t minLength = std::min(aStr1.Length(), aStr2.Length());
  1.4013 +  for (; i < minLength && aStr1[i] == aStr2[i]; i++) {
  1.4014 +    /* nothing to do */
  1.4015 +  }
  1.4016 +  return i;
  1.4017 +}
  1.4018 +
  1.4019 +void
  1.4020 +nsTextStore::Content::ReplaceTextWith(LONG aStart, LONG aLength,
  1.4021 +                                      const nsAString& aReplaceString)
  1.4022 +{
  1.4023 +  MOZ_ASSERT(mInitialized);
  1.4024 +  const nsDependentSubstring replacedString =
  1.4025 +    GetSubstring(static_cast<uint32_t>(aStart),
  1.4026 +                 static_cast<uint32_t>(aLength));
  1.4027 +  if (aReplaceString != replacedString) {
  1.4028 +    uint32_t firstDifferentOffset =
  1.4029 +      static_cast<uint32_t>(aStart) + FirstDifferentCharOffset(aReplaceString,
  1.4030 +                                                               replacedString);
  1.4031 +    mMinTextModifiedOffset =
  1.4032 +      std::min(mMinTextModifiedOffset, firstDifferentOffset);
  1.4033 +    if (mComposition.IsComposing()) {
  1.4034 +      // Emulate text insertion during compositions, because during a
  1.4035 +      // composition, editor expects the whole composition string to
  1.4036 +      // be sent in NS_TEXT_TEXT, not just the inserted part.
  1.4037 +      // The actual NS_TEXT_TEXT will be sent in SetSelection or
  1.4038 +      // OnUpdateComposition.
  1.4039 +      MOZ_ASSERT(aStart >= mComposition.mStart);
  1.4040 +      MOZ_ASSERT(aStart + aLength <= mComposition.EndOffset());
  1.4041 +      mComposition.mString.Replace(
  1.4042 +        static_cast<uint32_t>(aStart - mComposition.mStart),
  1.4043 +        static_cast<uint32_t>(aLength), aReplaceString);
  1.4044 +    }
  1.4045 +    mText.Replace(static_cast<uint32_t>(aStart),
  1.4046 +                  static_cast<uint32_t>(aLength), aReplaceString);
  1.4047 +  }
  1.4048 +  // Selection should be collapsed at the end of the inserted string.
  1.4049 +  mSelection.CollapseAt(
  1.4050 +    static_cast<uint32_t>(aStart) + aReplaceString.Length());
  1.4051 +}
  1.4052 +
  1.4053 +void
  1.4054 +nsTextStore::Content::StartComposition(ITfCompositionView* aCompositionView,
  1.4055 +                                       const PendingAction& aCompStart,
  1.4056 +                                       bool aPreserveSelection)
  1.4057 +{
  1.4058 +  MOZ_ASSERT(mInitialized);
  1.4059 +  MOZ_ASSERT(aCompositionView);
  1.4060 +  MOZ_ASSERT(!mComposition.mView);
  1.4061 +  MOZ_ASSERT(aCompStart.mType == PendingAction::COMPOSITION_START);
  1.4062 +
  1.4063 +  mComposition.Start(aCompositionView, aCompStart.mSelectionStart,
  1.4064 +    GetSubstring(static_cast<uint32_t>(aCompStart.mSelectionStart),
  1.4065 +                 static_cast<uint32_t>(aCompStart.mSelectionLength)));
  1.4066 +  if (!aPreserveSelection) {
  1.4067 +    mSelection.SetSelection(mComposition.mStart, mComposition.mString.Length(),
  1.4068 +                            false);
  1.4069 +  }
  1.4070 +}
  1.4071 +
  1.4072 +void
  1.4073 +nsTextStore::Content::EndComposition(const PendingAction& aCompEnd)
  1.4074 +{
  1.4075 +  MOZ_ASSERT(mInitialized);
  1.4076 +  MOZ_ASSERT(mComposition.mView);
  1.4077 +  MOZ_ASSERT(aCompEnd.mType == PendingAction::COMPOSITION_END);
  1.4078 +
  1.4079 +  mSelection.CollapseAt(mComposition.mStart + aCompEnd.mData.Length());
  1.4080 +  mComposition.End();
  1.4081 +}
  1.4082 +
  1.4083 +#ifdef DEBUG
  1.4084 +// static
  1.4085 +bool
  1.4086 +nsTextStore::CurrentKeyboardLayoutHasIME()
  1.4087 +{
  1.4088 +  if (!sInputProcessorProfiles) {
  1.4089 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.4090 +      ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED due to there is "
  1.4091 +       "no input processor profiles instance"));
  1.4092 +    return false;
  1.4093 +  }
  1.4094 +  nsRefPtr<ITfInputProcessorProfileMgr> profileMgr;
  1.4095 +  HRESULT hr =
  1.4096 +    sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
  1.4097 +                                            getter_AddRefs(profileMgr));
  1.4098 +  if (FAILED(hr) || !profileMgr) {
  1.4099 +    // On Windows Vista or later, ImmIsIME() API always returns true.
  1.4100 +    // If we failed to obtain the profile manager, we cannot know if current
  1.4101 +    // keyboard layout has IME.
  1.4102 +    if (IsVistaOrLater()) {
  1.4103 +      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.4104 +        ("TSF:   nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
  1.4105 +         "ITfInputProcessorProfileMgr"));
  1.4106 +      return false;
  1.4107 +    }
  1.4108 +    // If the profiles instance doesn't have ITfInputProcessorProfileMgr
  1.4109 +    // interface, that means probably we're running on WinXP or WinServer2003
  1.4110 +    // (except WinServer2003 R2).  Then, we should use ImmIsIME().
  1.4111 +    return ::ImmIsIME(::GetKeyboardLayout(0));
  1.4112 +  }
  1.4113 +
  1.4114 +  TF_INPUTPROCESSORPROFILE profile;
  1.4115 +  hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
  1.4116 +  if (hr == S_FALSE) {
  1.4117 +    return false; // not found or not active
  1.4118 +  }
  1.4119 +  if (FAILED(hr)) {
  1.4120 +    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1.4121 +      ("TSF:   nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive "
  1.4122 +       "active profile"));
  1.4123 +    return false;
  1.4124 +  }
  1.4125 +  return (profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR);
  1.4126 +}
  1.4127 +#endif // #ifdef DEBUG

mercurial