Bug 1419091: Make TextInputListener handle non-native events for input and textarea too. r=masayuki

platformHTMLBindings attaches event handlers directly to the <input> and
<textarea> elements. This matches that by making TextInputListener look up and
call the <input> and <textarea> event handlers from the static C array.
There is a slight difference in that the event handlers are added to the system
bubbling phase as opposed to the regular bubbling phase but in tests this does
not seem to cause problems.

Differential Revision: https://phabricator.services.mozilla.com/D8931

--HG--
extra : rebase_source : cd3cff63cb6a1da1d9130a04bd7ebe9cd7b62b4e
extra : source : 0e16c555c00a0321e96c9b62e3df9508dd4ba06a
This commit is contained in:
Dave Townsend 2018-10-15 12:19:30 -07:00
Родитель 3a9499b378
Коммит 1d9152ef0d
7 изменённых файлов: 92 добавлений и 74 удалений

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

@ -45,6 +45,9 @@
#include "nsFrameSelection.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ShortcutKeys.h"
#include "nsXBLPrototypeHandler.h"
#include "mozilla/dom/KeyboardEvent.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -957,13 +960,44 @@ TextInputListener::HandleEvent(Event* aEvent)
return NS_OK;
}
WidgetKeyboardEvent* keyEvent =
aEvent->WidgetEventPtr()->AsKeyboardEvent();
RefPtr<KeyboardEvent> keyEvent = aEvent->AsKeyboardEvent();
if (!keyEvent) {
return NS_ERROR_UNEXPECTED;
}
if (keyEvent->mMessage != eKeyPress) {
WidgetKeyboardEvent* widgetKeyEvent =
aEvent->WidgetEventPtr()->AsKeyboardEvent();
if (!keyEvent) {
return NS_ERROR_UNEXPECTED;
}
nsXBLPrototypeHandler* keyHandlers =
ShortcutKeys::GetHandlers(mTxtCtrlElement->IsTextArea() ?
HandlerType::eTextArea : HandlerType::eInput);
RefPtr<nsAtom> eventTypeAtom =
ShortcutKeys::ConvertEventToDOMEventType(widgetKeyEvent);
for (nsXBLPrototypeHandler* handler = keyHandlers;
handler;
handler = handler->GetNextHandler()) {
if (!handler->EventTypeEquals(eventTypeAtom)) {
continue;
}
if (!handler->KeyEventMatched(keyEvent, 0, IgnoreModifierState())) {
continue;
}
// XXX Do we execute only one handler even if the handler neither stops
// propagation nor prevents default of the event?
nsCOMPtr<EventTarget> target = do_QueryInterface(mTxtCtrlElement);
nsresult rv = handler->ExecuteHandler(target, aEvent);
if (NS_SUCCEEDED(rv)) {
return rv;
}
}
if (widgetKeyEvent->mMessage != eKeyPress) {
return NS_OK;
}
@ -972,7 +1006,7 @@ TextInputListener::HandleEvent(Event* aEvent)
nsIWidget::NativeKeyBindingsForMultiLineEditor :
nsIWidget::NativeKeyBindingsForSingleLineEditor;
nsIWidget* widget = keyEvent->mWidget;
nsIWidget* widget = widgetKeyEvent->mWidget;
// If the event is created by chrome script, the widget is nullptr.
if (!widget) {
widget = mFrame->GetNearestWidget();
@ -983,10 +1017,10 @@ TextInputListener::HandleEvent(Event* aEvent)
// If the event is created by chrome script, it is nullptr but we need to
// execute native key bindings. Therefore, we need to set widget to
// WidgetEvent::mWidget temporarily.
AutoRestore<nsCOMPtr<nsIWidget>> saveWidget(keyEvent->mWidget);
keyEvent->mWidget = widget;
if (keyEvent->ExecuteEditCommands(nativeKeyBindingsType,
DoCommandCallback, mFrame)) {
AutoRestore<nsCOMPtr<nsIWidget>> saveWidget(widgetKeyEvent->mWidget);
widgetKeyEvent->mWidget = widget;
if (widgetKeyEvent->ExecuteEditCommands(nativeKeyBindingsType,
DoCommandCallback, mFrame)) {
aEvent->PreventDefault();
}
return NS_OK;

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

@ -1,6 +1,8 @@
#include "mozilla/ShortcutKeys.h"
#include "../nsXBLPrototypeHandler.h"
#include "nsContentUtils.h"
#include "nsAtom.h"
#include "mozilla/TextEvents.h"
namespace mozilla {
@ -44,6 +46,28 @@ ShortcutKeys::GetHandlers(HandlerType aType)
return sInstance->EnsureHandlers(aType);
}
/* static */ nsAtom*
ShortcutKeys::ConvertEventToDOMEventType(const WidgetKeyboardEvent* aWidgetKeyboardEvent)
{
if (aWidgetKeyboardEvent->IsKeyDownOrKeyDownOnPlugin()) {
return nsGkAtoms::keydown;
}
if (aWidgetKeyboardEvent->IsKeyUpOrKeyUpOnPlugin()) {
return nsGkAtoms::keyup;
}
// eAccessKeyNotFound event is always created from eKeyPress event and
// the original eKeyPress event has stopped its propagation before dispatched
// into the DOM tree in this process and not matched with remote content's
// access keys. So, we should treat it as an eKeyPress event and execute
// a command if it's registered as a shortcut key.
if (aWidgetKeyboardEvent->mMessage == eKeyPress ||
aWidgetKeyboardEvent->mMessage == eAccessKeyNotFound) {
return nsGkAtoms::keypress;
}
MOZ_ASSERT_UNREACHABLE("All event messages relating to shortcut keys should be handled");
return nullptr;
}
nsXBLPrototypeHandler*
ShortcutKeys::EnsureHandlers(HandlerType aType)
{

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

@ -8,9 +8,12 @@
#include "nsIObserver.h"
class nsXBLPrototypeHandler;
class nsAtom;
namespace mozilla {
class WidgetKeyboardEvent;
typedef struct
{
const char16_t* event;
@ -37,6 +40,9 @@ public:
// Returns a pointer to the first handler for the given type.
static nsXBLPrototypeHandler* GetHandlers(HandlerType aType);
// Gets the event type for a widget keyboard event.
static nsAtom* ConvertEventToDOMEventType(const WidgetKeyboardEvent* aWidgetKeyboardEvent);
protected:
ShortcutKeys();
virtual ~ShortcutKeys();

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

@ -12,6 +12,7 @@ DIRS += ['builtin']
EXPORTS += [
'nsBindingManager.h',
'nsXBLBinding.h',
'nsXBLPrototypeHandler.h',
'nsXBLService.h',
'nsXBLWindowKeyHandler.h',
]

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

@ -137,7 +137,7 @@ nsXBLWindowKeyHandler::EnsureHandlers()
}
nsresult
nsXBLWindowKeyHandler::WalkHandlers(KeyboardEvent* aKeyEvent, nsAtom* aEventType)
nsXBLWindowKeyHandler::WalkHandlers(KeyboardEvent* aKeyEvent)
{
if (aKeyEvent->DefaultPrevented()) {
return NS_OK;
@ -159,7 +159,7 @@ nsXBLWindowKeyHandler::WalkHandlers(KeyboardEvent* aKeyEvent, nsAtom* aEventType
return NS_OK;
}
WalkHandlersInternal(aKeyEvent, aEventType, true);
WalkHandlersInternal(aKeyEvent, true);
return NS_OK;
}
@ -309,30 +309,6 @@ nsXBLWindowKeyHandler::CollectKeyboardShortcuts()
return KeyboardMap(std::move(shortcuts));
}
nsAtom*
nsXBLWindowKeyHandler::ConvertEventToDOMEventType(
const WidgetKeyboardEvent& aWidgetKeyboardEvent) const
{
if (aWidgetKeyboardEvent.IsKeyDownOrKeyDownOnPlugin()) {
return nsGkAtoms::keydown;
}
if (aWidgetKeyboardEvent.IsKeyUpOrKeyUpOnPlugin()) {
return nsGkAtoms::keyup;
}
// eAccessKeyNotFound event is always created from eKeyPress event and
// the original eKeyPress event has stopped its propagation before dispatched
// into the DOM tree in this process and not matched with remote content's
// access keys. So, we should treat it as an eKeyPress event and execute
// a command if it's registered as a shortcut key.
if (aWidgetKeyboardEvent.mMessage == eKeyPress ||
aWidgetKeyboardEvent.mMessage == eAccessKeyNotFound) {
return nsGkAtoms::keypress;
}
MOZ_ASSERT_UNREACHABLE(
"All event messages which this instance listens to should be handled");
return nullptr;
}
NS_IMETHODIMP
nsXBLWindowKeyHandler::HandleEvent(Event* aEvent)
{
@ -380,9 +356,7 @@ nsXBLWindowKeyHandler::HandleEvent(Event* aEvent)
return NS_OK;
}
RefPtr<nsAtom> eventTypeAtom =
ConvertEventToDOMEventType(*widgetKeyboardEvent);
return WalkHandlers(keyEvent, eventTypeAtom);
return WalkHandlers(keyEvent);
}
void
@ -490,7 +464,6 @@ nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
//
bool
nsXBLWindowKeyHandler::WalkHandlersInternal(KeyboardEvent* aKeyEvent,
nsAtom* aEventType,
bool aExecute,
bool* aOutReservedForChrome)
{
@ -502,8 +475,7 @@ nsXBLWindowKeyHandler::WalkHandlersInternal(KeyboardEvent* aKeyEvent,
nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys);
if (shortcutKeys.IsEmpty()) {
return WalkHandlersAndExecute(aKeyEvent, aEventType,
0, IgnoreModifierState(),
return WalkHandlersAndExecute(aKeyEvent, 0, IgnoreModifierState(),
aExecute, aOutReservedForChrome);
}
@ -511,8 +483,7 @@ nsXBLWindowKeyHandler::WalkHandlersInternal(KeyboardEvent* aKeyEvent,
ShortcutKeyCandidate& key = shortcutKeys[i];
IgnoreModifierState ignoreModifierState;
ignoreModifierState.mShift = key.mIgnoreShift;
if (WalkHandlersAndExecute(aKeyEvent, aEventType,
key.mCharCode, ignoreModifierState,
if (WalkHandlersAndExecute(aKeyEvent, key.mCharCode, ignoreModifierState,
aExecute, aOutReservedForChrome)) {
return true;
}
@ -523,7 +494,6 @@ nsXBLWindowKeyHandler::WalkHandlersInternal(KeyboardEvent* aKeyEvent,
bool
nsXBLWindowKeyHandler::WalkHandlersAndExecute(
KeyboardEvent* aKeyEvent,
nsAtom* aEventType,
uint32_t aCharCode,
const IgnoreModifierState& aIgnoreModifierState,
bool aExecute,
@ -539,6 +509,8 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
return false;
}
nsAtom* eventType = ShortcutKeys::ConvertEventToDOMEventType(widgetKeyboardEvent);
// Try all of the handlers until we find one that matches the event.
for (nsXBLPrototypeHandler* handler = mHandler;
handler;
@ -560,7 +532,7 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
}
// The other event types should exactly be matched with the handler's
// event type.
} else if (!handler->EventTypeEquals(aEventType)) {
} else if (!handler->EventTypeEquals(eventType)) {
continue;
}
} else {
@ -571,12 +543,12 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
// prevented, following keypress event won't be fired. However, if
// following keypress event is reserved, we shouldn't allow web
// contents to prevent the default of the preceding keydown event.
if (aEventType != nsGkAtoms::keydown &&
aEventType != nsGkAtoms::keypress) {
if (eventType != nsGkAtoms::keydown &&
eventType != nsGkAtoms::keypress) {
continue;
}
} else if (!handler->EventTypeEquals(aEventType)) {
// Otherwise, aEventType should exactly be matched.
} else if (!handler->EventTypeEquals(eventType)) {
// Otherwise, eventType should exactly be matched.
continue;
}
}
@ -601,7 +573,7 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
}
if (!aExecute) {
if (handler->EventTypeEquals(aEventType)) {
if (handler->EventTypeEquals(eventType)) {
if (aOutReservedForChrome) {
*aOutReservedForChrome = IsReservedKey(widgetKeyboardEvent, handler);
}
@ -612,7 +584,7 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
// If the command is reserved and the event is keydown, check also if
// the handler is for keypress because if following keypress event is
// reserved, we shouldn't dispatch the event into web contents.
if (aEventType == nsGkAtoms::keydown &&
if (eventType == nsGkAtoms::keydown &&
handler->EventTypeEquals(nsGkAtoms::keypress)) {
if (IsReservedKey(widgetKeyboardEvent, handler)) {
if (aOutReservedForChrome) {
@ -661,8 +633,8 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
if (!aIgnoreModifierState.mOS && widgetKeyboardEvent->IsOS()) {
IgnoreModifierState ignoreModifierState(aIgnoreModifierState);
ignoreModifierState.mOS = true;
return WalkHandlersAndExecute(aKeyEvent, aEventType,
aCharCode, ignoreModifierState, aExecute);
return WalkHandlersAndExecute(aKeyEvent, aCharCode, ignoreModifierState,
aExecute);
}
#endif
@ -707,10 +679,7 @@ nsXBLWindowKeyHandler::HasHandlerForEvent(KeyboardEvent* aEvent,
return false;
}
RefPtr<nsAtom> eventTypeAtom =
ConvertEventToDOMEventType(*widgetKeyboardEvent);
return WalkHandlersInternal(aEvent, eventTypeAtom, false,
aOutReservedForChrome);
return WalkHandlersInternal(aEvent, false, aOutReservedForChrome);
}
already_AddRefed<Element>

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

@ -50,17 +50,16 @@ public:
protected:
virtual ~nsXBLWindowKeyHandler();
nsresult WalkHandlers(KeyboardEvent* aKeyEvent, nsAtom* aEventType);
nsresult WalkHandlers(KeyboardEvent* aKeyEvent);
// walk the handlers, looking for one to handle the event
bool WalkHandlersInternal(KeyboardEvent* aKeyEvent,
nsAtom* aEventType,
bool aExecute,
bool* aOutReservedForChrome = nullptr);
// walk the handlers for aEvent, aCharCode and aIgnoreModifierState. Execute
// it if aExecute = true.
bool WalkHandlersAndExecute(KeyboardEvent* aKeyEvent, nsAtom* aEventType,
bool WalkHandlersAndExecute(KeyboardEvent* aKeyEvent,
uint32_t aCharCode,
const IgnoreModifierState& aIgnoreModifierState,
bool aExecute,
@ -82,12 +81,6 @@ protected:
bool IsReservedKey(mozilla::WidgetKeyboardEvent* aKeyEvent,
nsXBLPrototypeHandler* aHandler);
// Returns event type for matching between aWidgetKeyboardEvent and
// shortcut key handlers. This is used for calling WalkHandlers(),
// WalkHandlersInternal() and WalkHandlersAndExecute().
nsAtom* ConvertEventToDOMEventType(
const mozilla::WidgetKeyboardEvent& aWidgetKeyboardEvent) const;
// lazily load the handlers. Overridden to handle being attached
// to a particular element rather than the document
nsresult EnsureHandlers();

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

@ -102,7 +102,6 @@ input {
word-spacing: normal;
letter-spacing: normal;
cursor: text;
-moz-binding: url("chrome://global/content/platformHTMLBindings.xml#inputFields");
text-indent: 0;
-moz-user-select: text;
text-shadow: none;
@ -134,7 +133,6 @@ textarea {
vertical-align: text-bottom;
cursor: text;
resize: both;
-moz-binding: url("chrome://global/content/platformHTMLBindings.xml#textAreas");
-moz-appearance: textfield-multiline;
text-indent: 0;
-moz-user-select: text;
@ -448,7 +446,6 @@ input[type="hidden"] {
border: 0;
cursor: auto;
-moz-user-focus: ignore;
-moz-binding: none;
}
/* image buttons */
@ -460,7 +457,6 @@ input[type="image"] {
font-family: sans-serif;
font-size: small;
cursor: pointer;
-moz-binding: none;
}
input[type="image"]:disabled {
@ -482,7 +478,6 @@ input[type="file"] {
/* Revert rules which apply on all inputs. */
-moz-appearance: none;
-moz-binding: none;
cursor: default;
border: none;
@ -552,7 +547,6 @@ input[type="checkbox"] {
cursor: default;
/* unset some values from the general 'input' rule above: */
padding: unset;
-moz-binding: unset;
border: unset;
background-color: unset;
color: unset;
@ -608,7 +602,6 @@ input[type="submit"] {
cursor: default;
box-sizing: border-box;
-moz-user-select: none;
-moz-binding: none;
}
/* Text-related properties for buttons: these ones are not shared with
@ -893,7 +886,6 @@ input[type=range] {
cursor: default;
background: none;
border: none;
-moz-binding: none; /* we don't want any of platformHTMLBindings.xml#inputFields */
/* Prevent nsFrame::HandlePress setting mouse capture to this element. */
-moz-user-select: none ! important;
}
@ -1015,7 +1007,6 @@ input[type=range]::-moz-range-thumb {
input[type="number"] {
-moz-appearance: number-input;
/* Has to revert some properties applied by the generic input rule. */
-moz-binding: none;
inline-size: 20ch; /* It'd be nice if this matched the default inline-size
of <input type=text>, but that's not easy to achieve
due to platform differences. */