Bug 1573839, modify BrowserTestUtils.synthesizeMouse/Key/etc to handle fission. Remove support for passing arrays of selectors and instead modify the last argument to be a browsing context, or browser. Fix up tests that use this obsolete form, r=kmag

Differential Revision: https://phabricator.services.mozilla.com/D46307

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Neil Deakin 2019-10-07 08:51:52 +00:00
Родитель ae61a0ccfa
Коммит 71ba434e0a
20 изменённых файлов: 435 добавлений и 439 удалений

Просмотреть файл

@ -80,7 +80,8 @@ add_task(async function() {
{
name: "show only this frame",
url: "http://mochi.test:8888/",
element: ["iframe", "html"],
element: "html",
frameIndex: 0,
go() {
return ContentTask.spawn(
gBrowser.selectedBrowser,
@ -135,13 +136,33 @@ add_task(async function() {
contentAreaContextMenu,
"popupshown"
);
await BrowserTestUtils.synthesizeMouse(
test.element,
3,
3,
{ type: "contextmenu", button: 2 },
gBrowser.selectedBrowser
);
let browsingContext = gBrowser.selectedBrowser.browsingContext;
if (test.frameIndex != null) {
browsingContext = browsingContext.getChildren()[test.frameIndex];
}
await new Promise(r => {
SimpleTest.executeSoon(r);
});
// Sometimes, the iframe test fails as the child iframe hasn't finishing layout
// yet. Try again in this case.
while (true) {
try {
await BrowserTestUtils.synthesizeMouse(
test.element,
3,
3,
{ type: "contextmenu", button: 2 },
browsingContext
);
} catch (ex) {
continue;
}
break;
}
await popupShownPromise;
info("onImage: " + gContextMenu.onImage);

Просмотреть файл

@ -62,7 +62,6 @@ const whitelist = {
frameScripts: new Set([
// Test related
"chrome://mochikit/content/shutdown-leaks-collector.js",
"chrome://mochikit/content/tests/SimpleTest/AsyncUtilsContent.js",
"chrome://mochikit/content/tests/BrowserTestUtils/content-utils.js",
// Browser front-end

Просмотреть файл

@ -33,26 +33,26 @@ add_task(async function test_copy() {
let selectText = async target => {
let { width, height } = target.getBoundingClientRect();
await BrowserTestUtils.synthesizeMouse(
EventUtils.synthesizeMouse(
target,
1,
1,
{ type: "mousedown" },
this.browser
this.browser.contentWindow
);
await BrowserTestUtils.synthesizeMouse(
EventUtils.synthesizeMouse(
target,
width - 1,
height - 1,
{ type: "mousemove" },
this.browser
this.browser.contentWindow
);
await BrowserTestUtils.synthesizeMouse(
EventUtils.synthesizeMouse(
target,
width - 1,
height - 1,
{ type: "mouseup" },
this.browser
this.browser.contentWindow
);
};
@ -107,26 +107,27 @@ add_task(async function test_copy_multiple() {
let { width, height } = endRow.valueCell.getBoundingClientRect();
// Drag from the top left of the first row to the bottom right of the last.
await BrowserTestUtils.synthesizeMouse(
EventUtils.synthesizeMouse(
startRow.nameCell,
1,
1,
{ type: "mousedown" },
this.browser
this.browser.contentWindow
);
await BrowserTestUtils.synthesizeMouse(
EventUtils.synthesizeMouse(
endRow.valueCell,
width - 1,
height - 1,
{ type: "mousemove" },
this.browser
this.browser.contentWindow
);
await BrowserTestUtils.synthesizeMouse(
EventUtils.synthesizeMouse(
endRow.valueCell,
width - 1,
height - 1,
{ type: "mouseup" },
this.browser
this.browser.contentWindow
);
await SimpleTest.promiseClipboardChange(expectedString, async () => {

Просмотреть файл

@ -122,10 +122,10 @@ add_task(async function test_click_type_label_multiple_forms() {
// Even if this is the second form on the page, the click should select
// the radio button next to the label, not the one on the first form.
await BrowserTestUtils.synthesizeMouseAtCenter(
EventUtils.synthesizeMouseAtCenter(
radioLabels[radioIndex],
{},
this.browser
this.browser.contentWindow
);
// Adding the preference should set the default for the data type.

Просмотреть файл

@ -259,7 +259,7 @@ add_task(async function overrideContext_in_extension_tab() {
{
// Tests that overrideContext({}) can be used from a listener inside shadow DOM.
let menu = await openContextMenu(
() => content.document.getElementById("shadowHost").shadowRoot.firstChild
() => this.document.getElementById("shadowHost").shadowRoot.firstChild
);
await extension.awaitMessage("oncontextmenu_in_shadow_dom");
await extension.awaitMessage("onShown");

Просмотреть файл

@ -210,7 +210,7 @@ add_task(async function getTargetElement_in_frame() {
await extension.startup();
await extension.awaitMessage("ready");
let menu = await openContextMenuInFrame("#frame");
let menu = await openContextMenuInFrame();
await extension.awaitMessage("pageAndFrameChecked");
let menuItem = menu.getElementsByAttribute("label", "menu for frame")[0];
await closeExtensionContextMenu(menuItem);

Просмотреть файл

@ -71,27 +71,27 @@ add_task(async function menuInShadowDOM() {
info("Clicking in open shadow root");
await testShadowMenu(() => {
let doc = content.document;
let doc = this.document;
doc.body.innerHTML = `<div></div>`;
let host = doc.body.firstElementChild.attachShadow({ mode: "open" });
host.innerHTML = `<a href="http://example.com/?shadowlink">Test open</a>`;
content.testTarget = host.firstElementChild;
return content.testTarget;
this.document.testTarget = host.firstElementChild;
return this.document.testTarget;
});
info("Clicking in closed shadow root");
await testShadowMenu(() => {
let doc = content.document;
let doc = this.document;
doc.body.innerHTML = `<div></div>`;
let host = doc.body.firstElementChild.attachShadow({ mode: "closed" });
host.innerHTML = `<a href="http://example.com/?shadowlink">Test closed</a>`;
content.testTarget = host.firstElementChild;
return content.testTarget;
this.document.testTarget = host.firstElementChild;
return this.document.testTarget;
});
info("Clicking in nested shadow DOM");
await testShadowMenu(() => {
let doc = content.document;
let doc = this.document;
let host;
for (let container = doc.body, i = 0; i < 10; ++i) {
container.innerHTML = `<div id="level"></div>`;
@ -99,8 +99,8 @@ add_task(async function menuInShadowDOM() {
container = host;
}
host.innerHTML = `<a href="http://example.com/?shadowlink">Test nested</a>`;
content.testTarget = host.firstElementChild;
return content.testTarget;
this.document.testTarget = host.firstElementChild;
return this.document.testTarget;
});
await extension.unload();

Просмотреть файл

@ -139,15 +139,9 @@ add_task(
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter(
() => {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document.querySelector(
"#test-create-new-tab-from-mouse-click-subframe"
);
},
"#test-create-new-tab-from-mouse-click-subframe",
{ ctrlKey: true, metaKey: true },
tab.linkedBrowser
tab.linkedBrowser.browsingContext.getChildren()[0]
);
},
expectedWebNavProps: {
@ -163,15 +157,9 @@ add_task(
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter(
() => {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document.querySelector(
"#test-create-new-window-from-mouse-click-subframe"
);
},
"#test-create-new-window-from-mouse-click-subframe",
{ shiftKey: true },
tab.linkedBrowser
tab.linkedBrowser.browsingContext.getChildren()[0]
);
},
expectedWebNavProps: {
@ -187,15 +175,9 @@ add_task(
extension,
openNavTarget() {
BrowserTestUtils.synthesizeMouseAtCenter(
() => {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document.querySelector(
"#test-create-new-tab-from-targetblank-click-subframe"
);
},
"#test-create-new-tab-from-targetblank-click-subframe",
{},
tab.linkedBrowser
tab.linkedBrowser.browsingContext.getChildren()[0]
);
},
expectedWebNavProps: {

Просмотреть файл

@ -11,8 +11,17 @@ SpecialPowers.pushPrefEnv({
async function clickContextMenuItem({
pageElementSelector,
contextMenuItemLabel,
frameIndex,
}) {
const contentAreaContextMenu = await openContextMenu(pageElementSelector);
let contentAreaContextMenu;
if (frameIndex == null) {
contentAreaContextMenu = await openContextMenu(pageElementSelector);
} else {
contentAreaContextMenu = await openContextMenuInFrame(
pageElementSelector,
frameIndex
);
}
const item = contentAreaContextMenu.getElementsByAttribute(
"label",
contextMenuItemLabel
@ -133,14 +142,10 @@ add_task(
extension,
async openNavTarget() {
await clickContextMenuItem({
pageElementSelector: () => {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document.querySelector(
"#test-create-new-tab-from-context-menu-subframe"
);
},
pageElementSelector:
"#test-create-new-tab-from-context-menu-subframe",
contextMenuItemLabel: "Open Link in New Tab",
frameIndex: 0,
});
},
expectedWebNavProps: {
@ -156,14 +161,10 @@ add_task(
extension,
async openNavTarget() {
await clickContextMenuItem({
pageElementSelector: () => {
// This code runs as a framescript in the child process and it returns the
// target link in the subframe.
return this.content.frames[0].document.querySelector(
"#test-create-new-window-from-context-menu-subframe"
);
},
pageElementSelector:
"#test-create-new-window-from-context-menu-subframe",
contextMenuItemLabel: "Open Link in New Window",
frameIndex: 0,
});
},
expectedWebNavProps: {

Просмотреть файл

@ -444,7 +444,7 @@ async function openContextMenuInSidebar(selector = "body") {
return contentAreaContextMenu;
}
async function openContextMenuInFrame(frameSelector) {
async function openContextMenuInFrame(selector = "body", frameIndex = 0) {
let contentAreaContextMenu = document.getElementById(
"contentAreaContextMenu"
);
@ -453,9 +453,9 @@ async function openContextMenuInFrame(frameSelector) {
"popupshown"
);
await BrowserTestUtils.synthesizeMouseAtCenter(
[frameSelector, "body"],
selector,
{ type: "contextmenu" },
gBrowser.selectedBrowser
gBrowser.selectedBrowser.browsingContext.getChildren()[frameIndex]
);
await popupShownPromise;
return contentAreaContextMenu;

Просмотреть файл

@ -212,10 +212,10 @@ async function setDefaultEngine(
const popup = defaultEngineSelector.menupopup;
const popupShown = BrowserTestUtils.waitForEvent(popup, "popupshown");
BrowserTestUtils.synthesizeMouseAtCenter(
EventUtils.synthesizeMouseAtCenter(
defaultEngineSelector,
{},
gBrowser.selectedBrowser
defaultEngineSelector.ownerGlobal
);
await popupShown;
@ -229,11 +229,7 @@ async function setDefaultEngine(
"browser-search-engine-modified"
);
const popupHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
BrowserTestUtils.synthesizeMouseAtCenter(
engine2Item,
{},
gBrowser.selectedBrowser
);
EventUtils.synthesizeMouseAtCenter(engine2Item, {}, engine2Item.ownerGlobal);
await popupHidden;
await defaultChanged;

Просмотреть файл

@ -60,23 +60,20 @@ async function openPage(shouldClick) {
await BrowserTestUtils.loadURI(browser, PAGE_URL);
await BrowserTestUtils.browserLoaded(browser);
let frameBC = browser.browsingContext.getChildren()[0];
if (shouldClick) {
await BrowserTestUtils.synthesizeMouse(
function() {
return content.frames[0].document.body;
},
2,
2,
{},
browser
);
await BrowserTestUtils.synthesizeMouse("body", 2, 2, {}, frameBC);
}
let hasInteractedWith = await ContentTask.spawn(browser, "", function() {
return [
content.document.userHasInteracted,
content.frames[0].document.userHasInteracted,
];
});
let hasInteractedWith = await SpecialPowers.spawn(
frameBC,
[],
function() {
return [
content.document.userHasInteracted,
content.document.userHasInteracted,
];
}
);
is(
shouldClick,
hasInteractedWith[0],

Просмотреть файл

@ -79,11 +79,6 @@ registrar.registerFactory(OUR_PROCESSSELECTOR_CID, "", null, selectorFactory);
// some cases.
Cu.permitCPOWsInScope(this);
var gSendCharCount = 0;
var gSynthesizeKeyCount = 0;
var gSynthesizeCompositionCount = 0;
var gSynthesizeCompositionChangeCount = 0;
const kAboutPageRegistrationContentScript =
"chrome://mochikit/content/tests/BrowserTestUtils/content-about-page-utils.js";
@ -323,6 +318,18 @@ var BrowserTestUtils = {
return true;
},
/**
* If the argument is a browsingContext, return it. If the
* argument is a browser/frame, returns the browsing context for it.
*/
getBrowsingContextFrom(browser) {
if (Element.isInstance(browser)) {
return browser.browsingContext;
}
return browser;
},
/**
* Switches to a tab and resolves when it is ready.
*
@ -1435,17 +1442,13 @@ var BrowserTestUtils = {
/**
* Versions of EventUtils.jsm synthesizeMouse functions that synthesize a
* mouse event in a child process and return promises that resolve when the
* event has fired and completed. Instead of a window, a browser is required
* to be passed to this function.
* event has fired and completed. Instead of a window, a browser or
* browsing context is required to be passed to this function.
*
* @param target
* One of the following:
* - a selector string that identifies the element to target. The syntax is as
* for querySelector.
* - An array of selector strings. Each selector after the first
* selects for an element in the iframe specified by the previous
* selector.
* - a CPOW element (for easier test-conversion).
* - a function to be run in the content process that returns the element to
* target
* - null, in which case the offset is from the content document's edge.
@ -1455,58 +1458,41 @@ var BrowserTestUtils = {
* y offset from target's top bounding edge
* @param {Object} event object
* Additional arguments, similar to the EventUtils.jsm version
* @param {Browser} browser
* Browser element, must not be null
* @param {BrowserContext|MozFrameLoaderOwner} browsingContext
* Browsing context or browser element, must not be null
*
* @returns {Promise}
* @resolves True if the mouse event was cancelled.
*/
synthesizeMouse(target, offsetX, offsetY, event, browser) {
return new Promise((resolve, reject) => {
let mm = browser.messageManager;
mm.addMessageListener("Test:SynthesizeMouseDone", function mouseMsg(
message
) {
mm.removeMessageListener("Test:SynthesizeMouseDone", mouseMsg);
if (message.data.hasOwnProperty("defaultPrevented")) {
resolve(message.data.defaultPrevented);
} else {
reject(new Error(message.data.error));
}
});
synthesizeMouse(target, offsetX, offsetY, event, browsingContext) {
let targetFn = null;
if (typeof target == "function") {
targetFn = target.toString();
target = null;
} else if (typeof target != "string" && !Array.isArray(target)) {
target = null;
}
let cpowObject = null;
let targetFn = null;
if (typeof target == "function") {
targetFn = target.toString();
target = null;
} else if (typeof target != "string" && !Array.isArray(target)) {
cpowObject = target;
target = null;
}
mm.sendAsyncMessage(
"Test:SynthesizeMouse",
{ target, targetFn, x: offsetX, y: offsetY, event },
{ object: cpowObject }
);
browsingContext = this.getBrowsingContextFrom(browsingContext);
return this.sendQuery(browsingContext, "Test:SynthesizeMouse", {
target,
targetFn,
x: offsetX,
y: offsetY,
event,
});
},
/**
* Versions of EventUtils.jsm synthesizeTouch functions that synthesize a
* touch event in a child process and return promises that resolve when the
* event has fired and completed. Instead of a window, a browser is required
* to be passed to this function.
* event has fired and completed. Instead of a window, a browser or
* browsing context is required to be passed to this function.
*
* @param target
* One of the following:
* - a selector string that identifies the element to target. The syntax is as
* for querySelector.
* - An array of selector strings. Each selector after the first
* selects for an element in the iframe specified by the previous
* selector.
* - a CPOW element (for easier test-conversion).
* - a function to be run in the content process that returns the element to
* target
* - null, in which case the offset is from the content document's edge.
@ -1516,41 +1502,28 @@ var BrowserTestUtils = {
* y offset from target's top bounding edge
* @param {Object} event object
* Additional arguments, similar to the EventUtils.jsm version
* @param {Browser} browser
* Browser element, must not be null
* @param {BrowserContext|MozFrameLoaderOwner} browsingContext
* Browsing context or browser element, must not be null
*
* @returns {Promise}
* @resolves True if the touch event was cancelled.
*/
synthesizeTouch(target, offsetX, offsetY, event, browser) {
return new Promise((resolve, reject) => {
let mm = browser.messageManager;
mm.addMessageListener("Test:SynthesizeTouchDone", function touchMsg(
message
) {
mm.removeMessageListener("Test:SynthesizeTouchDone", touchMsg);
if (message.data.hasOwnProperty("defaultPrevented")) {
resolve(message.data.defaultPrevented);
} else {
reject(new Error(message.data.error));
}
});
synthesizeTouch(target, offsetX, offsetY, event, browsingContext) {
let targetFn = null;
if (typeof target == "function") {
targetFn = target.toString();
target = null;
} else if (typeof target != "string" && !Array.isArray(target)) {
target = null;
}
let cpowObject = null;
let targetFn = null;
if (typeof target == "function") {
targetFn = target.toString();
target = null;
} else if (typeof target != "string" && !Array.isArray(target)) {
cpowObject = target;
target = null;
}
mm.sendAsyncMessage(
"Test:SynthesizeTouch",
{ target, targetFn, x: offsetX, y: offsetY, event },
{ object: cpowObject }
);
browsingContext = this.getBrowsingContextFrom(browsingContext);
return this.sendQuery(browsingContext, "Test:SynthesizeTouch", {
target,
targetFn,
x: offsetX,
y: offsetY,
event,
});
},
@ -1579,10 +1552,16 @@ var BrowserTestUtils = {
* Version of synthesizeMouse that uses the center of the target as the mouse
* location. Arguments and the return value are the same.
*/
synthesizeMouseAtCenter(target, event, browser) {
synthesizeMouseAtCenter(target, event, browsingContext) {
// Use a flag to indicate to center rather than having a separate message.
event.centered = true;
return BrowserTestUtils.synthesizeMouse(target, 0, 0, event, browser);
return BrowserTestUtils.synthesizeMouse(
target,
0,
0,
event,
browsingContext
);
},
/**
@ -1590,13 +1569,13 @@ var BrowserTestUtils = {
* window instead of a target as the offset. Otherwise, the arguments and
* return value are the same as synthesizeMouse.
*/
synthesizeMouseAtPoint(offsetX, offsetY, event, browser) {
synthesizeMouseAtPoint(offsetX, offsetY, event, browsingContext) {
return BrowserTestUtils.synthesizeMouse(
null,
offsetX,
offsetY,
event,
browser
browsingContext
);
},
@ -1835,102 +1814,63 @@ var BrowserTestUtils = {
/**
* Version of EventUtils' `sendChar` function; it will synthesize a keypress
* 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.
* event was fired. Instead of a Window, a Browser or Browsing Context
* is required to be passed to this function.
*
* @param {String} char
* A character for the keypress event that is sent to the browser.
* @param {Browser} browser
* Browser element, must not be null.
* @param {BrowserContext|MozFrameLoaderOwner} browsingContext
* Browsing context or browser element, must not be null
*
* @returns {Promise}
* @resolves True if the keypress event was synthesized.
*/
sendChar(char, browser) {
return new Promise(resolve => {
let seq = ++gSendCharCount;
let mm = browser.messageManager;
mm.addMessageListener("Test:SendCharDone", function charMsg(message) {
if (message.data.seq != seq) {
return;
}
mm.removeMessageListener("Test:SendCharDone", charMsg);
resolve(message.data.result);
});
mm.sendAsyncMessage("Test:SendChar", {
char,
seq,
});
});
sendChar(char, browsingContext) {
browsingContext = this.getBrowsingContextFrom(browsingContext);
return this.sendQuery(browsingContext, "Test:SendChar", { char });
},
/**
* 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.
* event was fired. Instead of a Window, a Browser or Browsing Context
* 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.
* @param {BrowserContext|MozFrameLoaderOwner} browsingContext
* Browsing context or 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 });
synthesizeKey(key, event, browsingContext) {
browsingContext = this.getBrowsingContextFrom(browsingContext);
return this.sendQuery(browsingContext, "Test:SynthesizeKey", {
key,
event,
});
},
/**
* 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.
* resolve when the event was fired. Instead of a Window, a Browser or
* Browsing Context 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.
* @param {BrowserContext|MozFrameLoaderOwner} browsingContext
* Browsing context or 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 });
synthesizeComposition(event, browsingContext) {
browsingContext = this.getBrowsingContextFrom(browsingContext);
return this.sendQuery(browsingContext, "Test:SynthesizeComposition", {
event,
});
},
@ -1938,36 +1878,19 @@ var BrowserTestUtils = {
* 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.
* Browser or Browsing Context 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.
* @param {BrowserContext|MozFrameLoaderOwner} browsingContext
* Browsing context or 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 });
synthesizeCompositionChange(event, browsingContext) {
browsingContext = this.getBrowsingContextFrom(browsingContext);
return this.sendQuery(browsingContext, "Test:SynthesizeCompositionChange", {
event,
});
},
@ -2241,4 +2164,24 @@ var BrowserTestUtils = {
);
actor.sendAsyncMessage(aMessageName, aMessageData);
},
/**
* Sends a query to a specific BrowserTestUtils window actor.
* @param aBrowsingContext
* The browsing context where the actor lives.
* @param {string} aMessageName
* Name of the message to be sent to the actor.
* @param {object} aMessageData
* Extra information to pass to the actor.
*/
async sendQuery(aBrowsingContext, aMessageName, aMessageData) {
if (!aBrowsingContext.currentWindowGlobal) {
await this.waitForCondition(() => aBrowsingContext.currentWindowGlobal);
}
let actor = aBrowsingContext.currentWindowGlobal.getActor(
"BrowserTestUtils"
);
return actor.sendQuery(aMessageName, aMessageData);
},
};

Просмотреть файл

@ -6,9 +6,81 @@
var EXPORTED_SYMBOLS = ["BrowserTestUtilsChild"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
class BrowserTestUtilsChild extends JSWindowActorChild {
actorCreated() {
this._EventUtils = null;
}
get EventUtils() {
if (!this._EventUtils) {
// Set up a dummy environment so that EventUtils works. We need to be careful to
// pass a window object into each EventUtils method we call rather than having
// it rely on the |window| global.
let win = this.contentWindow;
let EventUtils = {
get KeyboardEvent() {
return win.KeyboardEvent;
},
// EventUtils' `sendChar` function relies on the navigator to synthetize events.
get navigator() {
return win.navigator;
},
};
EventUtils.window = {};
EventUtils.parent = EventUtils.window;
EventUtils._EU_Ci = Ci;
EventUtils._EU_Cc = Cc;
Services.scriptloader.loadSubScript(
"chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
EventUtils
);
this._EventUtils = EventUtils;
}
return this._EventUtils;
}
receiveMessage(aMessage) {
switch (aMessage.name) {
case "Test:SynthesizeMouse": {
return this.synthesizeMouse(aMessage.data, this.contentWindow);
}
case "Test:SynthesizeTouch": {
return this.synthesizeTouch(aMessage.data, this.contentWindow);
}
case "Test:SendChar": {
return this.EventUtils.sendChar(aMessage.data.char, this.contentWindow);
}
case "Test:SynthesizeKey":
this.EventUtils.synthesizeKey(
aMessage.data.key,
aMessage.data.event || {},
this.contentWindow
);
break;
case "Test:SynthesizeComposition": {
return this.EventUtils.synthesizeComposition(
aMessage.data.event,
this.contentWindow
);
}
case "Test:SynthesizeCompositionChange":
this.EventUtils.synthesizeCompositionChange(
aMessage.data.event,
this.contentWindow
);
break;
case "BrowserTestUtils:CrashFrame": {
// This is to intentionally crash the frame.
// We crash by using js-ctypes and dereferencing
@ -30,5 +102,106 @@ class BrowserTestUtilsChild extends JSWindowActorChild {
dies();
}
}
return undefined;
}
synthesizeMouse(data, window) {
let target = data.target;
if (typeof target == "string") {
target = this.document.querySelector(target);
} else if (typeof data.targetFn == "string") {
let runnablestr = `
(() => {
return (${data.targetFn});
})();`;
/* eslint-disable no-eval */
target = eval(runnablestr)();
/* eslint-enable no-eval */
}
let left = data.x;
let top = data.y;
if (target) {
if (target.ownerDocument !== this.document) {
// Account for nodes found in iframes.
let cur = target;
do {
let frame = cur.ownerGlobal.frameElement;
let rect = frame.getBoundingClientRect();
left += rect.left;
top += rect.top;
cur = frame;
} while (cur && cur.ownerDocument !== this.document);
// node must be in this document tree.
if (!cur) {
throw new Error("target must be in the main document tree");
}
}
let rect = target.getBoundingClientRect();
left += rect.left;
top += rect.top;
if (data.event.centered) {
left += rect.width / 2;
top += rect.height / 2;
}
}
let result;
if (data.event && data.event.wheel) {
this.EventUtils.synthesizeWheelAtPoint(left, top, data.event, window);
} else {
result = this.EventUtils.synthesizeMouseAtPoint(
left,
top,
data.event,
window
);
}
return result;
}
synthesizeTouch(data, window) {
let target = data.target;
if (typeof target == "string") {
target = this.document.querySelector(target);
} else if (typeof data.targetFn == "string") {
let runnablestr = `
(() => {
return (${data.targetFn});
})();`;
/* eslint-disable no-eval */
target = eval(runnablestr)();
/* eslint-enable no-eval */
}
if (target) {
if (target.ownerDocument !== this.document) {
// Account for nodes found in iframes.
let cur = target;
do {
cur = cur.ownerGlobal.frameElement;
} while (cur && cur.ownerDocument !== this.document);
// node must be in this document tree.
if (!cur) {
throw new Error("target must be in the main document tree");
}
}
}
return this.EventUtils.synthesizeTouch(
target,
data.x,
data.y,
data.event,
window
);
}
}

Просмотреть файл

@ -146,11 +146,6 @@ function testInit() {
ChromeUtils.import("chrome://mochikit/content/ShutdownLeaksCollector.jsm");
}
Services.mm.loadFrameScript(
"chrome://mochikit/content/tests/SimpleTest/AsyncUtilsContent.js",
true
);
var testSuite = Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment)
.get("TEST_SUITE");

Просмотреть файл

@ -46,7 +46,6 @@ FINAL_TARGET_FILES.content.static += [
FINAL_TARGET_FILES.content.tests.SimpleTest += [
'../../docshell/test/chrome/docshell_helpers.js',
'../modules/StructuredLog.jsm',
'tests/SimpleTest/AsyncUtilsContent.js',
'tests/SimpleTest/EventUtils.js',
'tests/SimpleTest/ExtensionTestUtils.js',
'tests/SimpleTest/iframe-between-tests.html',

Просмотреть файл

@ -1,156 +0,0 @@
/*
* This code is used for handling synthesizeMouse in a content process.
* Generally it just delegates to EventUtils.js.
*/
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
// Set up a dummy environment so that EventUtils works. We need to be careful to
// pass a window object into each EventUtils method we call rather than having
// it rely on the |window| global.
var EventUtils = {
get KeyboardEvent() {
return content.KeyboardEvent;
},
// EventUtils' `sendChar` function relies on the navigator to synthetize events.
get navigator() {
return content.navigator;
},
};
EventUtils.window = {};
EventUtils.parent = EventUtils.window;
EventUtils._EU_Ci = Ci;
EventUtils._EU_Cc = Cc;
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
addMessageListener("Test:SynthesizeMouse", (message) => {
let data = message.data;
let target = data.target;
if (typeof target == "string") {
target = content.document.querySelector(target);
}
else if (Array.isArray(target)) {
let elem = {contentDocument: content.document};
for (let sel of target) {
elem = elem.contentDocument.querySelector(sel);
}
target = elem;
}
else if (typeof data.targetFn == "string") {
let runnablestr = `
(() => {
return (${data.targetFn});
})();`
target = eval(runnablestr)();
}
else {
target = message.objects.object;
}
let left = data.x;
let top = data.y;
if (target) {
if (target.ownerDocument !== content.document) {
// Account for nodes found in iframes.
let cur = target;
do {
let frame = cur.ownerGlobal.frameElement;
let rect = frame.getBoundingClientRect();
left += rect.left;
top += rect.top;
cur = frame;
} while (cur && cur.ownerDocument !== content.document);
// node must be in this document tree.
if (!cur) {
sendAsyncMessage("Test:SynthesizeMouseDone",
{ error: "target must be in the main document tree" });
return;
}
}
let rect = target.getBoundingClientRect();
left += rect.left;
top += rect.top;
if (data.event.centered) {
left += rect.width / 2;
top += rect.height / 2;
}
}
let result;
if (data.event && data.event.wheel) {
EventUtils.synthesizeWheelAtPoint(left, top, data.event, content);
} else {
result = EventUtils.synthesizeMouseAtPoint(left, top, data.event, content);
}
sendAsyncMessage("Test:SynthesizeMouseDone", { defaultPrevented: result });
});
addMessageListener("Test:SynthesizeTouch", (message) => {
let data = message.data;
let target = data.target;
if (typeof target == "string") {
target = content.document.querySelector(target);
}
else if (Array.isArray(target)) {
let elem = {contentDocument: content.document};
for (let sel of target) {
elem = elem.contentDocument.querySelector(sel);
}
target = elem;
}
else if (typeof data.targetFn == "string") {
let runnablestr = `
(() => {
return (${data.targetFn});
})();`
target = eval(runnablestr)();
}
else {
target = message.objects.object;
}
if (target) {
if (target.ownerDocument !== content.document) {
// Account for nodes found in iframes.
let cur = target;
do {
cur = cur.ownerGlobal.frameElement;
} while (cur && cur.ownerDocument !== content.document);
// node must be in this document tree.
if (!cur) {
sendAsyncMessage("Test:SynthesizeTouchDone",
{ error: "target must be in the main document tree"});
return;
}
}
}
let result = EventUtils.synthesizeTouch(target, data.x, data.y, data.event, content)
sendAsyncMessage("Test:SynthesizeTouchDone", { defaultPrevented: result });
});
addMessageListener("Test:SendChar", message => {
let result = EventUtils.sendChar(message.data.char, content);
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 });
});

Просмотреть файл

@ -1,5 +1,5 @@
function getLastEventDetails(browser) {
return ContentTask.spawn(browser, {}, async function() {
return SpecialPowers.spawn(browser, [], async function() {
return content.document.getElementById("out").textContent;
});
}
@ -33,7 +33,12 @@ add_task(async function() {
details = await getLastEventDetails(browser);
is(details, "body,15,6", "synthesizeMouseAtPoint on body");
await BrowserTestUtils.synthesizeMouseAtPoint(20, 22, {}, browser);
await BrowserTestUtils.synthesizeMouseAtPoint(
20,
22,
{},
browser.browsingContext
);
details = await getLastEventDetails(browser);
is(details, "button,20,22", "synthesizeMouseAtPoint on button");
@ -76,6 +81,45 @@ add_task(async function() {
gBrowser.removeTab(tab);
});
add_task(async function mouse_in_iframe() {
let onClickEvt = "document.body.lastChild.textContent = event.target.id;";
const url = `<iframe style='margin: 30px;' src='data:text/html,<body onclick="${onClickEvt}">
<p><button>One</button></p><p><button id="two">Two</button></p><p id="out"></p></body>'></iframe>
<iframe style='margin: 10px;' src='data:text/html,<body onclick="${onClickEvt}">
<p><button>Three</button></p><p><button id="four">Four</button></p><p id="out"></p></body>'></iframe>`;
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"data:text/html," + url
);
let browser = tab.linkedBrowser;
await BrowserTestUtils.synthesizeMouse(
"#two",
5,
10,
{},
browser.browsingContext.getChildren()[0]
);
let details = await getLastEventDetails(
browser.browsingContext.getChildren()[0]
);
is(details, "two", "synthesizeMouse");
await BrowserTestUtils.synthesizeMouse(
"#four",
5,
10,
{},
browser.browsingContext.getChildren()[1]
);
details = await getLastEventDetails(browser.browsingContext.getChildren()[1]);
is(details, "four", "synthesizeMouse");
gBrowser.removeTab(tab);
});
add_task(async function() {
await BrowserTestUtils.registerAboutPage(
registerCleanupFunction,

Просмотреть файл

@ -45,6 +45,7 @@ add_task(async function init() {
) {
row = row.nextSibling;
}
Assert.ok(row, "found a table row for our test tab");
Assert.equal(
row.windowId,
@ -60,11 +61,12 @@ add_task(async function init() {
);
// Verify selecting a row works.
await BrowserTestUtils.synthesizeMouseAtCenter(
EventUtils.synthesizeMouseAtCenter(
row,
{},
tabAboutPerformance.linkedBrowser
tabAboutPerformance.linkedBrowser.contentWindow
);
Assert.equal(
row.getAttribute("selected"),
"true",
@ -77,10 +79,10 @@ add_task(async function init() {
tabAboutPerformance,
"the about:performance tab is selected"
);
await BrowserTestUtils.synthesizeMouseAtCenter(
EventUtils.synthesizeMouseAtCenter(
row,
{ clickCount: 2 },
tabAboutPerformance.linkedBrowser
tabAboutPerformance.linkedBrowser.contentWindow
);
Assert.equal(
gBrowser.selectedTab,
@ -93,10 +95,10 @@ add_task(async function init() {
await BrowserTestUtils.switchTab(gBrowser, tabAboutPerformance);
// ... and click the X button at the end of the row.
let tabClosing = BrowserTestUtils.waitForTabClosing(tabContent);
await BrowserTestUtils.synthesizeMouseAtCenter(
EventUtils.synthesizeMouseAtCenter(
row.children[4],
{},
tabAboutPerformance.linkedBrowser
tabAboutPerformance.linkedBrowser.contentWindow
);
await tabClosing;

Просмотреть файл

@ -335,14 +335,13 @@ add_task(async function testMouseSupport() {
let [card] = getTestCards(doc);
is(card.addon.id, "test@mochi.test", "The right card is found");
let menuButton = card.querySelector('[action="more-options"]');
let panel = card.querySelector("panel-list");
ok(!panel.open, "The panel is initially closed");
await BrowserTestUtils.synthesizeMouseAtCenter(
menuButton,
"addon-card[addon-id$='@mochi.test'] button[action='more-options']",
{ type: "mousedown" },
gBrowser.selectedBrowser
win.docShell.browsingContext
);
ok(panel.open, "The panel is now open");