Bug 1406446 - part 1: InputContextAction should treat focus change during handling a user input as caused by user input even if it's caused by JS r=smaug

Currently, widget doesn't show VKB when input context change is caused by JS.
However, if it's caused by an event handler of a user input, user may expect
to open VKB.  For example, if a touch event in fake editor causes moving
focus to actual editable node, user expect to show VKB.

Therefore, InputContextAction should declare two causes.  One is unknown but
occurred during handling non-keyboard event.  The other is unknown but occurred
during handling keyboard event.

However, EventStateManager doesn't have an API to check if it's being handling
a keyboard event.  Therefore, this patch adds it first.
AutoHandlingUserInputStatePusher sends event type to StartHandlingUserInput()
and StopHandlingUserInput() of EventStateManager and sUserKeyboardEventDepth
manages the number of nested keyboard event handling.  Therefore,
EventStateManager::IsHandlingKeyboardInput() can return if it's handling a
keyboard event.

IMEStateManager uses this new API to adjust the cause of changes of input
context.

Finally, InputContextAction::IsUserInput() is renamed to IsHandlingUserInput()
for consistency with EventStateManager and starts to return true when the
input context change is caused by script while it's handling a user input.

MozReview-Commit-ID: 5JsLqdqeGah

--HG--
extra : rebase_source : 9fcf7687d1bf90eeebbf6eac62d4488ff64b083c
This commit is contained in:
Masayuki Nakano 2017-10-24 02:46:15 +09:00
Родитель 0e8c7ea1cd
Коммит 93977460e2
8 изменённых файлов: 133 добавлений и 59 удалений

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

@ -4026,7 +4026,7 @@ HandlingUserInputHelper::HandlingUserInputHelper(bool aHandlingUserInput)
mDestructCalled(false)
{
if (aHandlingUserInput) {
EventStateManager::StartHandlingUserInput();
EventStateManager::StartHandlingUserInput(eVoidEvent);
}
}
@ -4048,7 +4048,7 @@ HandlingUserInputHelper::Destruct()
mDestructCalled = true;
if (mHandlingUserInput) {
EventStateManager::StopHandlingUserInput();
EventStateManager::StopHandlingUserInput(eVoidEvent);
}
return NS_OK;

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

@ -265,6 +265,7 @@ static uint32_t sESMInstanceCount = 0;
uint64_t EventStateManager::sUserInputCounter = 0;
int32_t EventStateManager::sUserInputEventDepth = 0;
int32_t EventStateManager::sUserKeyboardEventDepth = 0;
bool EventStateManager::sNormalLMouseEventInProcess = false;
EventStateManager* EventStateManager::sActiveESM = nullptr;
nsIDocument* EventStateManager::sMouseOverDocument = nullptr;
@ -3941,6 +3942,37 @@ EventStateManager::IsHandlingUserInput()
return sUserInputEventDepth > 0;
}
/*static*/ bool
EventStateManager::IsHandlingKeyboardInput()
{
return sUserKeyboardEventDepth > 0;
}
/*static*/ void
EventStateManager::StartHandlingUserInput(EventMessage aMessage)
{
++sUserInputEventDepth;
++sUserInputCounter;
if (sUserInputEventDepth == 1) {
sLatestUserInputStart = sHandlingInputStart = TimeStamp::Now();
}
if (WidgetEvent::IsKeyEventMessage(aMessage)) {
++sUserKeyboardEventDepth;
}
}
/*static*/ void
EventStateManager::StopHandlingUserInput(EventMessage aMessage)
{
--sUserInputEventDepth;
if (sUserInputEventDepth == 0) {
sHandlingInputStart = TimeStamp();
}
if (WidgetEvent::IsKeyEventMessage(aMessage)) {
--sUserKeyboardEventDepth;
}
}
static void
CreateMouseOrPointerWidgetEvent(WidgetMouseEvent* aMouseEvent,
EventMessage aMessage,
@ -6035,25 +6067,22 @@ EventStateManager::Prefs::Shutdown()
AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher(
bool aIsHandlingUserInput,
WidgetEvent* aEvent,
nsIDocument* aDocument) :
mIsHandlingUserInput(aIsHandlingUserInput),
mIsMouseDown(aEvent && aEvent->mMessage == eMouseDown),
mResetFMMouseButtonHandlingState(false)
nsIDocument* aDocument)
: mMessage(aEvent ? aEvent->mMessage : eVoidEvent)
, mIsHandlingUserInput(aIsHandlingUserInput)
{
if (!aIsHandlingUserInput) {
return;
}
EventStateManager::StartHandlingUserInput();
if (mIsMouseDown) {
EventStateManager::StartHandlingUserInput(mMessage);
if (mMessage == eMouseDown) {
nsIPresShell::SetCapturingContent(nullptr, 0);
nsIPresShell::AllowMouseCapture(true);
}
if (!aDocument || !aEvent || !aEvent->IsTrusted()) {
return;
}
mResetFMMouseButtonHandlingState =
(aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp);
if (mResetFMMouseButtonHandlingState) {
if (NeedsToResetFocusManagerMouseButtonHandlingState()) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE_VOID(fm);
// If it's in modal state, mouse button event handling may be nested.
@ -6070,11 +6099,11 @@ AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher()
if (!mIsHandlingUserInput) {
return;
}
EventStateManager::StopHandlingUserInput();
if (mIsMouseDown) {
EventStateManager::StopHandlingUserInput(mMessage);
if (mMessage == eMouseDown) {
nsIPresShell::AllowMouseCapture(false);
}
if (mResetFMMouseButtonHandlingState) {
if (NeedsToResetFocusManagerMouseButtonHandlingState()) {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE_VOID(fm);
nsCOMPtr<nsIDocument> handlingDocument =

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

@ -233,22 +233,17 @@ public:
bool aHaveHotspot, float aHotspotX, float aHotspotY,
nsIWidget* aWidget, bool aLockCursor);
static void StartHandlingUserInput()
{
++sUserInputEventDepth;
++sUserInputCounter;
if (sUserInputEventDepth == 1) {
sLatestUserInputStart = sHandlingInputStart = TimeStamp::Now();
}
}
static void StopHandlingUserInput()
{
--sUserInputEventDepth;
if (sUserInputEventDepth == 0) {
sHandlingInputStart = TimeStamp();
}
}
/**
* StartHandlingUserInput() is called when we start to handle a user input.
* StopHandlingUserInput() is called when we finish handling a user input.
* If the caller knows which input event caused that, it should set
* aMessage to the event message. Otherwise, set eVoidEvent.
* Note that StopHandlingUserInput() caller should call it with exactly same
* event message as its corresponding StartHandlingUserInput() call because
* these methods may count the number of specific event message.
*/
static void StartHandlingUserInput(EventMessage aMessage);
static void StopHandlingUserInput(EventMessage aMessage);
static TimeStamp GetHandlingInputStart() {
return sHandlingInputStart;
@ -256,12 +251,14 @@ public:
/**
* Returns true if the current code is being executed as a result of
* user input. This includes anything that is initiated by user,
* with the exception of page load events or mouse over events. If
* this method is called from asynchronously executed code, such as
* during layout reflows, it will return false.
* user input or keyboard input. The former includes anything that is
* initiated by user, with the exception of page load events or mouse
* over events. And the latter returns true when one of the user inputs
* is an input from keyboard. If these methods are called from asynchronously
* executed code, such as during layout reflows, it will return false.
*/
static bool IsHandlingUserInput();
static bool IsHandlingKeyboardInput();
/**
* Get the number of user inputs handled since process start. This
@ -1092,12 +1089,14 @@ public:
// of page load events or mouse over events.
static uint64_t sUserInputCounter;
// The current depth of user inputs. This includes anything that is
// initiated by user, with the exception of page load events or
// mouse over events. Incremented whenever we start handling a user
// input, decremented when we have finished handling a user
// input. This depth is *not* reset in case of nested event loops.
// The current depth of user and keyboard inputs. sUserInputEventDepth
// is the number of any user input events, page load events and mouse over
// events. sUserKeyboardEventDepth is the number of keyboard input events.
// Incremented whenever we start handling a user input, decremented when we
// have finished handling a user input. This depth is *not* reset in case
// of nested event loops.
static int32_t sUserInputEventDepth;
static int32_t sUserKeyboardEventDepth;
static bool sNormalLMouseEventInProcess;
@ -1130,11 +1129,14 @@ public:
~AutoHandlingUserInputStatePusher();
protected:
bool mIsHandlingUserInput;
bool mIsMouseDown;
bool mResetFMMouseButtonHandlingState;
nsCOMPtr<nsIDocument> mMouseButtonEventHandlingDocument;
EventMessage mMessage;
bool mIsHandlingUserInput;
bool NeedsToResetFocusManagerMouseButtonHandlingState() const
{
return mMessage == eMouseDown || mMessage == eMouseUp;
}
private:
// Hide so that this class can only be stack-allocated

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

@ -1365,6 +1365,14 @@ IMEStateManager::SetIMEState(const IMEState& aState,
aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
}
if ((aAction.mCause == InputContextAction::CAUSE_UNKNOWN ||
aAction.mCause == InputContextAction::CAUSE_UNKNOWN_CHROME) &&
EventStateManager::IsHandlingUserInput()) {
aAction.mCause = EventStateManager::IsHandlingKeyboardInput() ?
InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT :
InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT;
}
SetInputContext(aWidget, context, aAction);
}

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

@ -769,9 +769,13 @@ public:
*/
bool HasDragEventMessage() const;
/**
* Returns true if the event mMessage is one of key events.
* Returns true if aMessage or mMessage is one of key events.
*/
bool HasKeyEventMessage() const;
static bool IsKeyEventMessage(EventMessage aMessage);
bool HasKeyEventMessage() const
{
return IsKeyEventMessage(mMessage);
}
/**
* Returns true if the event mMessage is one of composition events or text
* event.

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

@ -370,7 +370,12 @@ struct InputContextAction final
// The cause is user's mouse operation.
CAUSE_MOUSE,
// The cause is user's touch operation (implies mouse)
CAUSE_TOUCH
CAUSE_TOUCH,
// The cause is unknown but it occurs during user input except keyboard
// input. E.g., an event handler of a user input event moves focus.
CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT,
// The cause is unknown but it occurs during keyboard input.
CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT,
};
Cause mCause;
@ -405,24 +410,49 @@ struct InputContextAction final
bool UserMightRequestOpenVKB() const
{
return (mFocusChange == FOCUS_NOT_CHANGED &&
(mCause == CAUSE_MOUSE || mCause == CAUSE_TOUCH));
}
static bool IsUserAction(Cause aCause)
{
switch (aCause) {
case CAUSE_KEY:
// If focus is changed, user must not request to open VKB.
if (mFocusChange != FOCUS_NOT_CHANGED) {
return false;
}
switch (mCause) {
// If user clicks or touches focused editor, user must request to open
// VKB.
case CAUSE_MOUSE:
case CAUSE_TOUCH:
// If script does something during a user input and that causes changing
// input context, user might request to open VKB. E.g., user clicks
// dummy editor and JS moves focus to an actual editable node. However,
// this should return false if the user input is a keyboard event since
// physical keyboard operation shouldn't cause opening VKB.
case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
return true;
default:
return false;
}
}
bool IsUserAction() const {
return IsUserAction(mCause);
/**
* IsHandlingUserInput() returns true if it's caused by a user action directly
* or it's caused by script or something but it occurred while we're handling
* a user action. E.g., when it's caused by Element.focus() in an event
* handler of a user input, this returns true.
*/
static bool IsHandlingUserInput(Cause aCause)
{
switch (aCause) {
case CAUSE_KEY:
case CAUSE_MOUSE:
case CAUSE_TOUCH:
case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
return true;
default:
return false;
}
}
bool IsHandlingUserInput() const {
return IsHandlingUserInput(mCause);
}
InputContextAction()

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

@ -272,10 +272,11 @@ WidgetEvent::HasDragEventMessage() const
}
}
/* static */
bool
WidgetEvent::HasKeyEventMessage() const
WidgetEvent::IsKeyEventMessage(EventMessage aMessage)
{
switch (mMessage) {
switch (aMessage) {
case eKeyDown:
case eKeyPress:
case eKeyUp:

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

@ -803,7 +803,7 @@ IMEHandler::NeedOnScreenKeyboard()
// avoids cases where the keyboard would pop up "just" because e.g. a
// web page chooses to focus a search field on the page, even when that
// really isn't what the user is trying to do at that moment.
if (!InputContextAction::IsUserAction(sLastContextActionCause)) {
if (!InputContextAction::IsHandlingUserInput(sLastContextActionCause)) {
return false;
}