☠☠ backed out by e026141c2ce9 ☠ ☠ | |
author | Andreas Tolfsen <ato@mozilla.com> |
Wed, 15 Apr 2015 12:18:00 +0100 | |
changeset 269198 | 043a824dd7b749192a8c7ec3f1a8d3ba4d2619d0 |
parent 269143 | 9ae890059b3c2580a0e3ee31f39c6047798464e4 |
child 269199 | 9f2ad189edc93b4f9d8baec7311040b6ca871b5b |
push id | 4830 |
push user | jlund@mozilla.com |
push date | Mon, 29 Jun 2015 20:18:48 +0000 |
treeherder | mozilla-beta@4c2175bb0420 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1153832, 1107706 |
milestone | 40.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -150,18 +150,17 @@ ListenerProxy.prototype.__noSuchMethod__ this.mm.addMessageListener(val, removeListeners(val, valListener)); this.mm.addMessageListener(err, removeListeners(err, errListener)); }; listeners.remove = () => listeners.map(l => this.mm.removeMessageListener(l[0], l[1])); let okListener = () => resolve(); let valListener = msg => resolve(msg.json.value); - let errListener = msg => reject( - "error" in msg.objects ? msg.objects.error : msg.json); + let errListener = msg => reject(msg.objects.error); let handleDialog = function(subject, topic) { listeners.remove(); modal.removeHandler(handleDialog); this.sendAsync("cancelRequest"); resolve(); }.bind(this); @@ -2057,17 +2056,17 @@ GeckoDriver.prototype.clickElement = fun break; case Context.CONTENT: // We need to protect against the click causing an OOP frame to close. // This fires the mozbrowserclose event when it closes so we need to // listen for it and then just send an error back. The person making the // call should be aware something isnt right and handle accordingly this.addFrameCloseListener("click"); - yield this.listener.clickElement({id: id}); + yield this.listener.clickElement(id); break; } }; /** * Get a given attribute of an element. * * @param {string} id @@ -2081,17 +2080,17 @@ GeckoDriver.prototype.getElementAttribut switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); let el = this.curBrowser.elementManager.getKnownElement(id, win); resp.value = utils.getElementAttribute(el, name); break; case Context.CONTENT: - resp.value = yield this.listener.getElementAttribute({id: id, name: name}); + resp.value = yield this.listener.getElementAttribute(id, name); break; } }; /** * Get the text of an element, if any. Includes the text of all child * elements. * @@ -2107,17 +2106,17 @@ GeckoDriver.prototype.getElementText = f let win = this.getCurrentWindow(); let el = this.curBrowser.elementManager.getKnownElement(id, win); let lines = []; this.getVisibleText(el, lines); resp.value = lines.join("\n"); break; case Context.CONTENT: - resp.value = yield this.listener.getElementText({id: id}); + resp.value = yield this.listener.getElementText(id); break; } }; /** * Get the tag name of the element. * * @param {string} id @@ -2129,17 +2128,17 @@ GeckoDriver.prototype.getElementTagName switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); let el = this.curBrowser.elementManager.getKnownElement(id, win); resp.value = el.tagName.toLowerCase(); break; case Context.CONTENT: - resp.value = yield this.listener.getElementTagName({id: id}); + resp.value = yield this.listener.getElementTagName(id); break; } }; /** * Check if element is displayed. * * @param {string} id @@ -2219,17 +2218,17 @@ GeckoDriver.prototype.isElementEnabled = case Context.CHROME: // Selenium atom doesn't quite work here let win = this.getCurrentWindow(); let el = this.curBrowser.elementManager.getKnownElement(id, win); resp.value = !(!!el.disabled); break; case Context.CONTENT: - resp.value = yield this.listener.isElementEnabled({id: id}); + resp.value = yield this.listener.isElementEnabled(id); break; } }, /** * Check if element is selected. * * @param {string} id @@ -2265,17 +2264,17 @@ GeckoDriver.prototype.getElementSize = f case Context.CHROME: let win = this.getCurrentWindow(); let el = this.curBrowser.elementManager.getKnownElement(id, win); let rect = el.getBoundingClientRect(); resp.value = {width: rect.width, height: rect.height}; break; case Context.CONTENT: - resp.value = yield this.listener.getElementSize({id: id}); + resp.value = yield this.listener.getElementSize(id); break; } }; GeckoDriver.prototype.getElementRect = function(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2287,17 +2286,17 @@ GeckoDriver.prototype.getElementRect = f x: rect.x + win.pageXOffset, y: rect.y + win.pageYOffset, width: rect.width, height: rect.height }; break; case Context.CONTENT: - resp.value = yield this.listener.getElementRect({id: id}); + resp.value = yield this.listener.getElementRect(id); break; } }; /** * Send key presses to element after focusing on it. * * @param {string} id
--- a/testing/marionette/elements.js +++ b/testing/marionette/elements.js @@ -1,13 +1,16 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* 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/. */ +let {utils: Cu} = Components; + +Cu.import("chrome://marionette/content/error.js"); + /** * The ElementManager manages DOM references and interactions with elements. * According to the WebDriver spec (http://code.google.com/p/selenium/wiki/JsonWireProtocol), the * server sends the client an element reference, and maintains the map of reference to element. * The client uses this reference when querying/interacting with the element, and the * server uses maps this reference to the actual element when it executes the command. */ @@ -23,36 +26,30 @@ this.EXPORTED_SYMBOLS = [ "TAG", "XPATH", "ANON", "ANON_ATTRIBUTE" ]; const DOCUMENT_POSITION_DISCONNECTED = 1; -let uuidGen = Components.classes["@mozilla.org/uuid-generator;1"] - .getService(Components.interfaces.nsIUUIDGenerator); +const uuidGen = Components.classes["@mozilla.org/uuid-generator;1"] + .getService(Components.interfaces.nsIUUIDGenerator); this.CLASS_NAME = "class name"; this.SELECTOR = "css selector"; this.ID = "id"; this.NAME = "name"; this.LINK_TEXT = "link text"; this.PARTIAL_LINK_TEXT = "partial link text"; this.TAG = "tag name"; this.XPATH = "xpath"; this.ANON= "anon"; this.ANON_ATTRIBUTE = "anon attribute"; -function ElementException(msg, num, stack) { - this.message = msg; - this.code = num; - this.stack = stack; -} - this.Accessibility = function Accessibility() { // A flag indicating whether the accessibility issue should be logged or cause // an exception. Default: log to stdout. this.strict = false; // An interface for in-process accessibility clients // Note: we access it lazily to not enable accessibility when it is not needed Object.defineProperty(this, 'accessibleRetrieval', { configurable: true, @@ -180,17 +177,17 @@ Accessibility.prototype = { * Send an error message or log the error message in the log * @param String message */ handleErrorMessage(message) { if (!message) { return; } if (this.strict) { - throw new ElementException(message, 56, null); + throw new ElementNotAccessibleError(message); } dump(Date.now() + " Marionette: " + message); } }; this.ElementManager = function ElementManager(notSupported) { this.seenItems = {}; this.timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); @@ -219,29 +216,27 @@ ElementManager.prototype = { * @return string * Returns the server-assigned reference ID */ addToKnownElements: function EM_addToKnownElements(element) { for (let i in this.seenItems) { let foundEl = null; try { foundEl = this.seenItems[i].get(); - } - catch(e) {} + } catch (e) {} if (foundEl) { if (XPCNativeWrapper(foundEl) == XPCNativeWrapper(element)) { return i; } - } - else { - //cleanup reference to GC'd element + } else { + // cleanup reference to GC'd element delete this.seenItems[i]; } } - var id = uuidGen.generateUUID().toString(); + let id = uuidGen.generateUUID().toString(); this.seenItems[id] = Components.utils.getWeakReference(element); return id; }, /** * Retrieve element from its unique ID * * @param String id @@ -250,33 +245,34 @@ ElementManager.prototype = { * The window that contains the element * * @returns nsIDOMElement * Returns the element or throws Exception if not found */ getKnownElement: function EM_getKnownElement(id, win) { let el = this.seenItems[id]; if (!el) { - throw new ElementException("Element has not been seen before. Id given was " + id, 17, null); + throw new JavaScriptError("Element has not been seen before. Id given was " + id); } try { el = el.get(); } catch(e) { el = null; delete this.seenItems[id]; } // use XPCNativeWrapper to compare elements; see bug 834266 let wrappedWin = XPCNativeWrapper(win); if (!el || !(XPCNativeWrapper(el).ownerDocument == wrappedWin.document) || (XPCNativeWrapper(el).compareDocumentPosition(wrappedWin.document.documentElement) & DOCUMENT_POSITION_DISCONNECTED)) { - throw new ElementException("The element reference is stale. Either the element " + - "is no longer attached to the DOM or the page has been refreshed.", 10, null); + throw new StaleElementReferenceError( + "The element reference is stale. Either the element " + + "is no longer attached to the DOM or the page has been refreshed."); } return el; }, /** * Convert values to primitives that can be transported over the * Marionette protocol. * @@ -364,18 +360,19 @@ ElementManager.prototype = { converted.push(this.convertWrappedArguments(args[i], win)); } } else if (((typeof(args[this.elementKey]) === 'string') && args.hasOwnProperty(this.elementKey)) || ((typeof(args[this.w3cElementKey]) === 'string') && args.hasOwnProperty(this.w3cElementKey))) { let elementUniqueIdentifier = args[this.w3cElementKey] ? args[this.w3cElementKey] : args[this.elementKey]; converted = this.getKnownElement(elementUniqueIdentifier, win); - if (converted == null) - throw new ElementException("Unknown element: " + elementUniqueIdentifier, 500, null); + if (converted == null) { + throw new WebDriverError(`Unknown element: ${elementUniqueIdentifier}`); + } } else { converted = {}; for (let prop in args) { converted[prop] = this.convertWrappedArguments(args[prop], win); } } break; @@ -438,17 +435,17 @@ ElementManager.prototype = { * @return nsIDOMElement or list of nsIDOMElements * Returns the element(s) by calling the on_success function. */ find: function EM_find(win, values, searchTimeout, all, on_success, on_error, command_id) { let startTime = values.time ? values.time : new Date().getTime(); let startNode = (values.element != undefined) ? this.getKnownElement(values.element, win) : win.document; if (this.elementStrategies.indexOf(values.using) < 0) { - throw new ElementException("No such strategy.", 32, null); + throw new InvalidSelectorError(`No such strategy: ${values.using}`); } let found = all ? this.findElements(values.using, values.value, win.document, startNode) : this.findElement(values.using, values.value, win.document, startNode); let type = Object.prototype.toString.call(found); let isArrayLike = ((type == '[object Array]') || (type == '[object HTMLCollection]') || (type == '[object NodeList]')); if (found == null || (isArrayLike && found.length <= 0)) { if (!searchTimeout || new Date().getTime() - startTime > searchTimeout) { if (all) { @@ -456,17 +453,17 @@ ElementManager.prototype = { } else { // Format message depending on strategy if necessary let message = "Unable to locate element: " + values.value; if (values.using == ANON) { message = "Unable to locate anonymous children"; } else if (values.using == ANON_ATTRIBUTE) { message = "Unable to locate anonymous element: " + JSON.stringify(values.value); } - on_error({message: message, code: 7}, command_id); + on_error(new NoSuchElementError(message), command_id); } } else { values.time = startTime; this.timer.initWithCallback(this.find.bind(this, win, values, searchTimeout, all, on_success, on_error, command_id), 100, @@ -589,17 +586,17 @@ ElementManager.prototype = { element = element[0]; } break; case ANON_ATTRIBUTE: let attr = Object.keys(value)[0]; element = rootNode.getAnonymousElementByAttribute(startNode, attr, value[attr]); break; default: - throw new ElementException("No such strategy", 500, null); + throw new WebDriverError("No such strategy"); } return element; }, /** * Helper method to find. Finds all element using find's criteria * * @param string using @@ -656,13 +653,13 @@ ElementManager.prototype = { case ANON_ATTRIBUTE: let attr = Object.keys(value)[0]; let el = rootNode.getAnonymousElementByAttribute(startNode, attr, value[attr]); if (el != null) { elements = [el]; } break; default: - throw new ElementException("No such strategy", 500, null); + throw new WebDriverError("No such strategy"); } return elements; }, }
--- a/testing/marionette/error.js +++ b/testing/marionette/error.js @@ -2,28 +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/. */ "use strict"; const {utils: Cu} = Components; const errors = [ + "ElementNotAccessibleError", "ElementNotVisibleError", "FrameSendFailureError", "FrameSendNotInitializedError", "IllegalArgumentError", "InvalidElementStateError", + "InvalidSelectorError", "JavaScriptError", "NoAlertOpenError", "NoSuchElementError", "NoSuchFrameError", "NoSuchWindowError", "ScriptTimeoutError", "SessionNotCreatedError", + "StaleElementReferenceError", "TimeoutError", "UnknownCommandError", "UnknownError", "UnsupportedOperationError", "WebDriverError", ]; this.EXPORTED_SYMBOLS = ["error"].concat(errors); @@ -34,21 +37,16 @@ error.toJSON = function(err) { return { message: err.message, stacktrace: err.stack || null, status: err.code }; }; /** - * Gets WebDriver error by its Selenium status code number. - */ -error.byCode = n => lookup.get(n); - -/** * Determines if the given status code is successful. */ error.isSuccess = code => code === 0; /** * Old-style errors are objects that has all of the properties * "message", "code", and "stack". * @@ -125,16 +123,24 @@ this.WebDriverError = function(msg) { Error.call(this, msg); this.name = "WebDriverError"; this.message = msg; this.status = "webdriver error"; this.code = 500; // overridden }; WebDriverError.prototype = Object.create(Error.prototype); +this.ElementNotAccessibleError = function(msg) { + WebDriverError.call(this, msg); + this.name = "ElementNotAccessibleError"; + this.status = "element not accessible"; + this.code = 56; +}; +ElementNotAccessibleError.prototype = Object.create(WebDriverError.prototype); + this.ElementNotVisibleError = function(msg) { WebDriverError.call(this, msg); this.name = "ElementNotVisibleError"; this.status = "element not visible"; this.code = 11; }; ElementNotVisibleError.prototype = Object.create(WebDriverError.prototype); @@ -171,16 +177,24 @@ IllegalArgumentError.prototype = Object. this.InvalidElementStateError = function(msg) { WebDriverError.call(this, msg); this.name = "InvalidElementStateError"; this.status = "invalid element state"; this.code = 12; }; InvalidElementStateError.prototype = Object.create(WebDriverError.prototype); +this.InvalidSelectorError = function(msg) { + WebDriverError.call(this, msg); + this.name = "InvalidSelectorError"; + this.status = "invalid selector"; + this.code = 32; +}; +InvalidSelectorError.prototype = Object.create(WebDriverError.prototype); + /** * Creates an error message for a JavaScript error thrown during * executeScript or executeAsyncScript. * * @param {Error} err * An Error object passed to a catch block or a message. * @param {string} fnName * The name of the function to use in the stack trace message @@ -265,19 +279,27 @@ this.ScriptTimeoutError = function(msg) ScriptTimeoutError.prototype = Object.create(WebDriverError.prototype); this.SessionNotCreatedError = function(msg) { WebDriverError.call(this, msg); this.name = "SessionNotCreatedError"; this.status = "session not created"; // should be 33 to match Selenium this.code = 71; -} +}; SessionNotCreatedError.prototype = Object.create(WebDriverError.prototype); +this.StaleElementReferenceError = function(msg) { + WebDriverError.call(this, msg); + this.name = "StaleElementReferenceError"; + this.status = "stale element reference"; + this.code = 10; +}; +StaleElementReferenceError.prototype = Object.create(WebDriverError.prototype); + this.TimeoutError = function(msg) { WebDriverError.call(this, msg); this.name = "TimeoutError"; this.status = "timeout"; this.code = 21; }; TimeoutError.prototype = Object.create(WebDriverError.prototype); @@ -299,29 +321,8 @@ UnknownError.prototype = Object.create(W this.UnsupportedOperationError = function(msg) { WebDriverError.call(this, msg); this.name = "UnsupportedOperationError"; this.status = "unsupported operation"; this.code = 405; }; UnsupportedOperationError.prototype = Object.create(WebDriverError.prototype); - -const errorObjs = [ - this.ElementNotVisibleError, - this.FrameSendFailureError, - this.FrameSendNotInitializedError, - this.IllegalArgumentError, - this.InvalidElementStateError, - this.JavaScriptError, - this.NoAlertOpenError, - this.NoSuchElementError, - this.NoSuchFrameError, - this.NoSuchWindowError, - this.ScriptTimeoutError, - this.SessionNotCreatedError, - this.TimeoutError, - this.UnknownCommandError, - this.UnknownError, - this.UnsupportedOperationError, - this.WebDriverError, -]; -const lookup = new Map(errorObjs.map(err => [new err().code, err]));
--- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -10,16 +10,17 @@ let uuidGen = Cc["@mozilla.org/uuid-gene let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] .getService(Ci.mozIJSSubScriptLoader); loader.loadSubScript("chrome://marionette/content/simpletest.js"); loader.loadSubScript("chrome://marionette/content/common.js"); loader.loadSubScript("chrome://marionette/content/actions.js"); Cu.import("chrome://marionette/content/elements.js"); +Cu.import("chrome://marionette/content/error.js"); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); let 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); @@ -88,17 +89,17 @@ let modalHandler = function() { }; /** * Called when listener is first started up. * The listener sends its unique window ID and its current URI to the actor. * If the actor returns an ID, we start the listeners. Otherwise, nothing happens. */ function registerSelf() { - let msg = {value: winUtil.outerWindowID} + let msg = {value: winUtil.outerWindowID}; // register will have the ID and a boolean describing if this is the main process or not let register = sendSyncMessage("Marionette:register", msg); if (register[0]) { let {id, remotenessChange} = register[0][0]; listenerId = id; if (typeof id != "undefined") { // check if we're the main process @@ -141,30 +142,61 @@ function emitTouchEventForIFrame(message typeForUtils = domWindowUtils.TOUCH_CONTACT; break; } domWindowUtils.sendNativeTouchPoint(identifier, typeForUtils, Math.round(message.screenX * ratio), Math.round(message.screenY * ratio), message.force, 90); } +function dispatch(fn) { + return function(msg) { + let id = msg.json.command_id; + try { + let rv; + if (typeof msg.json == "undefined" || msg.json instanceof Array) { + rv = fn.apply(null, msg.json); + } else { + rv = fn(msg.json); + } + + if (typeof rv == "undefined") { + sendOk(id); + } else { + sendResponse({value: rv}, id); + } + } catch (e) { + sendError(e, id); + } + }; +} + /** * Add a message listener that's tied to our listenerId. */ function addMessageListenerId(messageName, handler) { addMessageListener(messageName + listenerId, handler); } /** * Remove a message listener that's tied to our listenerId. */ function removeMessageListenerId(messageName, handler) { removeMessageListener(messageName + listenerId, handler); } +let getElementSizeFn = dispatch(getElementSize); +let getActiveElementFn = dispatch(getActiveElement); +let clickElementFn = dispatch(clickElement); +let getElementAttributeFn = dispatch(getElementAttribute); +let getElementTextFn = dispatch(getElementText); +let getElementTagNameFn = dispatch(getElementTagName); +let getElementRectFn = dispatch(getElementRect); +let isElementEnabledFn = dispatch(isElementEnabled); + /** * Start all message listeners */ function startListeners() { addMessageListenerId("Marionette:newSession", newSession); addMessageListenerId("Marionette:executeScript", executeScript); addMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript); addMessageListenerId("Marionette:executeJSScript", executeJSScript); @@ -177,27 +209,27 @@ function startListeners() { addMessageListenerId("Marionette:getCurrentUrl", getCurrentUrl); addMessageListenerId("Marionette:getTitle", getTitle); addMessageListenerId("Marionette:getPageSource", getPageSource); addMessageListenerId("Marionette:goBack", goBack); addMessageListenerId("Marionette:goForward", goForward); addMessageListenerId("Marionette:refresh", refresh); addMessageListenerId("Marionette:findElementContent", findElementContent); addMessageListenerId("Marionette:findElementsContent", findElementsContent); - addMessageListenerId("Marionette:getActiveElement", getActiveElement); - addMessageListenerId("Marionette:clickElement", clickElement); - addMessageListenerId("Marionette:getElementAttribute", getElementAttribute); - addMessageListenerId("Marionette:getElementText", getElementText); - addMessageListenerId("Marionette:getElementTagName", getElementTagName); + addMessageListenerId("Marionette:getActiveElement", getActiveElementFn); + addMessageListenerId("Marionette:clickElement", clickElementFn); + addMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn); + addMessageListenerId("Marionette:getElementText", getElementTextFn); + addMessageListenerId("Marionette:getElementTagName", getElementTagNameFn); addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed); addMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssProperty); addMessageListenerId("Marionette:submitElement", submitElement); - addMessageListenerId("Marionette:getElementSize", getElementSize); - addMessageListenerId("Marionette:getElementRect", getElementRect); - addMessageListenerId("Marionette:isElementEnabled", isElementEnabled); + addMessageListenerId("Marionette:getElementSize", getElementSizeFn); // deprecated + addMessageListenerId("Marionette:getElementRect", getElementRectFn); + addMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn); addMessageListenerId("Marionette:isElementSelected", isElementSelected); addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement); addMessageListenerId("Marionette:getElementLocation", getElementLocation); //deprecated addMessageListenerId("Marionette:clearElement", clearElement); addMessageListenerId("Marionette:switchToFrame", switchToFrame); addMessageListenerId("Marionette:deleteSession", deleteSession); addMessageListenerId("Marionette:sleepSession", sleepSession); addMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult); @@ -282,27 +314,27 @@ function deleteSession(msg) { removeMessageListenerId("Marionette:getTitle", getTitle); removeMessageListenerId("Marionette:getPageSource", getPageSource); removeMessageListenerId("Marionette:getCurrentUrl", getCurrentUrl); removeMessageListenerId("Marionette:goBack", goBack); removeMessageListenerId("Marionette:goForward", goForward); removeMessageListenerId("Marionette:refresh", refresh); removeMessageListenerId("Marionette:findElementContent", findElementContent); removeMessageListenerId("Marionette:findElementsContent", findElementsContent); - removeMessageListenerId("Marionette:getActiveElement", getActiveElement); - removeMessageListenerId("Marionette:clickElement", clickElement); - removeMessageListenerId("Marionette:getElementAttribute", getElementAttribute); - removeMessageListenerId("Marionette:getElementText", getElementText); - removeMessageListenerId("Marionette:getElementTagName", getElementTagName); + removeMessageListenerId("Marionette:getActiveElement", getActiveElementFn); + removeMessageListenerId("Marionette:clickElement", clickElementFn); + removeMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn); + removeMessageListenerId("Marionette:getElementText", getElementTextFn); + removeMessageListenerId("Marionette:getElementTagName", getElementTagNameFn); removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed); removeMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssProperty); removeMessageListenerId("Marionette:submitElement", submitElement); - removeMessageListenerId("Marionette:getElementSize", getElementSize); //deprecated - removeMessageListenerId("Marionette:getElementRect", getElementRect); - removeMessageListenerId("Marionette:isElementEnabled", isElementEnabled); + removeMessageListenerId("Marionette:getElementSize", getElementSizeFn); // deprecated + removeMessageListenerId("Marionette:getElementRect", getElementRectFn); + removeMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn); removeMessageListenerId("Marionette:isElementSelected", isElementSelected); removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement); removeMessageListenerId("Marionette:getElementLocation", getElementLocation); removeMessageListenerId("Marionette:clearElement", clearElement); removeMessageListenerId("Marionette:switchToFrame", switchToFrame); removeMessageListenerId("Marionette:deleteSession", deleteSession); removeMessageListenerId("Marionette:sleepSession", sleepSession); removeMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult); @@ -326,50 +358,52 @@ function deleteSession(msg) { /* * Helper methods */ /** * Generic method to send a message to the server */ -function sendToServer(msg, value, command_id) { - if (command_id) { - value.command_id = command_id; +function sendToServer(name, data, objs, id) { + if (!data) { + data = {} } - sendAsyncMessage(msg, value); + if (id) { + data.command_id = id; + } + sendAsyncMessage(name, data, objs); } /** * Send response back to server */ function sendResponse(value, command_id) { - sendToServer("Marionette:done", value, command_id); + sendToServer("Marionette:done", value, null, command_id); } /** * Send ack back to server */ function sendOk(command_id) { - sendToServer("Marionette:ok", {}, command_id); + sendToServer("Marionette:ok", null, null, command_id); } /** * Send log message to server */ function sendLog(msg) { - sendToServer("Marionette:log", { message: msg }); + sendToServer("Marionette:log", {message: msg}); } /** * Send error message to server */ -function sendError(msg, code, stack, cmdId) { - let payload = {message: msg, code: code, stack: stack}; - sendToServer("Marionette:error", payload, cmdId); +function sendError(err, cmdId) { + sendToServer("Marionette:error", null, {error: err}, cmdId); } /** * Clear test values after completion of test */ function resetValues() { sandbox = null; curFrame = content; @@ -453,97 +487,90 @@ function createExecuteContentSandbox(aWi }); } else { XPCOMUtils.defineLazyGetter(sandbox, 'SpecialPowers', function() { return new SpecialPowers(aWindow); }); } - sandbox.asyncComplete = function sandbox_asyncComplete(value, status, stack, commandId) { - if (commandId == asyncTestCommandId) { + sandbox.asyncComplete = function(obj, id) { + if (id == asyncTestCommandId) { curFrame.removeEventListener("unload", onunload, false); curFrame.clearTimeout(asyncTestTimeoutId); if (inactivityTimeoutId != null) { curFrame.clearTimeout(inactivityTimeoutId); } - sendSyncMessage("Marionette:shareData", - {log: elementManager.wrapValue(marionetteLogObj.getLogs())}); + {log: elementManager.wrapValue(marionetteLogObj.getLogs())}); marionetteLogObj.clearLogs(); - if (status == 0){ + if (error.isError(obj)) { + sendError(obj, id); + } else { if (Object.keys(_emu_cbs).length) { _emu_cbs = {}; - sendError("Emulator callback still pending when finish() called", - 500, null, commandId); + sendError({message: "Emulator callback still pending when finish() called"}, id); + } else { + sendResponse({value: elementManager.wrapValue(obj)}, id); } - else { - sendResponse({value: elementManager.wrapValue(value), status: status}, - commandId); - } - } - else { - sendError(value, status, stack, commandId); } asyncTestRunning = false; asyncTestTimeoutId = undefined; asyncTestCommandId = undefined; inactivityTimeoutId = null; } }; sandbox.finish = function sandbox_finish() { if (asyncTestRunning) { - sandbox.asyncComplete(marionette.generate_results(), 0, null, sandbox.asyncTestCommandId); + sandbox.asyncComplete(marionette.generate_results(), sandbox.asyncTestCommandId); } else { return marionette.generate_results(); } }; - sandbox.marionetteScriptFinished = function sandbox_marionetteScriptFinished(value) { - return sandbox.asyncComplete(value, 0, null, sandbox.asyncTestCommandId); - }; + sandbox.marionetteScriptFinished = val => + sandbox.asyncComplete(val, sandbox.asyncTestCommandId); return sandbox; } /** * Execute the given script either as a function body (executeScript) - * or directly (for 'mochitest' like JS Marionette tests) + * or directly (for mochitest like JS Marionette tests). */ function executeScript(msg, directInject) { // Set up inactivity timeout. if (msg.json.inactivityTimeout) { let setTimer = function() { - inactivityTimeoutId = curFrame.setTimeout(function() { - sendError('timed out due to inactivity', 28, null, asyncTestCommandId); + inactivityTimeoutId = curFrame.setTimeout(function() { + sendError(new ScriptTimeoutError("timed out due to inactivity"), asyncTestCommandId); }, msg.json.inactivityTimeout); }; setTimer(); - heartbeatCallback = function resetInactivityTimeout() { + heartbeatCallback = function() { curFrame.clearTimeout(inactivityTimeoutId); setTimer(); }; } asyncTestCommandId = msg.json.command_id; let script = msg.json.script; if (msg.json.newSandbox || !sandbox) { sandbox = createExecuteContentSandbox(curFrame, msg.json.timeout); if (!sandbox) { - sendError("Could not create sandbox!", 500, null, asyncTestCommandId); + sendError(new WebDriverError("Could not create sandbox!"), asyncTestCommandId); return; } - } - else { + } else { sandbox.asyncTestCommandId = asyncTestCommandId; } try { if (directInject) { if (importedScripts.exists()) { let stream = Components.classes["@mozilla.org/network/file-input-stream;1"]. createInstance(Components.interfaces.nsIFileInputStream); @@ -553,29 +580,28 @@ function executeScript(msg, directInject script = data + script; } let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file" ,0); sendSyncMessage("Marionette:shareData", {log: elementManager.wrapValue(marionetteLogObj.getLogs())}); marionetteLogObj.clearLogs(); if (res == undefined || res.passed == undefined) { - sendError("Marionette.finish() not called", 17, null, asyncTestCommandId); + sendError(new JavaScriptError("Marionette.finish() not called"), asyncTestCommandId); } else { sendResponse({value: elementManager.wrapValue(res)}, asyncTestCommandId); } } else { try { sandbox.__marionetteParams = Cu.cloneInto(elementManager.convertWrappedArguments( msg.json.args, curFrame), sandbox, { wrapReflectors: true }); - } - catch(e) { - sendError(e.message, e.code, e.stack, asyncTestCommandId); + } catch (e) { + sendError(e, asyncTestCommandId); return; } script = "let __marionetteFunc = function(){" + script + "};" + "__marionetteFunc.apply(null, __marionetteParams);"; if (importedScripts.exists()) { let stream = Components.classes["@mozilla.org/network/file-input-stream;1"]. createInstance(Components.interfaces.nsIFileInputStream); @@ -585,25 +611,24 @@ function executeScript(msg, directInject script = data + script; } let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file", 0); sendSyncMessage("Marionette:shareData", {log: elementManager.wrapValue(marionetteLogObj.getLogs())}); marionetteLogObj.clearLogs(); sendResponse({value: elementManager.wrapValue(res)}, asyncTestCommandId); } - } - catch (e) { - // 17 = JavascriptException - let error = createStackMessage(e, - "execute_script", - msg.json.filename, - msg.json.line, - script); - sendError(error[0], 17, error[1], asyncTestCommandId); + } catch (e) { + let err = new JavaScriptError( + e, + "execute_script", + msg.json.filename, + msg.json.line, + script); + sendError(err, asyncTestCommandId); } } /** * Sets the test name, used in logging messages. */ function setTestName(msg) { marionetteTestName = msg.json.value; @@ -637,77 +662,76 @@ function executeJSScript(msg) { * For executeAsync, it will return a response when marionetteScriptFinished/arguments[arguments.length-1] * method is called, or if it times out. */ function executeWithCallback(msg, useFinish) { // Set up inactivity timeout. if (msg.json.inactivityTimeout) { let setTimer = function() { inactivityTimeoutId = curFrame.setTimeout(function() { - sandbox.asyncComplete('timed out due to inactivity', 28, null, asyncTestCommandId); + sandbox.asyncComplete(new ScriptTimeout("timed out due to inactivity"), asyncTestCommandId); }, msg.json.inactivityTimeout); }; setTimer(); - heartbeatCallback = function resetInactivityTimeout() { + heartbeatCallback = function() { curFrame.clearTimeout(inactivityTimeoutId); setTimer(); }; } let script = msg.json.script; asyncTestCommandId = msg.json.command_id; onunload = function() { - sendError("unload was called", 17, null, asyncTestCommandId); + sendError(new JavaScriptError("unload was called"), asyncTestCommandId); }; curFrame.addEventListener("unload", onunload, false); if (msg.json.newSandbox || !sandbox) { sandbox = createExecuteContentSandbox(curFrame, msg.json.timeout); if (!sandbox) { - sendError("Could not create sandbox!", 17, null, asyncTestCommandId); + sendError(new JavaScriptError("Could not create sandbox!"), asyncTestCommandId); return; } } else { sandbox.asyncTestCommandId = asyncTestCommandId; } sandbox.tag = script; // Error code 28 is scriptTimeout, but spec says execute_async should return 21 (Timeout), // see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/execute_async. // However Selenium code returns 28, see // http://code.google.com/p/selenium/source/browse/trunk/javascript/firefox-driver/js/evaluate.js. // We'll stay compatible with the Selenium code. asyncTestTimeoutId = curFrame.setTimeout(function() { - sandbox.asyncComplete('timed out', 28, null, asyncTestCommandId); + sandbox.asyncComplete(new ScriptTimeoutError("timed out"), asyncTestCommandId); }, msg.json.timeout); originalOnError = curFrame.onerror; - curFrame.onerror = function errHandler(errMsg, url, line) { - sandbox.asyncComplete(errMsg, 17, "@" + url + ", line " + line, asyncTestCommandId); + curFrame.onerror = function errHandler(msg, url, line) { + sandbox.asyncComplete(new JavaScriptError(msg + "@" + url + ", line " + line), asyncTestCommandId); curFrame.onerror = originalOnError; }; let scriptSrc; if (useFinish) { if (msg.json.timeout == null || msg.json.timeout == 0) { - sendError("Please set a timeout", 21, null, asyncTestCommandId); + sendError(new TimeoutError("Please set a timeout"), asyncTestCommandId); } scriptSrc = script; } else { try { sandbox.__marionetteParams = Cu.cloneInto(elementManager.convertWrappedArguments( msg.json.args, curFrame), sandbox, { wrapReflectors: true }); - } - catch(e) { - sendError(e.message, e.code, e.stack, asyncTestCommandId); + } catch (e) { + sendError(e, asyncTestCommandId); return; } scriptSrc = "__marionetteParams.push(marionetteScriptFinished);" + "let __marionetteFunc = function() { " + script + "};" + "__marionetteFunc.apply(null, __marionetteParams); "; } @@ -718,23 +742,23 @@ function executeWithCallback(msg, useFin createInstance(Ci.nsIFileInputStream); stream.init(importedScripts, -1, 0, 0); let data = NetUtil.readInputStreamToString(stream, stream.available()); stream.close(); scriptSrc = data + scriptSrc; } Cu.evalInSandbox(scriptSrc, sandbox, "1.8", "dummy file", 0); } catch (e) { - // 17 = JavascriptException - let error = createStackMessage(e, - "execute_async_script", - msg.json.filename, - msg.json.line, - scriptSrc); - sandbox.asyncComplete(error[0], 17, error[1], asyncTestCommandId); + let err = new JavaScriptError( + e, + "execute_async_script", + msg.json.filename, + msg.json.line, + scriptSrc); + sandbox.asyncComplete(err, asyncTestCommandId); } } /** * This function creates a touch event given a touch type and a touch */ function emitTouchEvent(type, touch) { if (!wasInterrupted()) { @@ -851,35 +875,34 @@ function singleTap(msg) { let command_id = msg.json.command_id; try { let el = elementManager.getKnownElement(msg.json.id, curFrame); let acc = accessibility.getAccessibleObject(el, true); // after this block, the element will be scrolled into view let visible = checkVisible(el, msg.json.corx, msg.json.cory); checkVisibleAccessibility(acc, visible); if (!visible) { - sendError("Element is not currently visible and may not be manipulated", 11, null, command_id); + sendError(new ElementNotVisibleError("Element is not currently visible and may not be manipulated"), command_id); return; } checkActionableAccessibility(acc); if (!curFrame.document.createTouch) { actions.mouseEventsOnly = true; } let c = coordinates(el, msg.json.corx, msg.json.cory); if (!actions.mouseEventsOnly) { let touchId = actions.nextTouchId++; let touch = createATouch(el, c.x, c.y, touchId); emitTouchEvent('touchstart', touch); emitTouchEvent('touchend', touch); } actions.mouseTap(el.ownerDocument, c.x, c.y); - sendOk(msg.json.command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, msg.json.command_id); + sendOk(command_id); + } catch (e) { + sendError(e, command_id); } } /** * Check if the element's unavailable accessibility state matches the enabled * state * @param nsIAccessible object * @param Boolean enabled element's enabled state @@ -954,37 +977,33 @@ function createATouch(el, corx, cory, to * Function to start action chain on one finger */ function actionChain(msg) { let command_id = msg.json.command_id; let args = msg.json.chain; let touchId = msg.json.nextId; let callbacks = {}; - callbacks.onSuccess = (value) => { - sendResponse(value, command_id); - }; - callbacks.onError = (message, code, trace) => { - sendError(message, code, trace, msg.json.command_id); - }; + callbacks.onSuccess = value => sendResponse(value, command_id); + callbacks.onError = err => sendError(err, command_id); let touchProvider = {}; touchProvider.createATouch = createATouch; touchProvider.emitTouchEvent = emitTouchEvent; try { actions.dispatchActions( args, touchId, curFrame, elementManager, callbacks, touchProvider); } catch (e) { - sendError(e.message, e.code, e.stack, command_id); + sendError(e, command_id); } } /** * Function to emit touch events which allow multi touch on the screen * @param type represents the type of event, touch represents the current touch,touches are all pending touches */ function emitMultiEvents(type, touch, touches) { @@ -1139,19 +1158,18 @@ function multiAction(msg) { } concurrentEvent.push(row); } // now concurrent event is made of sets where each set contain a list of actions that need to be fired. // note: each action belongs to a different finger // pendingTouches keeps track of current touches that's on the screen let pendingTouches = []; setDispatch(concurrentEvent, pendingTouches, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, msg.json.command_id); + } catch (e) { + sendError(e, command_id); } } /* * This implements the latter part of a get request (for the case we need to resume one * when a remoteness update happens in the middle of a navigate request). This is most of * of the work of a navigate request, but doesn't assume DOMContentLoaded is yet to fire. */ @@ -1173,29 +1191,27 @@ function pollForReadyState(msg, start, c if (curFrame.document.readyState == "complete") { callback(); sendOk(command_id); } else if (curFrame.document.readyState == "interactive" && aboutErrorRegex.exec(curFrame.document.baseURI) && !curFrame.document.baseURI.startsWith(url)) { // We have reached an error url without requesting it. callback(); - sendError("Error loading page", 13, null, command_id); + sendError(new UnknownError("Error loading page"), command_id); } else if (curFrame.document.readyState == "interactive" && curFrame.document.baseURI.startsWith("about:")) { callback(); sendOk(command_id); } else { navTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); } - } - else { + } else { callback(); - sendError("Error loading page, timed out (checkLoad)", 21, null, - command_id); + sendError(new TimeoutError("Error loading page, timed out (checkLoad)"), command_id); } } checkLoad(); } /** * Navigate to the given URL. The operation will be performed on the * current browser context, and handles the case where we navigate @@ -1215,18 +1231,17 @@ function get(msg) { removeEventListener("DOMContentLoaded", onDOMContentLoaded, false); onDOMContentLoaded = null; }); } }; function timerFunc() { removeEventListener("DOMContentLoaded", onDOMContentLoaded, false); - sendError("Error loading page, timed out (onDOMContentLoaded)", 21, - null, msg.json.command_id); + sendError(new TimeoutError("Error loading page, timed out (onDOMContentLoaded)"), msg.json.command_id); } if (msg.json.pageTimeout != null) { navTimer.initWithCallback(timerFunc, msg.json.pageTimeout, Ci.nsITimer.TYPE_ONE_SHOT); } addEventListener("DOMContentLoaded", onDOMContentLoaded, false); curFrame.location = msg.json.url; } @@ -1301,138 +1316,129 @@ function refresh(msg) { } /** * Find an element in the document using requested search strategy */ function findElementContent(msg) { let command_id = msg.json.command_id; try { - let on_success = function(el, cmd_id) { sendResponse({value: el}, cmd_id) }; - let on_error = function(e, cmd_id) { sendError(e.message, e.code, null, cmd_id); }; + let onSuccess = (el, id) => sendResponse({value: el}, id); + let onError = (err, id) => sendError(err, id); elementManager.find(curFrame, msg.json, msg.json.searchTimeout, - false /* all */, on_success, on_error, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + false /* all */, onSuccess, onError, command_id); + } catch (e) { + sendError(e, command_id); } } /** * Find elements in the document using requested search strategy */ function findElementsContent(msg) { let command_id = msg.json.command_id; try { - let on_success = function(els, cmd_id) { sendResponse({value: els}, cmd_id); }; - let on_error = function(e, cmd_id) { sendError(e.message, e.code, null, cmd_id); }; + let onSuccess = (els, id) => sendResponse({value: els}, id); + let onError = (err, id) => sendError(err, id); elementManager.find(curFrame, msg.json, msg.json.searchTimeout, - true /* all */, on_success, on_error, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + true /* all */, onSuccess, onError, command_id); + } catch (e) { + sendError(e, command_id); } } /** - * Find and return the active element on the page + * Find and return the active element on the page. + * + * @return {WebElement} + * Reference to web element. */ -function getActiveElement(msg) { - let command_id = msg.json.command_id; - var element = curFrame.document.activeElement; - var id = elementManager.addToKnownElements(element); - sendResponse({value: id}, command_id); +function getActiveElement() { + let el = curFrame.document.activeElement; + return elementManager.addToKnownElements(el); } /** - * Send click event to element + * Send click event to element. + * + * @param {WebElement} id + * Reference to the web element to click. */ -function clickElement(msg) { - let command_id = msg.json.command_id; - let el; - try { - el = elementManager.getKnownElement(msg.json.id, curFrame); - let acc = accessibility.getAccessibleObject(el, true); - let visible = checkVisible(el); - checkVisibleAccessibility(acc, visible); - if (visible) { - checkActionableAccessibility(acc); - if (utils.isElementEnabled(el)) { - utils.synthesizeMouseAtCenter(el, {}, el.ownerDocument.defaultView) - } - else { - sendError("Element is not Enabled", 12, null, command_id) - } - } - else { - sendError("Element is not visible", 11, null, command_id) - } - sendOk(command_id); +function clickElement(id) { + let el = elementManager.getKnownElement(id, curFrame); + let acc = accessibility.getAccessibleObject(el, true); + let visible = checkVisible(el); + checkVisibleAccessibility(acc, visible); + if (!visible) { + throw new ElementNotVisibleError("Element is not visible"); } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + checkActionableAccessibility(acc); + if (utils.isElementEnabled(el)) { + utils.synthesizeMouseAtCenter(el, {}, el.ownerDocument.defaultView); + } else { + throw new InvalidElementStateError("Element is not Enabled"); } } /** - * Get a given attribute of an element + * Get a given attribute of an element. + * + * @param {WebElement} id + * Reference to the web element to get the attribute of. + * @param {string} name + * Name of the attribute. + * + * @return {string} + * The value of the attribute. */ -function getElementAttribute(msg) { - let command_id = msg.json.command_id; - try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); - sendResponse({value: utils.getElementAttribute(el, msg.json.name)}, - command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); - } +function getElementAttribute(id, name) { + let el = elementManager.getKnownElement(id, curFrame); + return utils.getElementAttribute(el, name); } /** * 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(msg) { - let command_id = msg.json.command_id; - try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); - sendResponse({value: utils.getElementText(el)}, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); - } +function getElementText(id) { + let el = elementManager.getKnownElement(id, curFrame); + return utils.getElementText(el); } /** * Get the tag name of an element. + * + * @param {WebElement} id + * Reference to web element. + * + * @return {string} + * Tag name of element. */ -function getElementTagName(msg) { - let command_id = msg.json.command_id; - try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); - sendResponse({value: el.tagName.toLowerCase()}, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); - } +function getElementTagName(id) { + let el = elementManager.getKnownElement(id, curFrame); + return el.tagName.toLowerCase(); } /** * Check if element is displayed */ function isElementDisplayed(msg) { let command_id = msg.json.command_id; try { let el = elementManager.getKnownElement(msg.json.id, curFrame); let displayed = utils.isElementDisplayed(el); checkVisibleAccessibility(accessibility.getAccessibleObject(el), displayed); sendResponse({value: displayed}, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + } catch (e) { + sendError(e, command_id); } } /** * Return the property of the computed style of an element * * @param object aRequest * 'element' member holds the reference id to @@ -1441,19 +1447,18 @@ function isElementDisplayed(msg) { */ function getElementValueOfCssProperty(msg){ let command_id = msg.json.command_id; let propertyName = msg.json.propertyName; try { let el = elementManager.getKnownElement(msg.json.id, curFrame); sendResponse({value: curFrame.document.defaultView.getComputedStyle(el, null).getPropertyValue(propertyName)}, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + } catch (e) { + sendError(e, command_id); } } /** * Submit a form on a content page by either using form or element in a form * @param object msg * 'json' JSON object containing 'id' member of the element */ @@ -1462,90 +1467,85 @@ function submitElement (msg) { try { let el = elementManager.getKnownElement(msg.json.id, curFrame); while (el.parentNode != null && el.tagName.toLowerCase() != 'form') { el = el.parentNode; } if (el.tagName && el.tagName.toLowerCase() == 'form') { el.submit(); sendOk(command_id); - } - else { - sendError("Element is not a form element or in a form", 7, null, command_id); + } else { + sendError(new NoSuchElementError("Element is not a form element or in a form"), command_id); } - - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); - } -} - -/** - * Get the size of the element and return it - */ -function getElementSize(msg){ - let command_id = msg.json.command_id; - try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); - let clientRect = el.getBoundingClientRect(); - sendResponse({value: {width: clientRect.width, height: clientRect.height}}, - command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + } catch (e) { + sendError(e, command_id); } } /** - * Get the size of the element and return it + * Get the size of the element. + * + * @param {WebElement} id + * Web element reference. + * + * @return {Object.<string, number>} + * The width/height dimensions of th element. */ -function getElementRect(msg){ - let command_id = msg.json.command_id; - try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); - let clientRect = el.getBoundingClientRect(); - sendResponse({value: {x: clientRect.x + curFrame.pageXOffset, - y: clientRect.y + curFrame.pageYOffset, - width: clientRect.width, - height: clientRect.height}}, - command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); - } +function getElementSize(id) { + let el = elementManager.getKnownElement(id, curFrame); + let clientRect = el.getBoundingClientRect(); + return {width: clientRect.width, height: clientRect.height}; } /** - * Check if element is enabled + * Get the size of the element. + * + * @param {WebElement} id + * Reference to web element. + * + * @return {Object.<string, number>} + * The x, y, width, and height properties of the element. */ -function isElementEnabled(msg) { - let command_id = msg.json.command_id; - try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); - let enabled = utils.isElementEnabled(el); - checkEnabledStateAccessibility(accessibility.getAccessibleObject(el), - enabled); - sendResponse({value: enabled}, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); - } +function getElementRect(id) { + let el = elementManager.getKnownElement(id, curFrame); + let clientRect = el.getBoundingClientRect(); + return { + x: clientRect.x + curFrame.pageXOffset, + y: clientRect.y + curFrame.pageYOffset, + width: clientRect.width, + height: clientRect.height + }; +} + +/** + * Check if element is enabled. + * + * @param {WebElement} id + * Reference to web element. + * + * @return {boolean} + * True if enabled, false otherwise. + */ +function isElementEnabled(id) { + let el = elementManager.getKnownElement(id, curFrame); + let enabled = utils.isElementEnabled(el); + checkEnabledStateAccessibility(accessibility.getAccessibleObject(el), enabled); + return enabled; } /** * Check if element is selected */ function isElementSelected(msg) { let command_id = msg.json.command_id; try { let el = elementManager.getKnownElement(msg.json.id, curFrame); sendResponse({value: utils.isElementSelected(el)}, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + } catch (e) { + sendError(e, command_id); } } /** * Send keys to element */ function sendKeysToElement(msg) { let command_id = msg.json.command_id; @@ -1593,19 +1593,18 @@ function getElementLocation(msg) { let el = elementManager.getKnownElement(msg.json.id, curFrame); let rect = el.getBoundingClientRect(); let location = {}; location.x = rect.left; location.y = rect.top; sendResponse({value: location}, command_id); - } - catch (e) { - sendError(e.message, e.code, e.stack, command_id); + } catch (e) { + sendError(e, command_id); } } /** * Clear the text of an element */ function clearElement(msg) { let command_id = msg.json.command_id; @@ -1613,34 +1612,34 @@ function clearElement(msg) { let el = elementManager.getKnownElement(msg.json.id, curFrame); if (el.type == "file") { el.value = null; } else { utils.clearElement(el); } sendOk(command_id); } catch (e) { - sendError(e.message, e.code, e.stack, command_id); + sendError(e, command_id); } } /** * Switch to frame given either the server-assigned element id, * its index in window.frames, or the iframe's name or id. */ function switchToFrame(msg) { let command_id = msg.json.command_id; function checkLoad() { let errorRegex = /about:.+(error)|(blocked)\?/; if (curFrame.document.readyState == "complete") { sendOk(command_id); return; - } - else if (curFrame.document.readyState == "interactive" && errorRegex.exec(curFrame.document.baseURI)) { - sendError("Error loading page", 13, null, command_id); + } else if (curFrame.document.readyState == "interactive" && + errorRegex.exec(curFrame.document.baseURI)) { + sendError(new UnknownError("Error loading page"), command_id); return; } checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); } let foundFrame = null; let frames = []; let parWindow = null; // Check of the curFrame reference is dead @@ -1670,19 +1669,18 @@ function switchToFrame(msg) { checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); return; } if (msg.json.element != undefined) { if (elementManager.seenItems[msg.json.element] != undefined) { let wantedFrame; try { wantedFrame = elementManager.getKnownElement(msg.json.element, curFrame); //Frame Element - } - catch(e) { - sendError(e.message, e.code, e.stack, command_id); + } catch (e) { + sendError(e, command_id); } if (frames.length > 0) { for (let i = 0; i < frames.length; i++) { // use XPCNativeWrapper to compare elements; see bug 834266 if (XPCNativeWrapper(frames[i].frameElement) == XPCNativeWrapper(wantedFrame)) { curFrame = frames[i].frameElement; foundFrame = i; @@ -1730,18 +1728,19 @@ function switchToFrame(msg) { let iframes = curFrame.document.getElementsByTagName("iframe"); if (msg.json.id >= 0 && msg.json.id < iframes.length) { curFrame = iframes[msg.json.id]; foundFrame = msg.json.id; } } } } + if (foundFrame === null) { - sendError("Unable to locate frame: " + (msg.json.id || msg.json.element), 8, null, command_id); + sendError(new NoSuchFrameError("Unable to locate frame: " + (msg.json.id || msg.json.element)), command_id); return true; } sandbox = null; // send a synchronous message to let the server update the currently active // frame element (for getActiveFrame) let frameValue = elementManager.wrapValue(curFrame.wrappedJSObject)['ELEMENT']; @@ -1772,40 +1771,39 @@ function addCookie(msg) { var thePresent = new Date(Date.now()); date.setYear(thePresent.getFullYear() + 20); cookie.expiry = date.getTime() / 1000; // Stored in seconds. } if (!cookie.domain) { var location = curFrame.document.location; cookie.domain = location.hostname; - } - else { + } else { var currLocation = curFrame.location; var currDomain = currLocation.host; if (currDomain.indexOf(cookie.domain) == -1) { - sendError("You may only set cookies for the current domain", 24, null, msg.json.command_id); + sendError(new InvalidCookieDomainError("You may only set cookies for the current domain"), msg.json.command_id); } } // The cookie's domain may include a port. Which is bad. Remove it // We'll catch ip6 addresses by mistake. Since no-one uses those // this will be okay for now. See Bug 814416 if (cookie.domain.match(/:\d+$/)) { cookie.domain = cookie.domain.replace(/:\d+$/, ''); } var document = curFrame.document; if (!document || !document.contentType.match(/html/i)) { - sendError('You may only set cookies on html documents', 25, null, msg.json.command_id); + sendError(new UnableToSetCookie("You may only set cookies on html documents"), msg.json.command_id); } let added = sendSyncMessage("Marionette:addCookie", {value: cookie}); if (added[0] !== true) { - sendError("Error setting cookie", 13, null, msg.json.command_id); + sendError(new UnknownError("Error setting cookie"), msg.json.command_id); return; } sendOk(msg.json.command_id); } /** * Get all cookies for the current domain. */ @@ -1836,34 +1834,34 @@ function getCookies(msg) { */ function deleteCookie(msg) { let toDelete = msg.json.name; let cookies = getVisibleCookies(curFrame.location); for (let cookie of cookies) { if (cookie.name == toDelete) { let deleted = sendSyncMessage("Marionette:deleteCookie", {value: cookie}); if (deleted[0] !== true) { - sendError("Could not delete cookie: " + msg.json.name, 13, null, msg.json.command_id); + sendError(new UnknownError("Could not delete cookie: " + msg.json.name), msg.json.command_id); return; } } } sendOk(msg.json.command_id); } /** * Delete all the visibile cookies on a page */ function deleteAllCookies(msg) { let cookies = getVisibleCookies(curFrame.location); for (let cookie of cookies) { let deleted = sendSyncMessage("Marionette:deleteCookie", {value: cookie}); if (!deleted[0]) { - sendError("Could not delete cookie: " + JSON.stringify(cookie), 13, null, msg.json.command_id); + sendError(new UnknownError("Could not delete cookie: " + JSON.stringify(cookie)), msg.json.command_id); return; } } sendOk(msg.json.command_id); } /** * Get all the visible cookies from a location @@ -1907,19 +1905,18 @@ function emulatorCmdResult(msg) { } let cb = _emu_cbs[message.id]; delete _emu_cbs[message.id]; if (!cb) { return; } try { cb(message.result); - } - catch(e) { - sendError(e.message, e.code, e.stack, -1); + } catch (e) { + sendError(e, -1); return; } } function importScript(msg) { let command_id = msg.json.command_id; let file; if (importedScripts.exists()) {
--- a/testing/marionette/sendkeys.js +++ b/testing/marionette/sendkeys.js @@ -12,18 +12,21 @@ * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("chrome://marionette/content/error.js"); + let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Ci.mozIJSSubScriptLoader); + .getService(Ci.mozIJSSubScriptLoader); let utils = {}; loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils); loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils); let keyModifierNames = { "VK_SHIFT": 'shiftKey', "VK_CONTROL": 'ctrlKey', @@ -133,13 +136,12 @@ function sendKeysToElement (document, el metaKey: false }; let value = keysToSend.join(""); for (var i = 0; i < value.length; i++) { var c = value.charAt(i); sendSingleKey(c, modifiers, document); } successCallback(command_id); - } - else { - errorCallback("Element is not visible", 11, null, command_id); + } else { + errorCallback(new ElementNotVisibleError("Element is not visible"), command_id); } };