Bug 1339543 part 2 eKeyPress event should have edit commands for all editor types when it's dispatched to a remote process r=smaug

When eKeyPress event is dispatched from TabParent to a remote process, it
should store edit command for all editor types.  Then, copied
WidgetKeyboardEvent in the remote process doesn't need to request the edit
commands when its ExecuteEditCommands() is called.

Note that this patch also changes a automated test, browser_bug1316330.js,
that uses nsIDOMWindowUtils.dispatchDOMEventViaPresShell() to dispatch
repeated keyboard events in the tab.  However, it should use synthesizeKey()
to emulate everything of native keyboard events and the API can dispatch
repeated keyboard events too.  (And the test has a bug.  It tries to wait 0.5
sec when every keydown or keypress event.  However, it fails since startTime
is never initialized.  This patch fixes this bug too.)

MozReview-Commit-ID: IYhyxqH3Ch8

--HG--
extra : rebase_source : 0db911e8e6ea7fc537bda76f4cc0f8952cc13dd2
This commit is contained in:
Masayuki Nakano 2017-05-19 17:24:20 +09:00
parent 6d8d004613
commit 730193bdb6
9 changed files with 110 additions and 76 deletions

View File

@ -691,7 +691,7 @@ child:
*/
async SynthMouseMoveEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
async RealMouseButtonEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
async RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding);
async RealKeyEvent(WidgetKeyboardEvent event);
async MouseWheelEvent(WidgetWheelEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
async RealTouchEvent(WidgetTouchEvent aEvent,
ScrollableLayerGuid aGuid,

View File

@ -1944,29 +1944,20 @@ TabChild::UpdateRepeatedKeyEventEndTime(const WidgetKeyboardEvent& aEvent)
}
mozilla::ipc::IPCResult
TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& aEvent,
const MaybeNativeKeyBinding& aBindings)
TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& aEvent)
{
if (SkipRepeatedKeyEvent(aEvent)) {
return IPC_OK();
}
AutoCacheNativeKeyCommands autoCache(mPuppetWidget);
MOZ_ASSERT(aEvent.mMessage != eKeyPress ||
aEvent.AreAllEditCommandsInitialized(),
"eKeyPress event should have native key binding information");
if (aEvent.mMessage == eKeyPress) {
// If content code called preventDefault() on a keydown event, then we don't
// want to process any following keypress events.
if (mIgnoreKeyPressEvent) {
return IPC_OK();
}
if (aBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) {
const NativeKeyBinding& bindings = aBindings;
autoCache.Cache(bindings.singleLineCommands(),
bindings.multiLineCommands(),
bindings.richTextCommands());
} else {
autoCache.CacheNoCommands();
}
// If content code called preventDefault() on a keydown event, then we don't
// want to process any following keypress events.
if (aEvent.mMessage == eKeyPress && mIgnoreKeyPressEvent) {
return IPC_OK();
}
WidgetKeyboardEvent localEvent(aEvent);

View File

@ -389,8 +389,7 @@ public:
const uint32_t& aDropEffect) override;
virtual mozilla::ipc::IPCResult
RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& aEvent,
const MaybeNativeKeyBinding& aBindings) override;
RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
virtual mozilla::ipc::IPCResult RecvMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent,
const ScrollableLayerGuid& aGuid,

View File

@ -1444,31 +1444,15 @@ TabParent::SendRealKeyEvent(WidgetKeyboardEvent& aEvent)
}
aEvent.mRefPoint += GetChildProcessOffset();
MaybeNativeKeyBinding bindings;
bindings = void_t();
if (aEvent.mMessage == eKeyPress) {
nsCOMPtr<nsIWidget> widget = GetWidget();
AutoTArray<mozilla::CommandInt, 4> singleLine;
AutoTArray<mozilla::CommandInt, 4> multiLine;
AutoTArray<mozilla::CommandInt, 4> richText;
widget->ExecuteNativeKeyBinding(
nsIWidget::NativeKeyBindingsForSingleLineEditor,
aEvent, DoCommandCallback, &singleLine);
widget->ExecuteNativeKeyBinding(
nsIWidget::NativeKeyBindingsForMultiLineEditor,
aEvent, DoCommandCallback, &multiLine);
widget->ExecuteNativeKeyBinding(
nsIWidget::NativeKeyBindingsForRichTextEditor,
aEvent, DoCommandCallback, &richText);
if (!singleLine.IsEmpty() || !multiLine.IsEmpty() || !richText.IsEmpty()) {
bindings = NativeKeyBinding(singleLine, multiLine, richText);
}
// XXX Should we do this only when input context indicates an editor having
// focus and the key event won't cause inputting text?
aEvent.InitAllEditCommands();
} else {
aEvent.PreventNativeKeyBindings();
}
return PBrowserParent::SendRealKeyEvent(aEvent, bindings);
return PBrowserParent::SendRealKeyEvent(aEvent);
}
bool

View File

@ -3,47 +3,29 @@ const URL =
"window.focus();" +
"var down = 0; var press = 0;" +
"onkeydown = function(e) {" +
" var startTime = Date.now();" +
" document.body.setAttribute('data-down', ++down);" +
" if (e.keyCode == 'D') while (Date.now() - startTime < 500) {}" +
" if (e.keyCode == KeyboardEvent.DOM_VK_D) while (Date.now() - startTime < 500) {}" +
"};" +
"onkeypress = function(e) {" +
" var startTime = Date.now();" +
" document.body.setAttribute('data-press', ++press);" +
" if (e.charCode == 'P') while (Date.now() - startTime < 500) {}" +
" if (e.charCode == 'p'.charCodeAt(0)) while (Date.now() - startTime < 500) {}" +
"};" +
"</script>";
function createKeyEvent(type, id, repeated) {
var code = id.charCodeAt(0);
return new KeyboardEvent(type, { keyCode: code, charCode: code, bubbles: true, repeat: repeated });
}
add_task(function* () {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
let browser = tab.linkedBrowser;
// Need to dispatch a DOM Event explicitly via PresShell to get KeyEvent with .repeat = true to
// be handled by EventStateManager, which then forwards the event to the child process.
var utils = EventUtils._getDOMWindowUtils(window);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keydown", "D", false), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keypress", "D", false), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keydown", "D", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keypress", "D", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keydown", "D", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keypress", "D", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keyup", "D", true), true);
EventUtils.synthesizeKey("d", { code: "KeyD", repeat: 3 });
yield ContentTask.spawn(browser, null, function* () {
is(content.document.body.getAttribute("data-down"), "2", "Correct number of events");
is(content.document.body.getAttribute("data-press"), "2", "Correct number of events");
});
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keydown", "P", false), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keypress", "P", false), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keydown", "P", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keypress", "P", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keydown", "P", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keypress", "P", true), true);
utils.dispatchDOMEventViaPresShell(browser, createKeyEvent("keyup", "P", true), true);
EventUtils.synthesizeKey("p", { code: "KeyP", repeat: 3 });
yield ContentTask.spawn(browser, null, function* () {
is(content.document.body.getAttribute("data-down"), "4", "Correct number of events");

View File

@ -342,9 +342,14 @@ PuppetWidget::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus)
MOZ_ASSERT(!mChild || mChild->mWindowType == eWindowType_popup,
"Unexpected event dispatch!");
MOZ_ASSERT(!aEvent->AsKeyboardEvent() ||
aEvent->mFlags.mIsSynthesizedForTests ||
aEvent->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
"Non-sysnthesized keyboard events should have edit commands for all types "
"before dispatched");
AutoCacheNativeKeyCommands autoCache(this);
if ((aEvent->mFlags.mIsSynthesizedForTests ||
aEvent->mFlags.mIsSuppressedOrDelayed) && !mNativeKeyCommandsValid) {
if (aEvent->mFlags.mIsSynthesizedForTests && !mNativeKeyCommandsValid) {
WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
if (keyEvent) {
mTabChild->RequestNativeKeyBindings(&autoCache, keyEvent);

View File

@ -109,6 +109,7 @@ class WidgetKeyboardEvent : public WidgetInputEvent
private:
friend class dom::PBrowserParent;
friend class dom::PBrowserChild;
friend struct IPC::ParamTraits<WidgetKeyboardEvent>;
protected:
WidgetKeyboardEvent()
@ -287,6 +288,27 @@ public:
// or an actual event from nsAppShell.
bool mIsSynthesizedByTIP;
/**
* Retrieves all edit commands from mWidget. This shouldn't be called when
* the instance is an untrusted event, doesn't have widget or in non-chrome
* process.
*/
void InitAllEditCommands();
/**
* PreventNativeKeyBindings() makes the instance to not cause any edit
* actions even if it matches with a native key binding.
*/
void PreventNativeKeyBindings()
{
mEditCommandsForSingleLineEditor.Clear();
mEditCommandsForMultiLineEditor.Clear();
mEditCommandsForRichTextEditor.Clear();
mEditCommandsForSingleLineEditorInitialized = true;
mEditCommandsForMultiLineEditorInitialized = true;
mEditCommandsForRichTextEditorInitialized = true;
}
#ifdef DEBUG
/**
* IsEditCommandsInitialized() returns true if edit commands for aType
@ -298,6 +320,17 @@ public:
return const_cast<WidgetKeyboardEvent*>(this)->
IsEditCommandsInitializedRef(aType);
}
/**
* AreAllEditCommandsInitialized() returns true if edit commands for all
* types were already initialized. Otherwise, false.
*/
bool AreAllEditCommandsInitialized() const
{
return mEditCommandsForSingleLineEditorInitialized &&
mEditCommandsForMultiLineEditorInitialized &&
mEditCommandsForRichTextEditorInitialized;
}
#endif // #ifdef DEBUG
/**

View File

@ -603,6 +603,31 @@ WidgetKeyboardEvent::KeyNameIndexHashtable*
WidgetKeyboardEvent::CodeNameIndexHashtable*
WidgetKeyboardEvent::sCodeNameIndexHashtable = nullptr;
void
WidgetKeyboardEvent::InitAllEditCommands()
{
// 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;
}
// 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;
}
MOZ_ASSERT(XRE_IsParentProcess(),
"It's too expensive to retrieve all edit commands from remote process");
MOZ_ASSERT(!AreAllEditCommandsInitialized(),
"Shouldn't be called two or more times");
InitEditCommandsFor(nsIWidget::NativeKeyBindingsForSingleLineEditor);
InitEditCommandsFor(nsIWidget::NativeKeyBindingsForMultiLineEditor);
InitEditCommandsFor(nsIWidget::NativeKeyBindingsForRichTextEditor);
}
static void
DoCommandCallback(Command aCommand, void* aData)
{

View File

@ -437,8 +437,16 @@ struct ParamTraits<mozilla::WidgetKeyboardEvent>
WriteParam(aMsg, aParam.mNativeCharactersIgnoringModifiers);
WriteParam(aMsg, aParam.mPluginTextEventString);
#endif
// An OS-specific native event might be attached in |mNativeKeyEvent|, but
// that cannot be copied across process boundaries.
WriteParam(aMsg, aParam.mEditCommandsForSingleLineEditor);
WriteParam(aMsg, aParam.mEditCommandsForMultiLineEditor);
WriteParam(aMsg, aParam.mEditCommandsForRichTextEditor);
WriteParam(aMsg, aParam.mEditCommandsForSingleLineEditorInitialized);
WriteParam(aMsg, aParam.mEditCommandsForMultiLineEditorInitialized);
WriteParam(aMsg, aParam.mEditCommandsForRichTextEditorInitialized);
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
@ -462,16 +470,23 @@ struct ParamTraits<mozilla::WidgetKeyboardEvent>
ReadParam(aMsg, aIter, &aResult->mLocation) &&
ReadParam(aMsg, aIter, &aResult->mUniqueId) &&
ReadParam(aMsg, aIter, &aResult->mIsSynthesizedByTIP) &&
ReadParam(aMsg, aIter, &inputMethodAppState)
ReadParam(aMsg, aIter, &inputMethodAppState) &&
#ifdef XP_MACOSX
&& ReadParam(aMsg, aIter, &aResult->mNativeKeyCode)
&& ReadParam(aMsg, aIter, &aResult->mNativeModifierFlags)
&& ReadParam(aMsg, aIter, &aResult->mNativeCharacters)
&& ReadParam(aMsg, aIter, &aResult->mNativeCharactersIgnoringModifiers)
&& ReadParam(aMsg, aIter, &aResult->mPluginTextEventString)
ReadParam(aMsg, aIter, &aResult->mNativeKeyCode) &&
ReadParam(aMsg, aIter, &aResult->mNativeModifierFlags) &&
ReadParam(aMsg, aIter, &aResult->mNativeCharacters) &&
ReadParam(aMsg, aIter, &aResult->mNativeCharactersIgnoringModifiers) &&
ReadParam(aMsg, aIter, &aResult->mPluginTextEventString) &&
#endif
)
{
ReadParam(aMsg, aIter, &aResult->mEditCommandsForSingleLineEditor) &&
ReadParam(aMsg, aIter, &aResult->mEditCommandsForMultiLineEditor) &&
ReadParam(aMsg, aIter, &aResult->mEditCommandsForRichTextEditor) &&
ReadParam(aMsg, aIter,
&aResult->mEditCommandsForSingleLineEditorInitialized) &&
ReadParam(aMsg, aIter,
&aResult->mEditCommandsForMultiLineEditorInitialized) &&
ReadParam(aMsg, aIter,
&aResult->mEditCommandsForRichTextEditorInitialized)) {
aResult->mKeyNameIndex = static_cast<mozilla::KeyNameIndex>(keyNameIndex);
aResult->mCodeNameIndex =
static_cast<mozilla::CodeNameIndex>(codeNameIndex);