widget/windows/nsTextStore.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial