mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1093153: introduce 'BrowserTestUtils#synthesizeKey', 'BrowserTestUtils#synthesizeComposition' and 'BrowserTestUtils#synthesizeCompositionChange' to allow mochitests to remotely invoke EventUtils' text composition utilities. Changes to EventUtils include 1) removed dependency on the 'navigator' object when 'nsIXULRuntime' is available and 2) make '_getKeyboardEvent' more robust when used in frame scripts. r=Enn
This commit is contained in:
parent
011ff7762d
commit
5378afd0c0
@ -38,6 +38,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||
Cu.permitCPOWsInScope(this);
|
||||
|
||||
var gSendCharCount = 0;
|
||||
var gSynthesizeKeyCount = 0;
|
||||
var gSynthesizeCompositionCount = 0;
|
||||
var gSynthesizeCompositionChangeCount = 0;
|
||||
|
||||
this.BrowserTestUtils = {
|
||||
/**
|
||||
@ -827,7 +830,7 @@ this.BrowserTestUtils = {
|
||||
|
||||
/**
|
||||
* Version of EventUtils' `sendChar` function; it will synthesize a keypress
|
||||
* event in a child process and returns a Promise that will result when the
|
||||
* event in a child process and returns a Promise that will resolve when the
|
||||
* event was fired. Instead of a Window, a Browser object is required to be
|
||||
* passed to this function.
|
||||
*
|
||||
@ -849,7 +852,7 @@ this.BrowserTestUtils = {
|
||||
return;
|
||||
|
||||
mm.removeMessageListener("Test:SendCharDone", charMsg);
|
||||
resolve(message.data.sendCharResult);
|
||||
resolve(message.data.result);
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage("Test:SendChar", {
|
||||
@ -859,6 +862,99 @@ this.BrowserTestUtils = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Version of EventUtils' `synthesizeKey` function; it will synthesize a key
|
||||
* event in a child process and returns a Promise that will resolve when the
|
||||
* event was fired. Instead of a Window, a Browser object is required to be
|
||||
* passed to this function.
|
||||
*
|
||||
* @param {String} key
|
||||
* See the documentation available for EventUtils#synthesizeKey.
|
||||
* @param {Object} event
|
||||
* See the documentation available for EventUtils#synthesizeKey.
|
||||
* @param {Browser} browser
|
||||
* Browser element, must not be null.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
synthesizeKey(key, event, browser) {
|
||||
return new Promise(resolve => {
|
||||
let seq = ++gSynthesizeKeyCount;
|
||||
let mm = browser.messageManager;
|
||||
|
||||
mm.addMessageListener("Test:SynthesizeKeyDone", function keyMsg(message) {
|
||||
if (message.data.seq != seq)
|
||||
return;
|
||||
|
||||
mm.removeMessageListener("Test:SynthesizeKeyDone", keyMsg);
|
||||
resolve();
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage("Test:SynthesizeKey", { key, event, seq });
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Version of EventUtils' `synthesizeComposition` function; it will synthesize
|
||||
* a composition event in a child process and returns a Promise that will
|
||||
* resolve when the event was fired. Instead of a Window, a Browser object is
|
||||
* required to be passed to this function.
|
||||
*
|
||||
* @param {Object} event
|
||||
* See the documentation available for EventUtils#synthesizeComposition.
|
||||
* @param {Browser} browser
|
||||
* Browser element, must not be null.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @resolves False if the composition event could not be synthesized.
|
||||
*/
|
||||
synthesizeComposition(event, browser) {
|
||||
return new Promise(resolve => {
|
||||
let seq = ++gSynthesizeCompositionCount;
|
||||
let mm = browser.messageManager;
|
||||
|
||||
mm.addMessageListener("Test:SynthesizeCompositionDone", function compMsg(message) {
|
||||
if (message.data.seq != seq)
|
||||
return;
|
||||
|
||||
mm.removeMessageListener("Test:SynthesizeCompositionDone", compMsg);
|
||||
resolve(message.data.result);
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage("Test:SynthesizeComposition", { event, seq });
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Version of EventUtils' `synthesizeCompositionChange` function; it will
|
||||
* synthesize a compositionchange event in a child process and returns a
|
||||
* Promise that will resolve when the event was fired. Instead of a Window, a
|
||||
* Browser object is required to be passed to this function.
|
||||
*
|
||||
* @param {Object} event
|
||||
* See the documentation available for EventUtils#synthesizeCompositionChange.
|
||||
* @param {Browser} browser
|
||||
* Browser element, must not be null.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
synthesizeCompositionChange(event, browser) {
|
||||
return new Promise(resolve => {
|
||||
let seq = ++gSynthesizeCompositionChangeCount;
|
||||
let mm = browser.messageManager;
|
||||
|
||||
mm.addMessageListener("Test:SynthesizeCompositionChangeDone", function compMsg(message) {
|
||||
if (message.data.seq != seq)
|
||||
return;
|
||||
|
||||
mm.removeMessageListener("Test:SynthesizeCompositionChangeDone", compMsg);
|
||||
resolve();
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage("Test:SynthesizeCompositionChange", { event, seq });
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Will poll a condition function until it returns true.
|
||||
*
|
||||
|
@ -58,8 +58,20 @@ addMessageListener("Test:SynthesizeMouse", (message) => {
|
||||
|
||||
addMessageListener("Test:SendChar", message => {
|
||||
let result = EventUtils.sendChar(message.data.char, content);
|
||||
sendAsyncMessage("Test:SendCharDone", {
|
||||
sendCharResult: result,
|
||||
seq: message.data.seq
|
||||
});
|
||||
sendAsyncMessage("Test:SendCharDone", { result, seq: message.data.seq });
|
||||
});
|
||||
|
||||
addMessageListener("Test:SynthesizeKey", message => {
|
||||
EventUtils.synthesizeKey(message.data.key, message.data.event || {}, content);
|
||||
sendAsyncMessage("Test:SynthesizeKeyDone", { seq: message.data.seq });
|
||||
});
|
||||
|
||||
addMessageListener("Test:SynthesizeComposition", message => {
|
||||
let result = EventUtils.synthesizeComposition(message.data.event, content);
|
||||
sendAsyncMessage("Test:SynthesizeCompositionDone", { result, seq: message.data.seq });
|
||||
});
|
||||
|
||||
addMessageListener("Test:SynthesizeCompositionChange", message => {
|
||||
EventUtils.synthesizeCompositionChange(message.data.event, content);
|
||||
sendAsyncMessage("Test:SynthesizeCompositionChangeDone", { seq: message.data.seq });
|
||||
});
|
||||
|
@ -43,6 +43,24 @@ window.__defineGetter__('_EU_Cu', function() {
|
||||
return c.value && !c.writable ? Components.utils : SpecialPowers.Cu;
|
||||
});
|
||||
|
||||
window.__defineGetter__("_EU_OS", function() {
|
||||
delete this._EU_OS;
|
||||
try {
|
||||
this._EU_OS = Cu.import("resource://gre/modules/AppConstants.jsm", {}).platform;
|
||||
} catch (ex) {
|
||||
this._EU_OS = null;
|
||||
}
|
||||
return this._EU_OS;
|
||||
});
|
||||
|
||||
function _EU_isMac(aWindow = window) {
|
||||
return window._EU_OS ? window._EU_OS == "macosx" : aWindow.navigator.platform.indexOf("Mac") > -1;
|
||||
}
|
||||
|
||||
function _EU_isWin(aWindow = window) {
|
||||
return window._EU_OS ? window._EU_OS == "win" : aWindow.navigator.platform.indexOf("Win") > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a mouse event to the node aTarget (aTarget can be an id, or an
|
||||
* actual node) . The "event" passed in to aEvent is just a JavaScript
|
||||
@ -237,7 +255,7 @@ function _parseModifiers(aEvent, aWindow = window)
|
||||
mval |= nsIDOMWindowUtils.MODIFIER_META;
|
||||
}
|
||||
if (aEvent.accelKey) {
|
||||
mval |= (navigator.platform.indexOf("Mac") >= 0) ?
|
||||
mval |= _EU_isMac(aWindow) ?
|
||||
nsIDOMWindowUtils.MODIFIER_META : nsIDOMWindowUtils.MODIFIER_CONTROL;
|
||||
}
|
||||
if (aEvent.altGrKey) {
|
||||
@ -789,16 +807,13 @@ function _parseNativeModifiers(aModifiers, aWindow = window)
|
||||
}
|
||||
|
||||
if (aModifiers.accelKey) {
|
||||
modifiers |=
|
||||
(navigator.platform.indexOf("Mac") == 0) ? 0x00004000 : 0x00000400;
|
||||
modifiers |= _EU_isMac(aWindow) ? 0x00004000 : 0x00000400;
|
||||
}
|
||||
if (aModifiers.accelRightKey) {
|
||||
modifiers |=
|
||||
(navigator.platform.indexOf("Mac") == 0) ? 0x00008000 : 0x00000800;
|
||||
modifiers |= _EU_isMac(aWindow) ? 0x00008000 : 0x00000800;
|
||||
}
|
||||
if (aModifiers.altGrKey) {
|
||||
modifiers |=
|
||||
(navigator.platform.indexOf("Win") == 0) ? 0x00002800 : 0x00001000;
|
||||
modifiers |= _EU_isWin(aWindow) ? 0x00002800 : 0x00001000;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
@ -873,9 +888,9 @@ function synthesizeNativeKey(aKeyboardLayout, aNativeKeyCode, aModifiers,
|
||||
}
|
||||
var navigator = _getNavigator(aWindow);
|
||||
var nativeKeyboardLayout = null;
|
||||
if (navigator.platform.indexOf("Mac") == 0) {
|
||||
if (_EU_isMac(aWindow)) {
|
||||
nativeKeyboardLayout = aKeyboardLayout.Mac;
|
||||
} else if (navigator.platform.indexOf("Win") == 0) {
|
||||
} else if (_EU_isWin(aWindow)) {
|
||||
nativeKeyboardLayout = aKeyboardLayout.Win;
|
||||
}
|
||||
if (nativeKeyboardLayout === null) {
|
||||
@ -1063,7 +1078,15 @@ function _getTIP(aWindow, aCallback)
|
||||
function _getKeyboardEvent(aWindow = window)
|
||||
{
|
||||
if (typeof KeyboardEvent != "undefined") {
|
||||
return KeyboardEvent;
|
||||
try {
|
||||
// See if the object can be instantiated; sometimes this yields
|
||||
// 'TypeError: can't access dead object' or 'KeyboardEvent is not a constructor'.
|
||||
new KeyboardEvent("", {});
|
||||
return KeyboardEvent;
|
||||
} catch (ex) {}
|
||||
}
|
||||
if (typeof content != "undefined" && ("KeyboardEvent" in content)) {
|
||||
return content.KeyboardEvent;
|
||||
}
|
||||
return aWindow.KeyboardEvent;
|
||||
}
|
||||
@ -1282,7 +1305,7 @@ function _emulateToActivateModifiers(aTIP, aKeyEvent, aWindow = window)
|
||||
{ key: "OS", attr: "osKey" },
|
||||
{ key: "Shift", attr: "shiftKey" },
|
||||
{ key: "Symbol", attr: "symbolKey" },
|
||||
{ key: (navigator.platform.indexOf("Mac") >= 0) ? "Meta" : "Control",
|
||||
{ key: _EU_isMac(aWindow) ? "Meta" : "Control",
|
||||
attr: "accelKey" },
|
||||
],
|
||||
lockable: [
|
||||
|
Loading…
Reference in New Issue
Block a user