Bug 1245153 - Employ new modules throughout Marionette; r=automatedtester
This change removes almost all the remaining uses of loadSubScript and
global scope pollution. The only remaining use is for common.js, which
is resolved by a later bug for evaluating scripts.
MozReview-Commit-ID: 96h0yLElauq
--- a/testing/marionette/actions.js
+++ b/testing/marionette/actions.js
@@ -2,29 +2,31 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("chrome://marionette/content/event.js");
+
const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms
this.EXPORTED_SYMBOLS = ["actions"];
const logger = Log.repository.getLogger("Marionette");
this.actions = {};
/**
* Functionality for (single finger) action chains.
*/
-actions.Chain = function(utils, checkForInterrupted) {
+actions.Chain = function(checkForInterrupted) {
// for assigning unique ids to all touches
this.nextTouchId = 1000;
// keep track of active Touches
this.touchIds = {};
// last touch for each fingerId
this.lastCoordinates = null;
this.isTap = false;
this.scrolling = false;
@@ -38,19 +40,16 @@ actions.Chain = function(utils, checkFor
if (typeof checkForInterrupted == "function") {
this.checkForInterrupted = checkForInterrupted;
} else {
this.checkForInterrupted = () => {};
}
// determines if we create touch events
this.inputSource = null;
-
- // test utilities providing some event synthesis code
- this.utils = utils;
};
actions.Chain.prototype.dispatchActions = function(
args,
touchId,
container,
elementManager,
callbacks,
@@ -122,17 +121,17 @@ actions.Chain.prototype.emitMouseEvent =
`clickCount: ${clickCount}`);
let win = doc.defaultView;
let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let mods;
if (typeof modifiers != "undefined") {
- mods = this.utils._parseModifiers(modifiers);
+ mods = event.parseModifiers_(modifiers);
} else {
mods = 0;
}
domUtils.sendMouseEvent(
type,
elClientX,
elClientY,
@@ -182,22 +181,22 @@ actions.Chain.prototype.actions = functi
if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
this.resetValues();
throw new WebDriverError("Element has not been pressed");
}
}
switch(command) {
case "keyDown":
- this.utils.sendKeyDown(pack[1], keyModifiers, this.container.frame);
+ event.sendKeyDown(pack[1], keyModifiers, this.container.frame);
this.actions(chain, touchId, i, keyModifiers);
break;
case "keyUp":
- this.utils.sendKeyUp(pack[1], keyModifiers, this.container.frame);
+ event.sendKeyUp(pack[1], keyModifiers, this.container.frame);
this.actions(chain, touchId, i, keyModifiers);
break;
case "click":
el = this.elementManager.getKnownElement(pack[1], this.container);
let button = pack[2];
let clickCount = pack[3];
c = this.coordinates(el, null, null);
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -19,33 +19,28 @@ Cu.import("resource://gre/modules/XPCOMU
var {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
this.DevToolsUtils = devtools.require("devtools/shared/DevToolsUtils");
XPCOMUtils.defineLazyServiceGetter(
this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
Cu.import("chrome://marionette/content/actions.js");
+Cu.import("chrome://marionette/content/atoms.js");
Cu.import("chrome://marionette/content/interactions.js");
Cu.import("chrome://marionette/content/elements.js");
+Cu.import("chrome://marionette/content/event.js");
+Cu.import("chrome://marionette/content/frame-manager.js");
Cu.import("chrome://marionette/content/error.js");
Cu.import("chrome://marionette/content/modal.js");
Cu.import("chrome://marionette/content/proxy.js");
Cu.import("chrome://marionette/content/simpletest.js");
loader.loadSubScript("chrome://marionette/content/common.js");
-// preserve this import order:
-var utils = {};
-loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
-loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils);
-loader.loadSubScript("chrome://marionette/content/atoms.js", utils);
-loader.loadSubScript("chrome://marionette/content/sendkeys.js", utils);
-loader.loadSubScript("chrome://marionette/content/frame-manager.js");
-
this.EXPORTED_SYMBOLS = ["GeckoDriver", "Context"];
var FRAME_SCRIPT = "chrome://marionette/content/listener.js";
const BROWSER_STARTUP_FINISHED = "browser-delayed-startup-finished";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const CLICK_TO_START_PREF = "marionette.debugging.clicktostart";
const CONTENT_LISTENER_PREF = "marionette.contentListener";
@@ -133,17 +128,17 @@ this.GeckoDriver = function(appName, dev
this.currentFrameElement = null;
this.testName = null;
this.mozBrowserClose = null;
this.sandboxes = {};
// frame ID of the current remote frame, used for mozbrowserclose events
this.oopFrameId = null;
this.observing = null;
this._browserIds = new WeakMap();
- this.actions = new actions.Chain(utils);
+ this.actions = new actions.Chain();
this.sessionCapabilities = {
// mandated capabilities
"browserName": Services.appinfo.name,
"browserVersion": Services.appinfo.version,
"platformName": Services.sysinfo.getProperty("name"),
"platformVersion": Services.sysinfo.getProperty("version"),
"specificationLevel": "1",
@@ -161,17 +156,17 @@ this.GeckoDriver = function(appName, dev
// proprietary extensions
"XULappId" : Services.appinfo.ID,
"appBuildId" : Services.appinfo.appBuildID,
"device": device,
"version": Services.appinfo.version,
};
- this.interactions = new Interactions(utils, () => this.sessionCapabilities);
+ this.interactions = new Interactions(() => this.sessionCapabilities);
this.mm = globalMessageManager;
this.listener = proxy.toListener(() => this.mm, this.sendAsync.bind(this));
// always keep weak reference to current dialogue
this.dialog = null;
let handleDialog = (subject, topic) => {
let winr;
@@ -349,18 +344,16 @@ GeckoDriver.prototype.startBrowser = fun
* Loads the Marionette frame script into the browser if needed.
*
* @param {nsIDOMWindow} win
* Window whose browser we need to access.
* @param {boolean} isNewSession
* True if this is the first time we're talking to this browser.
*/
GeckoDriver.prototype.whenBrowserStarted = function(win, isNewSession) {
- utils.window = win;
-
try {
let mm = win.window.messageManager;
if (!isNewSession) {
// Loading the frame script corresponds to a situation we need to
// return to the server. If the messageManager is a message broadcaster
// with no children, we don't have a hope of coming back from this call,
// so send the ack here. Otherwise, make a note of how many child scripts
// will be loaded so we known when it's safe to return.
@@ -385,17 +378,17 @@ GeckoDriver.prototype.whenBrowserStarted
*
* @param {nsIDOMElement} el
* The parent element.
* @param {Array.<string>} lines
* Array that holds the text lines.
*/
GeckoDriver.prototype.getVisibleText = function(el, lines) {
try {
- if (utils.isElementDisplayed(el)) {
+ if (atom.isElementDisplayed(el, this.getCurrentWindow())) {
if (el.value) {
lines.push(el.value);
}
for (let child in el.childNodes) {
this.getVisibleText(el.childNodes[child], lines);
}
}
} catch (e) {
@@ -781,17 +774,16 @@ GeckoDriver.prototype.createExecuteSandb
let principal = win;
if (sandboxName == 'system') {
principal = Cc["@mozilla.org/systemprincipal;1"].
createInstance(Ci.nsIPrincipal);
}
let sb = new Cu.Sandbox(principal,
{sandboxPrototype: win, wantXrays: false, sandboxName: ""});
sb.global = sb;
- sb.testUtils = utils;
sb.proto = win;
mn.exports.forEach(function(fn) {
if (typeof mn[fn] === 'function') {
sb[fn] = mn[fn].bind(mn);
} else {
sb[fn] = mn[fn];
}
@@ -1585,17 +1577,16 @@ GeckoDriver.prototype.switchToWindow = f
this.startBrowser(found.win, false /* isNewSession */);
if (registerBrowsers && browserListening) {
yield registerBrowsers;
yield browserListening;
}
} else {
- utils.window = found.win;
this.curBrowser = this.browsers[found.outerId];
if ("tabIndex" in found) {
this.curBrowser.switchToTab(found.tabIndex, found.win);
}
}
} else {
throw new NoSuchWindowError(`Unable to locate window: ${switchTo}`);
@@ -2012,17 +2003,17 @@ GeckoDriver.prototype.clickElement = fun
*/
GeckoDriver.prototype.getElementAttribute = function*(cmd, resp) {
let {id, name} = cmd.parameters;
switch (this.context) {
case Context.CHROME:
let win = this.getCurrentWindow();
let el = this.curBrowser.elementManager.getKnownElement(id, {frame: win});
- resp.body.value = utils.getElementAttribute(el, name);
+ resp.body.value = atom.getElementAttribute(el, name, this.getCurrentWindow());
break;
case Context.CONTENT:
resp.body.value = yield this.listener.getElementAttribute(id, name);
break;
}
};
@@ -2787,21 +2778,21 @@ GeckoDriver.prototype.sendKeysToDialog =
// see toolkit/components/prompts/content/commonDialog.js
let {loginContainer, loginTextbox} = this.dialog.ui;
if (loginContainer.hidden) {
throw new ElementNotVisibleError("This prompt does not accept text input");
}
let win = this.dialog.window ? this.dialog.window : this.getCurrentWindow();
- utils.sendKeysToElement(
- win,
+ event.sendKeysToElement(
+ cmd.parameters.value,
loginTextbox,
- cmd.parameters.value,
- true /* ignore visibility check */);
+ {ignoreVisibility: true},
+ win);
};
/**
* Quits Firefox with the provided flags and tears down the current
* session.
*/
GeckoDriver.prototype.quitApplication = function(cmd, resp) {
if (this.appName != "Firefox") {
@@ -3063,17 +3054,17 @@ var BrowserObj = function(win, driver) {
this.elementManager = new ElementManager([NAME, LINK_TEXT, PARTIAL_LINK_TEXT]);
this.setBrowser(win);
// A reference to the tab corresponding to the current window handle, if any.
this.tab = null;
this.pendingCommands = [];
// we should have one FM per BO so that we can handle modals in each Browser
- this.frameManager = new FrameManager(driver);
+ this.frameManager = new frame.Manager(driver);
this.frameRegsPending = 0;
// register all message listeners
this.frameManager.addMessageManagerListeners(driver.mm);
this.getIdForBrowser = driver.getIdForBrowser.bind(driver);
this.updateIdForBrowser = driver.updateIdForBrowser.bind(driver);
this._curFrameId = null;
this._browserWasRemote = null;
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -1,61 +1,56 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
var uuidGen = Cc["@mozilla.org/uuid-generator;1"]
- .getService(Ci.nsIUUIDGenerator);
+ .getService(Ci.nsIUUIDGenerator);
var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
- .getService(Ci.mozIJSSubScriptLoader);
+ .getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://marionette/content/simpletest.js");
loader.loadSubScript("chrome://marionette/content/common.js");
Cu.import("chrome://marionette/content/actions.js");
+Cu.import("chrome://marionette/content/atoms.js");
Cu.import("chrome://marionette/content/capture.js");
Cu.import("chrome://marionette/content/cookies.js");
Cu.import("chrome://marionette/content/elements.js");
Cu.import("chrome://marionette/content/error.js");
+Cu.import("chrome://marionette/content/event.js");
Cu.import("chrome://marionette/content/proxy.js");
Cu.import("chrome://marionette/content/interactions.js");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-var utils = {};
-utils.window = content;
-// Load Event/ChromeUtils for use with JS scripts:
-loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
-loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils);
-loader.loadSubScript("chrome://marionette/content/atoms.js", utils);
-loader.loadSubScript("chrome://marionette/content/sendkeys.js", utils);
var marionetteLogObj = new MarionetteLogObj();
var isB2G = false;
var marionetteTestName;
var winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
var listenerId = null; // unique ID of this listener
var curContainer = { frame: content, shadowRoot: null };
var isRemoteBrowser = () => curContainer.frame.contentWindow !== null;
var previousContainer = null;
var elementManager = new ElementManager([]);
// Holds session capabilities.
var capabilities = {};
-var interactions = new Interactions(utils, () => capabilities);
+var interactions = new Interactions(() => capabilities);
-var actions = new actions.Chain(utils, checkForInterrupted);
+var actions = new actions.Chain(checkForInterrupted);
var importedScripts = null;
// Contains the last file input element that was the target of
// sendKeysToElement.
var fileInputElement;
// A dict of sandboxes used this session
var sandboxes = {};
@@ -524,17 +519,16 @@ function createExecuteContentSandbox(win
if (sandboxName == "system") {
principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
}
let sandbox = new Cu.Sandbox(principal, {sandboxPrototype: win});
sandbox.global = sandbox;
sandbox.window = win;
sandbox.document = sandbox.window.document;
sandbox.navigator = sandbox.window.navigator;
- sandbox.testUtils = utils;
sandbox.asyncTestCommandId = asyncTestCommandId;
sandbox.marionette = mn;
mn.exports.forEach(fn => {
if (typeof mn[fn] == "function") {
sandbox[fn] = mn[fn].bind(mn);
} else {
sandbox[fn] = mn[fn];
@@ -888,77 +882,23 @@ function coordinates(target, x, y) {
y = box.height / 2;
}
let coords = {};
coords.x = box.left + x;
coords.y = box.top + y;
return coords;
}
-
-/**
- * This function returns true if the given coordinates are in the viewport.
- * @param 'x', and 'y' are the coordinates relative to the target.
- * If they are not specified, then the center of the target is used.
- */
-function elementInViewport(el, x, y) {
- let c = coordinates(el, x, y);
- let curFrame = curContainer.frame;
- let viewPort = {top: curFrame.pageYOffset,
- left: curFrame.pageXOffset,
- bottom: (curFrame.pageYOffset + curFrame.innerHeight),
- right:(curFrame.pageXOffset + curFrame.innerWidth)};
- return (viewPort.left <= c.x + curFrame.pageXOffset &&
- c.x + curFrame.pageXOffset <= viewPort.right &&
- viewPort.top <= c.y + curFrame.pageYOffset &&
- c.y + curFrame.pageYOffset <= viewPort.bottom);
-}
-
-/**
- * This function throws the visibility of the element error if the element is
- * not displayed or the given coordinates are not within the viewport.
- * @param 'x', and 'y' are the coordinates relative to the target.
- * If they are not specified, then the center of the target is used.
- */
-function checkVisible(el, x, y) {
- // Bug 1094246 - Webdriver's isShown doesn't work with content xul
- if (utils.getElementAttribute(el, "namespaceURI").indexOf("there.is.only.xul") == -1) {
- //check if the element is visible
- let visible = utils.isElementDisplayed(el);
- if (!visible) {
- return false;
- }
- }
-
- if (el.tagName.toLowerCase() === 'body') {
- return true;
- }
- if (!elementInViewport(el, x, y)) {
- //check if scroll function exist. If so, call it.
- if (el.scrollIntoView) {
- el.scrollIntoView(false);
- if (!elementInViewport(el)) {
- return false;
- }
- }
- else {
- return false;
- }
- }
- return true;
-}
-
-
/**
* Function that perform a single tap
*/
function singleTap(id, corx, cory) {
let el = elementManager.getKnownElement(id, curContainer);
// after this block, the element will be scrolled into view
- let visible = checkVisible(el, corx, cory);
+ let visible = elements.checkVisible(el, curContainer.frame, corx, cory);
if (!visible) {
throw new ElementNotVisibleError("Element is not currently visible and may not be manipulated");
}
return interactions.accessibility.getAccessibleObject(el, true).then(acc => {
interactions.accessibility.checkVisible(acc, el, visible);
interactions.accessibility.checkActionable(acc, el);
if (!curContainer.frame.document.createTouch) {
actions.mouseEventsOnly = true;
@@ -1388,31 +1328,31 @@ function clickElement(id) {
* @param {string} name
* Name of the attribute.
*
* @return {string}
* The value of the attribute.
*/
function getElementAttribute(id, name) {
let el = elementManager.getKnownElement(id, curContainer);
- return utils.getElementAttribute(el, name);
+ return atom.getElementAttribute(el, name, curContainer.frame);
}
/**
* Get the text of this element. This includes text from child elements.
*
* @param {WebElement} id
* Reference to web element.
*
* @return {string}
* Text of element.
*/
function getElementText(id) {
let el = elementManager.getKnownElement(id, curContainer);
- return utils.getElementText(el);
+ return atom.getElementText(el, curContainer.frame);
}
/**
* Get the tag name of an element.
*
* @param {WebElement} id
* Reference to web element.
*
@@ -1506,34 +1446,34 @@ function sendKeysToElement(msg) {
if (el.type == "file") {
let p = val.join("");
fileInputElement = el;
// In e10s, we can only construct File objects in the parent process,
// so pass the filename to driver.js, which in turn passes them back
// to this frame script in receiveFiles.
sendSyncMessage("Marionette:getFiles",
- {value: p, command_id: command_id});
+ {value: p, command_id: command_id});
} else {
interactions.sendKeysToElement(curContainer, elementManager, id, val)
- .then(() => sendOk(command_id))
- .catch(e => sendError(e, command_id));
+ .then(() => sendOk(command_id))
+ .catch(e => sendError(e, command_id));
}
}
/**
* Clear the text of an element.
*/
function clearElement(id) {
try {
let el = elementManager.getKnownElement(id, curContainer);
if (el.type == "file") {
el.value = null;
} else {
- utils.clearElement(el);
+ atom.clearElement(el, curContainer.frame);
}
} catch (e) {
// Bug 964738: Newer atoms contain status codes which makes wrapping
// this in an error prototype that has a status property unnecessary
if (e.name == "InvalidElementStateError") {
throw new InvalidElementStateError(e.message);
} else {
throw e;
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -18,19 +18,17 @@ Cu.import("chrome://marionette/content/d
Cu.import("chrome://marionette/content/elements.js");
Cu.import("chrome://marionette/content/simpletest.js");
// Bug 1083711: Load transport.js as an SDK module instead of subscript
loader.loadSubScript("resource://devtools/shared/transport/transport.js");
// Preserve this import order:
var events = {};
-loader.loadSubScript("chrome://marionette/content/EventUtils.js", events);
loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", events);
-loader.loadSubScript("chrome://marionette/content/frame-manager.js");
const logger = Log.repository.getLogger("Marionette");
this.EXPORTED_SYMBOLS = ["MarionetteServer"];
const CONTENT_LISTENER_PREF = "marionette.contentListener";
const MANAGE_OFFLINE_STATUS_PREF = "network.gonk.manage-offline-status";