diff --git a/testing/marionette/elements.js b/testing/marionette/elements.js index 3f9eaf91ed71..297178d75f95 100644 --- a/testing/marionette/elements.js +++ b/testing/marionette/elements.js @@ -2,8 +2,11 @@ * 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/. */ -let {classes: Cc, interfaces: Ci, utils: Cu} = Components; +"use strict"; +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("chrome://marionette/content/atoms.js"); Cu.import("chrome://marionette/content/error.js"); /** @@ -299,7 +302,7 @@ ElementManager.prototype = { * as its members. */ applyNamedArgs: function EM_applyNamedArgs(args) { - namedArgs = {}; + let namedArgs = {}; args.forEach(function(arg) { if (arg && typeof(arg['__marionetteArgs']) === 'object') { for (let prop in arg['__marionetteArgs']) { @@ -592,10 +595,113 @@ ElementManager.prototype = { } return elements; }, -} +}; this.elements = {}; + elements.generateUUID = function() { let uuid = uuidGen.generateUUID().toString(); return uuid.substring(1, uuid.length - 1); }; + +/** + * This function generates a pair of coordinates relative to the viewport + * given a target element and coordinates relative to that element's + * top-left corner. + * + * @param {Node} node + * Target node. + * @param {number=} x + * Horizontal offset relative to target. Defaults to the centre of + * the target's bounding box. + * @param {number=} y + * Vertical offset relative to target. Defaults to the centre of + * the target's bounding box. + */ +// TODO(ato): Replicated from listener.js for the time being +elements.coordinates = function(node, x = undefined, y = undefined) { + let box = node.getBoundingClientRect(); + if (!x) { + x = box.width / 2.0; + } + if (!y) { + y = box.height / 2.0; + } + return { + x: box.left + x, + y: box.top + y, + }; +} + +/** + * This function returns true if the node is in the viewport. + * + * @param {Element} element + * Target element. + * @param {number=} x + * Horizontal offset relative to target. Defaults to the centre of + * the target's bounding box. + * @param {number=} y + * Vertical offset relative to target. Defaults to the centre of + * the target's bounding box. + */ +elements.inViewport = function(el, x = undefined, y = undefined) { + let win = el.ownerDocument.defaultView; + let c = elements.coordinates(el, x, y); + let vp = { + top: win.pageYOffset, + left: win.pageXOffset, + bottom: (win.pageYOffset + win.innerHeight), + right: (win.pageXOffset + win.innerWidth) + }; + + return (vp.left <= c.x + win.pageXOffset && + c.x + win.pageXOffset <= vp.right && + vp.top <= c.y + win.pageYOffset && + c.y + win.pageYOffset <= vp.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 {Element} element + * Element to check if visible. + * @param {Window} window + * Window object. + * @param {number=} x + * Horizontal offset relative to target. Defaults to the centre of + * the target's bounding box. + * @param {number=} y + * Vertical offset relative to target. Defaults to the centre of + * the target's bounding box. + */ +elements.checkVisible = function(el, win, x = undefined, y = undefined) { + // Bug 1094246: Webdriver's isShown doesn't work with content xul + let ns = atom.getElementAttribute(el, "namespaceURI", win); + if (ns.indexOf("there.is.only.xul") < 0 && + !atom.isElementDisplayed(el, win)) { + return false; + } + + if (el.tagName.toLowerCase() == "body") { + return true; + } + + if (!elements.inViewport(el, x, y)) { + if (el.scrollIntoView) { + el.scrollIntoView(false); + if (!elements.inViewport(el)) { + return false; + } + } else { + return false; + } + } + return true; +}; + +elements.isXULElement = function(el) { + let ns = atom.getElementAttribute(el, "namespaceURI"); + return ns.indexOf("there.is.only.xul") >= 0; +}; diff --git a/testing/marionette/interactions.js b/testing/marionette/interactions.js index 1679fb9e2f23..5cea585322bd 100644 --- a/testing/marionette/interactions.js +++ b/testing/marionette/interactions.js @@ -2,15 +2,17 @@ * 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/. */ -/* global Components, Accessibility, ElementNotVisibleError, - InvalidElementStateError, Interactions */ +"use strict"; -var {utils: Cu} = Components; +const {utils: Cu} = Components; -this.EXPORTED_SYMBOLS = ['Interactions']; +Cu.import("chrome://marionette/content/accessibility.js"); +Cu.import("chrome://marionette/content/atoms.js"); +Cu.import("chrome://marionette/content/error.js"); +Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/event.js"); -Cu.import('chrome://marionette/content/accessibility.js'); -Cu.import('chrome://marionette/content/error.js'); +this.EXPORTED_SYMBOLS = ["Interactions"]; /** * XUL elements that support disabled attribtue. @@ -72,32 +74,11 @@ const SELECTED_PROPERTY_SUPPORTED_XUL = new Set([ 'TAB' ]); -/** - * This function generates a pair of coordinates relative to the viewport given - * a target element and coordinates relative to that element's top-left corner. - * @param 'x', and 'y' are the relative to the target. - * If they are not specified, then the center of the target is used. - */ -function coordinates(target, x, y) { - let box = target.getBoundingClientRect(); - if (typeof x === 'undefined') { - x = box.width / 2; - } - if (typeof y === 'undefined') { - y = box.height / 2; - } - return { - x: box.left + x, - y: box.top + y - }; -} - /** * A collection of interactions available in marionette. * @type {Object} */ -this.Interactions = function(utils, getCapabilies) { - this.utils = utils; +this.Interactions = function(getCapabilies) { this.accessibility = new Accessibility(getCapabilies); }; @@ -115,22 +96,25 @@ Interactions.prototype = { */ clickElement(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); - let visible = this.checkVisible(container, el); + let visible = elements.checkVisible(el, container.frame); if (!visible) { throw new ElementNotVisibleError('Element is not visible'); } return this.accessibility.getAccessibleObject(el, true).then(acc => { this.accessibility.checkVisible(acc, el, visible); - if (this.utils.isElementEnabled(el)) { + if (atom.isElementEnabled(el)) { this.accessibility.checkEnabled(acc, el, true, container); this.accessibility.checkActionable(acc, el); - if (this.isXULElement(el)) { + if (elements.isXULElement(el)) { el.click(); } else { let rects = el.getClientRects(); - this.utils.synthesizeMouseAtPoint(rects[0].left + rects[0].width/2, - rects[0].top + rects[0].height/2, - {}, el.ownerDocument.defaultView); + let win = el.ownerDocument.defaultView; + event.synthesizeMouseAtPoint( + rects[0].left + rects[0].width / 2, + rects[0].top + rects[0].height / 2, + {} /* opts */, + win); } } else { throw new InvalidElementStateError('Element is not enabled'); @@ -159,8 +143,8 @@ Interactions.prototype = { let el = elementManager.getKnownElement(id, container); return this.accessibility.getAccessibleObject(el, true).then(acc => { this.accessibility.checkActionable(acc, el); - this.utils.sendKeysToElement( - container.frame, el, value, ignoreVisibility); + event.sendKeysToElement( + value, el, {ignoreVisibility: false}, container.frame); }); }, @@ -180,7 +164,7 @@ Interactions.prototype = { */ isElementDisplayed(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); - let displayed = this.utils.isElementDisplayed(el); + let displayed = atom.isElementDisplayed(el, container.frame); return this.accessibility.getAccessibleObject(el).then(acc => { this.accessibility.checkVisible(acc, el, displayed); return displayed; @@ -204,16 +188,16 @@ Interactions.prototype = { isElementEnabled(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); let enabled = true; - if (this.isXULElement(el)) { + if (elements.isXULElement(el)) { // Check if XUL element supports disabled attribute if (DISABLED_ATTRIBUTE_SUPPORTED_XUL.has(el.tagName.toUpperCase())) { - let disabled = this.utils.getElementAttribute(el, 'disabled'); + let disabled = atom.getElementAttribute(el, 'disabled', container.frame); if (disabled && disabled === 'true') { enabled = false; } } } else { - enabled = this.utils.isElementEnabled(el); + enabled = atom.isElementEnabled(el, container.frame); } return this.accessibility.getAccessibleObject(el).then(acc => { this.accessibility.checkEnabled(acc, el, enabled, container); @@ -238,7 +222,7 @@ Interactions.prototype = { isElementSelected(container, elementManager, id) { let el = elementManager.getKnownElement(id, container); let selected = true; - if (this.isXULElement(el)) { + if (elements.isXULElement(el)) { let tagName = el.tagName.toUpperCase(); if (CHECKED_PROPERTY_SUPPORTED_XUL.has(tagName)) { selected = el.checked; @@ -247,71 +231,11 @@ Interactions.prototype = { selected = el.selected; } } else { - selected = this.utils.isElementSelected(el); + selected = atom.isElementSelected(el, container.frame); } return this.accessibility.getAccessibleObject(el).then(acc => { this.accessibility.checkSelected(acc, el, selected); return selected; }); }, - - /** - * 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. - */ - checkVisible(container, el, x, y) { - // Bug 1094246 - Webdriver's isShown doesn't work with content xul - if (!this.isXULElement(el)) { - //check if the element is visible - let visible = this.utils.isElementDisplayed(el); - if (!visible) { - return false; - } - } - - if (el.tagName.toLowerCase() === 'body') { - return true; - } - if (!this.elementInViewport(container, el, x, y)) { - //check if scroll function exist. If so, call it. - if (el.scrollIntoView) { - el.scrollIntoView(false); - if (!this.elementInViewport(container, el)) { - return false; - } - } - else { - return false; - } - } - return true; - }, - - isXULElement(el) { - return this.utils.getElementAttribute(el, 'namespaceURI').indexOf( - 'there.is.only.xul') >= 0; - }, - - /** - * 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. - */ - elementInViewport(container, el, x, y) { - let c = coordinates(el, x, y); - let win = container.frame; - let viewPort = { - top: win.pageYOffset, - left: win.pageXOffset, - bottom: win.pageYOffset + win.innerHeight, - right: win.pageXOffset + win.innerWidth - }; - return (viewPort.left <= c.x + win.pageXOffset && - c.x + win.pageXOffset <= viewPort.right && - viewPort.top <= c.y + win.pageYOffset && - c.y + win.pageYOffset <= viewPort.bottom); - } };