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
#ifndef TSFUtils_h
#define TSFUtils_h
#include <ostream>
#include <msctf.h>
#include <textstor.h>
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TextRange.h"
#include "mozilla/ToString.h"
#include "mozilla/widget/IMEData.h"
#include "nsPrintfCString.h"
#include "nsString.h"
#include "nsTArray.h"
// GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID.
// With initguid.h, we get its instance instead of extern declaration.
#ifdef INPUTSCOPE_INIT_GUID
# include <initguid.h>
#endif
#ifdef TEXTATTRS_INIT_GUID
# include <tsattrs.h>
#endif
#include <inputscope.h>
// TSF InputScope, for earlier SDK 8
#define IS_SEARCH static_cast<InputScope>(50)
class nsWindow;
namespace mozilla::widget {
class TSFEmptyTextStore;
class TSFTextStore;
class TSFTextStoreBase;
struct IMENotificationRequests;
struct InputContext;
struct InputContextAction;
class TSFUtils final {
public:
TSFUtils() = delete;
~TSFUtils() = delete;
static void Initialize();
static void Shutdown();
/**
* Return true while TSF is available.
*/
[[nodiscard]] static bool IsAvailable() { return sThreadMgr; }
[[nodiscard]] static IMENotificationRequests GetIMENotificationRequests();
enum class GotFocus : bool { No, Yes };
/**
* Called when focus changed in the DOM level and aContext has the details of
* the new focused element.
*/
static nsresult OnFocusChange(GotFocus aGotFocus, nsWindow* aFocusedWindow,
const InputContext& aContext);
/**
* Called when input context for aWindow is set. aWindow should have focus
* when this is called.
*/
static void OnSetInputContext(nsWindow* aWindow, const InputContext& aContext,
const InputContextAction& aAction);
// FIXME: Simplify the following APIs with TSFTextStoreBase::IsEditable().
/**
* Active TextStore is a TSFTextStoreBase instance which is for editable
* content.
* Note that it may disable IME, e.g., when the editable content is a password
* field or `ime-mode: disabled`.
*/
[[nodiscard]] static TSFTextStore* GetActiveTextStore() {
MOZ_ASSERT_IF(sActiveTextStore, (void*)sActiveTextStore.get() ==
(void*)sCurrentTextStore.get());
return sActiveTextStore.get();
}
/**
* Current TextStore ia TSFTextStoreBase instance for either the content is
* editable nor not editable.
*/
[[nodiscard]] static TSFTextStoreBase* GetCurrentTextStore() {
MOZ_ASSERT_IF(sActiveTextStore, (void*)sActiveTextStore.get() ==
(void*)sCurrentTextStore.get());
return sCurrentTextStore.get();
}
/**
* Check whether current TextStore is for editable one. I.e., when there is
* an active TextStore. This is just an alias to make the caller easier to
* read.
*/
[[nodiscard]] static bool CurrentTextStoreIsEditable() {
MOZ_ASSERT_IF(sActiveTextStore, (void*)sActiveTextStore.get() ==
(void*)sCurrentTextStore.get());
return sActiveTextStore;
}
template <typename TSFTextStoreClass>
static void ClearStoringTextStoresIf(
const RefPtr<TSFTextStoreClass>& aTextStore);
[[nodiscard]] static ITfThreadMgr* GetThreadMgr() { return sThreadMgr; }
[[nodiscard]] static ITfMessagePump* GetMessagePump() {
if (!sMessagePump) {
EnsureMessagePump();
}
return sMessagePump;
}
[[nodiscard]] static ITfKeystrokeMgr* GetKeystrokeMgr() {
if (!sKeystrokeMgr) {
EnsureKeystrokeMgr();
}
return sKeystrokeMgr;
}
[[nodiscard]] static ITfInputProcessorProfiles* GetInputProcessorProfiles();
[[nodiscard]] static ITfDisplayAttributeMgr* GetDisplayAttributeMgr();
[[nodiscard]] static ITfCategoryMgr* GetCategoryMgr();
[[nodiscard]] static ITfCompartment* GetCompartmentForOpenClose();
[[nodiscard]] static DWORD ClientId() { return sClientId; }
// TODO: GUID_PROP_URL has not been declared in the SDK yet. We should drop
// this after it's released by a new SDK and it becomes the minimum supported
// SDK version.
constexpr static const GUID sGUID_PROP_URL = {
0xd5138268,
0xa1bf,
0x4308,
{0xbc, 0xbf, 0x2e, 0x73, 0x93, 0x98, 0xe2, 0x34}};
constexpr static TsViewCookie sDefaultView = 1;
/**
* Returns true if the Windows may have a crash bug at handling
* ITfTextStoreACP::GetSelection() returns error.
*/
[[nodiscard]] static bool DoNotReturnErrorFromGetSelection();
struct MOZ_STACK_CLASS AutoRangeExtant {
explicit AutoRangeExtant(ITfRange* aRange);
[[nodiscard]] bool isErr() const { return FAILED(mHR); }
[[nodiscard]] bool isOk() const { return SUCCEEDED(mHR); }
[[nodiscard]] LONG End() const { return mStart + mLength; }
LONG mStart = 0;
LONG mLength = 0;
HRESULT mHR = E_NOT_SET;
};
/**
* Get TextRangeType corresponding to aDisplayAttr.
*/
[[nodiscard]] static TextRangeType GetTextRangeType(
TF_DISPLAYATTRIBUTE& aDisplayAttr);
/**
* Get nscolor corresponding to aTSFColor.
*/
[[nodiscard]] static Maybe<nscolor> GetColor(const TF_DA_COLOR& aTSFColor);
/**
* Get LineStyle corresponding to aTSFLineStyle.
*/
[[nodiscard]] static Maybe<TextRangeStyle::LineStyle> GetLineStyle(
TF_DA_LINESTYLE aTSFLineStyle);
/**
* Returns true if active TIP or IME is a black listed one and we should
* set input scope of URL bar to IS_DEFAULT rather than IS_URL.
*/
[[nodiscard]] static bool ShouldSetInputScopeOfURLBarToDefault();
// Support retrieving attributes.
// TODO: We should support RightToLeft, perhaps.
enum AttrIndex {
// Used for result of GetRequestedAttrIndex()
NotSupported = -1,
// Supported attributes even in TSFEmptyTextStore.
InputScope = 0,
DocumentURL,
// Count of the supported attrs in empty text store
NUM_OF_SUPPORTED_ATTRS_IN_EMPTY_TEXT_STORE,
// Supported attributes in any TextStores.
TextVerticalWriting = NUM_OF_SUPPORTED_ATTRS_IN_EMPTY_TEXT_STORE,
TextOrientation,
// Count of the supported attributes
NUM_OF_SUPPORTED_ATTRS,
};
/**
* Return AttrIndex fo aAttrID.
*/
[[nodiscard]] static AttrIndex GetRequestedAttrIndex(
const TS_ATTRID& aAttrID);
/**
* Return TS_ATTRID for aIndex.
*/
[[nodiscard]] static TS_ATTRID GetAttrID(AttrIndex aIndex);
/**
* Get compartment instance.
*/
[[nodiscard]] static Result<RefPtr<ITfCompartment>, bool> GetCompartment(
IUnknown* pUnk, const GUID& aID);
/**
* Mark aContent as keyboard disabled.
*
* @return true if succeeded, otherwise, false.
*/
static bool MarkContextAsKeyboardDisabled(ITfContext* aContext);
/**
* Mark aContext as empty.
*
* @return true if succeeded, otherwise, false.
*/
static bool MarkContextAsEmpty(ITfContext* aContext);
static const char* BoolToChar(bool aValue) {
return aValue ? "true" : "false";
}
static const char* MouseButtonToChar(int16_t aButton);
static const char* CommonHRESULTToChar(HRESULT);
static const char* HRESULTToChar(HRESULT);
static TS_SELECTION_ACP EmptySelectionACP() {
return TS_SELECTION_ACP{
.acpStart = 0,
.acpEnd = 0,
.style = {.ase = TS_AE_NONE, .fInterimChar = FALSE}};
}
private:
static void EnsureMessagePump();
static void EnsureKeystrokeMgr();
// TSF thread manager object for the current application
static StaticRefPtr<ITfThreadMgr> sThreadMgr;
// sMessagePump is QI'ed from sThreadMgr
static StaticRefPtr<ITfMessagePump> sMessagePump;
// sKeystrokeMgr is QI'ed from sThreadMgr
static StaticRefPtr<ITfKeystrokeMgr> sKeystrokeMgr;
// TSF display attribute manager
static StaticRefPtr<ITfDisplayAttributeMgr> sDisplayAttrMgr;
// TSF category manager
static StaticRefPtr<ITfCategoryMgr> sCategoryMgr;
// Compartment for (Get|Set)IMEOpenState()
static StaticRefPtr<ITfCompartment> sCompartmentForOpenClose;
static StaticRefPtr<ITfInputProcessorProfiles> sInputProcessorProfiles;
// Current active text store which is managing a keyboard enabled editor
// (i.e., editable editor). Currently only ONE TSFTextStore instance is ever
// used, although Create is called when an editor is focused and Destroy
// called when the focused editor is blurred.
static StaticRefPtr<TSFTextStore> sActiveTextStore;
// Current text store which may be an empty one for disabled state.
static StaticRefPtr<TSFTextStoreBase> sCurrentTextStore;
// Global instance for non-editable state.
static StaticRefPtr<TSFEmptyTextStore> sEmptyTextStore;
// TSF client ID for the current application
static DWORD sClientId;
};
class AutoFlagsCString : public nsAutoCString {
protected:
void AppendFlag(const nsCString& aFlagStr) { AppendFlag(aFlagStr.get()); }
void AppendFlag(const char* aFlagStr) {
if (!IsEmpty()) {
AppendLiteral(" | ");
}
Append(aFlagStr);
}
};
class AutoFindFlagsCString final : public AutoFlagsCString {
public:
explicit AutoFindFlagsCString(DWORD);
};
class AutoACPFromPointFlagsCString final : public AutoFlagsCString {
public:
explicit AutoACPFromPointFlagsCString(DWORD);
};
class AutoSinkMasksCString final : public AutoFlagsCString {
public:
explicit AutoSinkMasksCString(DWORD);
};
class AutoLockFlagsCString final : public AutoFlagsCString {
public:
explicit AutoLockFlagsCString(DWORD);
};
class AutoClsidCString final : public nsAutoCString {
public:
explicit AutoClsidCString(REFGUID);
};
class AutoRawGuidCString : public nsAutoCString {
public:
explicit AutoRawGuidCString(REFGUID aGUID) { AssignRawGuid(aGUID); }
protected:
AutoRawGuidCString() = default;
void AssignRawGuid(REFGUID);
};
class AutoGuidCString final : public AutoRawGuidCString {
public:
explicit AutoGuidCString(REFGUID);
};
class AutoRiidCString final : public nsAutoCString {
public:
explicit AutoRiidCString(REFIID);
};
class AutoMouseButtonsCString final : public AutoFlagsCString {
public:
explicit AutoMouseButtonsCString(int16_t aButtons);
};
class AutoInputScopesCString : public AutoFlagsCString {
public:
explicit AutoInputScopesCString(const nsTArray<InputScope>& aList);
};
class AutoEscapedUTF8String final : public NS_ConvertUTF16toUTF8 {
public:
explicit AutoEscapedUTF8String(const nsAString& aString)
: NS_ConvertUTF16toUTF8(aString) {
Escape();
}
explicit AutoEscapedUTF8String(const char16ptr_t aString)
: NS_ConvertUTF16toUTF8(aString) {
Escape();
}
AutoEscapedUTF8String(const char16ptr_t aString, uint32_t aLength)
: NS_ConvertUTF16toUTF8(aString, aLength) {
Escape();
}
private:
void Escape() {
ReplaceSubstring("\r", "\\r");
ReplaceSubstring("\n", "\\n");
ReplaceSubstring("\t", "\\t");
}
};
} // namespace mozilla::widget
inline std::ostream& operator<<(std::ostream& aStream,
const TS_SELECTIONSTYLE& aSelectionStyle) {
const char* ase = "Unknown";
switch (aSelectionStyle.ase) {
case TS_AE_START:
ase = "TS_AE_START";
break;
case TS_AE_END:
ase = "TS_AE_END";
break;
case TS_AE_NONE:
ase = "TS_AE_NONE";
break;
}
return aStream << "{ ase=" << ase << ", fInterimChar="
<< (aSelectionStyle.fInterimChar ? "TRUE" : "FALSE") << " }";
}
inline std::ostream& operator<<(std::ostream& aStream,
const TS_SELECTION_ACP& aACP) {
return aStream << "{ acpStart=" << aACP.acpStart << ", acpEnd=" << aACP.acpEnd
<< ", style=" << mozilla::ToString(aACP.style).c_str() << " }";
}
inline std::ostream& operator<<(std::ostream& aStream,
const TsRunType& aRunType) {
switch (aRunType) {
case TS_RT_PLAIN:
return aStream << "TS_RT_PLAIN";
case TS_RT_HIDDEN:
return aStream << "TS_RT_HIDDEN";
case TS_RT_OPAQUE:
return aStream << "TS_RT_OPAQUE";
default:
return aStream << nsPrintfCString("Unknown(%08X)",
static_cast<int32_t>(aRunType));
}
}
inline std::ostream& operator<<(std::ostream& aStream,
const TF_DA_COLOR& aColor) {
switch (aColor.type) {
case TF_CT_NONE:
return aStream << "TF_CT_NONE";
case TF_CT_SYSCOLOR:
return aStream << nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
static_cast<int32_t>(aColor.nIndex));
case TF_CT_COLORREF:
return aStream << nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
static_cast<int32_t>(aColor.cr));
break;
default:
return aStream << nsPrintfCString("Unknown(%08X)",
static_cast<int32_t>(aColor.type));
}
}
inline std::ostream& operator<<(std::ostream& aStream,
const TF_DA_LINESTYLE& aLineStyle) {
switch (aLineStyle) {
case TF_LS_NONE:
return aStream << "TF_LS_NONE";
case TF_LS_SOLID:
return aStream << "TF_LS_SOLID";
case TF_LS_DOT:
return aStream << "TF_LS_DOT";
case TF_LS_DASH:
return aStream << "TF_LS_DASH";
case TF_LS_SQUIGGLE:
return aStream << "TF_LS_SQUIGGLE";
default:
return aStream << nsPrintfCString("Unknown(%08X)",
static_cast<int32_t>(aLineStyle));
}
}
inline std::ostream& operator<<(std::ostream& aStream,
const TF_DA_ATTR_INFO& aAttr) {
switch (aAttr) {
case TF_ATTR_INPUT:
return aStream << "TF_ATTR_INPUT";
case TF_ATTR_TARGET_CONVERTED:
return aStream << "TF_ATTR_TARGET_CONVERTED";
case TF_ATTR_CONVERTED:
return aStream << "TF_ATTR_CONVERTED";
case TF_ATTR_TARGET_NOTCONVERTED:
return aStream << "TF_ATTR_TARGET_NOTCONVERTED";
case TF_ATTR_INPUT_ERROR:
return aStream << "TF_ATTR_INPUT_ERROR";
case TF_ATTR_FIXEDCONVERTED:
return aStream << "TF_ATTR_FIXEDCONVERTED";
case TF_ATTR_OTHER:
return aStream << "TF_ATTR_OTHER";
default:
return aStream << nsPrintfCString("Unknown(%08X)",
static_cast<int32_t>(aAttr));
}
}
inline std::ostream& operator<<(std::ostream& aStream,
const TF_DISPLAYATTRIBUTE& aDispAttr) {
return aStream << "{ crText:{" << aDispAttr.crText << " }, crBk:{ "
<< aDispAttr.crBk << " }, lsStyle: " << aDispAttr.lsStyle
<< ", fBoldLine: "
<< mozilla::widget::TSFUtils::BoolToChar(aDispAttr.fBoldLine)
<< ", crLine:{ " << aDispAttr.crLine
<< " }, bAttr: " << aDispAttr.bAttr << " }";
}
#endif // #ifndef TSFUtils_h