Bug 848672 Redesign focus handling of nsTextStore r=jimm

This commit is contained in:
Masayuki Nakano 2013-03-27 09:04:02 +09:00
Родитель ca9f3ff1a0
Коммит d068300221
6 изменённых файлов: 184 добавлений и 59 удалений

Просмотреть файл

@ -253,7 +253,9 @@ IMEHandler::OnDestroyWindow(nsWindow* aWindow)
// static
void
IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext)
IMEHandler::SetInputContext(nsWindow* aWindow,
InputContext& aInputContext,
const InputContextAction& aAction)
{
// FYI: If there is no composition, this call will do nothing.
NotifyIME(aWindow, REQUEST_TO_COMMIT_COMPOSITION);
@ -272,12 +274,14 @@ IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext)
#ifdef NS_ENABLE_TSF
// Note that even while a plugin has focus, we need to notify TSF of that.
if (sIsInTSFMode) {
nsTextStore::SetInputContext(aInputContext);
nsTextStore::SetInputContext(aWindow, aInputContext, aAction);
if (IsTSFAvailable()) {
aInputContext.mNativeIMEContext = nsTextStore::GetTextStore();
if (adjustOpenState) {
nsTextStore::SetIMEOpenState(open);
}
return;
}
// Currently, nsTextStore doesn't set focus to keyboard disabled document.
// Therefore, we still need to perform the following legacy code.
}
#endif // #ifdef NS_ENABLE_TSF
@ -298,12 +302,6 @@ IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext)
}
if (adjustOpenState) {
#ifdef NS_ENABLE_TSF
if (IsTSFAvailable()) {
nsTextStore::SetIMEOpenState(open);
return;
}
#endif // #ifdef NS_ENABLE_TSF
IMEContext.SetOpenState(open);
}
}
@ -317,7 +315,9 @@ IMEHandler::InitInputContext(nsWindow* aWindow, InputContext& aInputContext)
#ifdef NS_ENABLE_TSF
if (sIsInTSFMode) {
nsTextStore::SetInputContext(aInputContext);
nsTextStore::SetInputContext(aWindow, aInputContext,
InputContextAction(InputContextAction::CAUSE_UNKNOWN,
InputContextAction::GOT_FOCUS));
aInputContext.mNativeIMEContext = nsTextStore::GetTextStore();
MOZ_ASSERT(aInputContext.mNativeIMEContext);
return;

Просмотреть файл

@ -102,7 +102,9 @@ public:
* Called when nsIWidget::SetInputContext() is called before the window's
* InputContext is modified actually.
*/
static void SetInputContext(nsWindow* aWindow, InputContext& aInputContext);
static void SetInputContext(nsWindow* aWindow,
InputContext& aInputContext,
const InputContextAction& aAction);
/**
* Called when the window is created.

Просмотреть файл

@ -136,6 +136,8 @@ ITfMessagePump* nsTextStore::sMessagePump = NULL;
ITfKeystrokeMgr* nsTextStore::sKeystrokeMgr = NULL;
ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = NULL;
ITfCategoryMgr* nsTextStore::sCategoryMgr = NULL;
ITfDocumentMgr* nsTextStore::sTsfDisabledDocumentMgr = NULL;
ITfContext* nsTextStore::sTsfDisabledContext = NULL;
DWORD nsTextStore::sTsfClientId = 0;
nsTextStore* nsTextStore::sTsfTextStore = NULL;
@ -215,6 +217,25 @@ GetIMEEnabledName(IMEState::Enabled aIMEEnabled)
}
}
static const char*
GetFocusChangeName(InputContextAction::FocusChange aFocusChange)
{
switch (aFocusChange) {
case InputContextAction::FOCUS_NOT_CHANGED:
return "FOCUS_NOT_CHANGED";
case InputContextAction::GOT_FOCUS:
return "GOT_FOCUS";
case InputContextAction::LOST_FOCUS:
return "LOST_FOCUS";
case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
return "MENU_GOT_PSEUDO_FOCUS";
case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
return "MENU_LOST_PSEUDO_FOCUS";
default:
return "Unknown";
}
}
static nsCString
GetCLSIDNameStr(REFCLSID aCLSID)
{
@ -508,12 +529,11 @@ nsTextStore::~nsTextStore()
}
bool
nsTextStore::Create(nsWindowBase* aWidget,
IMEState::Enabled aIMEEnabled)
nsTextStore::Create(nsWindowBase* aWidget)
{
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: 0x%p nsTextStore::Create(aWidget=0x%p, aIMEEnabled=%s)",
this, aWidget, GetIMEEnabledName(aIMEEnabled)));
("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)",
this, aWidget));
if (mDocumentMgr) {
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
@ -545,8 +565,6 @@ nsTextStore::Create(nsWindowBase* aWidget,
return false;
}
SetInputContextInternal(aIMEEnabled);
hr = mDocumentMgr->Push(mContext);
if (FAILED(hr)) {
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
@ -2863,14 +2881,40 @@ nsTextStore::OnFocusChange(bool aGotFocus,
// no change notifications if TSF is disabled
NS_ENSURE_TRUE(sTsfThreadMgr && sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
if (aGotFocus) {
bool bRet = sTsfTextStore->Create(aFocusedWidget, aIMEEnabled);
nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
if (aGotFocus && (aIMEEnabled == IMEState::ENABLED ||
aIMEEnabled == IMEState::PASSWORD)) {
bool bRet = sTsfTextStore->Create(aFocusedWidget);
NS_ENSURE_TRUE(bRet, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(sTsfTextStore->mDocumentMgr, NS_ERROR_FAILURE);
if (aIMEEnabled == IMEState::PASSWORD) {
MarkContextAsKeyboardDisabled(sTsfTextStore->mContext);
nsRefPtr<ITfContext> topContext;
sTsfTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
if (topContext && topContext != sTsfTextStore->mContext) {
MarkContextAsKeyboardDisabled(topContext);
}
}
HRESULT hr = sTsfThreadMgr->SetFocus(sTsfTextStore->mDocumentMgr);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
// Use AssociateFocus() for ensuring that any native focus event
// never steal focus from our documentMgr.
hr = sTsfThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
sTsfTextStore->mDocumentMgr,
getter_AddRefs(prevFocusedDocumentMgr));
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
} else {
sTsfTextStore->Destroy();
if (ThinksHavingFocus()) {
DebugOnly<HRESULT> hr = sTsfThreadMgr->AssociateFocus(
sTsfTextStore->mWidget->GetWindowHandle(),
NULL, getter_AddRefs(prevFocusedDocumentMgr));
NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
NS_ASSERTION(prevFocusedDocumentMgr == sTsfTextStore->mDocumentMgr,
"different documentMgr has been associated with the window");
sTsfTextStore->Destroy();
}
HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
}
return NS_OK;
}
@ -3103,37 +3147,85 @@ nsTextStore::GetIMEOpenState(void)
return false;
}
// static
void
nsTextStore::SetInputContextInternal(IMEState::Enabled aState)
nsTextStore::SetInputContext(nsWindowBase* aWidget,
const InputContext& aContext,
const InputContextAction& aAction)
{
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
("TSF: 0x%p nsTextStore::SetInputContextInternal(aState=%s), "
"mContext=0x%p",
this, GetIMEEnabledName(aState), mContext.get()));
("TSF: nsTextStore::SetInputContext(aWidget=%p, "
"aContext.mIMEState.mEnabled=%s, aAction.mFocusChange=%s), "
"ThinksHavingFocus()=%s",
aWidget, GetIMEEnabledName(aContext.mIMEState.mEnabled),
GetFocusChangeName(aAction.mFocusChange),
GetBoolName(ThinksHavingFocus())));
VARIANT variant;
variant.vt = VT_I4;
variant.lVal = (aState != IMEState::ENABLED);
NS_ENSURE_TRUE_VOID(sTsfTextStore);
sTsfTextStore->SetInputScope(aContext.mHTMLInputType);
if (aAction.mFocusChange != InputContextAction::FOCUS_NOT_CHANGED) {
return;
}
// If focus isn't actually changed but the enabled state is changed,
// emulate the focus move.
if (!ThinksHavingFocus() &&
aContext.mIMEState.mEnabled == IMEState::ENABLED) {
OnFocusChange(true, aWidget, aContext.mIMEState.mEnabled);
} else if (ThinksHavingFocus() &&
aContext.mIMEState.mEnabled != IMEState::ENABLED) {
OnFocusChange(false, aWidget, aContext.mIMEState.mEnabled);
}
}
// static
void
nsTextStore::MarkContextAsKeyboardDisabled(ITfContext* aContext)
{
VARIANT variant_int4_value1;
variant_int4_value1.vt = VT_I4;
variant_int4_value1.lVal = 1;
// Set two contexts, the base context (mContext) and the top
// if the top context is not the same as the base context
nsRefPtr<ITfContext> context = mContext;
nsRefPtr<ITfCompartment> comp;
do {
if (GetCompartment(context, GUID_COMPARTMENT_KEYBOARD_DISABLED,
getter_AddRefs(comp))) {
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
("TSF: 0x%p nsTextStore::SetInputContextInternal(), setting "
"0x%04X to GUID_COMPARTMENT_KEYBOARD_DISABLED of context 0x%p...",
this, variant.lVal, context.get()));
comp->SetValue(sTsfClientId, &variant);
}
if (!GetCompartment(aContext,
GUID_COMPARTMENT_KEYBOARD_DISABLED,
getter_AddRefs(comp))) {
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
("TSF: nsTextStore::MarkContextAsKeyboardDisabled() failed"
"aContext=0x%p...", aContext));
return;
}
if (context != mContext)
break;
if (mDocumentMgr)
mDocumentMgr->GetTop(getter_AddRefs(context));
} while (context != mContext);
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
("TSF: nsTextStore::MarkContextAsKeyboardDisabled(), setting "
"to disable context 0x%p...",
aContext));
comp->SetValue(sTsfClientId, &variant_int4_value1);
}
// static
void
nsTextStore::MarkContextAsEmpty(ITfContext* aContext)
{
VARIANT variant_int4_value1;
variant_int4_value1.vt = VT_I4;
variant_int4_value1.lVal = 1;
nsRefPtr<ITfCompartment> comp;
if (!GetCompartment(aContext,
GUID_COMPARTMENT_EMPTYCONTEXT,
getter_AddRefs(comp))) {
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
("TSF: nsTextStore::MarkContextAsEmpty() failed"
"aContext=0x%p...", aContext));
return;
}
PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
("TSF: nsTextStore::MarkContextAsEmpty(), setting "
"to mark empty context 0x%p...", aContext));
comp->SetValue(sTsfClientId, &variant_int4_value1);
}
// static
@ -3223,6 +3315,32 @@ nsTextStore::Initialize(void)
NS_RELEASE(sDisplayAttrMgr);
}
}
if (sTsfThreadMgr && sTsfTextStore) {
HRESULT hr =
sTsfThreadMgr->CreateDocumentMgr(&sTsfDisabledDocumentMgr);
if (FAILED(hr) || !sTsfDisabledDocumentMgr) {
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
("TSF: nsTextStore::Initialize() FAILED to create "
"a document manager for disabled mode"));
}
if (sTsfDisabledDocumentMgr) {
DWORD editCookie = 0;
hr = sTsfDisabledDocumentMgr->CreateContext(sTsfClientId, 0, NULL,
&sTsfDisabledContext,
&editCookie);
if (FAILED(hr) || !sTsfDisabledContext) {
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
("TSF: nsTextStore::Initialize() FAILED to create "
"a context for disabled mode"));
}
if (sTsfDisabledContext) {
MarkContextAsKeyboardDisabled(sTsfDisabledContext);
MarkContextAsEmpty(sTsfDisabledContext);
}
}
}
if (sTsfThreadMgr && !sFlushTIPInputMessage) {
sFlushTIPInputMessage = ::RegisterWindowMessageW(
NS_LITERAL_STRING("Flush TIP Input Message").get());
@ -3236,9 +3354,9 @@ nsTextStore::Initialize(void)
PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
("TSF: nsTextStore::Initialize(), sTsfThreadMgr=0x%p, "
"sTsfClientId=0x%08X, sTsfTextStore=0x%p, sDisplayAttrMgr=0x%p, "
"sCategoryMgr=0x%p",
sTsfThreadMgr, sTsfClientId, sTsfTextStore,
sDisplayAttrMgr, sCategoryMgr));
"sCategoryMgr=0x%p, sTsfDisabledDocumentMgr=0x%p, sTsfDisabledContext=%p",
sTsfThreadMgr, sTsfClientId, sTsfTextStore, sDisplayAttrMgr, sCategoryMgr,
sTsfDisabledDocumentMgr, sTsfDisabledContext));
}
// static
@ -3250,6 +3368,8 @@ nsTextStore::Terminate(void)
NS_IF_RELEASE(sDisplayAttrMgr);
NS_IF_RELEASE(sCategoryMgr);
NS_IF_RELEASE(sTsfTextStore);
NS_IF_RELEASE(sTsfDisabledDocumentMgr);
NS_IF_RELEASE(sTsfDisabledContext);
sTsfClientId = 0;
if (sTsfThreadMgr) {
sTsfThreadMgr->Deactivate();

Просмотреть файл

@ -92,6 +92,7 @@ public: /*ITfContextOwnerCompositionSink*/
protected:
typedef mozilla::widget::IMEState IMEState;
typedef mozilla::widget::InputContext InputContext;
typedef mozilla::widget::InputContextAction InputContextAction;
public:
static void Initialize(void);
@ -108,12 +109,9 @@ public:
sTsfTextStore->CommitCompositionInternal(aDiscard);
}
static void SetInputContext(const InputContext& aContext)
{
NS_ENSURE_TRUE_VOID(sTsfTextStore);
sTsfTextStore->SetInputScope(aContext.mHTMLInputType);
sTsfTextStore->SetInputContextInternal(aContext.mIMEState.mEnabled);
}
static void SetInputContext(nsWindowBase* aWidget,
const InputContext& aContext,
const InputContextAction& aAction);
static nsresult OnFocusChange(bool aGotFocus,
nsWindowBase* aFocusedWidget,
@ -198,8 +196,10 @@ protected:
nsTextStore();
~nsTextStore();
bool Create(nsWindowBase* aWidget,
IMEState::Enabled aIMEEnabled);
static void MarkContextAsKeyboardDisabled(ITfContext* aContext);
static void MarkContextAsEmpty(ITfContext* aContext);
bool Create(nsWindowBase* aWidget);
bool Destroy(void);
bool IsReadLock(DWORD aLock) const
@ -223,7 +223,6 @@ protected:
bool InsertTextAtSelectionInternal(const nsAString &aInsertStr,
TS_TEXTCHANGE* aTextChange);
void CommitCompositionInternal(bool);
void SetInputContextInternal(IMEState::Enabled aState);
nsresult OnTextChangeInternal(uint32_t, uint32_t, uint32_t);
void OnTextChangeMsgInternal(void);
nsresult OnSelectionChangeInternal(void);
@ -648,6 +647,10 @@ protected:
// when the focused editor is blurred.
static nsTextStore* sTsfTextStore;
// For IME (keyboard) disabled state:
static ITfDocumentMgr* sTsfDisabledDocumentMgr;
static ITfContext* sTsfDisabledContext;
// Message the Tablet Input Panel uses to flush text during blurring.
// See comments in Destroy
static UINT sFlushTIPInputMessage;

Просмотреть файл

@ -7386,7 +7386,7 @@ nsWindow::SetInputContext(const InputContext& aContext,
const InputContextAction& aAction)
{
InputContext newInputContext = aContext;
IMEHandler::SetInputContext(this, newInputContext);
IMEHandler::SetInputContext(this, newInputContext, aAction);
mInputContext = newInputContext;
}

Просмотреть файл

@ -1168,7 +1168,7 @@ MetroWidget::SetInputContext(const InputContext& aContext,
const InputContextAction& aAction)
{
mInputContext = aContext;
nsTextStore::SetInputContext(mInputContext);
nsTextStore::SetInputContext(this, mInputContext, aAction);
bool enable = (mInputContext.mIMEState.mEnabled == IMEState::ENABLED ||
mInputContext.mIMEState.mEnabled == IMEState::PLUGIN);
if (enable &&