/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef TSFTextStoreBase_h #define TSFTextStoreBase_h #include "nsIWidget.h" #include "nsWindow.h" #include "TSFUtils.h" // for inputscope.h with the hack for MinGW #include "WinUtils.h" #include "WritingModes.h" #include "mozilla/Maybe.h" #include "mozilla/RefPtr.h" #include "mozilla/TextEventDispatcher.h" #include "mozilla/WritingModes.h" #include "mozilla/widget/IMEData.h" #include #include struct ITfDocumentMgr; class nsWindow; namespace mozilla::widget { class TSFTextStoreBase : public ITextStoreACP { protected: using SelectionChangeDataBase = IMENotification::SelectionChangeDataBase; using SelectionChangeData = IMENotification::SelectionChangeData; using TextChangeDataBase = IMENotification::TextChangeDataBase; using TextChangeData = IMENotification::TextChangeData; public: /*IUnknown*/ STDMETHODIMP QueryInterface(REFIID, void**); NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFTextStoreBase) public: /*ITextStoreACP*/ STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD); STDMETHODIMP UnadviseSink(IUnknown*); STDMETHODIMP RequestLock(DWORD, HRESULT*); STDMETHODIMP GetStatus(TS_STATUS*); STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*); STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*); STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*); STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG, ULONG*, LONG*); STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*); STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**); STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**); STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*); STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*); STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD); STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*, DWORD, LONG*, BOOL*, LONG*); STDMETHODIMP GetEndACP(LONG*); STDMETHODIMP GetActiveView(TsViewCookie*); STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*); STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*); STDMETHODIMP GetScreenExt(TsViewCookie, RECT*); STDMETHODIMP GetWnd(TsViewCookie, HWND*); STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*, TS_TEXTCHANGE*); STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*, TS_TEXTCHANGE*); public: [[nodiscard]] bool MaybeHasFocus() const { return mContext; } /** * Return true if this is for editable content, i.e., supporting retrieving * and modifying the content. I.e., when this is a TSFTextStore instance, * returns true. Otherwise, this is a TSFEmptyTextStoreInstance, returns * false. */ [[nodiscard]] bool IsEditable() const { return static_cast(mIsEditable); } [[nodiscard]] ITfDocumentMgr* GetDocumentMgr() const { return mDocumentMgr; } [[nodiscard]] ITfContext* GetContext() const { return mContext; } [[nodiscard]] nsWindow* GetWindow() const { return mWidget; } [[nodiscard]] virtual IMENotificationRequests GetIMENotificationRequests() const = 0; virtual void Destroy() = 0; static void SetInputContext(nsWindow* aWindow, const InputContext& aContext, const InputContextAction& aAction); protected: enum class Editable : bool { No, Yes }; friend inline std::ostream& operator<<(std::ostream& aStream, const Editable& aEditable) { return aStream << (static_cast(aEditable) ? "Editable::Yes" : "Editable::No"); }; explicit TSFTextStoreBase(Editable aIsEditable) : mIsEditable(aIsEditable) {} virtual ~TSFTextStoreBase() = default; [[nodiscard]] bool InitBase(nsWindow* aWidget, const InputContext& aContext); [[nodiscard]] static bool IsReadLock(DWORD aLock) { return (TS_LF_READ == (aLock & TS_LF_READ)); } [[nodiscard]] static bool IsReadWriteLock(DWORD aLock) { return (TS_LF_READWRITE == (aLock & TS_LF_READWRITE)); } [[nodiscard]] bool IsReadLocked() const { return IsReadLock(mLock); } [[nodiscard]] bool IsReadWriteLocked() const { return IsReadWriteLock(mLock); } // This is called immediately after a call of OnLockGranted() of mSink. // Note that mLock isn't cleared yet when this is called. virtual void DidLockGranted() {} using AttrIndices = EnumSet; constexpr static auto NothingChanged = AttrIndices{}; constexpr static auto OnlyURLChanged = AttrIndices{TSFUtils::AttrIndex::DocumentURL}; constexpr static auto OnlyInputScopeChanged = AttrIndices{TSFUtils::AttrIndex::InputScope}; constexpr static auto URLAndInputScopeChanged = AttrIndices{ TSFUtils::AttrIndex::DocumentURL, TSFUtils::AttrIndex::InputScope}; /** * Called when either the URL or the input scope is changed. */ void NotifyTSFOfInputContextChange(AttrIndices aAttrIndices); [[nodiscard]] bool GetScreenExtInternal(RECT& aScreenExt); [[nodiscard]] virtual Maybe GetWritingMode() { return Nothing(); } // DispatchEvent() dispatches the event and if it may not be handled // synchronously, this makes the instance not notify TSF of pending // notifications until next notification from content. void DispatchEvent(WidgetGUIEvent& aEvent); void SetInputScope(const nsString& aHTMLInputType, const nsString& aHTMLInputMode); /** * Return URL which can be exposed to TSF. Use ::SysFreeString() to delete * the result if you use it by yourself. */ BSTR GetExposingURL() const; /** * Debug utility method to print the result of GetExposingURL(). */ void PrintExposingURL(const char* aPrefix) const; HRESULT HandleRequestAttrs(DWORD aFlags, ULONG aFilterCount, const TS_ATTRID* aFilterAttrs, int32_t aNumOfSupportedAttrs); HRESULT RetrieveRequestedAttrsInternal(ULONG ulCount, TS_ATTRVAL* paAttrVals, ULONG* pcFetched, int32_t aNumOfSupportedAttrs); /** * IsHandlingCompositionInParent() returns true if eCompositionStart is * dispatched, but eCompositionCommit(AsIs) is not dispatched. This means * that if composition is handled in a content process, this status indicates * whether ContentCacheInParent has composition or not. On the other hand, * if it's handled in the chrome process, this is exactly same as * IsHandlingCompositionInContent(). */ [[nodiscard]] bool IsHandlingCompositionInParent() const { return mDispatcher && mDispatcher->IsComposing(); } /** * IsHandlingCompositionInContent() returns true if there is a composition in * the focused editor which may be in a content process. */ [[nodiscard]] bool IsHandlingCompositionInContent() const { return mDispatcher && mDispatcher->IsHandlingComposition(); } /** * Return error if editable state does not match with this instance. * Otherwise, updates mDocumentURL and mInPrivateBrowsing and notify TSF of * the changes. */ nsresult UpdateDocumentURLAndBrowsingMode(nsWindow* aWindow, const InputContext& aContext); // Holds the pointer to our current win32 widget RefPtr mWidget; // mDispatcher is a helper class to dispatch composition events. RefPtr mDispatcher; // Document manager for the currently focused editor RefPtr mDocumentMgr; // Edit cookie associated with the current editing context DWORD mEditCookie = 0; // Editing context at the bottom of mDocumentMgr's context stack RefPtr mContext; // Currently installed notification sink RefPtr mSink; // TS_AS_* mask of what events to notify DWORD mSinkMask = 0; // 0 if not locked, otherwise TS_LF_* indicating the current lock DWORD mLock = 0; // 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock DWORD mLockQueued = 0; // The input scopes for this context, defaults to IS_DEFAULT. nsTArray mInputScopes; // The URL cache of the focused document. nsString mDocumentURL; bool mRequestedAttrs[TSFUtils::NUM_OF_SUPPORTED_ATTRS] = {false}; bool mRequestedAttrValues = false; // Before calling ITextStoreACPSink::OnLayoutChange() and // ITfContextOwnerServices::OnLayoutChange(), mWaitingQueryLayout is set to // true. This is set to false when GetTextExt() or GetACPFromPoint() is // called. bool mWaitingQueryLayout = false; // During the document is locked, we shouldn't destroy the instance. // If this is true, the instance will be destroyed after unlocked. bool mPendingDestroy = false; // While the instance is initializing content/selection cache, another // initialization shouldn't run recursively. Therefore, while the // initialization is running, this is set to true. Use AutoNotifyingTSFBatch // to set this. bool mDeferNotifyingTSF = false; // While the instance is dispatching events, the event may not be handled // synchronously when remote content has focus. In the case, we cannot // return the latest layout/content information to TSF/TIP until we get next // update notification from ContentCacheInParent. For preventing TSF/TIP // retrieves the latest content/layout information while it becomes available, // we should put off notifying TSF of any updates. bool mDeferNotifyingTSFUntilNextUpdate = false; // Immediately after a call of Destroy(), mDestroyed becomes true. If this // is true, the instance shouldn't grant any requests from the TIP anymore. bool mDestroyed = false; // While the instance is being destroyed, this is set to true for avoiding // recursive Destroy() calls. bool mBeingDestroyed = false; // Whether we're in the private browsing mode. bool mInPrivateBrowsing = true; // Whether this is for editable content or not. Editable mIsEditable; }; } // namespace mozilla::widget #endif // #ifndef TSFTextStoreBase_h