Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 <msctf.h>
#include <textstor.h>
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<bool>(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<bool>(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<TSFUtils::AttrIndex>;
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<WritingMode> 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<nsWindow> mWidget;
// mDispatcher is a helper class to dispatch composition events.
RefPtr<TextEventDispatcher> mDispatcher;
// Document manager for the currently focused editor
RefPtr<ITfDocumentMgr> mDocumentMgr;
// Edit cookie associated with the current editing context
DWORD mEditCookie = 0;
// Editing context at the bottom of mDocumentMgr's context stack
RefPtr<ITfContext> mContext;
// Currently installed notification sink
RefPtr<ITextStoreACPSink> 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<InputScope> 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