widget/windows/nsTextStore.cpp

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

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

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

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include <olectl.h>
     7 #include <algorithm>
     9 #ifdef MOZ_LOGGING
    10 #define FORCE_PR_LOG /* Allow logging in the release build */
    11 #endif // MOZ_LOGGING
    12 #include "prlog.h"
    14 #include "nscore.h"
    15 #include "nsWindow.h"
    16 #ifdef MOZ_METRO
    17 #include "winrt/MetroWidget.h"
    18 #endif
    19 #include "nsPrintfCString.h"
    20 #include "WinUtils.h"
    21 #include "mozilla/Preferences.h"
    22 #include "mozilla/TextEvents.h"
    23 #include "mozilla/WindowsVersion.h"
    25 #define INPUTSCOPE_INIT_GUID
    26 #include "nsTextStore.h"
    28 using namespace mozilla;
    29 using namespace mozilla::widget;
    31 static const char* kPrefNameTSFEnabled = "intl.tsf.enable";
    33 static const char* kLegacyPrefNameTSFEnabled = "intl.enable_tsf_support";
    35 #ifdef PR_LOGGING
    36 /**
    37  * TSF related code should log its behavior even on release build especially
    38  * in the interface methods.
    39  *
    40  * In interface methods, use PR_LOG_ALWAYS.
    41  * In internal methods, use PR_LOG_DEBUG for logging normal behavior.
    42  * For logging error, use PR_LOG_ERROR.
    43  *
    44  * When an instance method is called, start with following text:
    45  *   "TSF: 0x%p nsFoo::Bar(", the 0x%p should be the "this" of the nsFoo.
    46  * after that, start with:
    47  *   "TSF: 0x%p   nsFoo::Bar("
    48  * In an internal method, start with following text:
    49  *   "TSF: 0x%p   nsFoo::Bar("
    50  * When a static method is called, start with following text:
    51  *   "TSF: nsFoo::Bar("
    52  */
    54 PRLogModuleInfo* sTextStoreLog = nullptr;
    55 #endif // #ifdef PR_LOGGING
    57 /******************************************************************/
    58 /* InputScopeImpl                                                 */
    59 /******************************************************************/
    61 class InputScopeImpl MOZ_FINAL : public ITfInputScope
    62 {
    63 public:
    64   InputScopeImpl(const nsTArray<InputScope>& aList) :
    65     mRefCnt(1),
    66     mInputScopes(aList)
    67   {
    68     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
    69       ("TSF: 0x%p InputScopeImpl()", this));
    70   }
    72   STDMETHODIMP_(ULONG) AddRef(void) { return ++mRefCnt; }
    74   STDMETHODIMP_(ULONG) Release(void)
    75   {
    76     --mRefCnt;
    77     if (mRefCnt) {
    78       return mRefCnt;
    79     }
    80     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
    81       ("TSF: 0x%p InputScopeImpl::Release() final", this));
    82     delete this;
    83     return 0;
    84   }
    86   STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    87   {
    88     *ppv=nullptr;
    89     if ( (IID_IUnknown == riid) || (IID_ITfInputScope == riid) ) {
    90       *ppv = static_cast<ITfInputScope*>(this);
    91     }
    92     if (*ppv) {
    93       AddRef();
    94       return S_OK;
    95     }
    96     return E_NOINTERFACE;
    97   }
    99   STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount)
   100   {
   101     uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length());
   103     InputScope* pScope = (InputScope*) CoTaskMemAlloc(sizeof(InputScope) * count);
   104     NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY);
   106     if (mInputScopes.IsEmpty()) {
   107       *pScope = IS_DEFAULT;
   108       *pcCount = 1;
   109       *pprgInputScopes = pScope;
   110       return S_OK;
   111     }
   113     *pcCount = 0;
   115     for (uint32_t idx = 0; idx < count; idx++) {
   116       *(pScope + idx) = mInputScopes[idx];
   117       (*pcCount)++;
   118     }
   120     *pprgInputScopes = pScope;
   121     return S_OK;
   122   }
   124   STDMETHODIMP GetPhrase(BSTR **ppbstrPhrases, UINT *pcCount) { return E_NOTIMPL; }
   125   STDMETHODIMP GetRegularExpression(BSTR *pbstrRegExp) { return E_NOTIMPL; }
   126   STDMETHODIMP GetSRGS(BSTR *pbstrSRGS) { return E_NOTIMPL; }
   127   STDMETHODIMP GetXML(BSTR *pbstrXML) { return E_NOTIMPL; }
   129 private:
   130   DWORD mRefCnt;
   131   nsTArray<InputScope> mInputScopes;
   132 };
   134 /******************************************************************/
   135 /* nsTextStore                                                    */
   136 /******************************************************************/
   138 ITfThreadMgr*           nsTextStore::sTsfThreadMgr   = nullptr;
   139 ITfMessagePump*         nsTextStore::sMessagePump    = nullptr;
   140 ITfKeystrokeMgr*        nsTextStore::sKeystrokeMgr   = nullptr;
   141 ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = nullptr;
   142 ITfCategoryMgr*         nsTextStore::sCategoryMgr    = nullptr;
   143 ITfDocumentMgr*         nsTextStore::sTsfDisabledDocumentMgr = nullptr;
   144 ITfContext*             nsTextStore::sTsfDisabledContext = nullptr;
   145 ITfInputProcessorProfiles* nsTextStore::sInputProcessorProfiles = nullptr;
   146 DWORD         nsTextStore::sTsfClientId  = 0;
   147 nsTextStore*  nsTextStore::sTsfTextStore = nullptr;
   149 bool nsTextStore::sCreateNativeCaretForATOK = false;
   151 UINT nsTextStore::sFlushTIPInputMessage  = 0;
   153 #define TEXTSTORE_DEFAULT_VIEW (1)
   155 #ifdef PR_LOGGING
   157 static const char*
   158 GetBoolName(bool aBool)
   159 {
   160   return aBool ? "true" : "false";
   161 }
   163 static void
   164 HandleSeparator(nsCString& aDesc)
   165 {
   166   if (!aDesc.IsEmpty()) {
   167     aDesc.AppendLiteral(" | ");
   168   }
   169 }
   171 static const nsCString
   172 GetFindFlagName(DWORD aFindFlag)
   173 {
   174   nsAutoCString description;
   175   if (!aFindFlag) {
   176     description.AppendLiteral("no flags (0)");
   177     return description;
   178   }
   179   if (aFindFlag & TS_ATTR_FIND_BACKWARDS) {
   180     description.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
   181   }
   182   if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) {
   183     HandleSeparator(description);
   184     description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
   185   }
   186   if (aFindFlag & TS_ATTR_FIND_UPDATESTART) {
   187     HandleSeparator(description);
   188     description.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
   189   }
   190   if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) {
   191     HandleSeparator(description);
   192     description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
   193   }
   194   if (aFindFlag & TS_ATTR_FIND_WANT_END) {
   195     HandleSeparator(description);
   196     description.AppendLiteral("TS_ATTR_FIND_WANT_END");
   197   }
   198   if (aFindFlag & TS_ATTR_FIND_HIDDEN) {
   199     HandleSeparator(description);
   200     description.AppendLiteral("TS_ATTR_FIND_HIDDEN");
   201   }
   202   if (description.IsEmpty()) {
   203     description.AppendLiteral("Unknown (");
   204     description.AppendInt(static_cast<uint32_t>(aFindFlag));
   205     description.AppendLiteral(")");
   206   }
   207   return description;
   208 }
   210 static const char*
   211 GetIMEEnabledName(IMEState::Enabled aIMEEnabled)
   212 {
   213   switch (aIMEEnabled) {
   214     case IMEState::DISABLED:
   215       return "DISABLED";
   216     case IMEState::ENABLED:
   217       return "ENABLED";
   218     case IMEState::PASSWORD:
   219       return "PASSWORD";
   220     case IMEState::PLUGIN:
   221       return "PLUGIN";
   222     default:
   223       return "Invalid";
   224   }
   225 }
   227 static const char*
   228 GetFocusChangeName(InputContextAction::FocusChange aFocusChange)
   229 {
   230   switch (aFocusChange) {
   231     case InputContextAction::FOCUS_NOT_CHANGED:
   232       return "FOCUS_NOT_CHANGED";
   233     case InputContextAction::GOT_FOCUS:
   234       return "GOT_FOCUS";
   235     case InputContextAction::LOST_FOCUS:
   236       return "LOST_FOCUS";
   237     case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
   238       return "MENU_GOT_PSEUDO_FOCUS";
   239     case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
   240       return "MENU_LOST_PSEUDO_FOCUS";
   241     default:
   242       return "Unknown";
   243   }
   244 }
   246 static nsCString
   247 GetCLSIDNameStr(REFCLSID aCLSID)
   248 {
   249   LPOLESTR str = nullptr;
   250   HRESULT hr = ::StringFromCLSID(aCLSID, &str);
   251   if (FAILED(hr) || !str || !str[0]) {
   252     return EmptyCString();
   253   }
   255   nsAutoCString result;
   256   result = NS_ConvertUTF16toUTF8(str);
   257   ::CoTaskMemFree(str);
   258   return result;
   259 }
   261 static nsCString
   262 GetGUIDNameStr(REFGUID aGUID)
   263 {
   264   OLECHAR str[40];
   265   int len = ::StringFromGUID2(aGUID, str, ArrayLength(str));
   266   if (!len || !str[0]) {
   267     return EmptyCString();
   268   }
   270   return NS_ConvertUTF16toUTF8(str);
   271 }
   273 static nsCString
   274 GetRIIDNameStr(REFIID aRIID)
   275 {
   276   LPOLESTR str = nullptr;
   277   HRESULT hr = ::StringFromIID(aRIID, &str);
   278   if (FAILED(hr) || !str || !str[0]) {
   279     return EmptyCString();
   280   }
   282   nsAutoString key(L"Interface\\");
   283   key += str;
   285   nsAutoCString result;
   286   wchar_t buf[256];
   287   if (WinUtils::GetRegistryKey(HKEY_CLASSES_ROOT, key.get(), nullptr,
   288                                buf, sizeof(buf))) {
   289     result = NS_ConvertUTF16toUTF8(buf);
   290   } else {
   291     result = NS_ConvertUTF16toUTF8(str);
   292   }
   294   ::CoTaskMemFree(str);
   295   return result;
   296 }
   298 static const char*
   299 GetCommonReturnValueName(HRESULT aResult)
   300 {
   301   switch (aResult) {
   302     case S_OK:
   303       return "S_OK";
   304     case E_ABORT:
   305       return "E_ABORT";
   306     case E_ACCESSDENIED:
   307       return "E_ACCESSDENIED";
   308     case E_FAIL:
   309       return "E_FAIL";
   310     case E_HANDLE:
   311       return "E_HANDLE";
   312     case E_INVALIDARG:
   313       return "E_INVALIDARG";
   314     case E_NOINTERFACE:
   315       return "E_NOINTERFACE";
   316     case E_NOTIMPL:
   317       return "E_NOTIMPL";
   318     case E_OUTOFMEMORY:
   319       return "E_OUTOFMEMORY";
   320     case E_POINTER:
   321       return "E_POINTER";
   322     case E_UNEXPECTED:
   323       return "E_UNEXPECTED";
   324     default:
   325       return SUCCEEDED(aResult) ? "Succeeded" : "Failed";
   326   }
   327 }
   329 static const char*
   330 GetTextStoreReturnValueName(HRESULT aResult)
   331 {
   332   switch (aResult) {
   333     case TS_E_FORMAT:
   334       return "TS_E_FORMAT";
   335     case TS_E_INVALIDPOINT:
   336       return "TS_E_INVALIDPOINT";
   337     case TS_E_INVALIDPOS:
   338       return "TS_E_INVALIDPOS";
   339     case TS_E_NOINTERFACE:
   340       return "TS_E_NOINTERFACE";
   341     case TS_E_NOLAYOUT:
   342       return "TS_E_NOLAYOUT";
   343     case TS_E_NOLOCK:
   344       return "TS_E_NOLOCK";
   345     case TS_E_NOOBJECT:
   346       return "TS_E_NOOBJECT";
   347     case TS_E_NOSELECTION:
   348       return "TS_E_NOSELECTION";
   349     case TS_E_NOSERVICE:
   350       return "TS_E_NOSERVICE";
   351     case TS_E_READONLY:
   352       return "TS_E_READONLY";
   353     case TS_E_SYNCHRONOUS:
   354       return "TS_E_SYNCHRONOUS";
   355     case TS_S_ASYNC:
   356       return "TS_S_ASYNC";
   357     default:
   358       return GetCommonReturnValueName(aResult);
   359   }
   360 }
   362 static const nsCString
   363 GetSinkMaskNameStr(DWORD aSinkMask)
   364 {
   365   nsAutoCString description;
   366   if (aSinkMask & TS_AS_TEXT_CHANGE) {
   367     description.AppendLiteral("TS_AS_TEXT_CHANGE");
   368   }
   369   if (aSinkMask & TS_AS_SEL_CHANGE) {
   370     HandleSeparator(description);
   371     description.AppendLiteral("TS_AS_SEL_CHANGE");
   372   }
   373   if (aSinkMask & TS_AS_LAYOUT_CHANGE) {
   374     HandleSeparator(description);
   375     description.AppendLiteral("TS_AS_LAYOUT_CHANGE");
   376   }
   377   if (aSinkMask & TS_AS_ATTR_CHANGE) {
   378     HandleSeparator(description);
   379     description.AppendLiteral("TS_AS_ATTR_CHANGE");
   380   }
   381   if (aSinkMask & TS_AS_STATUS_CHANGE) {
   382     HandleSeparator(description);
   383     description.AppendLiteral("TS_AS_STATUS_CHANGE");
   384   }
   385   if (description.IsEmpty()) {
   386     description.AppendLiteral("not-specified");
   387   }
   388   return description;
   389 }
   391 static const char*
   392 GetActiveSelEndName(TsActiveSelEnd aSelEnd)
   393 {
   394   return aSelEnd == TS_AE_NONE  ? "TS_AE_NONE" :
   395          aSelEnd == TS_AE_START ? "TS_AE_START" :
   396          aSelEnd == TS_AE_END   ? "TS_AE_END" : "Unknown";
   397 }
   399 static const nsCString
   400 GetLockFlagNameStr(DWORD aLockFlags)
   401 {
   402   nsAutoCString description;
   403   if ((aLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE) {
   404     description.AppendLiteral("TS_LF_READWRITE");
   405   } else if (aLockFlags & TS_LF_READ) {
   406     description.AppendLiteral("TS_LF_READ");
   407   }
   408   if (aLockFlags & TS_LF_SYNC) {
   409     if (!description.IsEmpty()) {
   410       description.AppendLiteral(" | ");
   411     }
   412     description.AppendLiteral("TS_LF_SYNC");
   413   }
   414   if (description.IsEmpty()) {
   415     description.AppendLiteral("not-specified");
   416   }
   417   return description;
   418 }
   420 static const char*
   421 GetTextRunTypeName(TsRunType aRunType)
   422 {
   423   switch (aRunType) {
   424     case TS_RT_PLAIN:
   425       return "TS_RT_PLAIN";
   426     case TS_RT_HIDDEN:
   427       return "TS_RT_HIDDEN";
   428     case  TS_RT_OPAQUE:
   429       return "TS_RT_OPAQUE";
   430     default:
   431       return "Unknown";
   432   }
   433 }
   435 static nsCString
   436 GetColorName(const TF_DA_COLOR &aColor)
   437 {
   438   switch (aColor.type) {
   439     case TF_CT_NONE:
   440       return NS_LITERAL_CSTRING("TF_CT_NONE");
   441     case TF_CT_SYSCOLOR:
   442       return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
   443                              static_cast<int32_t>(aColor.nIndex));
   444     case TF_CT_COLORREF:
   445       return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
   446                              static_cast<int32_t>(aColor.cr));
   447       break;
   448     default:
   449       return nsPrintfCString("Unknown(%08X)",
   450                              static_cast<int32_t>(aColor.type));
   451   }
   452 }
   454 static nsCString
   455 GetLineStyleName(TF_DA_LINESTYLE aLineStyle)
   456 {
   457   switch (aLineStyle) {
   458     case TF_LS_NONE:
   459       return NS_LITERAL_CSTRING("TF_LS_NONE");
   460     case TF_LS_SOLID:
   461       return NS_LITERAL_CSTRING("TF_LS_SOLID");
   462     case TF_LS_DOT:
   463       return NS_LITERAL_CSTRING("TF_LS_DOT");
   464     case TF_LS_DASH:
   465       return NS_LITERAL_CSTRING("TF_LS_DASH");
   466     case TF_LS_SQUIGGLE:
   467       return NS_LITERAL_CSTRING("TF_LS_SQUIGGLE");
   468     default: {
   469       return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aLineStyle));
   470     }
   471   }
   472 }
   474 static nsCString
   475 GetClauseAttrName(TF_DA_ATTR_INFO aAttr)
   476 {
   477   switch (aAttr) {
   478     case TF_ATTR_INPUT:
   479       return NS_LITERAL_CSTRING("TF_ATTR_INPUT");
   480     case TF_ATTR_TARGET_CONVERTED:
   481       return NS_LITERAL_CSTRING("TF_ATTR_TARGET_CONVERTED");
   482     case TF_ATTR_CONVERTED:
   483       return NS_LITERAL_CSTRING("TF_ATTR_CONVERTED");
   484     case TF_ATTR_TARGET_NOTCONVERTED:
   485       return NS_LITERAL_CSTRING("TF_ATTR_TARGET_NOTCONVERTED");
   486     case TF_ATTR_INPUT_ERROR:
   487       return NS_LITERAL_CSTRING("TF_ATTR_INPUT_ERROR");
   488     case TF_ATTR_FIXEDCONVERTED:
   489       return NS_LITERAL_CSTRING("TF_ATTR_FIXEDCONVERTED");
   490     case TF_ATTR_OTHER:
   491       return NS_LITERAL_CSTRING("TF_ATTR_OTHER");
   492     default: {
   493       return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aAttr));
   494     }
   495   }
   496 }
   498 static nsCString
   499 GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE &aDispAttr)
   500 {
   501   nsAutoCString str;
   502   str = "crText:{ ";
   503   str += GetColorName(aDispAttr.crText);
   504   str += " }, crBk:{ ";
   505   str += GetColorName(aDispAttr.crBk);
   506   str += " }, lsStyle: ";
   507   str += GetLineStyleName(aDispAttr.lsStyle);
   508   str += ", fBoldLine: ";
   509   str += GetBoolName(aDispAttr.fBoldLine);
   510   str += ", crLine:{ ";
   511   str += GetColorName(aDispAttr.crLine);
   512   str += " }, bAttr: ";
   513   str += GetClauseAttrName(aDispAttr.bAttr);
   514   return str;
   515 }
   517 #endif // #ifdef PR_LOGGING
   519 nsTextStore::nsTextStore()
   520  : mContent(mComposition, mSelection)
   521 {
   522   mRefCnt = 1;
   523   mEditCookie = 0;
   524   mIPProfileCookie = TF_INVALID_COOKIE;
   525   mLangProfileCookie = TF_INVALID_COOKIE;
   526   mSinkMask = 0;
   527   mLock = 0;
   528   mLockQueued = 0;
   529   mInputScopeDetected = false;
   530   mInputScopeRequested = false;
   531   mIsRecordingActionsWithoutLock = false;
   532   mPendingOnSelectionChange = false;
   533   mPendingOnLayoutChange = false;
   534   mNativeCaretIsCreated = false;
   535   mIsIMM_IME = false;
   536   mOnActivatedCalled = false;
   537   // We hope that 5 or more actions don't occur at once.
   538   mPendingActions.SetCapacity(5);
   540   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   541     ("TSF: 0x%p nsTextStore::nsTestStore() SUCCEEDED", this));
   542 }
   544 bool
   545 nsTextStore::Init(ITfThreadMgr* aThreadMgr)
   546 {
   547   nsRefPtr<ITfSource> source;
   548   HRESULT hr =
   549     aThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
   550   if (FAILED(hr)) {
   551     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   552       ("TSF: 0x%p nsTextStore::Init() FAILED to get ITfSource instance "
   553        "(0x%08X)", this, hr));
   554     return false;
   555   }
   557   // On Vista or later, Windows let us know activate IME changed only with
   558   // ITfInputProcessorProfileActivationSink.  However, it's not available on XP.
   559   // On XP, ITfActiveLanguageProfileNotifySink is available for it.
   560   // NOTE: Each OnActivated() should be called when TSF becomes available.
   561   if (IsVistaOrLater()) {
   562     hr = source->AdviseSink(IID_ITfInputProcessorProfileActivationSink,
   563                    static_cast<ITfInputProcessorProfileActivationSink*>(this),
   564                    &mIPProfileCookie);
   565     if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) {
   566       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   567         ("TSF: 0x%p nsTextStore::Init() FAILED to install "
   568          "ITfInputProcessorProfileActivationSink (0x%08X)", this, hr));
   569       return false;
   570     }
   571   } else {
   572     hr = source->AdviseSink(IID_ITfActiveLanguageProfileNotifySink,
   573                    static_cast<ITfActiveLanguageProfileNotifySink*>(this),
   574                    &mLangProfileCookie);
   575     if (FAILED(hr) || mLangProfileCookie == TF_INVALID_COOKIE) {
   576       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   577         ("TSF: 0x%p nsTextStore::Init() FAILED to install "
   578          "ITfActiveLanguageProfileNotifySink (0x%08X)", this, hr));
   579       return false;
   580     }
   581   }
   583   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   584     ("TSF: 0x%p nsTextStore::Init(), "
   585      "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X",
   586      this, mIPProfileCookie, mLangProfileCookie));
   588   return true;
   589 }
   591 nsTextStore::~nsTextStore()
   592 {
   593   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   594     ("TSF: 0x%p nsTextStore instance is destroyed, "
   595      "mWidget=0x%p, mDocumentMgr=0x%p, mContext=0x%p, mIPProfileCookie=0x%08X, "
   596      "mLangProfileCookie=0x%08X",
   597      this, mWidget.get(), mDocumentMgr.get(), mContext.get(),
   598      mIPProfileCookie, mLangProfileCookie));
   600   if (mIPProfileCookie != TF_INVALID_COOKIE) {
   601     nsRefPtr<ITfSource> source;
   602     HRESULT hr =
   603       sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
   604     if (FAILED(hr)) {
   605       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   606         ("TSF: 0x%p   ~nsTextStore FAILED to get ITfSource instance "
   607          "(0x%08X)", this, hr));
   608     } else {
   609       hr = source->UnadviseSink(mIPProfileCookie);
   610       if (FAILED(hr)) {
   611         PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   612           ("TSF: 0x%p   ~nsTextStore FAILED to uninstall "
   613            "ITfInputProcessorProfileActivationSink (0x%08X)",
   614            this, hr));
   615       }
   616     }
   617   }
   619   if (mLangProfileCookie != TF_INVALID_COOKIE) {
   620     nsRefPtr<ITfSource> source;
   621     HRESULT hr =
   622       sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
   623     if (FAILED(hr)) {
   624       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   625         ("TSF: 0x%p   ~nsTextStore FAILED to get ITfSource instance "
   626          "(0x%08X)", this, hr));
   627     } else {
   628       hr = source->UnadviseSink(mLangProfileCookie);
   629       if (FAILED(hr)) {
   630         PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   631           ("TSF: 0x%p   ~nsTextStore FAILED to uninstall "
   632            "ITfActiveLanguageProfileNotifySink (0x%08X)",
   633            this, hr));
   634       }
   635     }
   636   }
   637 }
   639 bool
   640 nsTextStore::Create(nsWindowBase* aWidget)
   641 {
   642   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   643     ("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)",
   644      this, aWidget));
   646   EnsureInitActiveTIPKeyboard();
   648   if (mDocumentMgr) {
   649     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   650       ("TSF: 0x%p   nsTextStore::Create() FAILED due to already initialized",
   651        this));
   652     return false;
   653   }
   655   // Create document manager
   656   HRESULT hr = sTsfThreadMgr->CreateDocumentMgr(
   657                                   getter_AddRefs(mDocumentMgr));
   658   if (FAILED(hr)) {
   659     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   660       ("TSF: 0x%p   nsTextStore::Create() FAILED to create DocumentMgr "
   661        "(0x%08X)", this, hr));
   662     return false;
   663   }
   664   mWidget = aWidget;
   666   // Create context and add it to document manager
   667   hr = mDocumentMgr->CreateContext(sTsfClientId, 0,
   668                                    static_cast<ITextStoreACP*>(this),
   669                                    getter_AddRefs(mContext), &mEditCookie);
   670   if (FAILED(hr)) {
   671     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   672       ("TSF: 0x%p   nsTextStore::Create() FAILED to create the context "
   673        "(0x%08X)", this, hr));
   674     mDocumentMgr = nullptr;
   675     return false;
   676   }
   678   hr = mDocumentMgr->Push(mContext);
   679   if (FAILED(hr)) {
   680     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   681       ("TSF: 0x%p   nsTextStore::Create() FAILED to push the context (0x%08X)",
   682        this, hr));
   683     // XXX Why don't we use NS_IF_RELEASE() here??
   684     mContext = nullptr;
   685     mDocumentMgr = nullptr;
   686     return false;
   687   }
   689   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   690     ("TSF: 0x%p   nsTextStore::Create() succeeded: "
   691      "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
   692      this, mDocumentMgr.get(), mContext.get(), mEditCookie));
   694   return true;
   695 }
   697 bool
   698 nsTextStore::Destroy(void)
   699 {
   700   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   701     ("TSF: 0x%p nsTextStore::Destroy(), mComposition.IsComposing()=%s",
   702      this, GetBoolName(mComposition.IsComposing())));
   704   // If there is composition, TSF keeps the composition even after the text
   705   // store destroyed.  So, we should clear the composition here.
   706   if (mComposition.IsComposing()) {
   707     NS_WARNING("Composition is still alive at destroying the text store");
   708     CommitCompositionInternal(false);
   709   }
   711   mContent.Clear();
   712   mSelection.MarkDirty();
   714   if (mWidget) {
   715     // When blurred, Tablet Input Panel posts "blur" messages
   716     // and try to insert text when the message is retrieved later.
   717     // But by that time the text store is already destroyed,
   718     // so try to get the message early
   719     MSG msg;
   720     if (WinUtils::PeekMessage(&msg, mWidget->GetWindowHandle(),
   721                               sFlushTIPInputMessage, sFlushTIPInputMessage,
   722                               PM_REMOVE)) {
   723       ::DispatchMessageW(&msg);
   724     }
   725   }
   726   mContext = nullptr;
   727   if (mDocumentMgr) {
   728     mDocumentMgr->Pop(TF_POPF_ALL);
   729     mDocumentMgr = nullptr;
   730   }
   731   mSink = nullptr;
   732   mWidget = nullptr;
   734   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   735     ("TSF: 0x%p   nsTextStore::Destroy() succeeded", this));
   736   return true;
   737 }
   739 STDMETHODIMP
   740 nsTextStore::QueryInterface(REFIID riid,
   741                             void** ppv)
   742 {
   743   *ppv=nullptr;
   744   if ( (IID_IUnknown == riid) || (IID_ITextStoreACP == riid) ) {
   745     *ppv = static_cast<ITextStoreACP*>(this);
   746   } else if (IID_ITfContextOwnerCompositionSink == riid) {
   747     *ppv = static_cast<ITfContextOwnerCompositionSink*>(this);
   748   } else if (IID_ITfActiveLanguageProfileNotifySink == riid) {
   749     *ppv = static_cast<ITfActiveLanguageProfileNotifySink*>(this);
   750   } else if (IID_ITfInputProcessorProfileActivationSink == riid) {
   751     *ppv = static_cast<ITfInputProcessorProfileActivationSink*>(this);
   752   }
   753   if (*ppv) {
   754     AddRef();
   755     return S_OK;
   756   }
   758   PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   759     ("TSF: 0x%p nsTextStore::QueryInterface() FAILED, riid=%s",
   760      this, GetRIIDNameStr(riid).get()));
   761   return E_NOINTERFACE;
   762 }
   764 STDMETHODIMP_(ULONG) nsTextStore::AddRef()
   765 {
   766   return ++mRefCnt;
   767 }
   769 STDMETHODIMP_(ULONG) nsTextStore::Release()
   770 {
   771   --mRefCnt;
   772   if (0 != mRefCnt)
   773     return mRefCnt;
   774   delete this;
   775   return 0;
   776 }
   778 STDMETHODIMP
   779 nsTextStore::AdviseSink(REFIID riid,
   780                         IUnknown *punk,
   781                         DWORD dwMask)
   782 {
   783   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   784     ("TSF: 0x%p nsTextStore::AdviseSink(riid=%s, punk=0x%p, dwMask=%s), "
   785      "mSink=0x%p, mSinkMask=%s",
   786      this, GetRIIDNameStr(riid).get(), punk, GetSinkMaskNameStr(dwMask).get(),
   787      mSink.get(), GetSinkMaskNameStr(mSinkMask).get()));
   789   if (!punk) {
   790     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   791       ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to the null punk",
   792        this));
   793     return E_UNEXPECTED;
   794   }
   796   if (IID_ITextStoreACPSink != riid) {
   797     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   798       ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to "
   799        "unsupported interface", this));
   800     return E_INVALIDARG; // means unsupported interface.
   801   }
   803   if (!mSink) {
   804     // Install sink
   805     punk->QueryInterface(IID_ITextStoreACPSink, getter_AddRefs(mSink));
   806     if (!mSink) {
   807       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   808         ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to "
   809          "punk not having the interface", this));
   810       return E_UNEXPECTED;
   811     }
   812   } else {
   813     // If sink is already installed we check to see if they are the same
   814     // Get IUnknown from both sides for comparison
   815     nsRefPtr<IUnknown> comparison1, comparison2;
   816     punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
   817     mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
   818     if (comparison1 != comparison2) {
   819       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   820         ("TSF: 0x%p   nsTextStore::AdviseSink() FAILED due to "
   821          "the sink being different from the stored sink", this));
   822       return CONNECT_E_ADVISELIMIT;
   823     }
   824   }
   825   // Update mask either for a new sink or an existing sink
   826   mSinkMask = dwMask;
   827   return S_OK;
   828 }
   830 STDMETHODIMP
   831 nsTextStore::UnadviseSink(IUnknown *punk)
   832 {
   833   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   834     ("TSF: 0x%p nsTextStore::UnadviseSink(punk=0x%p), mSink=0x%p",
   835      this, punk, mSink.get()));
   837   if (!punk) {
   838     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   839       ("TSF: 0x%p   nsTextStore::UnadviseSink() FAILED due to the null punk",
   840        this));
   841     return E_INVALIDARG;
   842   }
   843   if (!mSink) {
   844     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   845       ("TSF: 0x%p   nsTextStore::UnadviseSink() FAILED due to "
   846        "any sink not stored", this));
   847     return CONNECT_E_NOCONNECTION;
   848   }
   849   // Get IUnknown from both sides for comparison
   850   nsRefPtr<IUnknown> comparison1, comparison2;
   851   punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
   852   mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
   853   // Unadvise only if sinks are the same
   854   if (comparison1 != comparison2) {
   855     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   856       ("TSF: 0x%p   nsTextStore::UnadviseSink() FAILED due to "
   857        "the sink being different from the stored sink", this));
   858     return CONNECT_E_NOCONNECTION;
   859   }
   860   mSink = nullptr;
   861   mSinkMask = 0;
   862   return S_OK;
   863 }
   865 STDMETHODIMP
   866 nsTextStore::RequestLock(DWORD dwLockFlags,
   867                          HRESULT *phrSession)
   868 {
   869   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   870     ("TSF: 0x%p nsTextStore::RequestLock(dwLockFlags=%s, phrSession=0x%p), "
   871      "mLock=%s", this, GetLockFlagNameStr(dwLockFlags).get(), phrSession,
   872      GetLockFlagNameStr(mLock).get()));
   874   if (!mSink) {
   875     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   876       ("TSF: 0x%p   nsTextStore::RequestLock() FAILED due to "
   877        "any sink not stored", this));
   878     return E_FAIL;
   879   }
   880   if (!phrSession) {
   881     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
   882       ("TSF: 0x%p   nsTextStore::RequestLock() FAILED due to "
   883        "null phrSession", this));
   884     return E_INVALIDARG;
   885   }
   887   if (!mLock) {
   888     // put on lock
   889     mLock = dwLockFlags & (~TS_LF_SYNC);
   890     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   891       ("TSF: 0x%p   Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
   892        ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
   893        this, GetLockFlagNameStr(mLock).get()));
   894     *phrSession = mSink->OnLockGranted(mLock);
   895     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   896       ("TSF: 0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
   897        "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
   898        this, GetLockFlagNameStr(mLock).get()));
   899     DidLockGranted();
   900     while (mLockQueued) {
   901       mLock = mLockQueued;
   902       mLockQueued = 0;
   903       PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   904         ("TSF: 0x%p   Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
   905          ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
   906          this, GetLockFlagNameStr(mLock).get()));
   907       mSink->OnLockGranted(mLock);
   908       PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   909         ("TSF: 0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
   910          "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
   911          this, GetLockFlagNameStr(mLock).get()));
   912       DidLockGranted();
   913     }
   915     // The document is now completely unlocked.
   916     mLock = 0;
   918     if (mPendingOnLayoutChange) {
   919       mPendingOnLayoutChange = false;
   920       if (mSink) {
   921         PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   922                ("TSF: 0x%p   nsTextStore::RequestLock(), "
   923                 "calling ITextStoreACPSink::OnLayoutChange()...", this));
   924         mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
   925       }
   926       // The layout change caused by composition string change should cause
   927       // calling ITfContextOwnerServices::OnLayoutChange() too.
   928       if (mContext) {
   929         nsRefPtr<ITfContextOwnerServices> service;
   930         mContext->QueryInterface(IID_ITfContextOwnerServices,
   931                                  getter_AddRefs(service));
   932         if (service) {
   933           PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   934                  ("TSF: 0x%p   nsTextStore::RequestLock(), "
   935                   "calling ITfContextOwnerServices::OnLayoutChange()...",
   936                   this));
   937           service->OnLayoutChange();
   938         }
   939       }
   940     }
   942     if (mPendingOnSelectionChange) {
   943       mPendingOnSelectionChange = false;
   944       if (mSink) {
   945         PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   946                ("TSF: 0x%p   nsTextStore::RequestLock(), "
   947                 "calling ITextStoreACPSink::OnSelectionChange()...", this));
   948         mSink->OnSelectionChange();
   949       }
   950     }
   952     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   953       ("TSF: 0x%p   nsTextStore::RequestLock() succeeded: *phrSession=%s",
   954        this, GetTextStoreReturnValueName(*phrSession)));
   955     return S_OK;
   956   }
   958   // only time when reentrant lock is allowed is when caller holds a
   959   // read-only lock and is requesting an async write lock
   960   if (IsReadLocked() && !IsReadWriteLocked() && IsReadWriteLock(dwLockFlags) &&
   961       !(dwLockFlags & TS_LF_SYNC)) {
   962     *phrSession = TS_S_ASYNC;
   963     mLockQueued = dwLockFlags & (~TS_LF_SYNC);
   965     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   966       ("TSF: 0x%p   nsTextStore::RequestLock() stores the request in the "
   967        "queue, *phrSession=TS_S_ASYNC", this));
   968     return S_OK;
   969   }
   971   // no more locks allowed
   972   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
   973     ("TSF: 0x%p   nsTextStore::RequestLock() didn't allow to lock, "
   974      "*phrSession=TS_E_SYNCHRONOUS", this));
   975   *phrSession = TS_E_SYNCHRONOUS;
   976   return E_FAIL;
   977 }
   979 void
   980 nsTextStore::DidLockGranted()
   981 {
   982   if (mNativeCaretIsCreated) {
   983     ::DestroyCaret();
   984     mNativeCaretIsCreated = false;
   985   }
   986   if (IsReadWriteLocked()) {
   987     FlushPendingActions();
   988   }
   990   // If the widget has gone, we don't need to notify anything.
   991   if (!mWidget || mWidget->Destroyed()) {
   992     mPendingOnSelectionChange = false;
   993     mPendingOnLayoutChange = false;
   994   }
   995 }
   997 void
   998 nsTextStore::FlushPendingActions()
   999 {
  1000   if (!mWidget || mWidget->Destroyed()) {
  1001     mPendingActions.Clear();
  1002     mContent.Clear();
  1003     mPendingOnSelectionChange = false;
  1004     mPendingOnLayoutChange = false;
  1005     return;
  1008   mContent.Clear();
  1010   nsRefPtr<nsWindowBase> kungFuDeathGrip(mWidget);
  1011   for (uint32_t i = 0; i < mPendingActions.Length(); i++) {
  1012     PendingAction& action = mPendingActions[i];
  1013     switch (action.mType) {
  1014       case PendingAction::COMPOSITION_START: {
  1015         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1016                ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1017                 "flushing COMPOSITION_START={ mSelectionStart=%d, "
  1018                 "mSelectionLength=%d }",
  1019                 this, action.mSelectionStart, action.mSelectionLength));
  1021         MOZ_ASSERT(mComposition.mLastData.IsEmpty());
  1023         // Select composition range so the new composition replaces the range
  1024         WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
  1025         mWidget->InitEvent(selectionSet);
  1026         selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart);
  1027         selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength);
  1028         selectionSet.mReversed = false;
  1029         mWidget->DispatchWindowEvent(&selectionSet);
  1030         if (!selectionSet.mSucceeded) {
  1031           PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1032                  ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1033                   "FAILED due to NS_SELECTION_SET failure", this));
  1034           break;
  1036         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1037                ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1038                 "dispatching compositionstart event...", this));
  1039         WidgetCompositionEvent compositionStart(true, NS_COMPOSITION_START,
  1040                                                 mWidget);
  1041         mWidget->InitEvent(compositionStart);
  1042         mWidget->DispatchWindowEvent(&compositionStart);
  1043         if (!mWidget || mWidget->Destroyed()) {
  1044           break;
  1046         break;
  1048       case PendingAction::COMPOSITION_UPDATE: {
  1049         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1050                ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1051                 "flushing COMPOSITION_UPDATE={ mData=\"%s\", "
  1052                 "mRanges=0x%p, mRanges->Length()=%d }",
  1053                 this, NS_ConvertUTF16toUTF8(action.mData).get(), action.mRanges.get(),
  1054                 action.mRanges ? action.mRanges->Length() : 0));
  1056         if (!action.mRanges) {
  1057           NS_WARNING("How does this case occur?");
  1058           action.mRanges = new TextRangeArray();
  1061         // Adjust offsets in the ranges for XP linefeed character (only \n).
  1062         // XXX Following code is the safest approach.  However, it wastes
  1063         //     a little performance.  For ensuring the clauses do not
  1064         //     overlap each other, we should redesign TextRange later.
  1065         for (uint32_t i = 0; i < action.mRanges->Length(); ++i) {
  1066           TextRange& range = action.mRanges->ElementAt(i);
  1067           TextRange nativeRange = range;
  1068           if (nativeRange.mStartOffset > 0) {
  1069             nsAutoString preText(
  1070               Substring(action.mData, 0, nativeRange.mStartOffset));
  1071             preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1072                                      NS_LITERAL_STRING("\n"));
  1073             range.mStartOffset = preText.Length();
  1075           if (nativeRange.Length() == 0) {
  1076             range.mEndOffset = range.mStartOffset;
  1077           } else {
  1078             nsAutoString clause(
  1079               Substring(action.mData,
  1080                         nativeRange.mStartOffset, nativeRange.Length()));
  1081             clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1082                                     NS_LITERAL_STRING("\n"));
  1083             range.mEndOffset = range.mStartOffset + clause.Length();
  1087         action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1088                                       NS_LITERAL_STRING("\n"));
  1090         if (action.mData != mComposition.mLastData) {
  1091           PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1092                  ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1093                   "dispatching compositionupdate event...", this));
  1094           WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
  1095                                                    mWidget);
  1096           mWidget->InitEvent(compositionUpdate);
  1097           compositionUpdate.data = action.mData;
  1098           mComposition.mLastData = compositionUpdate.data;
  1099           mWidget->DispatchWindowEvent(&compositionUpdate);
  1100           if (!mWidget || mWidget->Destroyed()) {
  1101             break;
  1105         MOZ_ASSERT(action.mData == mComposition.mLastData);
  1107         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1108                ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1109                 "dispatching text event...", this));
  1110         WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
  1111         mWidget->InitEvent(textEvent);
  1112         textEvent.theText = mComposition.mLastData;
  1113         if (action.mRanges->IsEmpty()) {
  1114           TextRange wholeRange;
  1115           wholeRange.mStartOffset = 0;
  1116           wholeRange.mEndOffset = textEvent.theText.Length();
  1117           wholeRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
  1118           action.mRanges->AppendElement(wholeRange);
  1120         textEvent.mRanges = action.mRanges;
  1121         mWidget->DispatchWindowEvent(&textEvent);
  1122         // Be aware, the mWidget might already have been destroyed.
  1123         break;
  1125       case PendingAction::COMPOSITION_END: {
  1126         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1127                ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1128                 "flushing COMPOSITION_END={ mData=\"%s\" }",
  1129                 this, NS_ConvertUTF16toUTF8(action.mData).get()));
  1131         action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  1132                                       NS_LITERAL_STRING("\n"));
  1133         if (action.mData != mComposition.mLastData) {
  1134           PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1135                  ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1136                   "dispatching compositionupdate event...", this));
  1137           WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
  1138                                                    mWidget);
  1139           mWidget->InitEvent(compositionUpdate);
  1140           compositionUpdate.data = action.mData;
  1141           mComposition.mLastData = compositionUpdate.data;
  1142           mWidget->DispatchWindowEvent(&compositionUpdate);
  1143           if (!mWidget || mWidget->Destroyed()) {
  1144             break;
  1148         MOZ_ASSERT(action.mData == mComposition.mLastData);
  1150         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1151                ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1152                 "dispatching text event...", this));
  1153         WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
  1154         mWidget->InitEvent(textEvent);
  1155         textEvent.theText = mComposition.mLastData;
  1156         mWidget->DispatchWindowEvent(&textEvent);
  1157         if (!mWidget || mWidget->Destroyed()) {
  1158           break;
  1161         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1162                ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1163                 "dispatching compositionend event...", this));
  1164         WidgetCompositionEvent compositionEnd(true, NS_COMPOSITION_END,
  1165                                               mWidget);
  1166         compositionEnd.data = mComposition.mLastData;
  1167         mWidget->InitEvent(compositionEnd);
  1168         mWidget->DispatchWindowEvent(&compositionEnd);
  1169         if (!mWidget || mWidget->Destroyed()) {
  1170           break;
  1172         mComposition.mLastData.Truncate();
  1173         break;
  1175       case PendingAction::SELECTION_SET: {
  1176         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1177                ("TSF: 0x%p   nsTextStore::FlushPendingActions() "
  1178                 "flushing SELECTION_SET={ mSelectionStart=%d, "
  1179                 "mSelectionLength=%d, mSelectionReversed=%s }",
  1180                 this, action.mSelectionStart, action.mSelectionLength,
  1181                 GetBoolName(action.mSelectionReversed)));
  1183         WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
  1184         selectionSet.mOffset = 
  1185           static_cast<uint32_t>(action.mSelectionStart);
  1186         selectionSet.mLength =
  1187           static_cast<uint32_t>(action.mSelectionLength);
  1188         selectionSet.mReversed = action.mSelectionReversed;
  1189         break;
  1191       default:
  1192         MOZ_CRASH("unexpected action type");
  1195     if (mWidget && !mWidget->Destroyed()) {
  1196       continue;
  1199     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1200            ("TSF: 0x%p   nsTextStore::FlushPendingActions(), "
  1201             "qutting since the mWidget has gone", this));
  1202     break;
  1204   mPendingActions.Clear();
  1207 STDMETHODIMP
  1208 nsTextStore::GetStatus(TS_STATUS *pdcs)
  1210   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1211     ("TSF: 0x%p nsTextStore::GetStatus(pdcs=0x%p)", this, pdcs));
  1213   if (!pdcs) {
  1214     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1215       ("TSF: 0x%p   nsTextStore::GetStatus() FAILED due to null pdcs", this));
  1216     return E_INVALIDARG;
  1218   pdcs->dwDynamicFlags = 0;
  1219   // we use a "flat" text model for TSF support so no hidden text
  1220   pdcs->dwStaticFlags = TS_SS_NOHIDDENTEXT;
  1221   return S_OK;
  1224 STDMETHODIMP
  1225 nsTextStore::QueryInsert(LONG acpTestStart,
  1226                          LONG acpTestEnd,
  1227                          ULONG cch,
  1228                          LONG *pacpResultStart,
  1229                          LONG *pacpResultEnd)
  1231   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1232          ("TSF: 0x%p nsTextStore::QueryInsert(acpTestStart=%ld, "
  1233           "acpTestEnd=%ld, cch=%lu, pacpResultStart=0x%p, pacpResultEnd=0x%p)",
  1234           this, acpTestStart, acpTestEnd, cch, acpTestStart, acpTestEnd));
  1236   if (!pacpResultStart || !pacpResultEnd) {
  1237     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1238            ("TSF: 0x%p   nsTextStore::QueryInsert() FAILED due to "
  1239             "the null argument", this));
  1240     return E_INVALIDARG;
  1243   if (acpTestStart < 0 || acpTestStart > acpTestEnd) {
  1244     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1245            ("TSF: 0x%p   nsTextStore::QueryInsert() FAILED due to "
  1246             "wrong argument", this));
  1247     return E_INVALIDARG;
  1250   // XXX need to adjust to cluster boundary
  1251   // Assume we are given good offsets for now
  1252   *pacpResultStart = acpTestStart;
  1253   *pacpResultEnd = acpTestStart + cch;
  1255   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1256          ("TSF: 0x%p  nsTextStore::QueryInsert() succeeded: "
  1257           "*pacpResultStart=%ld, *pacpResultEnd=%ld)",
  1258           this, *pacpResultStart, *pacpResultEnd));
  1259   return S_OK;
  1262 STDMETHODIMP
  1263 nsTextStore::GetSelection(ULONG ulIndex,
  1264                           ULONG ulCount,
  1265                           TS_SELECTION_ACP *pSelection,
  1266                           ULONG *pcFetched)
  1268   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1269          ("TSF: 0x%p nsTextStore::GetSelection(ulIndex=%lu, ulCount=%lu, "
  1270           "pSelection=0x%p, pcFetched=0x%p)",
  1271           this, ulIndex, ulCount, pSelection, pcFetched));
  1273   if (!IsReadLocked()) {
  1274     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1275            ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to not locked",
  1276             this));
  1277     return TS_E_NOLOCK;
  1279   if (!ulCount || !pSelection || !pcFetched) {
  1280     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1281            ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to "
  1282             "null argument", this));
  1283     return E_INVALIDARG;
  1286   *pcFetched = 0;
  1288   if (ulIndex != static_cast<ULONG>(TS_DEFAULT_SELECTION) &&
  1289       ulIndex != 0) {
  1290     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1291            ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to "
  1292             "unsupported selection", this));
  1293     return TS_E_NOSELECTION;
  1296   Selection& currentSel = CurrentSelection();
  1297   if (currentSel.IsDirty()) {
  1298     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1299            ("TSF: 0x%p   nsTextStore::GetSelection() FAILED due to "
  1300             "CurrentSelection() failure", this));
  1301     return E_FAIL;
  1303   *pSelection = currentSel.ACP();
  1304   *pcFetched = 1;
  1305   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1306          ("TSF: 0x%p   nsTextStore::GetSelection() succeeded", this));
  1307   return S_OK;
  1310 nsTextStore::Content&
  1311 nsTextStore::CurrentContent()
  1313   Selection& currentSel = CurrentSelection();
  1314   if (currentSel.IsDirty()) {
  1315     mContent.Clear();
  1316     return mContent;
  1319   if (!mContent.IsInitialized()) {
  1320     MOZ_ASSERT(mWidget && !mWidget->Destroyed());
  1322     WidgetQueryContentEvent queryText(true, NS_QUERY_TEXT_CONTENT, mWidget);
  1323     queryText.InitForQueryTextContent(0, UINT32_MAX);
  1324     mWidget->InitEvent(queryText);
  1325     mWidget->DispatchWindowEvent(&queryText);
  1326     NS_ENSURE_TRUE(queryText.mSucceeded, mContent);
  1328     mContent.Init(queryText.mReply.mString);
  1331   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1332          ("TSF: 0x%p   nsTextStore::CurrentContent(): "
  1333           "mContent={ mText.Length()=%d }",
  1334           this, mContent.Text().Length()));
  1336   return mContent;
  1339 nsTextStore::Selection&
  1340 nsTextStore::CurrentSelection()
  1342   if (mSelection.IsDirty()) {
  1343     // If the window has never been available, we should crash since working
  1344     // with broken values may make TIP confused.
  1345     if (!mWidget || mWidget->Destroyed()) {
  1346       MOZ_CRASH();
  1349     WidgetQueryContentEvent querySelection(true, NS_QUERY_SELECTED_TEXT,
  1350                                            mWidget);
  1351     mWidget->InitEvent(querySelection);
  1352     mWidget->DispatchWindowEvent(&querySelection);
  1353     NS_ENSURE_TRUE(querySelection.mSucceeded, mSelection);
  1355     mSelection.SetSelection(querySelection.mReply.mOffset,
  1356                             querySelection.mReply.mString.Length(),
  1357                             querySelection.mReply.mReversed);
  1360   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1361          ("TSF: 0x%p   nsTextStore::CurrentSelection(): "
  1362           "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
  1363           this, mSelection.StartOffset(), mSelection.EndOffset(),
  1364           mSelection.Length(),
  1365           GetBoolName(mSelection.IsReversed())));
  1367   return mSelection;
  1370 static HRESULT
  1371 GetRangeExtent(ITfRange* aRange, LONG* aStart, LONG* aLength)
  1373   nsRefPtr<ITfRangeACP> rangeACP;
  1374   aRange->QueryInterface(IID_ITfRangeACP, getter_AddRefs(rangeACP));
  1375   NS_ENSURE_TRUE(rangeACP, E_FAIL);
  1376   return rangeACP->GetExtent(aStart, aLength);
  1379 static uint32_t
  1380 GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE &aDisplayAttr)
  1382   uint32_t result;
  1383   switch (aDisplayAttr.bAttr) {
  1384     case TF_ATTR_TARGET_CONVERTED:
  1385       result = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
  1386       break;
  1387     case TF_ATTR_CONVERTED:
  1388       result = NS_TEXTRANGE_CONVERTEDTEXT;
  1389       break;
  1390     case TF_ATTR_TARGET_NOTCONVERTED:
  1391       result = NS_TEXTRANGE_SELECTEDRAWTEXT;
  1392       break;
  1393     default:
  1394       result = NS_TEXTRANGE_RAWINPUT;
  1395       break;
  1397   return result;
  1400 HRESULT
  1401 nsTextStore::GetDisplayAttribute(ITfProperty* aAttrProperty,
  1402                                  ITfRange* aRange,
  1403                                  TF_DISPLAYATTRIBUTE* aResult)
  1405   NS_ENSURE_TRUE(aAttrProperty, E_FAIL);
  1406   NS_ENSURE_TRUE(aRange, E_FAIL);
  1407   NS_ENSURE_TRUE(aResult, E_FAIL);
  1409   HRESULT hr;
  1411 #ifdef PR_LOGGING
  1412   if (PR_LOG_TEST(sTextStoreLog, PR_LOG_DEBUG)) {
  1413     LONG start = 0, length = 0;
  1414     hr = GetRangeExtent(aRange, &start, &length);
  1415     PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1416            ("TSF: 0x%p   nsTextStore::GetDisplayAttribute(): "
  1417             "GetDisplayAttribute range=%ld-%ld (hr=%s)",
  1418             this, start - mComposition.mStart,
  1419             start - mComposition.mStart + length,
  1420             GetCommonReturnValueName(hr)));
  1422 #endif
  1424   VARIANT propValue;
  1425   ::VariantInit(&propValue);
  1426   hr = aAttrProperty->GetValue(TfEditCookie(mEditCookie), aRange, &propValue);
  1427   if (FAILED(hr)) {
  1428     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1429            ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1430             "ITfProperty::GetValue() failed", this));
  1431     return hr;
  1433   if (VT_I4 != propValue.vt) {
  1434     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1435            ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1436             "ITfProperty::GetValue() returns non-VT_I4 value", this));
  1437     ::VariantClear(&propValue);
  1438     return E_FAIL;
  1441   NS_ENSURE_TRUE(sCategoryMgr, E_FAIL);
  1442   GUID guid;
  1443   hr = sCategoryMgr->GetGUID(DWORD(propValue.lVal), &guid);
  1444   ::VariantClear(&propValue);
  1445   if (FAILED(hr)) {
  1446     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1447            ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1448             "ITfCategoryMgr::GetGUID() failed", this));
  1449     return hr;
  1452   NS_ENSURE_TRUE(sDisplayAttrMgr, E_FAIL);
  1453   nsRefPtr<ITfDisplayAttributeInfo> info;
  1454   hr = sDisplayAttrMgr->GetDisplayAttributeInfo(guid, getter_AddRefs(info),
  1455                                                 nullptr);
  1456   if (FAILED(hr) || !info) {
  1457     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1458            ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1459             "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed", this));
  1460     return hr;
  1463   hr = info->GetAttributeInfo(aResult);
  1464   if (FAILED(hr)) {
  1465     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1466            ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() FAILED due to "
  1467             "ITfDisplayAttributeInfo::GetAttributeInfo() failed", this));
  1468     return hr;
  1471   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1472          ("TSF: 0x%p   nsTextStore::GetDisplayAttribute() succeeded: "
  1473           "Result={ %s }", this, GetDisplayAttrStr(*aResult).get()));
  1474   return S_OK;
  1477 HRESULT
  1478 nsTextStore::RestartCompositionIfNecessary(ITfRange* aRangeNew)
  1480   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1481          ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary("
  1482           "aRangeNew=0x%p), mComposition.mView=0x%p",
  1483           this, aRangeNew, mComposition.mView.get()));
  1485   if (!mComposition.IsComposing()) {
  1486     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1487            ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() FAILED "
  1488             "due to no composition view", this));
  1489     return E_FAIL;
  1492   HRESULT hr;
  1493   nsRefPtr<ITfCompositionView> pComposition(mComposition.mView);
  1494   nsRefPtr<ITfRange> composingRange(aRangeNew);
  1495   if (!composingRange) {
  1496     hr = pComposition->GetRange(getter_AddRefs(composingRange));
  1497     if (FAILED(hr)) {
  1498       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1499              ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() FAILED "
  1500               "due to pComposition->GetRange() failure", this));
  1501       return hr;
  1505   // Get starting offset of the composition
  1506   LONG compStart = 0, compLength = 0;
  1507   hr = GetRangeExtent(composingRange, &compStart, &compLength);
  1508   if (FAILED(hr)) {
  1509     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1510            ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() FAILED "
  1511             "due to GetRangeExtent() failure", this));
  1512     return hr;
  1515   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1516          ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary(), "
  1517           "range=%ld-%ld, mComposition={ mStart=%ld, mString.Length()=%lu }",
  1518           this, compStart, compStart + compLength, mComposition.mStart,
  1519           mComposition.mString.Length()));
  1521   if (mComposition.mStart != compStart ||
  1522       mComposition.mString.Length() != (ULONG)compLength) {
  1523     // If the queried composition length is different from the length
  1524     // of our composition string, OnUpdateComposition is being called
  1525     // because a part of the original composition was committed.
  1526     // Reflect that by committing existing composition and starting
  1527     // a new one. RecordCompositionEndAction() followed by
  1528     // RecordCompositionStartAction() will accomplish this automagically.
  1529     RecordCompositionEndAction();
  1530     RecordCompositionStartAction(pComposition, composingRange, true);
  1533   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1534          ("TSF: 0x%p   nsTextStore::RestartCompositionIfNecessary() succeeded",
  1535           this));
  1536   return S_OK;
  1539 static bool
  1540 GetColor(const TF_DA_COLOR &aTSFColor, nscolor &aResult)
  1542   switch (aTSFColor.type) {
  1543     case TF_CT_SYSCOLOR: {
  1544       DWORD sysColor = ::GetSysColor(aTSFColor.nIndex);
  1545       aResult = NS_RGB(GetRValue(sysColor), GetGValue(sysColor),
  1546                        GetBValue(sysColor));
  1547       return true;
  1549     case TF_CT_COLORREF:
  1550       aResult = NS_RGB(GetRValue(aTSFColor.cr), GetGValue(aTSFColor.cr),
  1551                        GetBValue(aTSFColor.cr));
  1552       return true;
  1553     case TF_CT_NONE:
  1554     default:
  1555       return false;
  1559 static bool
  1560 GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle, uint8_t &aTextRangeLineStyle)
  1562   switch (aTSFLineStyle) {
  1563     case TF_LS_NONE:
  1564       aTextRangeLineStyle = TextRangeStyle::LINESTYLE_NONE;
  1565       return true;
  1566     case TF_LS_SOLID:
  1567       aTextRangeLineStyle = TextRangeStyle::LINESTYLE_SOLID;
  1568       return true;
  1569     case TF_LS_DOT:
  1570       aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DOTTED;
  1571       return true;
  1572     case TF_LS_DASH:
  1573       aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DASHED;
  1574       return true;
  1575     case TF_LS_SQUIGGLE:
  1576       aTextRangeLineStyle = TextRangeStyle::LINESTYLE_WAVY;
  1577       return true;
  1578     default:
  1579       return false;
  1583 HRESULT
  1584 nsTextStore::RecordCompositionUpdateAction()
  1586   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1587          ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction(), "
  1588           "mComposition={ mView=0x%p, mString=\"%s\" }",
  1589           this, mComposition.mView.get(),
  1590           NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  1592   if (!mComposition.IsComposing()) {
  1593     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1594            ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1595             "due to no composition view", this));
  1596     return E_FAIL;
  1599   // Getting display attributes is *really* complicated!
  1600   // We first get the context and the property objects to query for
  1601   // attributes, but since a big range can have a variety of values for
  1602   // the attribute, we have to find out all the ranges that have distinct
  1603   // attribute values. Then we query for what the value represents through
  1604   // the display attribute manager and translate that to TextRange to be
  1605   // sent in NS_TEXT_TEXT
  1607   nsRefPtr<ITfProperty> attrPropetry;
  1608   HRESULT hr = mContext->GetProperty(GUID_PROP_ATTRIBUTE,
  1609                                      getter_AddRefs(attrPropetry));
  1610   if (FAILED(hr) || !attrPropetry) {
  1611     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1612            ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1613             "due to mContext->GetProperty() failure", this));
  1614     return FAILED(hr) ? hr : E_FAIL;
  1617   nsRefPtr<ITfRange> composingRange;
  1618   hr = mComposition.mView->GetRange(getter_AddRefs(composingRange));
  1619   if (FAILED(hr)) {
  1620     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1621            ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() "
  1622             "FAILED due to mComposition.mView->GetRange() failure", this));
  1623     return hr;
  1626   nsRefPtr<IEnumTfRanges> enumRanges;
  1627   hr = attrPropetry->EnumRanges(TfEditCookie(mEditCookie),
  1628                                 getter_AddRefs(enumRanges), composingRange);
  1629   if (FAILED(hr) || !enumRanges) {
  1630     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1631            ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1632             "due to attrPropetry->EnumRanges() failure", this));
  1633     return FAILED(hr) ? hr : E_FAIL;
  1636   // First, put the log of content and selection here.
  1637   Selection& currentSel = CurrentSelection();
  1638   if (currentSel.IsDirty()) {
  1639     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1640            ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() FAILED "
  1641             "due to CurrentSelection() failure", this));
  1642     return E_FAIL;
  1645   PendingAction* action = GetPendingCompositionUpdate();
  1646   action->mData = mComposition.mString;
  1647   // The ranges might already have been initialized, however, if this is
  1648   // called again, that means we need to overwrite the ranges with current
  1649   // information.
  1650   action->mRanges->Clear();
  1652   TextRange newRange;
  1653   // No matter if we have display attribute info or not,
  1654   // we always pass in at least one range to NS_TEXT_TEXT
  1655   newRange.mStartOffset = 0;
  1656   newRange.mEndOffset = action->mData.Length();
  1657   newRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
  1658   action->mRanges->AppendElement(newRange);
  1660   nsRefPtr<ITfRange> range;
  1661   while (S_OK == enumRanges->Next(1, getter_AddRefs(range), nullptr) && range) {
  1663     LONG start = 0, length = 0;
  1664     if (FAILED(GetRangeExtent(range, &start, &length)))
  1665       continue;
  1667     TextRange newRange;
  1668     newRange.mStartOffset = uint32_t(start - mComposition.mStart);
  1669     // The end of the last range in the array is
  1670     // always kept at the end of composition
  1671     newRange.mEndOffset = mComposition.mString.Length();
  1673     TF_DISPLAYATTRIBUTE attr;
  1674     hr = GetDisplayAttribute(attrPropetry, range, &attr);
  1675     if (FAILED(hr)) {
  1676       newRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
  1677     } else {
  1678       newRange.mRangeType = GetGeckoSelectionValue(attr);
  1679       if (GetColor(attr.crText, newRange.mRangeStyle.mForegroundColor)) {
  1680         newRange.mRangeStyle.mDefinedStyles |=
  1681                                TextRangeStyle::DEFINED_FOREGROUND_COLOR;
  1683       if (GetColor(attr.crBk, newRange.mRangeStyle.mBackgroundColor)) {
  1684         newRange.mRangeStyle.mDefinedStyles |=
  1685                                TextRangeStyle::DEFINED_BACKGROUND_COLOR;
  1687       if (GetColor(attr.crLine, newRange.mRangeStyle.mUnderlineColor)) {
  1688         newRange.mRangeStyle.mDefinedStyles |=
  1689                                TextRangeStyle::DEFINED_UNDERLINE_COLOR;
  1691       if (GetLineStyle(attr.lsStyle, newRange.mRangeStyle.mLineStyle)) {
  1692         newRange.mRangeStyle.mDefinedStyles |=
  1693                                TextRangeStyle::DEFINED_LINESTYLE;
  1694         newRange.mRangeStyle.mIsBoldLine = attr.fBoldLine != 0;
  1698     TextRange& lastRange = action->mRanges->LastElement();
  1699     if (lastRange.mStartOffset == newRange.mStartOffset) {
  1700       // Replace range if last range is the same as this one
  1701       // So that ranges don't overlap and confuse the editor
  1702       lastRange = newRange;
  1703     } else {
  1704       lastRange.mEndOffset = newRange.mStartOffset;
  1705       action->mRanges->AppendElement(newRange);
  1709   // We need to hack for Korean Input System which is Korean standard TIP.
  1710   // It sets no change style to IME selection (the selection is always only
  1711   // one).  So, the composition string looks like normal (or committed) string.
  1712   // At this time, current selection range is same as the composition string
  1713   // range.  Other applications set a wide caret which covers the composition
  1714   // string,  however, Gecko doesn't support the wide caret drawing now (Gecko
  1715   // doesn't support XOR drawing), unfortunately.  For now, we should change
  1716   // the range style to undefined.
  1717   if (!currentSel.IsCollapsed() && action->mRanges->Length() == 1) {
  1718     TextRange& range = action->mRanges->ElementAt(0);
  1719     LONG start = currentSel.MinOffset();
  1720     LONG end = currentSel.MaxOffset();
  1721     if ((LONG)range.mStartOffset == start - mComposition.mStart &&
  1722         (LONG)range.mEndOffset == end - mComposition.mStart &&
  1723         range.mRangeStyle.IsNoChangeStyle()) {
  1724       range.mRangeStyle.Clear();
  1725       // The looks of selected type is better than others.
  1726       range.mRangeType = NS_TEXTRANGE_SELECTEDRAWTEXT;
  1730   // The caret position has to be collapsed.
  1731   LONG caretPosition = currentSel.MaxOffset();
  1732   caretPosition -= mComposition.mStart;
  1733   TextRange caretRange;
  1734   caretRange.mStartOffset = caretRange.mEndOffset = uint32_t(caretPosition);
  1735   caretRange.mRangeType = NS_TEXTRANGE_CARETPOSITION;
  1736   action->mRanges->AppendElement(caretRange);
  1738   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1739          ("TSF: 0x%p   nsTextStore::RecordCompositionUpdateAction() "
  1740           "succeeded", this));
  1742   return S_OK;
  1745 HRESULT
  1746 nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
  1747                                   bool aDispatchTextEvent)
  1749   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  1750          ("TSF: 0x%p   nsTextStore::SetSelectionInternal(pSelection={ "
  1751           "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s} }, "
  1752           "aDispatchTextEvent=%s), mComposition.IsComposing()=%s",
  1753           this, pSelection->acpStart, pSelection->acpEnd,
  1754           GetActiveSelEndName(pSelection->style.ase),
  1755           GetBoolName(pSelection->style.fInterimChar),
  1756           GetBoolName(aDispatchTextEvent),
  1757           GetBoolName(mComposition.IsComposing())));
  1759   MOZ_ASSERT(IsReadWriteLocked());
  1761   Selection& currentSel = CurrentSelection();
  1762   if (currentSel.IsDirty()) {
  1763     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1764        ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1765         "CurrentSelection() failure", this));
  1766     return E_FAIL;
  1769   if (mComposition.IsComposing()) {
  1770     if (aDispatchTextEvent) {
  1771       HRESULT hr = RestartCompositionIfNecessary();
  1772       if (FAILED(hr)) {
  1773         PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1774            ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1775             "RestartCompositionIfNecessary() failure", this));
  1776         return hr;
  1779     if (pSelection->acpStart < mComposition.mStart ||
  1780         pSelection->acpEnd > mComposition.EndOffset()) {
  1781       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1782          ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1783           "the selection being out of the composition string", this));
  1784       return TS_E_INVALIDPOS;
  1786     // Emulate selection during compositions
  1787     currentSel.SetSelection(*pSelection);
  1788     if (aDispatchTextEvent) {
  1789       HRESULT hr = RecordCompositionUpdateAction();
  1790       if (FAILED(hr)) {
  1791         PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1792            ("TSF: 0x%p   nsTextStore::SetSelectionInternal() FAILED due to "
  1793             "RecordCompositionUpdateAction() failure", this));
  1794         return hr;
  1797     return S_OK;
  1800   PendingAction* action = mPendingActions.AppendElement();
  1801   action->mType = PendingAction::SELECTION_SET;
  1802   action->mSelectionStart = pSelection->acpStart;
  1803   action->mSelectionLength = pSelection->acpEnd - pSelection->acpStart;
  1804   action->mSelectionReversed = (pSelection->style.ase == TS_AE_START);
  1806   currentSel.SetSelection(*pSelection);
  1808   return S_OK;
  1811 STDMETHODIMP
  1812 nsTextStore::SetSelection(ULONG ulCount,
  1813                           const TS_SELECTION_ACP *pSelection)
  1815   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1816          ("TSF: 0x%p nsTextStore::SetSelection(ulCount=%lu, pSelection=%p { "
  1817           "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s } }), "
  1818           "mComposition.IsComposing()=%s",
  1819           this, ulCount, pSelection,
  1820           pSelection ? pSelection->acpStart : 0,
  1821           pSelection ? pSelection->acpEnd : 0,
  1822           pSelection ? GetActiveSelEndName(pSelection->style.ase) : "",
  1823           pSelection ? GetBoolName(pSelection->style.fInterimChar) : "",
  1824           GetBoolName(mComposition.IsComposing())));
  1826   if (!IsReadWriteLocked()) {
  1827     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1828            ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1829             "not locked (read-write)", this));
  1830     return TS_E_NOLOCK;
  1832   if (ulCount != 1) {
  1833     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1834            ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1835             "trying setting multiple selection", this));
  1836     return E_INVALIDARG;
  1838   if (!pSelection) {
  1839     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1840            ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1841             "null argument", this));
  1842     return E_INVALIDARG;
  1845   HRESULT hr = SetSelectionInternal(pSelection, true);
  1846   if (FAILED(hr)) {
  1847     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1848            ("TSF: 0x%p   nsTextStore::SetSelection() FAILED due to "
  1849             "SetSelectionInternal() failure", this));
  1850   } else {
  1851     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1852            ("TSF: 0x%p   nsTextStore::SetSelection() succeeded", this));
  1854   return hr;
  1857 STDMETHODIMP
  1858 nsTextStore::GetText(LONG acpStart,
  1859                      LONG acpEnd,
  1860                      WCHAR *pchPlain,
  1861                      ULONG cchPlainReq,
  1862                      ULONG *pcchPlainOut,
  1863                      TS_RUNINFO *prgRunInfo,
  1864                      ULONG ulRunInfoReq,
  1865                      ULONG *pulRunInfoOut,
  1866                      LONG *pacpNext)
  1868   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1869     ("TSF: 0x%p nsTextStore::GetText(acpStart=%ld, acpEnd=%ld, pchPlain=0x%p, "
  1870      "cchPlainReq=%lu, pcchPlainOut=0x%p, prgRunInfo=0x%p, ulRunInfoReq=%lu, "
  1871      "pulRunInfoOut=0x%p, pacpNext=0x%p), mComposition={ mStart=%ld, "
  1872      "mString.Length()=%lu, IsComposing()=%s }",
  1873      this, acpStart, acpEnd, pchPlain, cchPlainReq, pcchPlainOut,
  1874      prgRunInfo, ulRunInfoReq, pulRunInfoOut, pacpNext,
  1875      mComposition.mStart, mComposition.mString.Length(),
  1876      GetBoolName(mComposition.IsComposing())));
  1878   if (!IsReadLocked()) {
  1879     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1880            ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1881             "not locked (read)", this));
  1882     return TS_E_NOLOCK;
  1885   if (!pcchPlainOut || (!pchPlain && !prgRunInfo) ||
  1886       !cchPlainReq != !pchPlain || !ulRunInfoReq != !prgRunInfo) {
  1887     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1888            ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1889             "invalid argument", this));
  1890     return E_INVALIDARG;
  1893   if (acpStart < 0 || acpEnd < -1 || (acpEnd != -1 && acpStart > acpEnd)) {
  1894     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1895            ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1896             "invalid position", this));
  1897     return TS_E_INVALIDPOS;
  1900   // Making sure to null-terminate string just to be on the safe side
  1901   *pcchPlainOut = 0;
  1902   if (pchPlain && cchPlainReq) *pchPlain = 0;
  1903   if (pulRunInfoOut) *pulRunInfoOut = 0;
  1904   if (pacpNext) *pacpNext = acpStart;
  1905   if (prgRunInfo && ulRunInfoReq) {
  1906     prgRunInfo->uCount = 0;
  1907     prgRunInfo->type = TS_RT_PLAIN;
  1910   Content& currentContent = CurrentContent();
  1911   if (!currentContent.IsInitialized()) {
  1912     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1913            ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1914             "CurrentContent() failure", this));
  1915     return E_FAIL;
  1917   if (currentContent.Text().Length() < static_cast<uint32_t>(acpStart)) {
  1918     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1919            ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1920             "acpStart is larger offset than the actual text length", this));
  1921     return TS_E_INVALIDPOS;
  1923   if (acpEnd != -1 &&
  1924       currentContent.Text().Length() < static_cast<uint32_t>(acpEnd)) {
  1925     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1926            ("TSF: 0x%p   nsTextStore::GetText() FAILED due to "
  1927             "acpEnd is larger offset than the actual text length", this));
  1928     return TS_E_INVALIDPOS;
  1930   uint32_t length = (acpEnd == -1) ?
  1931     currentContent.Text().Length() - static_cast<uint32_t>(acpStart) :
  1932     static_cast<uint32_t>(acpEnd - acpStart);
  1933   if (cchPlainReq && cchPlainReq - 1 < length) {
  1934     length = cchPlainReq - 1;
  1936   if (length) {
  1937     if (pchPlain && cchPlainReq) {
  1938       const char16_t* startChar =
  1939         currentContent.Text().BeginReading() + acpStart;
  1940       memcpy(pchPlain, startChar, length * sizeof(*pchPlain));
  1941       pchPlain[length] = 0;
  1942       *pcchPlainOut = length;
  1944     if (prgRunInfo && ulRunInfoReq) {
  1945       prgRunInfo->uCount = length;
  1946       prgRunInfo->type = TS_RT_PLAIN;
  1947       if (pulRunInfoOut) *pulRunInfoOut = 1;
  1949     if (pacpNext) *pacpNext = acpStart + length;
  1952   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1953          ("TSF: 0x%p   nsTextStore::GetText() succeeded: pcchPlainOut=0x%p, "
  1954           "*prgRunInfo={ uCount=%lu, type=%s }, *pulRunInfoOut=%lu, "
  1955           "*pacpNext=%ld)",
  1956           this, pcchPlainOut, prgRunInfo ? prgRunInfo->uCount : 0,
  1957           prgRunInfo ? GetTextRunTypeName(prgRunInfo->type) : "N/A",
  1958           pulRunInfoOut ? pulRunInfoOut : 0, pacpNext ? pacpNext : 0));
  1959   return S_OK;
  1962 STDMETHODIMP
  1963 nsTextStore::SetText(DWORD dwFlags,
  1964                      LONG acpStart,
  1965                      LONG acpEnd,
  1966                      const WCHAR *pchText,
  1967                      ULONG cch,
  1968                      TS_TEXTCHANGE *pChange)
  1970   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  1971          ("TSF: 0x%p nsTextStore::SetText(dwFlags=%s, acpStart=%ld, "
  1972           "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), "
  1973           "mComposition.IsComposing()=%s",
  1974           this, dwFlags == TS_ST_CORRECTION ? "TS_ST_CORRECTION" :
  1975                                               "not-specified",
  1976           acpStart, acpEnd, pchText,
  1977           pchText && cch ?
  1978             NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
  1979           cch, pChange, GetBoolName(mComposition.IsComposing())));
  1981   // Per SDK documentation, and since we don't have better
  1982   // ways to do this, this method acts as a helper to
  1983   // call SetSelection followed by InsertTextAtSelection
  1984   if (!IsReadWriteLocked()) {
  1985     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  1986            ("TSF: 0x%p   nsTextStore::SetText() FAILED due to "
  1987             "not locked (read)", this));
  1988     return TS_E_NOLOCK;
  1991   TS_SELECTION_ACP selection;
  1992   selection.acpStart = acpStart;
  1993   selection.acpEnd = acpEnd;
  1994   selection.style.ase = TS_AE_END;
  1995   selection.style.fInterimChar = 0;
  1996   // Set selection to desired range
  1997   HRESULT hr = SetSelectionInternal(&selection);
  1998   if (FAILED(hr)) {
  1999     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2000            ("TSF: 0x%p   nsTextStore::SetText() FAILED due to "
  2001             "SetSelectionInternal() failure", this));
  2002     return hr;
  2004   // Replace just selected text
  2005   if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
  2006                                      pChange)) {
  2007     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2008            ("TSF: 0x%p   nsTextStore::SetText() FAILED due to "
  2009             "InsertTextAtSelectionInternal() failure", this));
  2010     return E_FAIL;
  2013   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2014          ("TSF: 0x%p   nsTextStore::SetText() succeeded: pChange={ "
  2015           "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
  2016           this, pChange ? pChange->acpStart  : 0,
  2017           pChange ? pChange->acpOldEnd : 0, pChange ? pChange->acpNewEnd : 0));
  2018   return S_OK;
  2021 STDMETHODIMP
  2022 nsTextStore::GetFormattedText(LONG acpStart,
  2023                               LONG acpEnd,
  2024                               IDataObject **ppDataObject)
  2026   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2027          ("TSF: 0x%p nsTextStore::GetFormattedText() called "
  2028           "but not supported (E_NOTIMPL)", this));
  2030   // no support for formatted text
  2031   return E_NOTIMPL;
  2034 STDMETHODIMP
  2035 nsTextStore::GetEmbedded(LONG acpPos,
  2036                          REFGUID rguidService,
  2037                          REFIID riid,
  2038                          IUnknown **ppunk)
  2040   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2041          ("TSF: 0x%p nsTextStore::GetEmbedded() called "
  2042           "but not supported (E_NOTIMPL)", this));
  2044   // embedded objects are not supported
  2045   return E_NOTIMPL;
  2048 STDMETHODIMP
  2049 nsTextStore::QueryInsertEmbedded(const GUID *pguidService,
  2050                                  const FORMATETC *pFormatEtc,
  2051                                  BOOL *pfInsertable)
  2053   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2054          ("TSF: 0x%p nsTextStore::QueryInsertEmbedded() called "
  2055           "but not supported, *pfInsertable=FALSE (S_OK)", this));
  2057   // embedded objects are not supported
  2058   *pfInsertable = FALSE;
  2059   return S_OK;
  2062 STDMETHODIMP
  2063 nsTextStore::InsertEmbedded(DWORD dwFlags,
  2064                             LONG acpStart,
  2065                             LONG acpEnd,
  2066                             IDataObject *pDataObject,
  2067                             TS_TEXTCHANGE *pChange)
  2069   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2070          ("TSF: 0x%p nsTextStore::InsertEmbedded() called "
  2071           "but not supported (E_NOTIMPL)", this));
  2073   // embedded objects are not supported
  2074   return E_NOTIMPL;
  2077 void
  2078 nsTextStore::SetInputScope(const nsString& aHTMLInputType)
  2080   mInputScopes.Clear();
  2081   if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) {
  2082     return;
  2085   // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
  2086   if (aHTMLInputType.EqualsLiteral("url")) {
  2087     mInputScopes.AppendElement(IS_URL);
  2088   } else if (aHTMLInputType.EqualsLiteral("search")) {
  2089     mInputScopes.AppendElement(IS_SEARCH);
  2090   } else if (aHTMLInputType.EqualsLiteral("email")) {
  2091     mInputScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
  2092   } else if (aHTMLInputType.EqualsLiteral("password")) {
  2093     mInputScopes.AppendElement(IS_PASSWORD);
  2094   } else if (aHTMLInputType.EqualsLiteral("datetime") ||
  2095              aHTMLInputType.EqualsLiteral("datetime-local")) {
  2096     mInputScopes.AppendElement(IS_DATE_FULLDATE);
  2097     mInputScopes.AppendElement(IS_TIME_FULLTIME);
  2098   } else if (aHTMLInputType.EqualsLiteral("date") ||
  2099              aHTMLInputType.EqualsLiteral("month") ||
  2100              aHTMLInputType.EqualsLiteral("week")) {
  2101     mInputScopes.AppendElement(IS_DATE_FULLDATE);
  2102   } else if (aHTMLInputType.EqualsLiteral("time")) {
  2103     mInputScopes.AppendElement(IS_TIME_FULLTIME);
  2104   } else if (aHTMLInputType.EqualsLiteral("tel")) {
  2105     mInputScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
  2106     mInputScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
  2107   } else if (aHTMLInputType.EqualsLiteral("number")) {
  2108     mInputScopes.AppendElement(IS_NUMBER);
  2112 HRESULT
  2113 nsTextStore::ProcessScopeRequest(DWORD dwFlags,
  2114                                  ULONG cFilterAttrs,
  2115                                  const TS_ATTRID *paFilterAttrs)
  2117   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2118          ("TSF: 0x%p nsTextStore::ProcessScopeRequest(dwFlags=%s, "
  2119           "cFilterAttrs=%ld",
  2120           this, GetFindFlagName(dwFlags).get(), cFilterAttrs));
  2122   // This is a little weird! RequestSupportedAttrs gives us advanced notice
  2123   // of a support query via RetrieveRequestedAttrs for a specific attribute.
  2124   // RetrieveRequestedAttrs needs to return valid data for all attributes we
  2125   // support, but the text service will only want the input scope object
  2126   // returned in RetrieveRequestedAttrs if the dwFlags passed in here contains
  2127   // TS_ATTR_FIND_WANT_VALUE.
  2128   mInputScopeDetected = mInputScopeRequested = false;
  2130   // Currently we only support GUID_PROP_INPUTSCOPE
  2131   for (uint32_t idx = 0; idx < cFilterAttrs; ++idx) {
  2132     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2133            ("TSF: 0x%p   nsTextStore::ProcessScopeRequest() "
  2134             "requested attr=%s",
  2135             this, GetCLSIDNameStr(paFilterAttrs[idx]).get()));
  2136     if (IsEqualGUID(paFilterAttrs[idx], GUID_PROP_INPUTSCOPE)) {
  2137       PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2138              ("TSF: 0x%p   nsTextStore::ProcessScopeRequest() "
  2139               "GUID_PROP_INPUTSCOPE queried", this));
  2140       mInputScopeDetected = true;
  2141       if (dwFlags & TS_ATTR_FIND_WANT_VALUE) {
  2142         PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2143                ("TSF: 0x%p   nsTextStore::ProcessScopeRequest() "
  2144                 "TS_ATTR_FIND_WANT_VALUE specified", this));
  2145         mInputScopeRequested = true;
  2147       break;
  2150   return S_OK;
  2153 STDMETHODIMP
  2154 nsTextStore::RequestSupportedAttrs(DWORD dwFlags,
  2155                                    ULONG cFilterAttrs,
  2156                                    const TS_ATTRID *paFilterAttrs)
  2158   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2159          ("TSF: 0x%p nsTextStore::RequestSupportedAttrs(dwFlags=%s, "
  2160           "cFilterAttrs=%lu)",
  2161           this, GetFindFlagName(dwFlags).get(), cFilterAttrs));
  2163   return ProcessScopeRequest(dwFlags, cFilterAttrs, paFilterAttrs);
  2166 STDMETHODIMP
  2167 nsTextStore::RequestAttrsAtPosition(LONG acpPos,
  2168                                     ULONG cFilterAttrs,
  2169                                     const TS_ATTRID *paFilterAttrs,
  2170                                     DWORD dwFlags)
  2172   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2173          ("TSF: 0x%p nsTextStore::RequestAttrsAtPosition(acpPos=%ld, "
  2174           "cFilterAttrs=%lu, dwFlags=%s)",
  2175           this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
  2177   return ProcessScopeRequest(dwFlags | TS_ATTR_FIND_WANT_VALUE,
  2178                              cFilterAttrs, paFilterAttrs);
  2181 STDMETHODIMP
  2182 nsTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos,
  2183                                                  ULONG cFilterAttrs,
  2184                                                  const TS_ATTRID *paFilterAttr,
  2185                                                  DWORD dwFlags)
  2187   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2188          ("TSF: 0x%p nsTextStore::RequestAttrsTransitioningAtPosition("
  2189           "acpPos=%ld, cFilterAttrs=%lu, dwFlags=%s) called but not supported "
  2190           "(S_OK)",
  2191           this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
  2193   // no per character attributes defined
  2194   return S_OK;
  2197 STDMETHODIMP
  2198 nsTextStore::FindNextAttrTransition(LONG acpStart,
  2199                                     LONG acpHalt,
  2200                                     ULONG cFilterAttrs,
  2201                                     const TS_ATTRID *paFilterAttrs,
  2202                                     DWORD dwFlags,
  2203                                     LONG *pacpNext,
  2204                                     BOOL *pfFound,
  2205                                     LONG *plFoundOffset)
  2207   if (!pacpNext || !pfFound || !plFoundOffset) {
  2208     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2209            ("TSF:   0x%p nsTextStore::FindNextAttrTransition() FAILED due to "
  2210             "null argument", this));
  2211     return E_INVALIDARG;
  2214   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2215          ("TSF: 0x%p   nsTextStore::FindNextAttrTransition() called "
  2216           "but not supported (S_OK)", this));
  2218   // no per character attributes defined
  2219   *pacpNext = *plFoundOffset = acpHalt;
  2220   *pfFound = FALSE;
  2221   return S_OK;
  2224 STDMETHODIMP
  2225 nsTextStore::RetrieveRequestedAttrs(ULONG ulCount,
  2226                                     TS_ATTRVAL *paAttrVals,
  2227                                     ULONG *pcFetched)
  2229   if (!pcFetched || !ulCount || !paAttrVals) {
  2230     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2231            ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() FAILED due to "
  2232             "null argument", this));
  2233     return E_INVALIDARG;
  2236   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2237          ("TSF: 0x%p   nsTextStore::RetrieveRequestedAttrs() called "
  2238           "ulCount=%d", this, ulCount));
  2240   if (mInputScopeDetected || mInputScopeRequested) {
  2241     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2242            ("TSF: 0x%p   nsTextStore::RetrieveRequestedAttrs() for "
  2243             "GUID_PROP_INPUTSCOPE: "
  2244             "mInputScopeDetected=%s mInputScopeRequested=%s",
  2245             this, GetBoolName(mInputScopeDetected),
  2246             GetBoolName(mInputScopeRequested)));
  2248     paAttrVals->idAttr = GUID_PROP_INPUTSCOPE;
  2249     paAttrVals->dwOverlapId = 0;
  2250     paAttrVals->varValue.vt = VT_EMPTY;
  2251     *pcFetched = 1;
  2253     if (mInputScopeRequested) {
  2254       paAttrVals->varValue.vt = VT_UNKNOWN;
  2255       paAttrVals->varValue.punkVal = (IUnknown*) new InputScopeImpl(mInputScopes);
  2258     mInputScopeDetected = mInputScopeRequested = false;
  2259     return S_OK;
  2262   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2263          ("TSF: 0x%p   nsTextStore::RetrieveRequestedAttrs() called "
  2264           "for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)", this));
  2266   paAttrVals->dwOverlapId = 0;
  2267   paAttrVals->varValue.vt = VT_EMPTY;
  2268   *pcFetched = 0;
  2269   return S_OK;
  2272 STDMETHODIMP
  2273 nsTextStore::GetEndACP(LONG *pacp)
  2275   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2276          ("TSF: 0x%p nsTextStore::GetEndACP(pacp=0x%p)", this, pacp));
  2278   if (!IsReadLocked()) {
  2279     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2280            ("TSF: 0x%p   nsTextStore::GetEndACP() FAILED due to "
  2281             "not locked (read)", this));
  2282     return TS_E_NOLOCK;
  2285   if (!pacp) {
  2286     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2287            ("TSF: 0x%p   nsTextStore::GetEndACP() FAILED due to "
  2288             "null argument", this));
  2289     return E_INVALIDARG;
  2292   Content& currentContent = CurrentContent();
  2293   if (!currentContent.IsInitialized()) {
  2294     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2295            ("TSF: 0x%p   nsTextStore::GetEndACP() FAILED due to "
  2296             "CurrentContent() failure", this));
  2297     return E_FAIL;
  2299   *pacp = static_cast<LONG>(currentContent.Text().Length());
  2300   return S_OK;
  2303 STDMETHODIMP
  2304 nsTextStore::GetActiveView(TsViewCookie *pvcView)
  2306   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2307          ("TSF: 0x%p nsTextStore::GetActiveView(pvcView=0x%p)", this, pvcView));
  2309   if (!pvcView) {
  2310     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2311            ("TSF: 0x%p   nsTextStore::GetActiveView() FAILED due to "
  2312             "null argument", this));
  2313     return E_INVALIDARG;
  2316   *pvcView = TEXTSTORE_DEFAULT_VIEW;
  2318   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2319          ("TSF: 0x%p   nsTextStore::GetActiveView() succeeded: *pvcView=%ld",
  2320           this, *pvcView));
  2321   return S_OK;
  2324 STDMETHODIMP
  2325 nsTextStore::GetACPFromPoint(TsViewCookie vcView,
  2326                              const POINT *pt,
  2327                              DWORD dwFlags,
  2328                              LONG *pacp)
  2330   if (!IsReadLocked()) {
  2331     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2332            ("TSF: 0x%p   nsTextStore::GetACPFromPoint() FAILED due to "
  2333             "not locked (read)", this));
  2334     return TS_E_NOLOCK;
  2337   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  2338     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2339            ("TSF: 0x%p   nsTextStore::GetACPFromPoint() FAILED due to "
  2340             "called with invalid view", this));
  2341     return E_INVALIDARG;
  2344   if (mContent.IsLayoutChanged()) {
  2345     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2346            ("TSF: 0x%p   nsTextStore::GetACPFromPoint() FAILED due to "
  2347             "layout not recomputed", this));
  2348     mPendingOnLayoutChange = true;
  2349     return TS_E_NOLAYOUT;
  2352   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2353          ("TSF: 0x%p nsTextStore::GetACPFromPoint(vcView=%ld, "
  2354           "pt(0x%p)={ x=%ld, y=%ld }, dwFlags=%s, pacp=0x%p) called "
  2355           "but not supported (E_NOTIMPL)", this));
  2357   // not supported for now
  2358   return E_NOTIMPL;
  2361 STDMETHODIMP
  2362 nsTextStore::GetTextExt(TsViewCookie vcView,
  2363                         LONG acpStart,
  2364                         LONG acpEnd,
  2365                         RECT *prc,
  2366                         BOOL *pfClipped)
  2368   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2369          ("TSF: 0x%p nsTextStore::GetTextExt(vcView=%ld, "
  2370           "acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p)",
  2371           this, vcView, acpStart, acpEnd, prc, pfClipped));
  2373   if (!IsReadLocked()) {
  2374     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2375            ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2376             "not locked (read)", this));
  2377     return TS_E_NOLOCK;
  2380   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  2381     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2382            ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2383             "called with invalid view", this));
  2384     return E_INVALIDARG;
  2387   if (!prc || !pfClipped) {
  2388     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2389            ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2390             "null argument", this));
  2391     return E_INVALIDARG;
  2394   if (acpStart < 0 || acpEnd < acpStart) {
  2395     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2396            ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2397             "invalid position", this));
  2398     return TS_E_INVALIDPOS;
  2401   if (mContent.IsLayoutChangedAfter(acpEnd)) {
  2402     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2403            ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2404             "layout not recomputed at %d", this, acpEnd));
  2405     mPendingOnLayoutChange = true;
  2406     return TS_E_NOLAYOUT;
  2409   // use NS_QUERY_TEXT_RECT to get rect in system, screen coordinates
  2410   WidgetQueryContentEvent event(true, NS_QUERY_TEXT_RECT, mWidget);
  2411   mWidget->InitEvent(event);
  2412   event.InitForQueryTextRect(acpStart, acpEnd - acpStart);
  2413   mWidget->DispatchWindowEvent(&event);
  2414   if (!event.mSucceeded) {
  2415     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2416            ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2417             "NS_QUERY_TEXT_RECT failure", this));
  2418     return TS_E_INVALIDPOS; // but unexpected failure, maybe.
  2420   // IMEs don't like empty rects, fix here
  2421   if (event.mReply.mRect.width <= 0)
  2422     event.mReply.mRect.width = 1;
  2423   if (event.mReply.mRect.height <= 0)
  2424     event.mReply.mRect.height = 1;
  2426   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
  2427     // convert to unclipped screen rect
  2428     nsWindow* refWindow = static_cast<nsWindow*>(
  2429       event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWidget);
  2430     // Result rect is in top level widget coordinates
  2431     refWindow = refWindow->GetTopLevelWindow(false);
  2432     if (!refWindow) {
  2433       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2434              ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2435               "no top level window", this));
  2436       return E_FAIL;
  2439     event.mReply.mRect.MoveBy(refWindow->WidgetToScreenOffset());
  2442   // get bounding screen rect to test for clipping
  2443   if (!GetScreenExtInternal(*prc)) {
  2444     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2445            ("TSF: 0x%p   nsTextStore::GetTextExt() FAILED due to "
  2446             "GetScreenExtInternal() failure", this));
  2447     return E_FAIL;
  2450   // clip text rect to bounding rect
  2451   RECT textRect;
  2452   ::SetRect(&textRect, event.mReply.mRect.x, event.mReply.mRect.y,
  2453             event.mReply.mRect.XMost(), event.mReply.mRect.YMost());
  2454   if (!::IntersectRect(prc, prc, &textRect))
  2455     // Text is not visible
  2456     ::SetRectEmpty(prc);
  2458   // not equal if text rect was clipped
  2459   *pfClipped = !::EqualRect(prc, &textRect);
  2461   // ATOK refers native caret position and size on Desktop applications for
  2462   // deciding candidate window.  Therefore, we need to create native caret
  2463   // for hacking the bug.
  2464   if (sCreateNativeCaretForATOK &&
  2465       StringBeginsWith(
  2466         mActiveTIPKeyboardDescription, NS_LITERAL_STRING("ATOK ")) &&
  2467       mComposition.IsComposing() &&
  2468       mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart &&
  2469       mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) {
  2470     if (mNativeCaretIsCreated) {
  2471       ::DestroyCaret();
  2472       mNativeCaretIsCreated = false;
  2474     CreateNativeCaret();
  2477   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2478          ("TSF: 0x%p   nsTextStore::GetTextExt() succeeded: "
  2479           "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }, *pfClipped=%s",
  2480           this, prc->left, prc->top, prc->right, prc->bottom,
  2481           GetBoolName(*pfClipped)));
  2483   return S_OK;
  2486 STDMETHODIMP
  2487 nsTextStore::GetScreenExt(TsViewCookie vcView,
  2488                           RECT *prc)
  2490   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2491          ("TSF: 0x%p nsTextStore::GetScreenExt(vcView=%ld, prc=0x%p)",
  2492           this, vcView, prc));
  2494   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  2495     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2496            ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  2497             "called with invalid view", this));
  2498     return E_INVALIDARG;
  2501   if (!prc) {
  2502     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2503            ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  2504             "null argument", this));
  2505     return E_INVALIDARG;
  2508   if (!GetScreenExtInternal(*prc)) {
  2509     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2510            ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  2511             "GetScreenExtInternal() failure", this));
  2512     return E_FAIL;
  2515   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2516          ("TSF: 0x%p   nsTextStore::GetScreenExt() succeeded: "
  2517           "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
  2518           this, prc->left, prc->top, prc->right, prc->bottom));
  2519   return S_OK;
  2522 bool
  2523 nsTextStore::GetScreenExtInternal(RECT &aScreenExt)
  2525   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  2526          ("TSF: 0x%p   nsTextStore::GetScreenExtInternal()", this));
  2528   // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates
  2529   WidgetQueryContentEvent event(true, NS_QUERY_EDITOR_RECT, mWidget);
  2530   mWidget->InitEvent(event);
  2531   mWidget->DispatchWindowEvent(&event);
  2532   if (!event.mSucceeded) {
  2533     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2534            ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  2535             "NS_QUERY_EDITOR_RECT failure", this));
  2536     return false;
  2539   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
  2540     nsIntRect boundRect;
  2541     if (NS_FAILED(mWidget->GetClientBounds(boundRect))) {
  2542       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2543              ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  2544               "failed to get the client bounds", this));
  2545       return false;
  2547     ::SetRect(&aScreenExt, boundRect.x, boundRect.y,
  2548               boundRect.XMost(), boundRect.YMost());
  2549   } else {
  2550     NS_ASSERTION(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop,
  2551                  "environment isn't WindowsEnvironmentType_Desktop!");
  2552     nsWindow* refWindow = static_cast<nsWindow*>(
  2553       event.mReply.mFocusedWidget ?
  2554         event.mReply.mFocusedWidget : mWidget);
  2555     // Result rect is in top level widget coordinates
  2556     refWindow = refWindow->GetTopLevelWindow(false);
  2557     if (!refWindow) {
  2558       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2559              ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  2560               "no top level window", this));
  2561       return false;
  2564     nsIntRect boundRect;
  2565     if (NS_FAILED(refWindow->GetClientBounds(boundRect))) {
  2566       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2567              ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() FAILED due to "
  2568               "failed to get the client bounds", this));
  2569       return false;
  2572     boundRect.MoveTo(0, 0);
  2574     // Clip frame rect to window rect
  2575     boundRect.IntersectRect(event.mReply.mRect, boundRect);
  2576     if (!boundRect.IsEmpty()) {
  2577       boundRect.MoveBy(refWindow->WidgetToScreenOffset());
  2578       ::SetRect(&aScreenExt, boundRect.x, boundRect.y,
  2579                 boundRect.XMost(), boundRect.YMost());
  2580     } else {
  2581       ::SetRectEmpty(&aScreenExt);
  2585   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  2586          ("TSF: 0x%p   nsTextStore::GetScreenExtInternal() succeeded: "
  2587           "aScreenExt={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
  2588           this, aScreenExt.left, aScreenExt.top,
  2589           aScreenExt.right, aScreenExt.bottom));
  2590   return true;
  2593 STDMETHODIMP
  2594 nsTextStore::GetWnd(TsViewCookie vcView,
  2595                     HWND *phwnd)
  2597   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2598          ("TSF: 0x%p nsTextStore::GetWnd(vcView=%ld, phwnd=0x%p), "
  2599           "mWidget=0x%p",
  2600           this, vcView, phwnd, mWidget.get()));
  2602   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
  2603     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2604            ("TSF: 0x%p   nsTextStore::GetWnd() FAILED due to "
  2605             "called with invalid view", this));
  2606     return E_INVALIDARG;
  2609   if (!phwnd) {
  2610     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2611            ("TSF: 0x%p   nsTextStore::GetScreenExt() FAILED due to "
  2612             "null argument", this));
  2613     return E_INVALIDARG;
  2616   *phwnd = mWidget->GetWindowHandle();
  2618   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2619          ("TSF: 0x%p   nsTextStore::GetWnd() succeeded: *phwnd=0x%p",
  2620           this, static_cast<void*>(*phwnd)));
  2621   return S_OK;
  2624 STDMETHODIMP
  2625 nsTextStore::InsertTextAtSelection(DWORD dwFlags,
  2626                                    const WCHAR *pchText,
  2627                                    ULONG cch,
  2628                                    LONG *pacpStart,
  2629                                    LONG *pacpEnd,
  2630                                    TS_TEXTCHANGE *pChange)
  2632   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2633          ("TSF: 0x%p nsTextStore::InsertTextAtSelection(dwFlags=%s, "
  2634           "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, "
  2635           "pChange=0x%p), IsComposing()=%s",
  2636           this, dwFlags == 0 ? "0" :
  2637                 dwFlags == TF_IAS_NOQUERY ? "TF_IAS_NOQUERY" :
  2638                 dwFlags == TF_IAS_QUERYONLY ? "TF_IAS_QUERYONLY" : "Unknown",
  2639           pchText,
  2640           pchText && cch ? NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
  2641           cch, pacpStart, pacpEnd, pChange,
  2642           GetBoolName(mComposition.IsComposing())));
  2644   if (cch && !pchText) {
  2645     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2646            ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2647             "null pchText", this));
  2648     return E_INVALIDARG;
  2651   if (TS_IAS_QUERYONLY == dwFlags) {
  2652     if (!IsReadLocked()) {
  2653       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2654              ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2655               "not locked (read)", this));
  2656       return TS_E_NOLOCK;
  2659     if (!pacpStart || !pacpEnd) {
  2660       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2661              ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2662               "null argument", this));
  2663       return E_INVALIDARG;
  2666     // Get selection first
  2667     Selection& currentSel = CurrentSelection();
  2668     if (currentSel.IsDirty()) {
  2669       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2670              ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2671               "CurrentSelection() failure", this));
  2672       return E_FAIL;
  2675     // Simulate text insertion
  2676     *pacpStart = currentSel.StartOffset();
  2677     *pacpEnd = currentSel.EndOffset();
  2678     if (pChange) {
  2679       pChange->acpStart = currentSel.StartOffset();
  2680       pChange->acpOldEnd = currentSel.EndOffset();
  2681       pChange->acpNewEnd = currentSel.StartOffset() + static_cast<LONG>(cch);
  2683   } else {
  2684     if (!IsReadWriteLocked()) {
  2685       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2686              ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2687               "not locked (read-write)", this));
  2688       return TS_E_NOLOCK;
  2691     if (!pChange) {
  2692       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2693              ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2694               "null pChange", this));
  2695       return E_INVALIDARG;
  2698     if (TS_IAS_NOQUERY != dwFlags && (!pacpStart || !pacpEnd)) {
  2699       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2700              ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2701               "null argument", this));
  2702       return E_INVALIDARG;
  2705     if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
  2706                                        pChange)) {
  2707       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2708              ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() FAILED due to "
  2709               "InsertTextAtSelectionInternal() failure", this));
  2710       return E_FAIL;
  2713     if (TS_IAS_NOQUERY != dwFlags) {
  2714       *pacpStart = pChange->acpStart;
  2715       *pacpEnd = pChange->acpNewEnd;
  2718   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2719          ("TSF: 0x%p   nsTextStore::InsertTextAtSelection() succeeded: "
  2720           "*pacpStart=%ld, *pacpEnd=%ld, "
  2721           "*pChange={ acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld })",
  2722           this, pacpStart ? *pacpStart : 0, pacpEnd ? *pacpEnd : 0,
  2723           pChange ? pChange->acpStart: 0, pChange ? pChange->acpOldEnd : 0,
  2724           pChange ? pChange->acpNewEnd : 0));
  2725   return S_OK;
  2728 bool
  2729 nsTextStore::InsertTextAtSelectionInternal(const nsAString &aInsertStr,
  2730                                            TS_TEXTCHANGE* aTextChange)
  2732   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  2733          ("TSF: 0x%p   nsTextStore::InsertTextAtSelectionInternal("
  2734           "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s",
  2735           this, NS_ConvertUTF16toUTF8(aInsertStr).get(), aTextChange,
  2736           GetBoolName(mComposition.IsComposing())));
  2738   Content& currentContent = CurrentContent();
  2739   if (!currentContent.IsInitialized()) {
  2740     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2741            ("TSF: 0x%p   nsTextStore::InsertTextAtSelectionInternal() failed "
  2742             "due to CurrentContent() failure()", this));
  2743     return false;
  2746   TS_SELECTION_ACP oldSelection = currentContent.Selection().ACP();
  2747   if (!mComposition.IsComposing()) {
  2748     // Use a temporary composition to contain the text
  2749     PendingAction* compositionStart = mPendingActions.AppendElement();
  2750     compositionStart->mType = PendingAction::COMPOSITION_START;
  2751     compositionStart->mSelectionStart = oldSelection.acpStart;
  2752     compositionStart->mSelectionLength =
  2753       oldSelection.acpEnd - oldSelection.acpStart;
  2755     PendingAction* compositionEnd = mPendingActions.AppendElement();
  2756     compositionEnd->mType = PendingAction::COMPOSITION_END;
  2757     compositionEnd->mData = aInsertStr;
  2760   currentContent.ReplaceSelectedTextWith(aInsertStr);
  2762   if (aTextChange) {
  2763     aTextChange->acpStart = oldSelection.acpStart;
  2764     aTextChange->acpOldEnd = oldSelection.acpEnd;
  2765     aTextChange->acpNewEnd = currentContent.Selection().EndOffset();
  2768   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  2769          ("TSF: 0x%p   nsTextStore::InsertTextAtSelectionInternal() succeeded: "
  2770           "mWidget=0x%p, mWidget->Destroyed()=%s, aTextChange={ acpStart=%ld, "
  2771           "acpOldEnd=%ld, acpNewEnd=%ld }",
  2772           this, mWidget.get(),
  2773           GetBoolName(mWidget ? mWidget->Destroyed() : true),
  2774           aTextChange ? aTextChange->acpStart : 0,
  2775           aTextChange ? aTextChange->acpOldEnd : 0,
  2776           aTextChange ? aTextChange->acpNewEnd : 0));
  2777   return true;
  2780 STDMETHODIMP
  2781 nsTextStore::InsertEmbeddedAtSelection(DWORD dwFlags,
  2782                                        IDataObject *pDataObject,
  2783                                        LONG *pacpStart,
  2784                                        LONG *pacpEnd,
  2785                                        TS_TEXTCHANGE *pChange)
  2787   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2788          ("TSF: 0x%p nsTextStore::InsertEmbeddedAtSelection() called "
  2789           "but not supported (E_NOTIMPL)", this));
  2791   // embedded objects are not supported
  2792   return E_NOTIMPL;
  2795 HRESULT
  2796 nsTextStore::RecordCompositionStartAction(ITfCompositionView* pComposition,
  2797                                           ITfRange* aRange,
  2798                                           bool aPreserveSelection)
  2800   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  2801          ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction("
  2802           "pComposition=0x%p, aRange=0x%p, aPreserveSelection=%s), "
  2803           "mComposition.mView=0x%p",
  2804           this, pComposition, aRange, GetBoolName(aPreserveSelection),
  2805           mComposition.mView.get()));
  2807   LONG start = 0, length = 0;
  2808   HRESULT hr = GetRangeExtent(aRange, &start, &length);
  2809   if (FAILED(hr)) {
  2810     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2811            ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction() FAILED "
  2812             "due to GetRangeExtent() failure", this));
  2813     return hr;
  2816   Content& currentContent = CurrentContent();
  2817   if (!currentContent.IsInitialized()) {
  2818     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2819            ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction() FAILED "
  2820             "due to CurrentContent() failure", this));
  2821     return E_FAIL;
  2824   PendingAction* action = mPendingActions.AppendElement();
  2825   action->mType = PendingAction::COMPOSITION_START;
  2826   action->mSelectionStart = start;
  2827   action->mSelectionLength = length;
  2829   currentContent.StartComposition(pComposition, *action, aPreserveSelection);
  2831   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2832          ("TSF: 0x%p   nsTextStore::RecordCompositionStartAction() succeeded: "
  2833           "mComposition={ mStart=%ld, mString.Length()=%ld, "
  2834           "mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
  2835           "style.fInterimChar=%s } }",
  2836           this, mComposition.mStart, mComposition.mString.Length(),
  2837           mSelection.StartOffset(), mSelection.EndOffset(),
  2838           GetActiveSelEndName(mSelection.ActiveSelEnd()),
  2839           GetBoolName(mSelection.IsInterimChar())));
  2840   return S_OK;
  2843 HRESULT
  2844 nsTextStore::RecordCompositionEndAction()
  2846   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  2847          ("TSF: 0x%p nsTextStore::RecordCompositionEndAction(), "
  2848           "mComposition={ mView=0x%p, mString=\"%s\" }",
  2849           this, mComposition.mView.get(),
  2850           NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  2852   MOZ_ASSERT(mComposition.IsComposing());
  2854   PendingAction* action = mPendingActions.AppendElement();
  2855   action->mType = PendingAction::COMPOSITION_END;
  2856   action->mData = mComposition.mString;
  2858   Content& currentContent = CurrentContent();
  2859   if (!currentContent.IsInitialized()) {
  2860     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2861            ("TSF: 0x%p   nsTextStore::RecordCompositionEndAction() FAILED due "
  2862             "to CurrentContent() failure", this));
  2863     return E_FAIL;
  2865   currentContent.EndComposition(*action);
  2867   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2868          ("TSF: 0x%p   nsTextStore::RecordCompositionEndAction(), succeeded",
  2869           this));
  2870   return S_OK;
  2873 STDMETHODIMP
  2874 nsTextStore::OnStartComposition(ITfCompositionView* pComposition,
  2875                                 BOOL* pfOk)
  2877   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2878          ("TSF: 0x%p nsTextStore::OnStartComposition(pComposition=0x%p, "
  2879           "pfOk=0x%p), mComposition.mView=0x%p",
  2880           this, pComposition, pfOk, mComposition.mView.get()));
  2882   AutoPendingActionAndContentFlusher flusher(this);
  2884   *pfOk = FALSE;
  2886   // Only one composition at a time
  2887   if (mComposition.IsComposing()) {
  2888     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2889            ("TSF: 0x%p   nsTextStore::OnStartComposition() FAILED due to "
  2890             "there is another composition already (but returns S_OK)", this));
  2891     return S_OK;
  2894   nsRefPtr<ITfRange> range;
  2895   HRESULT hr = pComposition->GetRange(getter_AddRefs(range));
  2896   if (FAILED(hr)) {
  2897     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2898            ("TSF: 0x%p   nsTextStore::OnStartComposition() FAILED due to "
  2899             "pComposition->GetRange() failure", this));
  2900     return hr;
  2902   hr = RecordCompositionStartAction(pComposition, range, false);
  2903   if (FAILED(hr)) {
  2904     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2905            ("TSF: 0x%p   nsTextStore::OnStartComposition() FAILED due to "
  2906             "RecordCompositionStartAction() failure", this));
  2907     return hr;
  2910   *pfOk = TRUE;
  2911   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2912          ("TSF: 0x%p   nsTextStore::OnStartComposition() succeeded", this));
  2913   return S_OK;
  2916 STDMETHODIMP
  2917 nsTextStore::OnUpdateComposition(ITfCompositionView* pComposition,
  2918                                  ITfRange* pRangeNew)
  2920   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2921          ("TSF: 0x%p nsTextStore::OnUpdateComposition(pComposition=0x%p, "
  2922           "pRangeNew=0x%p), mComposition.mView=0x%p",
  2923           this, pComposition, pRangeNew, mComposition.mView.get()));
  2925   AutoPendingActionAndContentFlusher flusher(this);
  2927   if (!mDocumentMgr || !mContext) {
  2928     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2929            ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  2930             "not ready for the composition", this));
  2931     return E_UNEXPECTED;
  2933   if (!mComposition.IsComposing()) {
  2934     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2935            ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  2936             "no active composition", this));
  2937     return E_UNEXPECTED;
  2939   if (mComposition.mView != pComposition) {
  2940     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2941            ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  2942             "different composition view specified", this));
  2943     return E_UNEXPECTED;
  2946   // pRangeNew is null when the update is not complete
  2947   if (!pRangeNew) {
  2948     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2949            ("TSF: 0x%p   nsTextStore::OnUpdateComposition() succeeded but "
  2950             "not complete", this));
  2951     return S_OK;
  2954   HRESULT hr = RestartCompositionIfNecessary(pRangeNew);
  2955   if (FAILED(hr)) {
  2956     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2957            ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  2958             "RestartCompositionIfNecessary() failure", this));
  2959     return hr;
  2962   hr = RecordCompositionUpdateAction();
  2963   if (FAILED(hr)) {
  2964     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2965            ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  2966             "RecordCompositionUpdateAction() failure", this));
  2967     return hr;
  2970 #ifdef PR_LOGGING
  2971   if (PR_LOG_TEST(sTextStoreLog, PR_LOG_ALWAYS)) {
  2972     Selection& currentSel = CurrentSelection();
  2973     if (currentSel.IsDirty()) {
  2974       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  2975              ("TSF: 0x%p   nsTextStore::OnUpdateComposition() FAILED due to "
  2976               "CurrentSelection() failure", this));
  2977       return E_FAIL;
  2979     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2980            ("TSF: 0x%p   nsTextStore::OnUpdateComposition() succeeded: "
  2981             "mComposition={ mStart=%ld, mString=\"%s\" }, "
  2982             "CurrentSelection()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
  2983             this, mComposition.mStart,
  2984             NS_ConvertUTF16toUTF8(mComposition.mString).get(),
  2985             currentSel.StartOffset(), currentSel.EndOffset(),
  2986             GetActiveSelEndName(currentSel.ActiveSelEnd())));
  2988 #endif // #ifdef PR_LOGGING
  2989   return S_OK;
  2992 STDMETHODIMP
  2993 nsTextStore::OnEndComposition(ITfCompositionView* pComposition)
  2995   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  2996          ("TSF: 0x%p nsTextStore::OnEndComposition(pComposition=0x%p), "
  2997           "mComposition={ mView=0x%p, mString=\"%s\" }",
  2998           this, pComposition, mComposition.mView.get(),
  2999           NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  3001   AutoPendingActionAndContentFlusher flusher(this);
  3003   if (!mComposition.IsComposing()) {
  3004     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3005            ("TSF: 0x%p   nsTextStore::OnEndComposition() FAILED due to "
  3006             "no active composition", this));
  3007     return E_UNEXPECTED;
  3010   if (mComposition.mView != pComposition) {
  3011     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3012            ("TSF: 0x%p   nsTextStore::OnEndComposition() FAILED due to "
  3013             "different composition view specified", this));
  3014     return E_UNEXPECTED;
  3017   HRESULT hr = RecordCompositionEndAction();
  3018   if (FAILED(hr)) {
  3019     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3020            ("TSF: 0x%p   nsTextStore::OnEndComposition() FAILED due to "
  3021             "RecordCompositionEndAction() failure", this));
  3022     return hr;
  3025   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3026          ("TSF: 0x%p   nsTextStore::OnEndComposition(), succeeded", this));
  3027   return S_OK;
  3030 STDMETHODIMP
  3031 nsTextStore::OnActivated(REFCLSID clsid, REFGUID guidProfile,
  3032                          BOOL fActivated)
  3034   // NOTE: This is installed only on XP or Server 2003.
  3035   if (fActivated) {
  3036     // TODO: We should check if the profile's category is keyboard or not.
  3037     mOnActivatedCalled = true;
  3038     mIsIMM_IME = IsIMM_IME(::GetKeyboardLayout(0));
  3040     LANGID langID;
  3041     HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID);
  3042     if (FAILED(hr)) {
  3043       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3044              ("TSF: nsTextStore::OnActivated() FAILED due to "
  3045               "GetCurrentLanguage() failure, hr=0x%08X", hr));
  3046     } else if (IsTIPCategoryKeyboard(clsid, langID, guidProfile)) {
  3047       GetTIPDescription(clsid, langID, guidProfile,
  3048                         mActiveTIPKeyboardDescription);
  3049     } else if (clsid == CLSID_NULL || guidProfile == GUID_NULL) {
  3050       // Perhaps, this case is that keyboard layout without TIP is activated.
  3051       mActiveTIPKeyboardDescription.Truncate();
  3055   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3056          ("TSF: 0x%p nsTextStore::OnActivated(rclsid=%s, guidProfile=%s, "
  3057           "fActivated=%s), mIsIMM_IME=%s, mActiveTIPDescription=\"%s\"",
  3058           this, GetCLSIDNameStr(clsid).get(),
  3059           GetGUIDNameStr(guidProfile).get(), GetBoolName(fActivated),
  3060           GetBoolName(mIsIMM_IME),
  3061           NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
  3062   return S_OK;
  3065 STDMETHODIMP
  3066 nsTextStore::OnActivated(DWORD dwProfileType,
  3067                          LANGID langid,
  3068                          REFCLSID rclsid,
  3069                          REFGUID catid,
  3070                          REFGUID guidProfile,
  3071                          HKL hkl,
  3072                          DWORD dwFlags)
  3074   // NOTE: This is installed only on Vista or later.  However, this may be
  3075   //       called by EnsureInitActiveLanguageProfile() even on XP or Server
  3076   //       2003.
  3077   if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) &&
  3078       (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ||
  3079        catid == GUID_TFCAT_TIP_KEYBOARD)) {
  3080     mOnActivatedCalled = true;
  3081     mIsIMM_IME = IsIMM_IME(hkl);
  3082     GetTIPDescription(rclsid, langid, guidProfile,
  3083                       mActiveTIPKeyboardDescription);
  3085   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3086          ("TSF: 0x%p nsTextStore::OnActivated(dwProfileType=%s (0x%08X), "
  3087           "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, "
  3088           "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
  3089           "mActiveTIPDescription=\"%s\"",
  3090           this, dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR ?
  3091                   "TF_PROFILETYPE_INPUTPROCESSOR" :
  3092                 dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ?
  3093                   "TF_PROFILETYPE_KEYBOARDLAYOUT" : "Unknown", dwProfileType,
  3094           langid, GetCLSIDNameStr(rclsid).get(), GetGUIDNameStr(catid).get(),
  3095           GetGUIDNameStr(guidProfile).get(), hkl, dwFlags,
  3096           GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE),
  3097           GetBoolName(mIsIMM_IME),
  3098           NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
  3099   return S_OK;
  3102 // static
  3103 nsresult
  3104 nsTextStore::OnFocusChange(bool aGotFocus,
  3105                            nsWindowBase* aFocusedWidget,
  3106                            IMEState::Enabled aIMEEnabled)
  3108   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3109          ("TSF:   nsTextStore::OnFocusChange(aGotFocus=%s, "
  3110           "aFocusedWidget=0x%p, aIMEEnabled=%s), sTsfThreadMgr=0x%p, "
  3111           "sTsfTextStore=0x%p",
  3112           GetBoolName(aGotFocus), aFocusedWidget,
  3113           GetIMEEnabledName(aIMEEnabled), sTsfThreadMgr, sTsfTextStore));
  3115   // no change notifications if TSF is disabled
  3116   NS_ENSURE_TRUE(sTsfThreadMgr && sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
  3118   nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
  3119   if (aGotFocus && (aIMEEnabled == IMEState::ENABLED ||
  3120                     aIMEEnabled == IMEState::PASSWORD)) {
  3121     bool bRet = sTsfTextStore->Create(aFocusedWidget);
  3122     NS_ENSURE_TRUE(bRet, NS_ERROR_FAILURE);
  3123     NS_ENSURE_TRUE(sTsfTextStore->mDocumentMgr, NS_ERROR_FAILURE);
  3124     if (aIMEEnabled == IMEState::PASSWORD) {
  3125       MarkContextAsKeyboardDisabled(sTsfTextStore->mContext);
  3126       nsRefPtr<ITfContext> topContext;
  3127       sTsfTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
  3128       if (topContext && topContext != sTsfTextStore->mContext) {
  3129         MarkContextAsKeyboardDisabled(topContext);
  3132     HRESULT hr = sTsfThreadMgr->SetFocus(sTsfTextStore->mDocumentMgr);
  3133     NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  3134     // Use AssociateFocus() for ensuring that any native focus event
  3135     // never steal focus from our documentMgr.
  3136     hr = sTsfThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
  3137                                        sTsfTextStore->mDocumentMgr,
  3138                                        getter_AddRefs(prevFocusedDocumentMgr));
  3139     NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  3140   } else {
  3141     if (ThinksHavingFocus()) {
  3142       DebugOnly<HRESULT> hr = sTsfThreadMgr->AssociateFocus(
  3143                                 sTsfTextStore->mWidget->GetWindowHandle(),
  3144                                 nullptr, getter_AddRefs(prevFocusedDocumentMgr));
  3145       NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
  3146       NS_ASSERTION(prevFocusedDocumentMgr == sTsfTextStore->mDocumentMgr,
  3147                    "different documentMgr has been associated with the window");
  3148       sTsfTextStore->Destroy();
  3150     HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr);
  3151     NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  3153   return NS_OK;
  3156 // static
  3157 nsIMEUpdatePreference
  3158 nsTextStore::GetIMEUpdatePreference()
  3160   if (sTsfThreadMgr && sTsfTextStore && sTsfTextStore->mDocumentMgr) {
  3161     nsRefPtr<ITfDocumentMgr> docMgr;
  3162     sTsfThreadMgr->GetFocus(getter_AddRefs(docMgr));
  3163     if (docMgr == sTsfTextStore->mDocumentMgr) {
  3164       nsIMEUpdatePreference updatePreference(
  3165         nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
  3166         nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE |
  3167         nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE |
  3168         nsIMEUpdatePreference::NOTIFY_DURING_DEACTIVE);
  3169       // nsTextStore shouldn't notify TSF of selection change and text change
  3170       // which are caused by composition.
  3171       updatePreference.DontNotifyChangesCausedByComposition();
  3172       return updatePreference;
  3175   return nsIMEUpdatePreference();
  3178 nsresult
  3179 nsTextStore::OnTextChangeInternal(const IMENotification& aIMENotification)
  3181   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3182          ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(aIMENotification={ "
  3183           "mMessage=0x%08X, mTextChangeData={ mStartOffset=%lu, "
  3184           "mOldEndOffset=%lu, mNewEndOffset=%lu}), mSink=0x%p, mSinkMask=%s, "
  3185           "mComposition.IsComposing()=%s",
  3186           this, aIMENotification.mMessage,
  3187           aIMENotification.mTextChangeData.mStartOffset,
  3188           aIMENotification.mTextChangeData.mOldEndOffset,
  3189           aIMENotification.mTextChangeData.mNewEndOffset, mSink.get(),
  3190           GetSinkMaskNameStr(mSinkMask).get(),
  3191           GetBoolName(mComposition.IsComposing())));
  3193   if (IsReadLocked()) {
  3194     return NS_OK;
  3197   mSelection.MarkDirty();
  3199   if (!mSink || !(mSinkMask & TS_AS_TEXT_CHANGE)) {
  3200     return NS_OK;
  3203   if (!aIMENotification.mTextChangeData.IsInInt32Range()) {
  3204     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3205            ("TSF: 0x%p   nsTextStore::OnTextChangeInternal() FAILED due to "
  3206             "offset is too big for calling mSink->OnTextChange()...",
  3207             this));
  3208     return NS_OK;
  3211   // Some TIPs are confused by text change notification during composition.
  3212   // Especially, some of them stop working for composition in our process.
  3213   // For preventing it, let's commit the composition.
  3214   if (mComposition.IsComposing()) {
  3215     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3216            ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(), "
  3217             "committing the composition for avoiding making TIP confused...",
  3218             this));
  3219     CommitCompositionInternal(false);
  3220     return NS_OK;
  3223   TS_TEXTCHANGE textChange;
  3224   textChange.acpStart =
  3225     static_cast<LONG>(aIMENotification.mTextChangeData.mStartOffset);
  3226   textChange.acpOldEnd =
  3227     static_cast<LONG>(aIMENotification.mTextChangeData.mOldEndOffset);
  3228   textChange.acpNewEnd =
  3229     static_cast<LONG>(aIMENotification.mTextChangeData.mNewEndOffset);
  3231   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3232          ("TSF: 0x%p   nsTextStore::OnTextChangeInternal(), calling "
  3233           "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
  3234           "acpNewEnd=%ld })...", this, textChange.acpStart,
  3235           textChange.acpOldEnd, textChange.acpNewEnd));
  3236   mSink->OnTextChange(0, &textChange);
  3238   return NS_OK;
  3241 nsresult
  3242 nsTextStore::OnSelectionChangeInternal(void)
  3244   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3245          ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), "
  3246           "mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, "
  3247           "mComposition.IsComposing()=%s",
  3248           this, mSink.get(), GetSinkMaskNameStr(mSinkMask).get(),
  3249           GetBoolName(mIsRecordingActionsWithoutLock),
  3250           GetBoolName(mComposition.IsComposing())));
  3252   if (IsReadLocked()) {
  3253     return NS_OK;
  3256   mSelection.MarkDirty();
  3258   if (!mSink || !(mSinkMask & TS_AS_SEL_CHANGE)) {
  3259     return NS_OK;
  3262   // Some TIPs are confused by selection change notification during composition.
  3263   // Especially, some of them stop working for composition in our process.
  3264   // For preventing it, let's commit the composition.
  3265   if (mComposition.IsComposing()) {
  3266     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3267            ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), "
  3268             "committing the composition for avoiding making TIP confused...",
  3269             this));
  3270     CommitCompositionInternal(false);
  3271     return NS_OK;
  3274   if (!mIsRecordingActionsWithoutLock) {
  3275     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3276            ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), calling "
  3277             "mSink->OnSelectionChange()...", this));
  3278     mSink->OnSelectionChange();
  3279   } else {
  3280     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3281            ("TSF: 0x%p   nsTextStore::OnSelectionChangeInternal(), pending "
  3282             "a call of mSink->OnSelectionChange()...", this));
  3283     mPendingOnSelectionChange = true;
  3285   return NS_OK;
  3288 nsresult
  3289 nsTextStore::OnLayoutChangeInternal()
  3291   NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
  3292   NS_ENSURE_TRUE(mSink, NS_ERROR_FAILURE);
  3294   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3295          ("TSF: 0x%p   nsTextStore::OnLayoutChangeInternal(), calling "
  3296           "mSink->OnLayoutChange()...", this));
  3297   HRESULT hr = mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
  3298   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
  3300   return NS_OK;
  3303 void
  3304 nsTextStore::CreateNativeCaret()
  3306   // This method must work only on desktop application.
  3307   if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Desktop) {
  3308     return;
  3311   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3312          ("TSF: 0x%p   nsTextStore::CreateNativeCaret(), "
  3313           "mComposition.IsComposing()=%s",
  3314           this, GetBoolName(mComposition.IsComposing())));
  3316   Selection& currentSel = CurrentSelection();
  3317   if (currentSel.IsDirty()) {
  3318     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3319            ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  3320             "CurrentSelection() failure", this));
  3321     return;
  3324   // XXX If this is called without composition and the selection isn't
  3325   //     collapsed, is it OK?
  3326   uint32_t caretOffset = currentSel.MaxOffset();
  3328   WidgetQueryContentEvent queryCaretRect(true, NS_QUERY_CARET_RECT, mWidget);
  3329   queryCaretRect.InitForQueryCaretRect(caretOffset);
  3330   mWidget->InitEvent(queryCaretRect);
  3331   mWidget->DispatchWindowEvent(&queryCaretRect);
  3332   if (!queryCaretRect.mSucceeded) {
  3333     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3334            ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  3335             "NS_QUERY_CARET_RECT failure (offset=%d)", this, caretOffset));
  3336     return;
  3339   nsIntRect& caretRect = queryCaretRect.mReply.mRect;
  3340   mNativeCaretIsCreated = ::CreateCaret(mWidget->GetWindowHandle(), nullptr,
  3341                                         caretRect.width, caretRect.height);
  3342   if (!mNativeCaretIsCreated) {
  3343     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3344            ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  3345             "CreateCaret() failure", this));
  3346     return;
  3349   nsWindow* window = static_cast<nsWindow*>(mWidget.get());
  3350   nsWindow* toplevelWindow = window->GetTopLevelWindow(false);
  3351   if (!toplevelWindow) {
  3352     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3353            ("TSF: 0x%p   nsTextStore::CreateNativeCaret() FAILED due to "
  3354             "no top level window", this));
  3355     return;
  3358   if (toplevelWindow != window) {
  3359     caretRect.MoveBy(toplevelWindow->WidgetToScreenOffset());
  3360     caretRect.MoveBy(-window->WidgetToScreenOffset());
  3363   ::SetCaretPos(caretRect.x, caretRect.y);
  3366 bool
  3367 nsTextStore::EnsureInitActiveTIPKeyboard()
  3369   if (mOnActivatedCalled) {
  3370     return true;
  3373   if (IsVistaOrLater()) {
  3374     nsRefPtr<ITfInputProcessorProfileMgr> profileMgr;
  3375     HRESULT hr =
  3376       sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
  3377                                               getter_AddRefs(profileMgr));
  3378     if (FAILED(hr) || !profileMgr) {
  3379       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3380         ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  3381          "to get input processor profile manager, hr=0x%08X", this, hr));
  3382       return false;
  3385     TF_INPUTPROCESSORPROFILE profile;
  3386     hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
  3387     if (hr == S_FALSE) {
  3388       PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3389         ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  3390          "to get active keyboard layout profile due to no active profile, "
  3391          "hr=0x%08X", this, hr));
  3392       // XXX Should we call OnActivated() with arguments like non-TIP in this
  3393       //     case?
  3394       return false;
  3396     if (FAILED(hr)) {
  3397       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3398         ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  3399          "to get active TIP keyboard, hr=0x%08X", this, hr));
  3400       return false;
  3403     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3404       ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), "
  3405        "calling OnActivated() manually...", this));
  3406     OnActivated(profile.dwProfileType, profile.langid, profile.clsid,
  3407                 profile.catid, profile.guidProfile, ::GetKeyboardLayout(0),
  3408                 TF_IPSINK_FLAG_ACTIVE);
  3409     return true;
  3412   LANGID langID;
  3413   HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID);
  3414   if (FAILED(hr)) {
  3415     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3416       ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  3417        "to get current language ID, hr=0x%08X", this, hr));
  3418     return false;
  3421   nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
  3422   hr = sInputProcessorProfiles->EnumLanguageProfiles(langID,
  3423                                   getter_AddRefs(enumLangProfiles));
  3424   if (FAILED(hr) || !enumLangProfiles) {
  3425     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3426       ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
  3427        "to get language profiles enumerator, hr=0x%08X", this, hr));
  3428     return false;
  3431   TF_LANGUAGEPROFILE profile;
  3432   ULONG fetch = 0;
  3433   while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
  3434     if (!profile.fActive || profile.catid != GUID_TFCAT_TIP_KEYBOARD) {
  3435       continue;
  3437     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3438       ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), "
  3439        "calling OnActivated() manually...", this));
  3440     bool isTIP = profile.guidProfile != GUID_NULL;
  3441     OnActivated(isTIP ? TF_PROFILETYPE_INPUTPROCESSOR :
  3442                         TF_PROFILETYPE_KEYBOARDLAYOUT,
  3443                 profile.langid, profile.clsid, profile.catid,
  3444                 profile.guidProfile, ::GetKeyboardLayout(0),
  3445                 TF_IPSINK_FLAG_ACTIVE);
  3446     return true;
  3449   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3450     ("TSF: 0x%p   nsTextStore::EnsureInitActiveLanguageProfile(), "
  3451      "calling OnActivated() without active TIP manually...", this));
  3452   OnActivated(TF_PROFILETYPE_KEYBOARDLAYOUT,
  3453               langID, CLSID_NULL, GUID_TFCAT_TIP_KEYBOARD,
  3454               GUID_NULL, ::GetKeyboardLayout(0),
  3455               TF_IPSINK_FLAG_ACTIVE);
  3456   return true;
  3459 void
  3460 nsTextStore::CommitCompositionInternal(bool aDiscard)
  3462   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3463          ("TSF: 0x%p   nsTextStore::CommitCompositionInternal(aDiscard=%s), "
  3464           "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, "
  3465           "mComposition.mString=\"%s\"",
  3466           this, GetBoolName(aDiscard), mSink.get(), mContext.get(),
  3467           mComposition.mView.get(),
  3468           NS_ConvertUTF16toUTF8(mComposition.mString).get()));
  3470   if (mComposition.IsComposing() && aDiscard) {
  3471     LONG endOffset = mComposition.EndOffset();
  3472     mComposition.mString.Truncate(0);
  3473     if (mSink && !mLock) {
  3474       TS_TEXTCHANGE textChange;
  3475       textChange.acpStart = mComposition.mStart;
  3476       textChange.acpOldEnd = endOffset;
  3477       textChange.acpNewEnd = mComposition.mStart;
  3478       PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3479              ("TSF: 0x%p   nsTextStore::CommitCompositionInternal(), calling"
  3480               "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
  3481               "acpNewEnd=%ld })...", this, textChange.acpStart,
  3482               textChange.acpOldEnd, textChange.acpNewEnd));
  3483       mSink->OnTextChange(0, &textChange);
  3486   // Terminate two contexts, the base context (mContext) and the top
  3487   // if the top context is not the same as the base context
  3488   nsRefPtr<ITfContext> context = mContext;
  3489   do {
  3490     if (context) {
  3491       nsRefPtr<ITfContextOwnerCompositionServices> services;
  3492       context->QueryInterface(IID_ITfContextOwnerCompositionServices,
  3493                               getter_AddRefs(services));
  3494       if (services) {
  3495         PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3496                ("TSF: 0x%p   nsTextStore::CommitCompositionInternal(), "
  3497                 "requesting TerminateComposition() for the context 0x%p...",
  3498                 this, context.get()));
  3499         services->TerminateComposition(nullptr);
  3502     if (context != mContext)
  3503       break;
  3504     if (mDocumentMgr)
  3505       mDocumentMgr->GetTop(getter_AddRefs(context));
  3506   } while (context != mContext);
  3509 static
  3510 bool
  3511 GetCompartment(IUnknown* pUnk,
  3512                const GUID& aID,
  3513                ITfCompartment** aCompartment)
  3515   if (!pUnk) return false;
  3517   nsRefPtr<ITfCompartmentMgr> compMgr;
  3518   pUnk->QueryInterface(IID_ITfCompartmentMgr, getter_AddRefs(compMgr));
  3519   if (!compMgr) return false;
  3521   return SUCCEEDED(compMgr->GetCompartment(aID, aCompartment)) &&
  3522          (*aCompartment) != nullptr;
  3525 // static
  3526 void
  3527 nsTextStore::SetIMEOpenState(bool aState)
  3529   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3530          ("TSF: nsTextStore::SetIMEOpenState(aState=%s)", GetBoolName(aState)));
  3532   nsRefPtr<ITfCompartment> comp;
  3533   if (!GetCompartment(sTsfThreadMgr,
  3534                       GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
  3535                       getter_AddRefs(comp))) {
  3536     PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3537            ("TSF:   nsTextStore::SetIMEOpenState() FAILED due to"
  3538             "no compartment available"));
  3539     return;
  3542   VARIANT variant;
  3543   variant.vt = VT_I4;
  3544   variant.lVal = aState;
  3545   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3546          ("TSF:   nsTextStore::SetIMEOpenState(), setting "
  3547           "0x%04X to GUID_COMPARTMENT_KEYBOARD_OPENCLOSE...",
  3548           variant.lVal));
  3549   comp->SetValue(sTsfClientId, &variant);
  3552 // static
  3553 bool
  3554 nsTextStore::GetIMEOpenState(void)
  3556   nsRefPtr<ITfCompartment> comp;
  3557   if (!GetCompartment(sTsfThreadMgr,
  3558                       GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
  3559                       getter_AddRefs(comp)))
  3560     return false;
  3562   VARIANT variant;
  3563   ::VariantInit(&variant);
  3564   if (SUCCEEDED(comp->GetValue(&variant)) && variant.vt == VT_I4)
  3565     return variant.lVal != 0;
  3567   ::VariantClear(&variant); // clear up in case variant.vt != VT_I4
  3568   return false;
  3571 // static
  3572 void
  3573 nsTextStore::SetInputContext(nsWindowBase* aWidget,
  3574                              const InputContext& aContext,
  3575                              const InputContextAction& aAction)
  3577   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3578          ("TSF: nsTextStore::SetInputContext(aWidget=%p, "
  3579           "aContext.mIMEState.mEnabled=%s, aAction.mFocusChange=%s), "
  3580           "ThinksHavingFocus()=%s",
  3581           aWidget, GetIMEEnabledName(aContext.mIMEState.mEnabled),
  3582           GetFocusChangeName(aAction.mFocusChange),
  3583           GetBoolName(ThinksHavingFocus())));
  3585   NS_ENSURE_TRUE_VOID(sTsfTextStore);
  3586   sTsfTextStore->SetInputScope(aContext.mHTMLInputType);
  3588   if (aAction.mFocusChange != InputContextAction::FOCUS_NOT_CHANGED) {
  3589     return;
  3592   // If focus isn't actually changed but the enabled state is changed,
  3593   // emulate the focus move.
  3594   if (!ThinksHavingFocus() &&
  3595       aContext.mIMEState.mEnabled == IMEState::ENABLED) {
  3596     OnFocusChange(true, aWidget, aContext.mIMEState.mEnabled);
  3597   } else if (ThinksHavingFocus() &&
  3598              aContext.mIMEState.mEnabled != IMEState::ENABLED) {
  3599     OnFocusChange(false, aWidget, aContext.mIMEState.mEnabled);
  3603 // static
  3604 void
  3605 nsTextStore::MarkContextAsKeyboardDisabled(ITfContext* aContext)
  3607   VARIANT variant_int4_value1;
  3608   variant_int4_value1.vt = VT_I4;
  3609   variant_int4_value1.lVal = 1;
  3611   nsRefPtr<ITfCompartment> comp;
  3612   if (!GetCompartment(aContext,
  3613                       GUID_COMPARTMENT_KEYBOARD_DISABLED,
  3614                       getter_AddRefs(comp))) {
  3615     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3616            ("TSF: nsTextStore::MarkContextAsKeyboardDisabled() failed"
  3617             "aContext=0x%p...", aContext));
  3618     return;
  3621   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3622          ("TSF: nsTextStore::MarkContextAsKeyboardDisabled(), setting "
  3623           "to disable context 0x%p...",
  3624           aContext));
  3625   comp->SetValue(sTsfClientId, &variant_int4_value1);
  3628 // static
  3629 void
  3630 nsTextStore::MarkContextAsEmpty(ITfContext* aContext)
  3632   VARIANT variant_int4_value1;
  3633   variant_int4_value1.vt = VT_I4;
  3634   variant_int4_value1.lVal = 1;
  3636   nsRefPtr<ITfCompartment> comp;
  3637   if (!GetCompartment(aContext,
  3638                       GUID_COMPARTMENT_EMPTYCONTEXT,
  3639                       getter_AddRefs(comp))) {
  3640     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3641            ("TSF: nsTextStore::MarkContextAsEmpty() failed"
  3642             "aContext=0x%p...", aContext));
  3643     return;
  3646   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
  3647          ("TSF: nsTextStore::MarkContextAsEmpty(), setting "
  3648           "to mark empty context 0x%p...", aContext));
  3649   comp->SetValue(sTsfClientId, &variant_int4_value1);
  3652 // static
  3653 bool
  3654 nsTextStore::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
  3655                                    REFGUID aProfile)
  3657   if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
  3658     return false;
  3661   nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
  3662   HRESULT hr =
  3663     sInputProcessorProfiles->EnumLanguageProfiles(aLangID,
  3664                                getter_AddRefs(enumLangProfiles));
  3665   if (FAILED(hr) || !enumLangProfiles) {
  3666     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3667       ("TSF:   nsTextStore::IsTIPCategoryKeyboard(), FAILED "
  3668        "to get language profiles enumerator, hr=0x%08X", hr));
  3669     return false;
  3672   TF_LANGUAGEPROFILE profile;
  3673   ULONG fetch = 0;
  3674   while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
  3675     // XXX We're not sure a profile is registered with two or more categories.
  3676     if (profile.clsid == aTextService &&
  3677         profile.guidProfile == aProfile &&
  3678         profile.catid == GUID_TFCAT_TIP_KEYBOARD) {
  3679       return true;
  3682   return false;
  3685 // static
  3686 void
  3687 nsTextStore::GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
  3688                                REFGUID aProfile, nsAString& aDescription)
  3690   aDescription.Truncate();
  3692   if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
  3693     return;
  3696   BSTR description = nullptr;
  3697   HRESULT hr =
  3698     sInputProcessorProfiles->GetLanguageProfileDescription(aTextService,
  3699                                                            aLangID,
  3700                                                            aProfile,
  3701                                                            &description);
  3702   if (FAILED(hr)) {
  3703     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3704            ("TSF:   nsTextStore::InitActiveTIPDescription() FAILED due to "
  3705             "GetLanguageProfileDescription() failure, hr=0x%08X", hr));
  3706     return;
  3709   if (description && description[0]) {
  3710     aDescription.Assign(description);
  3712   ::SysFreeString(description);
  3715 // static
  3716 void
  3717 nsTextStore::Initialize()
  3719 #ifdef PR_LOGGING
  3720   if (!sTextStoreLog) {
  3721     sTextStoreLog = PR_NewLogModule("nsTextStoreWidgets");
  3723 #endif
  3725   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3726     ("TSF: nsTextStore::Initialize() is called..."));
  3728   if (sTsfThreadMgr) {
  3729     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3730       ("TSF:   nsTextStore::Initialize() FAILED due to already initialized"));
  3731     return;
  3734   bool enableTsf = Preferences::GetBool(kPrefNameTSFEnabled, false);
  3735   // Migrate legacy TSF pref to new pref.  This should be removed in next
  3736   // release cycle or later.
  3737   if (!enableTsf && Preferences::GetBool(kLegacyPrefNameTSFEnabled, false)) {
  3738     enableTsf = true;
  3739     Preferences::SetBool(kPrefNameTSFEnabled, true);
  3740     Preferences::ClearUser(kLegacyPrefNameTSFEnabled);
  3742   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3743     ("TSF:   nsTextStore::Initialize(), TSF is %s",
  3744      enableTsf ? "enabled" : "disabled"));
  3745   if (!enableTsf) {
  3746     return;
  3749   // XXX MSDN documents that ITfInputProcessorProfiles is available only on
  3750   //     desktop apps.  However, there is no known way to obtain
  3751   //     ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
  3752   //     instance.
  3753   nsRefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
  3754   HRESULT hr =
  3755     ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
  3756                        CLSCTX_INPROC_SERVER,
  3757                        IID_ITfInputProcessorProfiles,
  3758                        getter_AddRefs(inputProcessorProfiles));
  3759   if (FAILED(hr) || !inputProcessorProfiles) {
  3760     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3761       ("TSF:   nsTextStore::Initialize() FAILED to create input processor "
  3762        "profiles, hr=0x%08X", hr));
  3763     return;
  3766   nsRefPtr<ITfThreadMgr> threadMgr;
  3767   hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr,
  3768                           CLSCTX_INPROC_SERVER, IID_ITfThreadMgr,
  3769                           getter_AddRefs(threadMgr));
  3770   if (FAILED(hr) || !threadMgr) {
  3771     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3772       ("TSF:   nsTextStore::Initialize() FAILED to "
  3773        "create the thread manager, hr=0x%08X", hr));
  3774     return;
  3777   nsRefPtr<ITfMessagePump> messagePump;
  3778   hr = threadMgr->QueryInterface(IID_ITfMessagePump,
  3779                                  getter_AddRefs(messagePump));
  3780   if (FAILED(hr) || !messagePump) {
  3781     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3782       ("TSF:   nsTextStore::Initialize() FAILED to "
  3783        "QI message pump from the thread manager, hr=0x%08X", hr));
  3784     return;
  3787   nsRefPtr<ITfKeystrokeMgr> keystrokeMgr;
  3788   hr = threadMgr->QueryInterface(IID_ITfKeystrokeMgr,
  3789                                  getter_AddRefs(keystrokeMgr));
  3790   if (FAILED(hr) || !keystrokeMgr) {
  3791     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3792       ("TSF:   nsTextStore::Initialize() FAILED to "
  3793        "QI keystroke manager from the thread manager, hr=0x%08X", hr));
  3794     return;
  3797   hr = threadMgr->Activate(&sTsfClientId);
  3798   if (FAILED(hr)) {
  3799     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3800       ("TSF:   nsTextStore::Initialize() FAILED to activate, hr=0x%08X", hr));
  3801     return;
  3804   nsRefPtr<ITfDisplayAttributeMgr> displayAttributeMgr;
  3805   hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr,
  3806                           CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr,
  3807                           getter_AddRefs(displayAttributeMgr));
  3808   if (FAILED(hr) || !displayAttributeMgr) {
  3809     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3810       ("TSF:   nsTextStore::Initialize() FAILED to create "
  3811        "a display attribute manager instance, hr=0x%08X", hr));
  3812     return;
  3815   nsRefPtr<ITfCategoryMgr> categoryMgr;
  3816   hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr,
  3817                           CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr,
  3818                           getter_AddRefs(categoryMgr));
  3819   if (FAILED(hr) || !categoryMgr) {
  3820     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3821       ("TSF:   nsTextStore::Initialize() FAILED to create "
  3822        "a category manager instance, hr=0x%08X", hr));
  3823     return;
  3826   nsRefPtr<ITfDocumentMgr> disabledDocumentMgr;
  3827   hr = threadMgr->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr));
  3828   if (FAILED(hr) || !disabledDocumentMgr) {
  3829     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3830       ("TSF:   nsTextStore::Initialize() FAILED to create "
  3831        "a document manager for disabled mode, hr=0x%08X", hr));
  3832     return;
  3835   nsRefPtr<ITfContext> disabledContext;
  3836   DWORD editCookie = 0;
  3837   hr = disabledDocumentMgr->CreateContext(sTsfClientId, 0, nullptr,
  3838                                           getter_AddRefs(disabledContext),
  3839                                           &editCookie);
  3840   if (FAILED(hr) || !disabledContext) {
  3841     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3842       ("TSF:   nsTextStore::Initialize() FAILED to create "
  3843        "a context for disabled mode, hr=0x%08X", hr));
  3844     return;
  3847   MarkContextAsKeyboardDisabled(disabledContext);
  3848   MarkContextAsEmpty(disabledContext);
  3850   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3851     ("TSF:   nsTextStore::Initialize() is creating "
  3852      "an nsTextStore instance..."));
  3853   nsRefPtr<nsTextStore> textStore = new nsTextStore();
  3854   if (!textStore->Init(threadMgr)) {
  3855     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  3856       ("TSF:   nsTextStore::Initialize() FAILED to initialize nsTextStore "
  3857        "instance"));
  3858     return;
  3861   inputProcessorProfiles.swap(sInputProcessorProfiles);
  3862   threadMgr.swap(sTsfThreadMgr);
  3863   messagePump.swap(sMessagePump);
  3864   keystrokeMgr.swap(sKeystrokeMgr);
  3865   displayAttributeMgr.swap(sDisplayAttrMgr);
  3866   categoryMgr.swap(sCategoryMgr);
  3867   disabledDocumentMgr.swap(sTsfDisabledDocumentMgr);
  3868   disabledContext.swap(sTsfDisabledContext);
  3869   textStore.swap(sTsfTextStore);
  3871   sCreateNativeCaretForATOK =
  3872     Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true);
  3874   MOZ_ASSERT(!sFlushTIPInputMessage);
  3875   sFlushTIPInputMessage = ::RegisterWindowMessageW(L"Flush TIP Input Message");
  3877   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
  3878     ("TSF:   nsTextStore::Initialize(), sTsfThreadMgr=0x%p, "
  3879      "sTsfClientId=0x%08X, sTsfTextStore=0x%p, sDisplayAttrMgr=0x%p, "
  3880      "sCategoryMgr=0x%p, sTsfDisabledDocumentMgr=0x%p, sTsfDisabledContext=%p, "
  3881      "sCreateNativeCaretForATOK=%s",
  3882      sTsfThreadMgr, sTsfClientId, sTsfTextStore, sDisplayAttrMgr, sCategoryMgr,
  3883      sTsfDisabledDocumentMgr, sTsfDisabledContext,
  3884      GetBoolName(sCreateNativeCaretForATOK)));
  3887 // static
  3888 void
  3889 nsTextStore::Terminate(void)
  3891   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Terminate()"));
  3893   NS_IF_RELEASE(sDisplayAttrMgr);
  3894   NS_IF_RELEASE(sCategoryMgr);
  3895   NS_IF_RELEASE(sTsfTextStore);
  3896   NS_IF_RELEASE(sTsfDisabledDocumentMgr);
  3897   NS_IF_RELEASE(sTsfDisabledContext);
  3898   NS_IF_RELEASE(sInputProcessorProfiles);
  3899   sTsfClientId = 0;
  3900   if (sTsfThreadMgr) {
  3901     sTsfThreadMgr->Deactivate();
  3902     NS_RELEASE(sTsfThreadMgr);
  3903     NS_RELEASE(sMessagePump);
  3904     NS_RELEASE(sKeystrokeMgr);
  3908 // static
  3909 bool
  3910 nsTextStore::ProcessRawKeyMessage(const MSG& aMsg)
  3912   if (!sKeystrokeMgr) {
  3913     return false; // not in TSF mode
  3916   if (aMsg.message == WM_KEYDOWN) {
  3917     BOOL eaten;
  3918     HRESULT hr = sKeystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
  3919     if (FAILED(hr) || !eaten) {
  3920       return false;
  3922     hr = sKeystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
  3923     return SUCCEEDED(hr) && eaten;
  3925   if (aMsg.message == WM_KEYUP) {
  3926     BOOL eaten;
  3927     HRESULT hr = sKeystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
  3928     if (FAILED(hr) || !eaten) {
  3929       return false;
  3931     hr = sKeystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
  3932     return SUCCEEDED(hr) && eaten;
  3934   return false;
  3937 // static
  3938 void
  3939 nsTextStore::ProcessMessage(nsWindowBase* aWindow, UINT aMessage,
  3940                             WPARAM& aWParam, LPARAM& aLParam,
  3941                             MSGResult& aResult)
  3943   switch (aMessage) {
  3944     case WM_IME_SETCONTEXT:
  3945       // If a windowless plugin had focus and IME was handled on it, composition
  3946       // window was set the position.  After that, even in TSF mode, WinXP keeps
  3947       // to use composition window at the position if the active IME is not
  3948       // aware TSF.  For avoiding this issue, we need to hide the composition
  3949       // window here.
  3950       if (aWParam) {
  3951         aLParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  3953       break;
  3957 /******************************************************************/
  3958 /* nsTextStore::Composition                                       */
  3959 /******************************************************************/
  3961 void
  3962 nsTextStore::Composition::Start(ITfCompositionView* aCompositionView,
  3963                                 LONG aCompositionStartOffset,
  3964                                 const nsAString& aCompositionString)
  3966   mView = aCompositionView;
  3967   mString = aCompositionString;
  3968   mStart = aCompositionStartOffset;
  3971 void
  3972 nsTextStore::Composition::End()
  3974   mView = nullptr;
  3975   mString.Truncate();
  3978 /******************************************************************************
  3979  *  nsTextStore::Content
  3980  *****************************************************************************/
  3982 const nsDependentSubstring
  3983 nsTextStore::Content::GetSelectedText() const
  3985   MOZ_ASSERT(mInitialized);
  3986   return GetSubstring(static_cast<uint32_t>(mSelection.StartOffset()),
  3987                       static_cast<uint32_t>(mSelection.Length()));
  3990 const nsDependentSubstring
  3991 nsTextStore::Content::GetSubstring(uint32_t aStart, uint32_t aLength) const
  3993   MOZ_ASSERT(mInitialized);
  3994   return nsDependentSubstring(mText, aStart, aLength);
  3997 void
  3998 nsTextStore::Content::ReplaceSelectedTextWith(const nsAString& aString)
  4000   MOZ_ASSERT(mInitialized);
  4001   ReplaceTextWith(mSelection.StartOffset(), mSelection.Length(), aString);
  4004 inline uint32_t
  4005 FirstDifferentCharOffset(const nsAString& aStr1, const nsAString& aStr2)
  4007   MOZ_ASSERT(aStr1 != aStr2);
  4008   uint32_t i = 0;
  4009   uint32_t minLength = std::min(aStr1.Length(), aStr2.Length());
  4010   for (; i < minLength && aStr1[i] == aStr2[i]; i++) {
  4011     /* nothing to do */
  4013   return i;
  4016 void
  4017 nsTextStore::Content::ReplaceTextWith(LONG aStart, LONG aLength,
  4018                                       const nsAString& aReplaceString)
  4020   MOZ_ASSERT(mInitialized);
  4021   const nsDependentSubstring replacedString =
  4022     GetSubstring(static_cast<uint32_t>(aStart),
  4023                  static_cast<uint32_t>(aLength));
  4024   if (aReplaceString != replacedString) {
  4025     uint32_t firstDifferentOffset =
  4026       static_cast<uint32_t>(aStart) + FirstDifferentCharOffset(aReplaceString,
  4027                                                                replacedString);
  4028     mMinTextModifiedOffset =
  4029       std::min(mMinTextModifiedOffset, firstDifferentOffset);
  4030     if (mComposition.IsComposing()) {
  4031       // Emulate text insertion during compositions, because during a
  4032       // composition, editor expects the whole composition string to
  4033       // be sent in NS_TEXT_TEXT, not just the inserted part.
  4034       // The actual NS_TEXT_TEXT will be sent in SetSelection or
  4035       // OnUpdateComposition.
  4036       MOZ_ASSERT(aStart >= mComposition.mStart);
  4037       MOZ_ASSERT(aStart + aLength <= mComposition.EndOffset());
  4038       mComposition.mString.Replace(
  4039         static_cast<uint32_t>(aStart - mComposition.mStart),
  4040         static_cast<uint32_t>(aLength), aReplaceString);
  4042     mText.Replace(static_cast<uint32_t>(aStart),
  4043                   static_cast<uint32_t>(aLength), aReplaceString);
  4045   // Selection should be collapsed at the end of the inserted string.
  4046   mSelection.CollapseAt(
  4047     static_cast<uint32_t>(aStart) + aReplaceString.Length());
  4050 void
  4051 nsTextStore::Content::StartComposition(ITfCompositionView* aCompositionView,
  4052                                        const PendingAction& aCompStart,
  4053                                        bool aPreserveSelection)
  4055   MOZ_ASSERT(mInitialized);
  4056   MOZ_ASSERT(aCompositionView);
  4057   MOZ_ASSERT(!mComposition.mView);
  4058   MOZ_ASSERT(aCompStart.mType == PendingAction::COMPOSITION_START);
  4060   mComposition.Start(aCompositionView, aCompStart.mSelectionStart,
  4061     GetSubstring(static_cast<uint32_t>(aCompStart.mSelectionStart),
  4062                  static_cast<uint32_t>(aCompStart.mSelectionLength)));
  4063   if (!aPreserveSelection) {
  4064     mSelection.SetSelection(mComposition.mStart, mComposition.mString.Length(),
  4065                             false);
  4069 void
  4070 nsTextStore::Content::EndComposition(const PendingAction& aCompEnd)
  4072   MOZ_ASSERT(mInitialized);
  4073   MOZ_ASSERT(mComposition.mView);
  4074   MOZ_ASSERT(aCompEnd.mType == PendingAction::COMPOSITION_END);
  4076   mSelection.CollapseAt(mComposition.mStart + aCompEnd.mData.Length());
  4077   mComposition.End();
  4080 #ifdef DEBUG
  4081 // static
  4082 bool
  4083 nsTextStore::CurrentKeyboardLayoutHasIME()
  4085   if (!sInputProcessorProfiles) {
  4086     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  4087       ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED due to there is "
  4088        "no input processor profiles instance"));
  4089     return false;
  4091   nsRefPtr<ITfInputProcessorProfileMgr> profileMgr;
  4092   HRESULT hr =
  4093     sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
  4094                                             getter_AddRefs(profileMgr));
  4095   if (FAILED(hr) || !profileMgr) {
  4096     // On Windows Vista or later, ImmIsIME() API always returns true.
  4097     // If we failed to obtain the profile manager, we cannot know if current
  4098     // keyboard layout has IME.
  4099     if (IsVistaOrLater()) {
  4100       PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  4101         ("TSF:   nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
  4102          "ITfInputProcessorProfileMgr"));
  4103       return false;
  4105     // If the profiles instance doesn't have ITfInputProcessorProfileMgr
  4106     // interface, that means probably we're running on WinXP or WinServer2003
  4107     // (except WinServer2003 R2).  Then, we should use ImmIsIME().
  4108     return ::ImmIsIME(::GetKeyboardLayout(0));
  4111   TF_INPUTPROCESSORPROFILE profile;
  4112   hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
  4113   if (hr == S_FALSE) {
  4114     return false; // not found or not active
  4116   if (FAILED(hr)) {
  4117     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
  4118       ("TSF:   nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive "
  4119        "active profile"));
  4120     return false;
  4122   return (profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR);
  4124 #endif // #ifdef DEBUG

mercurial