зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1343955 - part 4: Implement static API to synthesize keyboard events into FuzzingFunctions r=smaug
Synthesizing keyboard events is dangerous and such API is requested only by fuzzing test. So, we should add it into FuzzingFunctions which is built only when |ac_add_options --enable-fuzzing| is specified and enabled by the pref. This patch implements the API as synthesizing keyboard events in the focused widget and the synthesized events are propagated as native key events except APZ (because keyboard events are synthesized only in the process). This behavior allows to test including any default action handlers such as EventStateManager and setting WidgetGUIEvent::mWidget since some C++ handler checks if it's nullptr. Differential Revision: https://phabricator.services.mozilla.com/D5516 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
e90e04f506
Коммит
339308fcab
|
@ -8,8 +8,14 @@
|
|||
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "mozIDOMWindow.h"
|
||||
#include "mozilla/dom/KeyboardEvent.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/TextInputProcessor.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIAccessibilityService.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "xpcAccessibilityService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -42,5 +48,317 @@ FuzzingFunctions::EnableAccessibility(const GlobalObject&,
|
|||
}
|
||||
}
|
||||
|
||||
struct ModifierKey final
|
||||
{
|
||||
Modifier mModifier;
|
||||
KeyNameIndex mKeyNameIndex;
|
||||
bool mLockable;
|
||||
|
||||
ModifierKey(Modifier aModifier,
|
||||
KeyNameIndex aKeyNameIndex,
|
||||
bool aLockable)
|
||||
: mModifier(aModifier)
|
||||
, mKeyNameIndex(aKeyNameIndex)
|
||||
, mLockable(aLockable)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static const ModifierKey kModifierKeys[] = {
|
||||
ModifierKey(MODIFIER_ALT, KEY_NAME_INDEX_Alt, false),
|
||||
ModifierKey(MODIFIER_ALTGRAPH, KEY_NAME_INDEX_AltGraph, false),
|
||||
ModifierKey(MODIFIER_CONTROL, KEY_NAME_INDEX_Control, false),
|
||||
ModifierKey(MODIFIER_FN, KEY_NAME_INDEX_Fn, false),
|
||||
ModifierKey(MODIFIER_META, KEY_NAME_INDEX_Meta, false),
|
||||
ModifierKey(MODIFIER_OS, KEY_NAME_INDEX_OS, false),
|
||||
ModifierKey(MODIFIER_SHIFT, KEY_NAME_INDEX_Shift, false),
|
||||
ModifierKey(MODIFIER_SYMBOL, KEY_NAME_INDEX_Symbol, false),
|
||||
ModifierKey(MODIFIER_CAPSLOCK, KEY_NAME_INDEX_CapsLock, true),
|
||||
ModifierKey(MODIFIER_FNLOCK, KEY_NAME_INDEX_FnLock, true),
|
||||
ModifierKey(MODIFIER_NUMLOCK, KEY_NAME_INDEX_NumLock, true),
|
||||
ModifierKey(MODIFIER_SCROLLLOCK, KEY_NAME_INDEX_ScrollLock, true),
|
||||
ModifierKey(MODIFIER_SYMBOLLOCK, KEY_NAME_INDEX_SymbolLock, true),
|
||||
};
|
||||
|
||||
/* static */ Modifiers
|
||||
FuzzingFunctions::ActivateModifiers(TextInputProcessor* aTextInputProcessor,
|
||||
Modifiers aModifiers,
|
||||
nsIWidget* aWidget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aTextInputProcessor);
|
||||
|
||||
if (aModifiers == MODIFIER_NONE) {
|
||||
return MODIFIER_NONE;
|
||||
}
|
||||
|
||||
// We don't want to dispatch modifier key event from here. In strictly
|
||||
// speaking, all necessary modifiers should be activated with dispatching
|
||||
// each modifier key event. However, we cannot keep storing
|
||||
// TextInputProcessor instance for multiple SynthesizeKeyboardEvents() calls.
|
||||
// So, if some callers need to emulate modifier key events, they should do
|
||||
// it by themselves.
|
||||
uint32_t flags =
|
||||
nsITextInputProcessor::KEY_NON_PRINTABLE_KEY |
|
||||
nsITextInputProcessor::KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT;
|
||||
|
||||
Modifiers activatedModifiers = MODIFIER_NONE;
|
||||
Modifiers activeModifiers = aTextInputProcessor->GetActiveModifiers();
|
||||
for (const ModifierKey& kModifierKey : kModifierKeys) {
|
||||
if (!(kModifierKey.mModifier & aModifiers)) {
|
||||
continue; // Not requested modifier.
|
||||
}
|
||||
if (kModifierKey.mModifier & activeModifiers) {
|
||||
continue; // Already active, do nothing.
|
||||
}
|
||||
WidgetKeyboardEvent event(true, eVoidEvent, aWidget);
|
||||
// mKeyCode will be computed by TextInputProcessor automatically.
|
||||
event.mKeyNameIndex = kModifierKey.mKeyNameIndex;
|
||||
aRv = aTextInputProcessor->Keydown(event, flags);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return activatedModifiers;
|
||||
}
|
||||
if (kModifierKey.mLockable) {
|
||||
aRv = aTextInputProcessor->Keyup(event, flags);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return activatedModifiers;
|
||||
}
|
||||
}
|
||||
activatedModifiers |= kModifierKey.mModifier;
|
||||
}
|
||||
return activatedModifiers;
|
||||
}
|
||||
|
||||
/* static */ Modifiers
|
||||
FuzzingFunctions::InactivateModifiers(TextInputProcessor* aTextInputProcessor,
|
||||
Modifiers aModifiers,
|
||||
nsIWidget* aWidget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aTextInputProcessor);
|
||||
|
||||
if (aModifiers == MODIFIER_NONE) {
|
||||
return MODIFIER_NONE;
|
||||
}
|
||||
|
||||
// We don't want to dispatch modifier key event from here. In strictly
|
||||
// speaking, all necessary modifiers should be activated with dispatching
|
||||
// each modifier key event. However, we cannot keep storing
|
||||
// TextInputProcessor instance for multiple SynthesizeKeyboardEvents() calls.
|
||||
// So, if some callers need to emulate modifier key events, they should do
|
||||
// it by themselves.
|
||||
uint32_t flags =
|
||||
nsITextInputProcessor::KEY_NON_PRINTABLE_KEY |
|
||||
nsITextInputProcessor::KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT;
|
||||
|
||||
Modifiers inactivatedModifiers = MODIFIER_NONE;
|
||||
Modifiers activeModifiers = aTextInputProcessor->GetActiveModifiers();
|
||||
for (const ModifierKey& kModifierKey : kModifierKeys) {
|
||||
if (!(kModifierKey.mModifier & aModifiers)) {
|
||||
continue; // Not requested modifier.
|
||||
}
|
||||
if (kModifierKey.mModifier & activeModifiers) {
|
||||
continue; // Already active, do nothing.
|
||||
}
|
||||
WidgetKeyboardEvent event(true, eVoidEvent, aWidget);
|
||||
// mKeyCode will be computed by TextInputProcessor automatically.
|
||||
event.mKeyNameIndex = kModifierKey.mKeyNameIndex;
|
||||
if (kModifierKey.mLockable) {
|
||||
aRv = aTextInputProcessor->Keydown(event, flags);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return inactivatedModifiers;
|
||||
}
|
||||
}
|
||||
aRv = aTextInputProcessor->Keyup(event, flags);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return inactivatedModifiers;
|
||||
}
|
||||
inactivatedModifiers |= kModifierKey.mModifier;
|
||||
}
|
||||
return inactivatedModifiers;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FuzzingFunctions::SynthesizeKeyboardEvents(const GlobalObject&,
|
||||
const nsAString& aKeyValue,
|
||||
const KeyboardEventInit& aDict,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Prepare keyboard event to synthesize first.
|
||||
uint32_t flags = 0;
|
||||
// Don't modify the given dictionary since caller may want to modify
|
||||
// a part of it and call this with it again.
|
||||
WidgetKeyboardEvent event(true, eVoidEvent, nullptr);
|
||||
event.mKeyCode = aDict.mKeyCode;
|
||||
event.mCharCode = 0; // Ignore.
|
||||
event.mKeyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue);
|
||||
if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
|
||||
event.mKeyValue = aKeyValue;
|
||||
}
|
||||
// code value should be empty string or one of valid code value.
|
||||
event.mCodeNameIndex =
|
||||
aDict.mCode.IsEmpty() ? CODE_NAME_INDEX_UNKNOWN :
|
||||
WidgetKeyboardEvent::GetCodeNameIndex(aDict.mCode);
|
||||
if (NS_WARN_IF(event.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) {
|
||||
// Meaning that the code value is specified but it's not a known code
|
||||
// value. TextInputProcessor does not support synthesizing keyboard
|
||||
// events with unknown code value. So, returns error now.
|
||||
aRv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return;
|
||||
}
|
||||
event.mLocation = aDict.mLocation;
|
||||
event.mIsRepeat = aDict.mRepeat;
|
||||
|
||||
#define SET_MODIFIER(aName, aValue) \
|
||||
if (aDict.m##aName) { \
|
||||
event.mModifiers |= aValue; \
|
||||
} \
|
||||
|
||||
SET_MODIFIER(CtrlKey, MODIFIER_CONTROL)
|
||||
SET_MODIFIER(ShiftKey, MODIFIER_SHIFT)
|
||||
SET_MODIFIER(AltKey, MODIFIER_ALT)
|
||||
SET_MODIFIER(MetaKey, MODIFIER_META)
|
||||
SET_MODIFIER(ModifierAltGraph, MODIFIER_ALTGRAPH)
|
||||
SET_MODIFIER(ModifierCapsLock, MODIFIER_CAPSLOCK)
|
||||
SET_MODIFIER(ModifierFn, MODIFIER_FN)
|
||||
SET_MODIFIER(ModifierFnLock, MODIFIER_FNLOCK)
|
||||
SET_MODIFIER(ModifierNumLock, MODIFIER_NUMLOCK)
|
||||
SET_MODIFIER(ModifierOS, MODIFIER_OS)
|
||||
SET_MODIFIER(ModifierScrollLock, MODIFIER_SCROLLLOCK)
|
||||
SET_MODIFIER(ModifierSymbol, MODIFIER_SYMBOL)
|
||||
SET_MODIFIER(ModifierSymbolLock, MODIFIER_SYMBOLLOCK)
|
||||
|
||||
#undef SET_MODIFIER
|
||||
|
||||
// If we could distinguish whether the caller specified 0 explicitly or
|
||||
// not, we would skip computing the key location when it's specified
|
||||
// explicitly. However, this caller probably won't test tricky keyboard
|
||||
// events, so, it must be enough even though caller cannot set location
|
||||
// to 0.
|
||||
Maybe<uint32_t> maybeNonStandardLocation;
|
||||
if (!event.mLocation) {
|
||||
maybeNonStandardLocation = mozilla::Some(event.mLocation);
|
||||
}
|
||||
|
||||
// If the key is a printable key and |.code| and/or |.keyCode| value is
|
||||
// not specified as non-zero explicitly, let's assume that the caller
|
||||
// emulates US-English keyboard's behavior (because otherwise, caller
|
||||
// should set both values.
|
||||
if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
|
||||
if (event.mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
|
||||
event.mCodeNameIndex =
|
||||
TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
|
||||
event.mKeyValue, maybeNonStandardLocation);
|
||||
MOZ_ASSERT(event.mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
|
||||
}
|
||||
if (!event.mKeyCode) {
|
||||
event.mKeyCode =
|
||||
TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
|
||||
event.mKeyValue, maybeNonStandardLocation);
|
||||
if (!event.mKeyCode) {
|
||||
// Prevent to recompute keyCode in TextInputProcessor.
|
||||
flags |= nsITextInputProcessor::KEY_KEEP_KEYCODE_ZERO;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the key is a non-printable key, we can compute |.code| value of
|
||||
// usual keyboard of the platform. Note that |.keyCode| value for
|
||||
// non-printable key will be computed by TextInputProcessor. So, we need
|
||||
// to take care only |.code| value here.
|
||||
else if (event.mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
|
||||
event.mCodeNameIndex =
|
||||
WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(
|
||||
event.mKeyNameIndex, maybeNonStandardLocation);
|
||||
}
|
||||
|
||||
// Synthesize keyboard events on focused widget.
|
||||
nsFocusManager* focusManager = nsFocusManager::GetFocusManager();
|
||||
if (NS_WARN_IF(!focusManager)) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsPIDOMWindowOuter* activeWindow = focusManager->GetActiveWindow();
|
||||
if (NS_WARN_IF(!activeWindow)) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocShell* docShell = activeWindow->GetDocShell();
|
||||
if (NS_WARN_IF(!docShell)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<nsPresContext> presContext;
|
||||
docShell->GetPresContext(getter_AddRefs(presContext));
|
||||
if (NS_WARN_IF(!presContext)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
event.mWidget = presContext->GetRootWidget();
|
||||
if (NS_WARN_IF(!event.mWidget)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> activeWindowInner =
|
||||
activeWindow->EnsureInnerWindow();
|
||||
if (NS_WARN_IF(!activeWindowInner)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<TextInputProcessor> textInputProcessor = new TextInputProcessor();
|
||||
bool beganInputTransaction = false;
|
||||
aRv = textInputProcessor->BeginInputTransactionForFuzzing(
|
||||
activeWindowInner,
|
||||
nullptr,
|
||||
&beganInputTransaction);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
if (NS_WARN_IF(!beganInputTransaction)) {
|
||||
// This is possible if a keyboard event listener or something tries to
|
||||
// dispatch next keyboard events during dispatching a keyboard event via
|
||||
// TextInputProcessor.
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// First, activate necessary modifiers.
|
||||
Modifiers activatedModifiers =
|
||||
ActivateModifiers(textInputProcessor, event.mModifiers, event.mWidget, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Then, dispatch keydown and keypress.
|
||||
aRv = textInputProcessor->Keydown(event, flags);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Then, dispatch keyup.
|
||||
aRv = textInputProcessor->Keyup(event, flags);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, inactivate some modifiers which are activated by this call.
|
||||
InactivateModifiers(textInputProcessor, activatedModifiers, event.mWidget,
|
||||
aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Unfortunately, we cannot keep storing modifier state in the
|
||||
// TextInputProcessor since if we store it into a static variable,
|
||||
// we need to take care of resetting it when the caller wants.
|
||||
// However, that makes API more complicated. So, until they need
|
||||
// to want
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,13 +7,19 @@
|
|||
#ifndef mozilla_dom_FuzzingFunctions
|
||||
#define mozilla_dom_FuzzingFunctions
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
|
||||
class nsIWidget;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
class TextInputProcessor;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
struct KeyboardEventInit;
|
||||
|
||||
class FuzzingFunctions final
|
||||
{
|
||||
|
@ -26,6 +32,44 @@ public:
|
|||
|
||||
static void
|
||||
EnableAccessibility(const GlobalObject&, ErrorResult& aRv);
|
||||
|
||||
static void
|
||||
SynthesizeKeyboardEvents(const GlobalObject& aGlobalObject,
|
||||
const nsAString& aKeyValue,
|
||||
const KeyboardEventInit& aKeyboardEvent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
/**
|
||||
* ActivateModifiers() activates aModifiers in the TextInputProcessor.
|
||||
*
|
||||
* @param aTextInputProcessor The TIP whose modifier state you want to change.
|
||||
* @param aModifiers Modifiers which you want to activate.
|
||||
* @param aWidget The widget which should be set to
|
||||
* WidgetKeyboardEvent.
|
||||
* @param aRv Returns error if TextInputProcessor fails to
|
||||
* dispatch a modifier key event.
|
||||
* @return Modifiers which are activated by the call.
|
||||
*/
|
||||
static Modifiers
|
||||
ActivateModifiers(TextInputProcessor* aTextInputProcessor,
|
||||
Modifiers aModifiers, nsIWidget* aWidget, ErrorResult& aRv);
|
||||
|
||||
/**
|
||||
* InactivateModifiers() inactivates aModifiers in the TextInputProcessor.
|
||||
*
|
||||
* @param aTextInputProcessor The TIP whose modifier state you want to change.
|
||||
* @param aModifiers Modifiers which you want to inactivate.
|
||||
* @param aWidget The widget which should be set to
|
||||
* WidgetKeyboardEvent.
|
||||
* @param aRv Returns error if TextInputProcessor fails to
|
||||
* dispatch a modifier key event.
|
||||
* @return Modifiers which are inactivated by the call.
|
||||
*/
|
||||
static Modifiers
|
||||
InactivateModifiers(TextInputProcessor* aTextInputProcessor,
|
||||
Modifiers aModifiers, nsIWidget* aWidget,
|
||||
ErrorResult& aRv);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -363,6 +363,16 @@ TextInputProcessor::BeginInputTransactionForTests(
|
|||
return BeginInputTransactionInternal(aWindow, callback, true, *aSucceeded);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::BeginInputTransactionForFuzzing(
|
||||
nsPIDOMWindowInner* aWindow,
|
||||
nsITextInputProcessorCallback* aCallback,
|
||||
bool* aSucceeded)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
|
||||
return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::BeginInputTransactionInternal(
|
||||
mozIDOMWindow* aWindow,
|
||||
|
@ -1076,7 +1086,30 @@ TextInputProcessor::PrepareKeyboardEventToDispatch(
|
|||
aKeyboardEvent.mKeyNameIndex);
|
||||
}
|
||||
|
||||
aKeyboardEvent.mIsSynthesizedByTIP = (mForTests)? false : true;
|
||||
aKeyboardEvent.mIsSynthesizedByTIP = !mForTests;
|
||||
|
||||
// When this emulates real input only in content process, we need to
|
||||
// initialize edit commands with the main process's widget via PuppetWidget
|
||||
// because they are initialized by TabParent before content process treats
|
||||
// them.
|
||||
if (aKeyboardEvent.mIsSynthesizedByTIP && !XRE_IsParentProcess()) {
|
||||
// Note that retrieving edit commands from content process is expensive.
|
||||
// Let's skip it when the keyboard event is inputting text.
|
||||
if (!aKeyboardEvent.IsInputtingText()) {
|
||||
// FYI: WidgetKeyboardEvent::InitAllEditCommands() isn't available here
|
||||
// since it checks whether it's called in the main process to
|
||||
// avoid performance issues so that we need to initialize each
|
||||
// command manually here.
|
||||
aKeyboardEvent.InitEditCommandsFor(
|
||||
nsIWidget::NativeKeyBindingsForSingleLineEditor);
|
||||
aKeyboardEvent.InitEditCommandsFor(
|
||||
nsIWidget::NativeKeyBindingsForMultiLineEditor);
|
||||
aKeyboardEvent.InitEditCommandsFor(
|
||||
nsIWidget::NativeKeyBindingsForRichTextEditor);
|
||||
} else {
|
||||
aKeyboardEvent.PreventNativeKeyBindings();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1103,6 +1136,16 @@ TextInputProcessor::Keydown(Event* aDOMKeyEvent,
|
|||
return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::Keydown(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
uint32_t* aConsumedFlags)
|
||||
{
|
||||
uint32_t consumedFlags = 0;
|
||||
return KeydownInternal(aKeyboardEvent, aKeyFlags, true,
|
||||
aConsumedFlags ? *aConsumedFlags : consumedFlags);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
|
@ -1196,6 +1239,16 @@ TextInputProcessor::Keyup(Event* aDOMKeyEvent,
|
|||
return KeyupInternal(*originalKeyEvent, aKeyFlags, *aDoDefault);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::Keyup(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
bool* aDoDefault)
|
||||
{
|
||||
bool doDefault = false;
|
||||
return KeyupInternal(aKeyboardEvent, aKeyFlags,
|
||||
aDoDefault ? *aDoDefault : doDefault);
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextInputProcessor::KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
|
@ -1250,13 +1303,8 @@ TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(aActive, "aActive must not be null");
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
if (!mModifierKeyDataArray) {
|
||||
*aActive = false;
|
||||
return NS_OK;
|
||||
}
|
||||
Modifiers activeModifiers = mModifierKeyDataArray->GetActiveModifiers();
|
||||
Modifiers modifier = WidgetInputEvent::GetModifier(aModifierKeyName);
|
||||
*aActive = ((activeModifiers & modifier) != 0);
|
||||
*aActive = ((GetActiveModifiers() & modifier) != 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "nsITextInputProcessorCallback.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
@ -50,6 +52,41 @@ public:
|
|||
uint32_t aIndexOfKeypress,
|
||||
void* aData) override;
|
||||
|
||||
/**
|
||||
* TextInputProcessor manages modifier key state. E.g., when it dispatches
|
||||
* a modifier keydown event, activates proper modifier state and when it
|
||||
* dispatches a modifier keyup event, inactivates proper modifier state.
|
||||
* This returns all active modifiers in the instance.
|
||||
*/
|
||||
Modifiers GetActiveModifiers() const
|
||||
{
|
||||
return mModifierKeyDataArray ?
|
||||
mModifierKeyDataArray->GetActiveModifiers() : MODIFIER_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* This begins transaction for fuzzing. This must be called only by
|
||||
* FuzzingFunctions since this skips the permission check.
|
||||
* See explanation of nsITextInputProcessor::BeginInputTransaction() for
|
||||
* the detail.
|
||||
*/
|
||||
nsresult
|
||||
BeginInputTransactionForFuzzing(nsPIDOMWindowInner* aWindow,
|
||||
nsITextInputProcessorCallback* aCallback,
|
||||
bool* aSucceeded);
|
||||
|
||||
/**
|
||||
* The following Keydown() and KeyUp() are same as nsITextInputProcessor's
|
||||
* same name methods except the type of event class. See explanation in
|
||||
* nsITextInputProcessor for the detail.
|
||||
*/
|
||||
nsresult Keydown(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
uint32_t* aConsumedFlags = nullptr);
|
||||
nsresult Keyup(const WidgetKeyboardEvent& aKeyboardEvent,
|
||||
uint32_t aKeyFlags,
|
||||
bool* aDoDefault = nullptr);
|
||||
|
||||
/**
|
||||
* GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout() returns CodeNameIndex
|
||||
* of a printable key which is in usual keyboard of the platform and when
|
||||
|
@ -197,11 +234,6 @@ private:
|
|||
virtual ~ModifierKeyDataArray() { }
|
||||
};
|
||||
|
||||
Modifiers GetActiveModifiers() const
|
||||
{
|
||||
return mModifierKeyDataArray ?
|
||||
mModifierKeyDataArray->GetActiveModifiers() : 0;
|
||||
}
|
||||
void EnsureModifierKeyDataArray()
|
||||
{
|
||||
if (mModifierKeyDataArray) {
|
||||
|
|
|
@ -27,4 +27,86 @@ interface FuzzingFunctions {
|
|||
*/
|
||||
[Throws]
|
||||
static void enableAccessibility();
|
||||
|
||||
/**
|
||||
* synthesizeKeyboardEvents() synthesizes a set of "keydown",
|
||||
* "keypress" (only when it's necessary) and "keyup" events on focused
|
||||
* widget. This is currently not aware of APZ since this dispatches the
|
||||
* events into focused PresShell in current process. I.e., dispatched
|
||||
* events won't be handled by some default action handlers which are only
|
||||
* in the main process. Note that this does not allow to synthesize
|
||||
* keyboard events if this is called from a keyboard event or composition
|
||||
* event listener.
|
||||
*
|
||||
* @param aKeyValue If you want to synthesize non-printable key
|
||||
* events, you need to set one of key values
|
||||
* defined by "UI Events KeyboardEvent key Values".
|
||||
* You can check our current support values in
|
||||
* dom/events/KeyNameList.h
|
||||
* If you want to synthesize printable key events,
|
||||
* you can set any string value including empty
|
||||
* string.
|
||||
* Note that |key| value in aDictionary is always
|
||||
* ignored.
|
||||
* @param aDictionary If you want to synthesize simple key press
|
||||
* without any modifiers, you can omit this.
|
||||
* Otherwise, specify this with proper values.
|
||||
* If |code| is omitted or empty string, this
|
||||
* guesses proper code value in US-English
|
||||
* keyboard. Otherwise, the value must be empty
|
||||
* string or known code value defined by "UI Events
|
||||
* KeyboardEvent code Values". You can check our
|
||||
* current support values in
|
||||
* dom/events/PhysicalKeyCodeNameList.h.
|
||||
* If |keyCode| is omitted or 0, this guesses
|
||||
* proper keyCode value in US-English keyboard.
|
||||
* If |location| is omitted or 0, this assumes
|
||||
* that left modifier key is pressed if aKeyValue
|
||||
* is one of such modifier keys.
|
||||
* |key|, |isComposing|, |charCode| and |which|
|
||||
* are always ignored.
|
||||
* Modifier states like |shiftKey|, |altKey|,
|
||||
* |modifierAltGraph|, |modifierCapsLock| and
|
||||
* |modifierNumLock| are not adjusted for
|
||||
* aKeyValue. Please specify them manually if
|
||||
* necessary.
|
||||
* Note that this API does not allow to dispatch
|
||||
* known key events with empty |code| value and
|
||||
* 0 |keyCode| value since it's unsual situation
|
||||
* especially 0 |keyCode| value with known key.
|
||||
* Note that when you specify only one of |code|
|
||||
* and |keyCode| value, the other will be guessed
|
||||
* from US-English keyboard layout. So, if you
|
||||
* want to emulate key press with another keyboard
|
||||
* layout, you should specify both values.
|
||||
*
|
||||
* For example:
|
||||
* // Synthesize "Tab" key events.
|
||||
* synthesizeKeyboardEvents("Tab");
|
||||
* // Synthesize Shift + Tab key events.
|
||||
* synthesizeKeyboardEvents("Tab", { shiftKey: true });
|
||||
* // Synthesize Control + A key events.
|
||||
* synthesizeKeyboardEvents("a", { controlKey: true });
|
||||
* // Synthesize Control + Shift + A key events.
|
||||
* synthesizeKeyboardEvents("A", { controlKey: true,
|
||||
* shitKey: true });
|
||||
* // Synthesize "Enter" key on numpad.
|
||||
* synthesizeKeyboardEvents("Enter", { code: "NumpadEnter" });
|
||||
* // Synthesize right "Shift" key.
|
||||
* synthesizeKeyboardEvents("Shift", { code: "ShiftRight" });
|
||||
* // Synthesize "1" on numpad.
|
||||
* synthesizeKeyboardEvents("1", { code: "Numpad1",
|
||||
* modifierNumLock: true });
|
||||
* // Synthesize "End" on numpad.
|
||||
* synthesizeKeyboardEvents("End", { code: "Numpad1" });
|
||||
* // Synthesize "%" key of US-English keyboard layout.
|
||||
* synthesizeKeyboardEvents("%", { shiftKey: true });
|
||||
* // Synthesize "*" key of Japanese keyboard layout.
|
||||
* synthesizeKeyboardEvents("*", { code: "Quote",
|
||||
* shiftKey: true,
|
||||
* keyCode: KeyboardEvent.DOM_VK_COLON });
|
||||
*/
|
||||
[Throws]
|
||||
static void synthesizeKeyboardEvents(DOMString aKeyValue,
|
||||
optional KeyboardEventInit aDictionary);
|
||||
};
|
||||
|
|
|
@ -600,6 +600,13 @@ TextEventDispatcher::DispatchKeyboardEventInternal(
|
|||
WidgetKeyboardEvent keyEvent(true, aMessage, mWidget);
|
||||
InitEvent(keyEvent);
|
||||
keyEvent.AssignKeyEventData(aKeyboardEvent, false);
|
||||
// Command arrays are not duplicated by AssignKeyEventData() due to
|
||||
// both performance and footprint reasons. So, when TextInputProcessor
|
||||
// emulates real text input, the arrays may be initialized all commands
|
||||
// already. If so, we need to duplicate the arrays here.
|
||||
if (keyEvent.mIsSynthesizedByTIP) {
|
||||
keyEvent.AssignCommands(aKeyboardEvent);
|
||||
}
|
||||
|
||||
if (aStatus == nsEventStatus_eConsumeNoDefault) {
|
||||
// If the key event should be dispatched as consumed event, marking it here.
|
||||
|
|
|
@ -401,12 +401,10 @@ private:
|
|||
// tests because this instance won't dispatch the events via the parent
|
||||
// process again.
|
||||
eSameProcessSyncTestInputTransaction,
|
||||
// Input transaction for Others (must be IME on B2G). Events are fired
|
||||
// synchronously because TextInputProcessor which is the only user of
|
||||
// this input transaction type supports only keyboard apps on B2G.
|
||||
// Keyboard apps on B2G doesn't want to dispatch keyboard events to
|
||||
// chrome process. Therefore, this should dispatch key events only in
|
||||
// the current process.
|
||||
// Input transaction for others (currently, only FuzzingFunctions).
|
||||
// Events are fired synchronously in the process.
|
||||
// XXX Should we make this async for testing default action handlers in
|
||||
// the main process?
|
||||
eSameProcessSyncInputTransaction
|
||||
};
|
||||
|
||||
|
|
|
@ -694,6 +694,34 @@ public:
|
|||
aEvent.mEditCommandsForRichTextEditorInitialized;
|
||||
}
|
||||
|
||||
void AssignCommands(const WidgetKeyboardEvent& aEvent)
|
||||
{
|
||||
mEditCommandsForSingleLineEditorInitialized =
|
||||
aEvent.mEditCommandsForSingleLineEditorInitialized;
|
||||
if (mEditCommandsForSingleLineEditorInitialized) {
|
||||
mEditCommandsForSingleLineEditor =
|
||||
aEvent.mEditCommandsForSingleLineEditor;
|
||||
} else {
|
||||
mEditCommandsForSingleLineEditor.Clear();
|
||||
}
|
||||
mEditCommandsForMultiLineEditorInitialized =
|
||||
aEvent.mEditCommandsForMultiLineEditorInitialized;
|
||||
if (mEditCommandsForMultiLineEditorInitialized) {
|
||||
mEditCommandsForMultiLineEditor =
|
||||
aEvent.mEditCommandsForMultiLineEditor;
|
||||
} else {
|
||||
mEditCommandsForMultiLineEditor.Clear();
|
||||
}
|
||||
mEditCommandsForRichTextEditorInitialized =
|
||||
aEvent.mEditCommandsForRichTextEditorInitialized;
|
||||
if (mEditCommandsForRichTextEditorInitialized) {
|
||||
mEditCommandsForRichTextEditor =
|
||||
aEvent.mEditCommandsForRichTextEditor;
|
||||
} else {
|
||||
mEditCommandsForRichTextEditor.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const char16_t* const kKeyNames[];
|
||||
static const char16_t* const kCodeNames[];
|
||||
|
|
Загрузка…
Ссылка в новой задаче