1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/nsTextStore.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,704 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef NSTEXTSTORE_H_ 1.10 +#define NSTEXTSTORE_H_ 1.11 + 1.12 +#include "nsAutoPtr.h" 1.13 +#include "nsString.h" 1.14 +#include "nsCOMPtr.h" 1.15 +#include "nsIWidget.h" 1.16 +#include "nsWindowBase.h" 1.17 +#include "mozilla/Attributes.h" 1.18 +#include "mozilla/TextRange.h" 1.19 +#include "mozilla/WindowsVersion.h" 1.20 + 1.21 +#include <msctf.h> 1.22 +#include <textstor.h> 1.23 + 1.24 +// GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID. 1.25 +// With initguid.h, we get its instance instead of extern declaration. 1.26 +#ifdef INPUTSCOPE_INIT_GUID 1.27 +#include <initguid.h> 1.28 +#endif 1.29 +#include <inputscope.h> 1.30 + 1.31 +// TSF InputScope, for earlier SDK 8 1.32 +#define IS_SEARCH static_cast<InputScope>(50) 1.33 + 1.34 +struct ITfThreadMgr; 1.35 +struct ITfDocumentMgr; 1.36 +struct ITfDisplayAttributeMgr; 1.37 +struct ITfCategoryMgr; 1.38 +class nsWindow; 1.39 +#ifdef MOZ_METRO 1.40 +class MetroWidget; 1.41 +#endif 1.42 + 1.43 +namespace mozilla { 1.44 +namespace widget { 1.45 +struct MSGResult; 1.46 +} // namespace widget 1.47 +} // namespace mozilla 1.48 + 1.49 +/* 1.50 + * Text Services Framework text store 1.51 + */ 1.52 + 1.53 +class nsTextStore MOZ_FINAL : public ITextStoreACP, 1.54 + public ITfContextOwnerCompositionSink, 1.55 + public ITfActiveLanguageProfileNotifySink, 1.56 + public ITfInputProcessorProfileActivationSink 1.57 +{ 1.58 +public: /*IUnknown*/ 1.59 + STDMETHODIMP_(ULONG) AddRef(void); 1.60 + STDMETHODIMP QueryInterface(REFIID, void**); 1.61 + STDMETHODIMP_(ULONG) Release(void); 1.62 + 1.63 +public: /*ITextStoreACP*/ 1.64 + STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD); 1.65 + STDMETHODIMP UnadviseSink(IUnknown*); 1.66 + STDMETHODIMP RequestLock(DWORD, HRESULT*); 1.67 + STDMETHODIMP GetStatus(TS_STATUS*); 1.68 + STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*); 1.69 + STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*); 1.70 + STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*); 1.71 + STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG, 1.72 + ULONG*, LONG*); 1.73 + STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*); 1.74 + STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**); 1.75 + STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**); 1.76 + STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*); 1.77 + STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*); 1.78 + STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*); 1.79 + STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD); 1.80 + STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG, 1.81 + const TS_ATTRID*, DWORD); 1.82 + STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*, 1.83 + DWORD, LONG*, BOOL*, LONG*); 1.84 + STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*); 1.85 + STDMETHODIMP GetEndACP(LONG*); 1.86 + STDMETHODIMP GetActiveView(TsViewCookie*); 1.87 + STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*); 1.88 + STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*); 1.89 + STDMETHODIMP GetScreenExt(TsViewCookie, RECT*); 1.90 + STDMETHODIMP GetWnd(TsViewCookie, HWND*); 1.91 + STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*, 1.92 + TS_TEXTCHANGE*); 1.93 + STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*, 1.94 + TS_TEXTCHANGE*); 1.95 + 1.96 +public: /*ITfContextOwnerCompositionSink*/ 1.97 + STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*); 1.98 + STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*); 1.99 + STDMETHODIMP OnEndComposition(ITfCompositionView*); 1.100 + 1.101 +public: /*ITfActiveLanguageProfileNotifySink*/ 1.102 + STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, 1.103 + BOOL fActivated); 1.104 + 1.105 +public: /*ITfInputProcessorProfileActivationSink*/ 1.106 + STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID, 1.107 + HKL, DWORD); 1.108 + 1.109 +protected: 1.110 + typedef mozilla::widget::IMENotification IMENotification; 1.111 + typedef mozilla::widget::IMEState IMEState; 1.112 + typedef mozilla::widget::InputContext InputContext; 1.113 + typedef mozilla::widget::InputContextAction InputContextAction; 1.114 + 1.115 +public: 1.116 + static void Initialize(void); 1.117 + static void Terminate(void); 1.118 + 1.119 + static bool ProcessRawKeyMessage(const MSG& aMsg); 1.120 + static void ProcessMessage(nsWindowBase* aWindow, UINT aMessage, 1.121 + WPARAM& aWParam, LPARAM& aLParam, 1.122 + mozilla::widget::MSGResult& aResult); 1.123 + 1.124 + 1.125 + static void SetIMEOpenState(bool); 1.126 + static bool GetIMEOpenState(void); 1.127 + 1.128 + static void CommitComposition(bool aDiscard) 1.129 + { 1.130 + NS_ENSURE_TRUE_VOID(sTsfTextStore); 1.131 + sTsfTextStore->CommitCompositionInternal(aDiscard); 1.132 + } 1.133 + 1.134 + static void SetInputContext(nsWindowBase* aWidget, 1.135 + const InputContext& aContext, 1.136 + const InputContextAction& aAction); 1.137 + 1.138 + static nsresult OnFocusChange(bool aGotFocus, 1.139 + nsWindowBase* aFocusedWidget, 1.140 + IMEState::Enabled aIMEEnabled); 1.141 + static nsresult OnTextChange(const IMENotification& aIMENotification) 1.142 + { 1.143 + NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); 1.144 + return sTsfTextStore->OnTextChangeInternal(aIMENotification); 1.145 + } 1.146 + 1.147 + static nsresult OnSelectionChange(void) 1.148 + { 1.149 + NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); 1.150 + return sTsfTextStore->OnSelectionChangeInternal(); 1.151 + } 1.152 + 1.153 + static nsresult OnLayoutChange() 1.154 + { 1.155 + NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); 1.156 + return sTsfTextStore->OnLayoutChangeInternal(); 1.157 + } 1.158 + 1.159 + static nsIMEUpdatePreference GetIMEUpdatePreference(); 1.160 + 1.161 + // Returns the address of the pointer so that the TSF automatic test can 1.162 + // replace the system object with a custom implementation for testing. 1.163 + static void* GetNativeData(uint32_t aDataType) 1.164 + { 1.165 + switch (aDataType) { 1.166 + case NS_NATIVE_TSF_THREAD_MGR: 1.167 + Initialize(); // Apply any previous changes 1.168 + return static_cast<void*>(&sTsfThreadMgr); 1.169 + case NS_NATIVE_TSF_CATEGORY_MGR: 1.170 + return static_cast<void*>(&sCategoryMgr); 1.171 + case NS_NATIVE_TSF_DISPLAY_ATTR_MGR: 1.172 + return static_cast<void*>(&sDisplayAttrMgr); 1.173 + default: 1.174 + return nullptr; 1.175 + } 1.176 + } 1.177 + 1.178 + static ITfMessagePump* GetMessagePump() 1.179 + { 1.180 + return sMessagePump; 1.181 + } 1.182 + 1.183 + static void* GetTextStore() 1.184 + { 1.185 + return static_cast<void*>(sTsfTextStore); 1.186 + } 1.187 + 1.188 + static bool ThinksHavingFocus() 1.189 + { 1.190 + return (sTsfTextStore && sTsfTextStore->mContext); 1.191 + } 1.192 + 1.193 + static bool IsInTSFMode() 1.194 + { 1.195 + return sTsfThreadMgr != nullptr; 1.196 + } 1.197 + 1.198 + static bool IsComposing() 1.199 + { 1.200 + return (sTsfTextStore && sTsfTextStore->mComposition.IsComposing()); 1.201 + } 1.202 + 1.203 + static bool IsComposingOn(nsWindowBase* aWidget) 1.204 + { 1.205 + return (IsComposing() && sTsfTextStore->mWidget == aWidget); 1.206 + } 1.207 + 1.208 + static bool IsIMM_IME() 1.209 + { 1.210 + if (!sTsfTextStore || !sTsfTextStore->EnsureInitActiveTIPKeyboard()) { 1.211 + return IsIMM_IME(::GetKeyboardLayout(0)); 1.212 + } 1.213 + return sTsfTextStore->mIsIMM_IME; 1.214 + } 1.215 + 1.216 + static bool IsIMM_IME(HKL aHKL) 1.217 + { 1.218 + return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0); 1.219 + } 1.220 + 1.221 +#ifdef DEBUG 1.222 + // Returns true when keyboard layout has IME (TIP). 1.223 + static bool CurrentKeyboardLayoutHasIME(); 1.224 +#endif // #ifdef DEBUG 1.225 + 1.226 +protected: 1.227 + nsTextStore(); 1.228 + ~nsTextStore(); 1.229 + 1.230 + bool Init(ITfThreadMgr* aThreadMgr); 1.231 + 1.232 + static void MarkContextAsKeyboardDisabled(ITfContext* aContext); 1.233 + static void MarkContextAsEmpty(ITfContext* aContext); 1.234 + 1.235 + static bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID, 1.236 + REFGUID aProfile); 1.237 + static void GetTIPDescription(REFCLSID aTextService, LANGID aLangID, 1.238 + REFGUID aProfile, nsAString& aDescription); 1.239 + 1.240 + bool Create(nsWindowBase* aWidget); 1.241 + bool Destroy(void); 1.242 + 1.243 + bool IsReadLock(DWORD aLock) const 1.244 + { 1.245 + return (TS_LF_READ == (aLock & TS_LF_READ)); 1.246 + } 1.247 + bool IsReadWriteLock(DWORD aLock) const 1.248 + { 1.249 + return (TS_LF_READWRITE == (aLock & TS_LF_READWRITE)); 1.250 + } 1.251 + bool IsReadLocked() const { return IsReadLock(mLock); } 1.252 + bool IsReadWriteLocked() const { return IsReadWriteLock(mLock); } 1.253 + 1.254 + // This is called immediately after a call of OnLockGranted() of mSink. 1.255 + // Note that mLock isn't cleared yet when this is called. 1.256 + void DidLockGranted(); 1.257 + 1.258 + bool GetScreenExtInternal(RECT &aScreenExt); 1.259 + // If aDispatchTextEvent is true, this method will dispatch text event if 1.260 + // this is called during IME composing. aDispatchTextEvent should be true 1.261 + // only when this is called from SetSelection. Because otherwise, the text 1.262 + // event should not be sent from here. 1.263 + HRESULT SetSelectionInternal(const TS_SELECTION_ACP*, 1.264 + bool aDispatchTextEvent = false); 1.265 + bool InsertTextAtSelectionInternal(const nsAString &aInsertStr, 1.266 + TS_TEXTCHANGE* aTextChange); 1.267 + void CommitCompositionInternal(bool); 1.268 + nsresult OnTextChangeInternal(const IMENotification& aIMENotification); 1.269 + nsresult OnSelectionChangeInternal(void); 1.270 + HRESULT GetDisplayAttribute(ITfProperty* aProperty, 1.271 + ITfRange* aRange, 1.272 + TF_DISPLAYATTRIBUTE* aResult); 1.273 + HRESULT RestartCompositionIfNecessary(ITfRange* pRangeNew = nullptr); 1.274 + 1.275 + // Following methods record composing action(s) to mPendingActions. 1.276 + // They will be flushed FlushPendingActions(). 1.277 + HRESULT RecordCompositionStartAction(ITfCompositionView* aCompositionView, 1.278 + ITfRange* aRange, 1.279 + bool aPreserveSelection); 1.280 + HRESULT RecordCompositionUpdateAction(); 1.281 + HRESULT RecordCompositionEndAction(); 1.282 + 1.283 + // FlushPendingActions() performs pending actions recorded in mPendingActions 1.284 + // and clear it. 1.285 + void FlushPendingActions(); 1.286 + 1.287 + nsresult OnLayoutChangeInternal(); 1.288 + HRESULT ProcessScopeRequest(DWORD dwFlags, 1.289 + ULONG cFilterAttrs, 1.290 + const TS_ATTRID *paFilterAttrs); 1.291 + void SetInputScope(const nsString& aHTMLInputType); 1.292 + 1.293 + // Creates native caret over our caret. This method only works on desktop 1.294 + // application. Otherwise, this does nothing. 1.295 + void CreateNativeCaret(); 1.296 + 1.297 + bool EnsureInitActiveTIPKeyboard(); 1.298 + 1.299 + // Holds the pointer to our current win32 or metro widget 1.300 + nsRefPtr<nsWindowBase> mWidget; 1.301 + // Document manager for the currently focused editor 1.302 + nsRefPtr<ITfDocumentMgr> mDocumentMgr; 1.303 + // Edit cookie associated with the current editing context 1.304 + DWORD mEditCookie; 1.305 + // Cookie of installing ITfInputProcessorProfileActivationSink 1.306 + DWORD mIPProfileCookie; 1.307 + // Cookie of installing ITfActiveLanguageProfileNotifySink 1.308 + DWORD mLangProfileCookie; 1.309 + // Editing context at the bottom of mDocumentMgr's context stack 1.310 + nsRefPtr<ITfContext> mContext; 1.311 + // Currently installed notification sink 1.312 + nsRefPtr<ITextStoreACPSink> mSink; 1.313 + // TS_AS_* mask of what events to notify 1.314 + DWORD mSinkMask; 1.315 + // 0 if not locked, otherwise TS_LF_* indicating the current lock 1.316 + DWORD mLock; 1.317 + // 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock 1.318 + DWORD mLockQueued; 1.319 + // Active TIP keyboard's description. If active language profile isn't TIP, 1.320 + // i.e., IMM-IME or just a keyboard layout, this is empty. 1.321 + nsString mActiveTIPKeyboardDescription; 1.322 + 1.323 + class Composition MOZ_FINAL 1.324 + { 1.325 + public: 1.326 + // nullptr if no composition is active, otherwise the current composition 1.327 + nsRefPtr<ITfCompositionView> mView; 1.328 + 1.329 + // Current copy of the active composition string. Only mString is 1.330 + // changed during a InsertTextAtSelection call if we have a composition. 1.331 + // mString acts as a buffer until OnUpdateComposition is called 1.332 + // and mString is flushed to editor through NS_TEXT_TEXT. This 1.333 + // way all changes are updated in batches to avoid 1.334 + // inconsistencies/artifacts. 1.335 + nsString mString; 1.336 + 1.337 + // The latest composition string which was dispatched by composition update 1.338 + // event. 1.339 + nsString mLastData; 1.340 + 1.341 + // The start of the current active composition, in ACP offsets 1.342 + LONG mStart; 1.343 + 1.344 + bool IsComposing() const 1.345 + { 1.346 + return (mView != nullptr); 1.347 + } 1.348 + 1.349 + LONG EndOffset() const 1.350 + { 1.351 + return mStart + static_cast<LONG>(mString.Length()); 1.352 + } 1.353 + 1.354 + // Start() and End() updates the members for emulating the latest state. 1.355 + // Unless flush the pending actions, this data never matches the actual 1.356 + // content. 1.357 + void Start(ITfCompositionView* aCompositionView, 1.358 + LONG aCompositionStartOffset, 1.359 + const nsAString& aCompositionString); 1.360 + void End(); 1.361 + }; 1.362 + // While the document is locked, we cannot dispatch any events which cause 1.363 + // DOM events since the DOM events' handlers may modify the locked document. 1.364 + // However, even while the document is locked, TSF may queries us. 1.365 + // For that, nsTextStore modifies mComposition even while the document is 1.366 + // locked. With mComposition, query methods can returns the text content 1.367 + // information. 1.368 + Composition mComposition; 1.369 + 1.370 + class Selection 1.371 + { 1.372 + public: 1.373 + Selection() : mDirty(true) {} 1.374 + 1.375 + bool IsDirty() const { return mDirty; }; 1.376 + void MarkDirty() { mDirty = true; } 1.377 + 1.378 + TS_SELECTION_ACP& ACP() 1.379 + { 1.380 + MOZ_ASSERT(!mDirty); 1.381 + return mACP; 1.382 + } 1.383 + 1.384 + void SetSelection(const TS_SELECTION_ACP& aSelection) 1.385 + { 1.386 + mDirty = false; 1.387 + mACP = aSelection; 1.388 + // Selection end must be active in our editor. 1.389 + if (mACP.style.ase != TS_AE_START) { 1.390 + mACP.style.ase = TS_AE_END; 1.391 + } 1.392 + // We're not support interim char selection for now. 1.393 + // XXX Probably, this is necessary for supporting South Asian languages. 1.394 + mACP.style.fInterimChar = FALSE; 1.395 + } 1.396 + 1.397 + void SetSelection(uint32_t aStart, uint32_t aLength, bool aReversed) 1.398 + { 1.399 + mDirty = false; 1.400 + mACP.acpStart = static_cast<LONG>(aStart); 1.401 + mACP.acpEnd = static_cast<LONG>(aStart + aLength); 1.402 + mACP.style.ase = aReversed ? TS_AE_START : TS_AE_END; 1.403 + mACP.style.fInterimChar = FALSE; 1.404 + } 1.405 + 1.406 + bool IsCollapsed() const 1.407 + { 1.408 + MOZ_ASSERT(!mDirty); 1.409 + return (mACP.acpStart == mACP.acpEnd); 1.410 + } 1.411 + 1.412 + void CollapseAt(uint32_t aOffset) 1.413 + { 1.414 + mDirty = false; 1.415 + mACP.acpStart = mACP.acpEnd = static_cast<LONG>(aOffset); 1.416 + mACP.style.ase = TS_AE_END; 1.417 + mACP.style.fInterimChar = FALSE; 1.418 + } 1.419 + 1.420 + LONG MinOffset() const 1.421 + { 1.422 + MOZ_ASSERT(!mDirty); 1.423 + LONG min = std::min(mACP.acpStart, mACP.acpEnd); 1.424 + MOZ_ASSERT(min >= 0); 1.425 + return min; 1.426 + } 1.427 + 1.428 + LONG MaxOffset() const 1.429 + { 1.430 + MOZ_ASSERT(!mDirty); 1.431 + LONG max = std::max(mACP.acpStart, mACP.acpEnd); 1.432 + MOZ_ASSERT(max >= 0); 1.433 + return max; 1.434 + } 1.435 + 1.436 + LONG StartOffset() const 1.437 + { 1.438 + MOZ_ASSERT(!mDirty); 1.439 + MOZ_ASSERT(mACP.acpStart >= 0); 1.440 + return mACP.acpStart; 1.441 + } 1.442 + 1.443 + LONG EndOffset() const 1.444 + { 1.445 + MOZ_ASSERT(!mDirty); 1.446 + MOZ_ASSERT(mACP.acpEnd >= 0); 1.447 + return mACP.acpEnd; 1.448 + } 1.449 + 1.450 + LONG Length() const 1.451 + { 1.452 + MOZ_ASSERT(!mDirty); 1.453 + MOZ_ASSERT(mACP.acpEnd >= mACP.acpStart); 1.454 + return std::abs(mACP.acpEnd - mACP.acpStart); 1.455 + } 1.456 + 1.457 + bool IsReversed() const 1.458 + { 1.459 + MOZ_ASSERT(!mDirty); 1.460 + return (mACP.style.ase == TS_AE_START); 1.461 + } 1.462 + 1.463 + TsActiveSelEnd ActiveSelEnd() const 1.464 + { 1.465 + MOZ_ASSERT(!mDirty); 1.466 + return mACP.style.ase; 1.467 + } 1.468 + 1.469 + bool IsInterimChar() const 1.470 + { 1.471 + MOZ_ASSERT(!mDirty); 1.472 + return (mACP.style.fInterimChar != FALSE); 1.473 + } 1.474 + 1.475 + private: 1.476 + TS_SELECTION_ACP mACP; 1.477 + bool mDirty; 1.478 + }; 1.479 + // Don't access mSelection directly except at calling MarkDirty(). 1.480 + // Use CurrentSelection() instead. This is marked as dirty when the 1.481 + // selection or content is changed without document lock. 1.482 + Selection mSelection; 1.483 + 1.484 + // Get "current selection" while the document is locked. The selection is 1.485 + // NOT modified immediately during document lock. The pending changes will 1.486 + // be flushed at unlocking the document. The "current selection" is the 1.487 + // modified selection during document lock. This is also called 1.488 + // CurrentContent() too. 1.489 + Selection& CurrentSelection(); 1.490 + 1.491 + struct PendingAction MOZ_FINAL 1.492 + { 1.493 + enum ActionType MOZ_ENUM_TYPE(uint8_t) 1.494 + { 1.495 + COMPOSITION_START, 1.496 + COMPOSITION_UPDATE, 1.497 + COMPOSITION_END, 1.498 + SELECTION_SET 1.499 + }; 1.500 + ActionType mType; 1.501 + // For compositionstart and selectionset 1.502 + LONG mSelectionStart; 1.503 + LONG mSelectionLength; 1.504 + // For compositionupdate and compositionend 1.505 + nsString mData; 1.506 + // For compositionupdate 1.507 + nsRefPtr<mozilla::TextRangeArray> mRanges; 1.508 + // For selectionset 1.509 + bool mSelectionReversed; 1.510 + }; 1.511 + // Items of mPendingActions are appended when TSF tells us to need to dispatch 1.512 + // DOM composition events. However, we cannot dispatch while the document is 1.513 + // locked because it can cause modifying the locked document. So, the pending 1.514 + // actions should be performed when document lock is unlocked. 1.515 + nsTArray<PendingAction> mPendingActions; 1.516 + 1.517 + PendingAction* GetPendingCompositionUpdate() 1.518 + { 1.519 + if (!mPendingActions.IsEmpty()) { 1.520 + PendingAction& lastAction = mPendingActions.LastElement(); 1.521 + if (lastAction.mType == PendingAction::COMPOSITION_UPDATE) { 1.522 + return &lastAction; 1.523 + } 1.524 + } 1.525 + PendingAction* newAction = mPendingActions.AppendElement(); 1.526 + newAction->mType = PendingAction::COMPOSITION_UPDATE; 1.527 + newAction->mRanges = new mozilla::TextRangeArray(); 1.528 + return newAction; 1.529 + } 1.530 + 1.531 + // When On*Composition() is called without document lock, we need to flush 1.532 + // the recorded actions at quitting the method. 1.533 + // AutoPendingActionAndContentFlusher class is usedful for it. 1.534 + class MOZ_STACK_CLASS AutoPendingActionAndContentFlusher MOZ_FINAL 1.535 + { 1.536 + public: 1.537 + AutoPendingActionAndContentFlusher(nsTextStore* aTextStore) 1.538 + : mTextStore(aTextStore) 1.539 + { 1.540 + MOZ_ASSERT(!mTextStore->mIsRecordingActionsWithoutLock); 1.541 + if (!mTextStore->IsReadWriteLocked()) { 1.542 + mTextStore->mIsRecordingActionsWithoutLock = true; 1.543 + } 1.544 + } 1.545 + 1.546 + ~AutoPendingActionAndContentFlusher() 1.547 + { 1.548 + if (!mTextStore->mIsRecordingActionsWithoutLock) { 1.549 + return; 1.550 + } 1.551 + mTextStore->FlushPendingActions(); 1.552 + mTextStore->mIsRecordingActionsWithoutLock = false; 1.553 + } 1.554 + 1.555 + private: 1.556 + AutoPendingActionAndContentFlusher() {} 1.557 + 1.558 + nsRefPtr<nsTextStore> mTextStore; 1.559 + }; 1.560 + 1.561 + class Content MOZ_FINAL 1.562 + { 1.563 + public: 1.564 + Content(nsTextStore::Composition& aComposition, 1.565 + nsTextStore::Selection& aSelection) : 1.566 + mComposition(aComposition), mSelection(aSelection) 1.567 + { 1.568 + Clear(); 1.569 + } 1.570 + 1.571 + void Clear() 1.572 + { 1.573 + mText.Truncate(); 1.574 + mInitialized = false; 1.575 + } 1.576 + 1.577 + bool IsInitialized() const { return mInitialized; } 1.578 + 1.579 + void Init(const nsAString& aText) 1.580 + { 1.581 + mText = aText; 1.582 + mMinTextModifiedOffset = NOT_MODIFIED; 1.583 + mInitialized = true; 1.584 + } 1.585 + 1.586 + const nsDependentSubstring GetSelectedText() const; 1.587 + const nsDependentSubstring GetSubstring(uint32_t aStart, 1.588 + uint32_t aLength) const; 1.589 + void ReplaceSelectedTextWith(const nsAString& aString); 1.590 + void ReplaceTextWith(LONG aStart, LONG aLength, const nsAString& aString); 1.591 + 1.592 + void StartComposition(ITfCompositionView* aCompositionView, 1.593 + const PendingAction& aCompStart, 1.594 + bool aPreserveSelection); 1.595 + void EndComposition(const PendingAction& aCompEnd); 1.596 + 1.597 + const nsString& Text() const 1.598 + { 1.599 + MOZ_ASSERT(mInitialized); 1.600 + return mText; 1.601 + } 1.602 + 1.603 + // Returns true if layout of the character at the aOffset has not been 1.604 + // calculated. 1.605 + bool IsLayoutChangedAfter(uint32_t aOffset) const 1.606 + { 1.607 + return mInitialized && (mMinTextModifiedOffset < aOffset); 1.608 + } 1.609 + // Returns true if layout of the content has been changed, i.e., the new 1.610 + // layout has not been calculated. 1.611 + bool IsLayoutChanged() const 1.612 + { 1.613 + return mInitialized && (mMinTextModifiedOffset != NOT_MODIFIED); 1.614 + } 1.615 + 1.616 + nsTextStore::Composition& Composition() { return mComposition; } 1.617 + nsTextStore::Selection& Selection() { return mSelection; } 1.618 + 1.619 + private: 1.620 + nsString mText; 1.621 + nsTextStore::Composition& mComposition; 1.622 + nsTextStore::Selection& mSelection; 1.623 + 1.624 + // The minimum offset of modified part of the text. 1.625 + enum MOZ_ENUM_TYPE(uint32_t) 1.626 + { 1.627 + NOT_MODIFIED = UINT32_MAX 1.628 + }; 1.629 + uint32_t mMinTextModifiedOffset; 1.630 + 1.631 + bool mInitialized; 1.632 + }; 1.633 + // mContent caches "current content" of the document ONLY while the document 1.634 + // is locked. I.e., the content is cleared at unlocking the document since 1.635 + // we need to reduce the memory usage. This is initialized by 1.636 + // CurrentContent() automatically, so, don't access this member directly 1.637 + // except at calling Clear(), IsInitialized(), IsLayoutChangedAfter() or 1.638 + // IsLayoutChanged(). 1.639 + Content mContent; 1.640 + 1.641 + Content& CurrentContent(); 1.642 + 1.643 + // The input scopes for this context, defaults to IS_DEFAULT. 1.644 + nsTArray<InputScope> mInputScopes; 1.645 + bool mInputScopeDetected; 1.646 + bool mInputScopeRequested; 1.647 + // If edit actions are being recorded without document lock, this is true. 1.648 + // Otherwise, false. 1.649 + bool mIsRecordingActionsWithoutLock; 1.650 + // During recording actions, we shouldn't call mSink->OnSelectionChange() 1.651 + // because it may cause TSF request new lock. This is a problem if the 1.652 + // selection change is caused by a call of On*Composition() without document 1.653 + // lock since RequestLock() tries to flush the pending actions again (which 1.654 + // are flushing). Therefore, OnSelectionChangeInternal() sets this true 1.655 + // during recoding actions and then, RequestLock() will call 1.656 + // mSink->OnSelectionChange() after mLock becomes 0. 1.657 + bool mPendingOnSelectionChange; 1.658 + // If GetTextExt() or GetACPFromPoint() is called and the layout hasn't been 1.659 + // calculated yet, these methods return TS_E_NOLAYOUT. Then, RequestLock() 1.660 + // will call mSink->OnLayoutChange() and 1.661 + // ITfContextOwnerServices::OnLayoutChange() after the layout is fixed and 1.662 + // the document is unlocked. 1.663 + bool mPendingOnLayoutChange; 1.664 + // While there is native caret, this is true. Otherwise, false. 1.665 + bool mNativeCaretIsCreated; 1.666 + 1.667 + // True if current IME is implemented with IMM. 1.668 + bool mIsIMM_IME; 1.669 + // True if OnActivated() is already called 1.670 + bool mOnActivatedCalled; 1.671 + 1.672 + // TSF thread manager object for the current application 1.673 + static ITfThreadMgr* sTsfThreadMgr; 1.674 + // sMessagePump is QI'ed from sTsfThreadMgr 1.675 + static ITfMessagePump* sMessagePump; 1.676 + // sKeystrokeMgr is QI'ed from sTsfThreadMgr 1.677 + static ITfKeystrokeMgr* sKeystrokeMgr; 1.678 + // TSF display attribute manager 1.679 + static ITfDisplayAttributeMgr* sDisplayAttrMgr; 1.680 + // TSF category manager 1.681 + static ITfCategoryMgr* sCategoryMgr; 1.682 + 1.683 + // TSF client ID for the current application 1.684 + static DWORD sTsfClientId; 1.685 + // Current text store. Currently only ONE nsTextStore instance is ever used, 1.686 + // although Create is called when an editor is focused and Destroy called 1.687 + // when the focused editor is blurred. 1.688 + static nsTextStore* sTsfTextStore; 1.689 + 1.690 + // For IME (keyboard) disabled state: 1.691 + static ITfDocumentMgr* sTsfDisabledDocumentMgr; 1.692 + static ITfContext* sTsfDisabledContext; 1.693 + 1.694 + static ITfInputProcessorProfiles* sInputProcessorProfiles; 1.695 + 1.696 + // Enables/Disables hack for specific TIP. 1.697 + static bool sCreateNativeCaretForATOK; 1.698 + 1.699 + // Message the Tablet Input Panel uses to flush text during blurring. 1.700 + // See comments in Destroy 1.701 + static UINT sFlushTIPInputMessage; 1.702 + 1.703 +private: 1.704 + ULONG mRefCnt; 1.705 +}; 1.706 + 1.707 +#endif /*NSTEXTSTORE_H_*/