зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1339543 part 1 Wrap nsIWidget::ExecuteNativeKeyBinding() with a WidgetKeyboardEvent method and users of the method should use it r=smaug
Currently, edit commands for native key bindings are stored in widget. This is stateful and really complicated in content process because it needs to cache them. We can make this simpler if we make WidgetKeyboardEvent store edit commands for the key combination. Then, child process can handle it even if it's delayed event or it's a nested event. This patch adds arrays to WidgetKeyboardEvent to store edit commands which are initialized with nsIWidget::ExecuteNativeKeyBinding() and adds WidgetKeyboardEvent::ExecuteEditCommands() to execute stored edit commands as same as nsIWidget::ExecutenativeKeyBinding(). MozReview-Commit-ID: BGRvBrLz5lp --HG-- extra : rebase_source : b7ecd704d9c331ca1e0aedc66f230114015b853b
This commit is contained in:
Родитель
1a9dd92127
Коммит
6d8d004613
|
@ -41,6 +41,7 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "nsTextNode.h"
|
||||
#include "nsIController.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
|
@ -992,15 +993,22 @@ nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent)
|
|||
mTxtCtrlElement->IsTextArea() ?
|
||||
nsIWidget::NativeKeyBindingsForMultiLineEditor :
|
||||
nsIWidget::NativeKeyBindingsForSingleLineEditor;
|
||||
|
||||
nsIWidget* widget = keyEvent->mWidget;
|
||||
// If the event is created by chrome script, the widget is nullptr.
|
||||
if (!widget) {
|
||||
widget = mFrame->GetNearestWidget();
|
||||
NS_ENSURE_TRUE(widget, NS_OK);
|
||||
}
|
||||
|
||||
if (widget->ExecuteNativeKeyBinding(nativeKeyBindingsType,
|
||||
*keyEvent, DoCommandCallback, mFrame)) {
|
||||
|
||||
// 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(keyEvent->mWidget);
|
||||
keyEvent->mWidget = widget;
|
||||
if (keyEvent->ExecuteEditCommands(nativeKeyBindingsType,
|
||||
DoCommandCallback, mFrame)) {
|
||||
aEvent->PreventDefault();
|
||||
}
|
||||
return NS_OK;
|
||||
|
|
|
@ -627,10 +627,16 @@ EditorEventListener::KeyPress(WidgetKeyboardEvent* aKeyboardEvent)
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
|
||||
bool handled = widget->ExecuteNativeKeyBinding(
|
||||
nsIWidget::NativeKeyBindingsForRichTextEditor,
|
||||
*aKeyboardEvent, DoCommandCallback, doc);
|
||||
if (handled) {
|
||||
|
||||
// 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(aKeyboardEvent->mWidget);
|
||||
aKeyboardEvent->mWidget = widget;
|
||||
if (aKeyboardEvent->ExecuteEditCommands(
|
||||
nsIWidget::NativeKeyBindingsForRichTextEditor,
|
||||
DoCommandCallback, doc)) {
|
||||
aKeyboardEvent->PreventDefault();
|
||||
}
|
||||
return NS_OK;
|
||||
|
|
|
@ -163,6 +163,7 @@ enum class TextRangeType : RawTextRangeType;
|
|||
struct TextRangeStyle;
|
||||
struct TextRange;
|
||||
|
||||
class EditCommands;
|
||||
class TextRangeArray;
|
||||
|
||||
// FontRange.h
|
||||
|
|
|
@ -559,9 +559,10 @@ PuppetWidget::ExecuteNativeKeyBinding(
|
|||
DoCommandCallback aCallback,
|
||||
void* aCallbackData)
|
||||
{
|
||||
MOZ_ASSERT(!aEvent.IsEditCommandsInitialized(aType));
|
||||
|
||||
AutoCacheNativeKeyCommands autoCache(this);
|
||||
if (!aEvent.mWidget && !mNativeKeyCommandsValid) {
|
||||
MOZ_ASSERT(!aEvent.mFlags.mIsSynthesizedForTests);
|
||||
if (!mNativeKeyCommandsValid) {
|
||||
// Abort if untrusted to avoid leaking system settings
|
||||
if (NS_WARN_IF(!aEvent.IsTrusted())) {
|
||||
return false;
|
||||
|
|
|
@ -130,6 +130,9 @@ protected:
|
|||
, mIsComposing(false)
|
||||
, mIsReserved(false)
|
||||
, mIsSynthesizedByTIP(false)
|
||||
, mEditCommandsForSingleLineEditorInitialized(false)
|
||||
, mEditCommandsForMultiLineEditorInitialized(false)
|
||||
, mEditCommandsForRichTextEditorInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -158,6 +161,9 @@ public:
|
|||
, mIsComposing(false)
|
||||
, mIsReserved(false)
|
||||
, mIsSynthesizedByTIP(false)
|
||||
, mEditCommandsForSingleLineEditorInitialized(false)
|
||||
, mEditCommandsForMultiLineEditorInitialized(false)
|
||||
, mEditCommandsForRichTextEditorInitialized(false)
|
||||
{
|
||||
// If this is a keyboard event on a plugin, it shouldn't fired on content.
|
||||
mFlags.mOnlySystemGroupDispatchInContent =
|
||||
|
@ -197,6 +203,9 @@ public:
|
|||
WidgetKeyboardEvent* result =
|
||||
new WidgetKeyboardEvent(false, mMessage, nullptr);
|
||||
result->AssignKeyEventData(*this, true);
|
||||
result->mEditCommandsForSingleLineEditor = mEditCommandsForSingleLineEditor;
|
||||
result->mEditCommandsForMultiLineEditor = mEditCommandsForMultiLineEditor;
|
||||
result->mEditCommandsForRichTextEditor = mEditCommandsForRichTextEditor;
|
||||
result->mFlags = mFlags;
|
||||
return result;
|
||||
}
|
||||
|
@ -278,6 +287,29 @@ public:
|
|||
// or an actual event from nsAppShell.
|
||||
bool mIsSynthesizedByTIP;
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* IsEditCommandsInitialized() returns true if edit commands for aType
|
||||
* was already initialized. Otherwise, false.
|
||||
*/
|
||||
bool IsEditCommandsInitialized(
|
||||
nsIWidget::NativeKeyBindingsType aType) const
|
||||
{
|
||||
return const_cast<WidgetKeyboardEvent*>(this)->
|
||||
IsEditCommandsInitializedRef(aType);
|
||||
}
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
/**
|
||||
* Execute edit commands for aType.
|
||||
*
|
||||
* @return true if the caller should do nothing anymore.
|
||||
* false, otherwise.
|
||||
*/
|
||||
bool ExecuteEditCommands(nsIWidget::NativeKeyBindingsType aType,
|
||||
nsIWidget::DoCommandCallback aCallback,
|
||||
void* aCallbackData);
|
||||
|
||||
// If the key should cause keypress events, this returns true.
|
||||
// Otherwise, false.
|
||||
bool ShouldCauseKeypressEvents() const;
|
||||
|
@ -408,6 +440,17 @@ public:
|
|||
#endif
|
||||
mInputMethodAppState = aEvent.mInputMethodAppState;
|
||||
mIsSynthesizedByTIP = aEvent.mIsSynthesizedByTIP;
|
||||
|
||||
// Don't copy mEditCommandsFor*Editor because it may require a lot of
|
||||
// memory space. For example, if the event is dispatched but grabbed by
|
||||
// a JS variable, they are not necessary anymore.
|
||||
|
||||
mEditCommandsForSingleLineEditorInitialized =
|
||||
aEvent.mEditCommandsForSingleLineEditorInitialized;
|
||||
mEditCommandsForMultiLineEditorInitialized =
|
||||
aEvent.mEditCommandsForMultiLineEditorInitialized;
|
||||
mEditCommandsForRichTextEditorInitialized =
|
||||
aEvent.mEditCommandsForRichTextEditorInitialized;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -419,6 +462,52 @@ private:
|
|||
CodeNameIndex> CodeNameIndexHashtable;
|
||||
static KeyNameIndexHashtable* sKeyNameIndexHashtable;
|
||||
static CodeNameIndexHashtable* sCodeNameIndexHashtable;
|
||||
|
||||
// mEditCommandsFor*Editor store edit commands. This should be initialized
|
||||
// with InitEditCommandsFor().
|
||||
// XXX Ideally, this should be array of Command rather than CommandInt.
|
||||
// However, ParamTraits isn't aware of enum array.
|
||||
nsTArray<CommandInt> mEditCommandsForSingleLineEditor;
|
||||
nsTArray<CommandInt> mEditCommandsForMultiLineEditor;
|
||||
nsTArray<CommandInt> mEditCommandsForRichTextEditor;
|
||||
|
||||
nsTArray<CommandInt>& EditCommandsRef(nsIWidget::NativeKeyBindingsType aType)
|
||||
{
|
||||
switch (aType) {
|
||||
case nsIWidget::NativeKeyBindingsForSingleLineEditor:
|
||||
return mEditCommandsForSingleLineEditor;
|
||||
case nsIWidget::NativeKeyBindingsForMultiLineEditor:
|
||||
return mEditCommandsForMultiLineEditor;
|
||||
case nsIWidget::NativeKeyBindingsForRichTextEditor:
|
||||
return mEditCommandsForRichTextEditor;
|
||||
default:
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
|
||||
"Invalid native key binding type");
|
||||
}
|
||||
}
|
||||
|
||||
// mEditCommandsFor*EditorInitialized are set to true when
|
||||
// InitEditCommandsFor() initializes edit commands for the type.
|
||||
bool mEditCommandsForSingleLineEditorInitialized;
|
||||
bool mEditCommandsForMultiLineEditorInitialized;
|
||||
bool mEditCommandsForRichTextEditorInitialized;
|
||||
|
||||
bool& IsEditCommandsInitializedRef(nsIWidget::NativeKeyBindingsType aType)
|
||||
{
|
||||
switch (aType) {
|
||||
case nsIWidget::NativeKeyBindingsForSingleLineEditor:
|
||||
return mEditCommandsForSingleLineEditorInitialized;
|
||||
case nsIWidget::NativeKeyBindingsForMultiLineEditor:
|
||||
return mEditCommandsForMultiLineEditorInitialized;
|
||||
case nsIWidget::NativeKeyBindingsForRichTextEditor:
|
||||
return mEditCommandsForRichTextEditorInitialized;
|
||||
default:
|
||||
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
|
||||
"Invalid native key binding type");
|
||||
}
|
||||
}
|
||||
|
||||
void InitEditCommandsFor(nsIWidget::NativeKeyBindingsType aType);
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
|
|
|
@ -603,6 +603,57 @@ WidgetKeyboardEvent::KeyNameIndexHashtable*
|
|||
WidgetKeyboardEvent::CodeNameIndexHashtable*
|
||||
WidgetKeyboardEvent::sCodeNameIndexHashtable = nullptr;
|
||||
|
||||
static void
|
||||
DoCommandCallback(Command aCommand, void* aData)
|
||||
{
|
||||
static_cast<nsTArray<CommandInt>*>(aData)->AppendElement(aCommand);
|
||||
}
|
||||
|
||||
void
|
||||
WidgetKeyboardEvent::InitEditCommandsFor(nsIWidget::NativeKeyBindingsType aType)
|
||||
{
|
||||
MOZ_ASSERT(mWidget);
|
||||
MOZ_RELEASE_ASSERT(IsTrusted());
|
||||
|
||||
bool& initialized = IsEditCommandsInitializedRef(aType);
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
nsTArray<CommandInt>& commands = EditCommandsRef(aType);
|
||||
mWidget->ExecuteNativeKeyBinding(aType, *this, DoCommandCallback, &commands);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
bool
|
||||
WidgetKeyboardEvent::ExecuteEditCommands(nsIWidget::NativeKeyBindingsType aType,
|
||||
nsIWidget::DoCommandCallback aCallback,
|
||||
void* aCallbackData)
|
||||
{
|
||||
// If the event was created without widget, e.g., created event in chrome
|
||||
// script, this shouldn't execute native key bindings.
|
||||
if (NS_WARN_IF(!mWidget)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This event should be trusted event here and we shouldn't expose native
|
||||
// key binding information to web contents with untrusted events.
|
||||
if (NS_WARN_IF(!IsTrusted())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InitEditCommandsFor(aType);
|
||||
|
||||
const nsTArray<CommandInt>& commands = EditCommandsRef(aType);
|
||||
if (commands.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CommandInt command : commands) {
|
||||
aCallback(static_cast<Command>(command), aCallbackData);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WidgetKeyboardEvent::ShouldCauseKeypressEvents() const
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче