Bug 783882 - Add ITfInputScope support to nsTextStore, and add support for various html5 form input types. r=masayuki

This commit is contained in:
Jim Mathies 2013-01-26 08:35:21 -06:00
parent 2a04d088b8
commit 9696bcab0b
2 changed files with 292 additions and 37 deletions

View File

@ -24,20 +24,7 @@
using namespace mozilla;
using namespace mozilla::widget;
/******************************************************************/
/* nsTextStore */
/******************************************************************/
ITfThreadMgr* nsTextStore::sTsfThreadMgr = NULL;
ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = NULL;
ITfCategoryMgr* nsTextStore::sCategoryMgr = NULL;
DWORD nsTextStore::sTsfClientId = 0;
nsTextStore* nsTextStore::sTsfTextStore = NULL;
UINT nsTextStore::sFlushTIPInputMessage = 0;
#ifdef PR_LOGGING
/**
* TSF related code should log its behavior even on release build especially
* in the interface methods.
@ -57,6 +44,105 @@ UINT nsTextStore::sFlushTIPInputMessage = 0;
*/
PRLogModuleInfo* sTextStoreLog = nullptr;
#endif // #ifdef PR_LOGGING
#define IS_SEARCH static_cast<InputScope>(50)
/******************************************************************/
/* InputScopeImpl */
/******************************************************************/
// InputScope property GUID
static const GUID GUID_PROP_INPUTSCOPE =
{ 0x1713dd5a, 0x68e7, 0x4a5b,
{ 0x9a, 0xf6, 0x59, 0x2a, 0x59, 0x5c, 0x77, 0x8d } };
class InputScopeImpl MOZ_FINAL : public ITfInputScope
{
public:
InputScopeImpl(const nsTArray<InputScope>& aList) :
mRefCnt(1),
mInputScopes(aList)
{
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p InputScopeImpl()", this));
}
STDMETHODIMP_(ULONG) AddRef(void) { return ++mRefCnt; }
STDMETHODIMP_(ULONG) Release(void)
{
--mRefCnt;
if (mRefCnt) {
return mRefCnt;
}
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p InputScopeImpl::Release() final", this));
delete this;
return 0;
}
STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
*ppv=NULL;
if ( (IID_IUnknown == riid) || (IID_ITfInputScope == riid) ) {
*ppv = static_cast<ITfInputScope*>(this);
}
if (*ppv) {
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount)
{
uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length());
InputScope* pScope = (InputScope*) CoTaskMemAlloc(sizeof(InputScope) * count);
NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY);
if (mInputScopes.IsEmpty()) {
*pScope = IS_DEFAULT;
*pcCount = 1;
*pprgInputScopes = pScope;
return S_OK;
}
*pcCount = 0;
for (uint32_t idx = 0; idx < count; idx++) {
*(pScope + idx) = mInputScopes[idx];
(*pcCount)++;
}
*pprgInputScopes = pScope;
return S_OK;
}
STDMETHODIMP GetPhrase(BSTR **ppbstrPhrases, UINT *pcCount) { return E_NOTIMPL; }
STDMETHODIMP GetRegularExpression(BSTR *pbstrRegExp) { return E_NOTIMPL; }
STDMETHODIMP GetSRGS(BSTR *pbstrSRGS) { return E_NOTIMPL; }
STDMETHODIMP GetXML(BSTR *pbstrXML) { return E_NOTIMPL; }
private:
DWORD mRefCnt;
nsTArray<InputScope> mInputScopes;
};
/******************************************************************/
/* nsTextStore */
/******************************************************************/
ITfThreadMgr* nsTextStore::sTsfThreadMgr = NULL;
ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = NULL;
ITfCategoryMgr* nsTextStore::sCategoryMgr = NULL;
DWORD nsTextStore::sTsfClientId = 0;
nsTextStore* nsTextStore::sTsfTextStore = NULL;
UINT nsTextStore::sFlushTIPInputMessage = 0;
#ifdef PR_LOGGING
static const char*
GetBoolName(bool aBool)
@ -64,6 +150,53 @@ GetBoolName(bool aBool)
return aBool ? "true" : "false";
}
static void
HandleSeparator(nsCString& aDesc)
{
if (!aDesc.IsEmpty()) {
aDesc.AppendLiteral(" | ");
}
}
static const nsCString
GetFindFlagName(DWORD aFindFlag)
{
nsAutoCString description;
if (!aFindFlag) {
description.AppendLiteral("no flags (0)");
return description;
}
if (aFindFlag & TS_ATTR_FIND_BACKWARDS) {
description.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
}
if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
}
if (aFindFlag & TS_ATTR_FIND_UPDATESTART) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
}
if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
}
if (aFindFlag & TS_ATTR_FIND_WANT_END) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_WANT_END");
}
if (aFindFlag & TS_ATTR_FIND_HIDDEN) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_HIDDEN");
}
if (description.IsEmpty()) {
description.AppendLiteral("Unknown (");
description.AppendInt(static_cast<uint32_t>(aFindFlag));
description.AppendLiteral(")");
}
return description;
}
static const char*
GetIMEEnabledName(IMEState::Enabled aIMEEnabled)
{
@ -81,6 +214,21 @@ GetIMEEnabledName(IMEState::Enabled aIMEEnabled)
}
}
static nsCString
GetCLSIDNameStr(REFCLSID aCLSID)
{
LPOLESTR str = nullptr;
HRESULT hr = ::StringFromCLSID(aCLSID, &str);
if (FAILED(hr) || !str || !str[0]) {
return EmptyCString();
}
nsAutoCString result;
result = NS_ConvertUTF16toUTF8(str);
::CoTaskMemFree(str);
return result;
}
static nsCString
GetRIIDNameStr(REFIID aRIID)
{
@ -178,27 +326,19 @@ GetSinkMaskNameStr(DWORD aSinkMask)
description.AppendLiteral("TS_AS_TEXT_CHANGE");
}
if (aSinkMask & TS_AS_SEL_CHANGE) {
if (!description.IsEmpty()) {
description.AppendLiteral(" | ");
}
HandleSeparator(description);
description.AppendLiteral("TS_AS_SEL_CHANGE");
}
if (aSinkMask & TS_AS_LAYOUT_CHANGE) {
if (!description.IsEmpty()) {
description.AppendLiteral(" | ");
}
HandleSeparator(description);
description.AppendLiteral("TS_AS_LAYOUT_CHANGE");
}
if (aSinkMask & TS_AS_ATTR_CHANGE) {
if (!description.IsEmpty()) {
description.AppendLiteral(" | ");
}
HandleSeparator(description);
description.AppendLiteral("TS_AS_ATTR_CHANGE");
}
if (aSinkMask & TS_AS_STATUS_CHANGE) {
if (!description.IsEmpty()) {
description.AppendLiteral(" | ");
}
HandleSeparator(description);
description.AppendLiteral("TS_AS_STATUS_CHANGE");
}
if (description.IsEmpty()) {
@ -348,6 +488,8 @@ nsTextStore::nsTextStore()
mTextChange.acpStart = INT32_MAX;
mTextChange.acpOldEnd = mTextChange.acpNewEnd = 0;
mLastDispatchedTextEvent = nullptr;
mInputScopeDetected = false;
mInputScopeRequested = false;
}
nsTextStore::~nsTextStore()
@ -1587,6 +1729,81 @@ nsTextStore::InsertEmbedded(DWORD dwFlags,
return E_NOTIMPL;
}
void
nsTextStore::SetInputScope(const nsString& aHTMLInputType)
{
mInputScopes.Clear();
if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) {
return;
}
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
if (aHTMLInputType.EqualsLiteral("url")) {
mInputScopes.AppendElement(IS_URL);
} else if (aHTMLInputType.EqualsLiteral("search")) {
mInputScopes.AppendElement(IS_SEARCH);
} else if (aHTMLInputType.EqualsLiteral("email")) {
mInputScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
} else if (aHTMLInputType.EqualsLiteral("password")) {
mInputScopes.AppendElement(IS_PASSWORD);
} else if (aHTMLInputType.EqualsLiteral("datetime") ||
aHTMLInputType.EqualsLiteral("datetime-local")) {
mInputScopes.AppendElement(IS_DATE_FULLDATE);
mInputScopes.AppendElement(IS_TIME_FULLTIME);
} else if (aHTMLInputType.EqualsLiteral("date") ||
aHTMLInputType.EqualsLiteral("month") ||
aHTMLInputType.EqualsLiteral("week")) {
mInputScopes.AppendElement(IS_DATE_FULLDATE);
} else if (aHTMLInputType.EqualsLiteral("time")) {
mInputScopes.AppendElement(IS_TIME_FULLTIME);
} else if (aHTMLInputType.EqualsLiteral("tel")) {
mInputScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
mInputScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
} else if (aHTMLInputType.EqualsLiteral("number")) {
mInputScopes.AppendElement(IS_NUMBER);
}
}
HRESULT
nsTextStore::ProcessScopeRequest(DWORD dwFlags,
ULONG cFilterAttrs,
const TS_ATTRID *paFilterAttrs)
{
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::ProcessScopeRequest() called "
"cFilterAttrs=%d dwFlags=%s", this, cFilterAttrs,
GetFindFlagName(dwFlags).get()));
// This is a little weird! RequestSupportedAttrs gives us advanced notice
// of a support query via RetrieveRequestedAttrs for a specific attribute.
// RetrieveRequestedAttrs needs to return valid data for all attributes we
// support, but the text service will only want the input scope object
// returned in RetrieveRequestedAttrs if the dwFlags passed in here contains
// TS_ATTR_FIND_WANT_VALUE.
mInputScopeDetected = mInputScopeRequested = false;
// Currently we only support GUID_PROP_INPUTSCOPE
for (uint32_t idx = 0; idx < cFilterAttrs; ++idx) {
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::ProcessScopeRequest() "
"requested attr=%s", this, GetCLSIDNameStr(paFilterAttrs[idx]).get()));
if (IsEqualGUID(paFilterAttrs[idx], GUID_PROP_INPUTSCOPE)) {
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::ProcessScopeRequest() "
"GUID_PROP_INPUTSCOPE queried", this));
mInputScopeDetected = true;
if (dwFlags & TS_ATTR_FIND_WANT_VALUE) {
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::ProcessScopeRequest() "
"TS_ATTR_FIND_WANT_VALUE specified", this));
mInputScopeRequested = true;
}
break;
}
}
return S_OK;
}
STDMETHODIMP
nsTextStore::RequestSupportedAttrs(DWORD dwFlags,
ULONG cFilterAttrs,
@ -1594,10 +1811,10 @@ nsTextStore::RequestSupportedAttrs(DWORD dwFlags,
{
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::RequestSupportedAttrs() called "
"but not supported (S_OK)", this));
"cFilterAttrs=%d dwFlags=%s", this, cFilterAttrs,
GetFindFlagName(dwFlags).get()));
// no attributes defined
return S_OK;
return ProcessScopeRequest(dwFlags, cFilterAttrs, paFilterAttrs);
}
STDMETHODIMP
@ -1608,10 +1825,11 @@ nsTextStore::RequestAttrsAtPosition(LONG acpPos,
{
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::RequestAttrsAtPosition() called "
"but not supported (S_OK)", this));
"acpPos=%d cFilterAttrs=%d dwFlags=%s", this, acpPos, cFilterAttrs,
GetFindFlagName(dwFlags).get()));
// no per character attributes defined
return S_OK;
return ProcessScopeRequest(dwFlags | TS_ATTR_FIND_WANT_VALUE,
cFilterAttrs, paFilterAttrs);
}
STDMETHODIMP
@ -1669,9 +1887,36 @@ nsTextStore::RetrieveRequestedAttrs(ULONG ulCount,
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() called "
"but not supported, *pcFetched=0 (S_OK)", this));
"ulCount=%d", this, ulCount));
// no attributes defined
if (mInputScopeDetected || mInputScopeRequested) {
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() for "
"GUID_PROP_INPUTSCOPE: "
"mInputScopeDetected=%s mInputScopeRequested=%s",
this, GetBoolName(mInputScopeDetected),
GetBoolName(mInputScopeRequested)));
paAttrVals->idAttr = GUID_PROP_INPUTSCOPE;
paAttrVals->dwOverlapId = 0;
paAttrVals->varValue.vt = VT_EMPTY;
*pcFetched = 1;
if (mInputScopeRequested) {
paAttrVals->varValue.vt = VT_UNKNOWN;
paAttrVals->varValue.punkVal = (IUnknown*) new InputScopeImpl(mInputScopes);
}
mInputScopeDetected = mInputScopeRequested = false;
return S_OK;
}
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() called "
"for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)", this));
paAttrVals->dwOverlapId = 0;
paAttrVals->varValue.vt = VT_EMPTY;
*pcFetched = 0;
return S_OK;
}

View File

@ -16,6 +16,7 @@
#include <msctf.h>
#include <textstor.h>
#include <InputScope.h>
struct ITfThreadMgr;
struct ITfDocumentMgr;
@ -101,6 +102,7 @@ public:
static void SetInputContext(const InputContext& aContext)
{
if (!sTsfTextStore) return;
sTsfTextStore->SetInputScope(aContext.mHTMLInputType);
sTsfTextStore->SetInputContextInternal(aContext.mIMEState.mEnabled);
}
@ -197,6 +199,10 @@ protected:
HRESULT SendTextEventForCompositionString();
HRESULT SaveTextEvent(const nsTextEvent* aEvent);
nsresult OnCompositionTimer();
HRESULT ProcessScopeRequest(DWORD dwFlags,
ULONG cFilterAttrs,
const TS_ATTRID *paFilterAttrs);
void SetInputScope(const nsString& aHTMLInputType);
// Holds the pointer to our current win32 or metro widget
nsRefPtr<nsWindowBase> mWidget;
@ -242,6 +248,10 @@ protected:
// Timer for calling ITextStoreACPSink::OnLayoutChange. This is only used
// during composing.
nsCOMPtr<nsITimer> mCompositionTimer;
// The input scopes for this context, defaults to IS_DEFAULT.
nsTArray<InputScope> mInputScopes;
bool mInputScopeDetected;
bool mInputScopeRequested;
// TSF thread manager object for the current application
static ITfThreadMgr* sTsfThreadMgr;