/* 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