Bug 1743346 - Make TextInputHandler::HandleEvent handle native key bindings first, then, our shortcut keys r=smaug
Oddly, `TextInputHandler` which is keyboard event handler for `<input type="text">` and `<textarea>` handles our shortcut keys first, then, refer native key bindings. So if a key combination matches in both definitions, our shortcut key wins. On the other hand, if `HTMLEditor` has focus, `keypress` event listener on the `Document` node hanldes native key bindings: https://searchfox.org/mozilla-central/rev/70b32246fce5ca1f53af573a21c1939df58cb969/editor/libeditor/EditorEventListener.cpp#641-644 Then, global key listener will be run later: https://searchfox.org/mozilla-central/rev/70b32246fce5ca1f53af573a21c1939df58cb969/dom/events/GlobalKeyListener.cpp#163-164 Perhaps, it's better to refer native key bindings first because unusual shortcut key definition must be caused by customization by the user in the system level. Therefore, this patch makes the order switchable with the new pref for making it easier to back it out. Differential Revision: https://phabricator.services.mozilla.com/D132451
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TextControlState.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TextInputListener.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#include "mozilla/dom/HTMLTextAreaElement.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_ui.h"
|
||||
#include "nsFrameSelection.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@@ -967,58 +969,79 @@ TextInputListener::HandleEvent(Event* aEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
KeyEventHandler* keyHandlers = ShortcutKeys::GetHandlers(
|
||||
mTxtCtrlElement->IsTextArea() ? HandlerType::eTextArea
|
||||
: HandlerType::eInput);
|
||||
auto ExecuteOurShortcutKeys = [&](TextControlElement& aTextControlElement)
|
||||
MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> bool {
|
||||
KeyEventHandler* keyHandlers = ShortcutKeys::GetHandlers(
|
||||
aTextControlElement.IsTextArea() ? HandlerType::eTextArea
|
||||
: HandlerType::eInput);
|
||||
|
||||
RefPtr<nsAtom> eventTypeAtom =
|
||||
ShortcutKeys::ConvertEventToDOMEventType(widgetKeyEvent);
|
||||
for (KeyEventHandler* handler = keyHandlers; handler;
|
||||
handler = handler->GetNextHandler()) {
|
||||
if (!handler->EventTypeEquals(eventTypeAtom)) {
|
||||
continue;
|
||||
RefPtr<nsAtom> eventTypeAtom =
|
||||
ShortcutKeys::ConvertEventToDOMEventType(widgetKeyEvent);
|
||||
for (KeyEventHandler* 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?
|
||||
nsresult rv = handler->ExecuteHandler(&aTextControlElement, aEvent);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto ExecuteNativeKeyBindings =
|
||||
[&](TextControlElement& aTextControlElement)
|
||||
MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> bool {
|
||||
if (widgetKeyEvent->mMessage != eKeyPress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handler->KeyEventMatched(keyEvent, 0, IgnoreModifierState())) {
|
||||
continue;
|
||||
nsIWidget::NativeKeyBindingsType nativeKeyBindingsType =
|
||||
aTextControlElement.IsTextArea()
|
||||
? nsIWidget::NativeKeyBindingsForMultiLineEditor
|
||||
: nsIWidget::NativeKeyBindingsForSingleLineEditor;
|
||||
|
||||
nsIWidget* widget = widgetKeyEvent->mWidget;
|
||||
// If the event is created by chrome script, the widget is nullptr.
|
||||
if (MOZ_UNLIKELY(!widget)) {
|
||||
widget = mFrame->GetNearestWidget();
|
||||
if (MOZ_UNLIKELY(NS_WARN_IF(!widget))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// XXX Do we execute only one handler even if the handler neither stops
|
||||
// propagation nor prevents default of the event?
|
||||
RefPtr<TextControlElement> textControlElement(mTxtCtrlElement);
|
||||
nsresult rv = handler->ExecuteHandler(textControlElement, aEvent);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return rv;
|
||||
// WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
|
||||
// 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(widgetKeyEvent->mWidget);
|
||||
widgetKeyEvent->mWidget = widget;
|
||||
if (widgetKeyEvent->ExecuteEditCommands(nativeKeyBindingsType,
|
||||
DoCommandCallback, mFrame)) {
|
||||
aEvent->PreventDefault();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (widgetKeyEvent->mMessage != eKeyPress) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIWidget::NativeKeyBindingsType nativeKeyBindingsType =
|
||||
mTxtCtrlElement->IsTextArea()
|
||||
? nsIWidget::NativeKeyBindingsForMultiLineEditor
|
||||
: nsIWidget::NativeKeyBindingsForSingleLineEditor;
|
||||
|
||||
nsIWidget* widget = widgetKeyEvent->mWidget;
|
||||
// If the event is created by chrome script, the widget is nullptr.
|
||||
if (!widget) {
|
||||
widget = mFrame->GetNearestWidget();
|
||||
if (NS_WARN_IF(!widget)) {
|
||||
return NS_OK;
|
||||
OwningNonNull<TextControlElement> textControlElement(*mTxtCtrlElement);
|
||||
if (StaticPrefs::
|
||||
ui_key_textcontrol_prefer_native_key_bindings_over_builtin_shortcut_key_definitions()) {
|
||||
if (!ExecuteNativeKeyBindings(textControlElement)) {
|
||||
ExecuteOurShortcutKeys(textControlElement);
|
||||
}
|
||||
} else {
|
||||
if (!ExecuteOurShortcutKeys(textControlElement)) {
|
||||
ExecuteNativeKeyBindings(textControlElement);
|
||||
}
|
||||
}
|
||||
|
||||
// WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
|
||||
// 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(widgetKeyEvent->mWidget);
|
||||
widgetKeyEvent->mWidget = widget;
|
||||
if (widgetKeyEvent->ExecuteEditCommands(nativeKeyBindingsType,
|
||||
DoCommandCallback, mFrame)) {
|
||||
aEvent->PreventDefault();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user