123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- /* 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/. */
- "use strict";
- const {interfaces: Ci, utils: Cu} = Components;
- const ERRORS = new Set([
- "ElementClickInterceptedError",
- "ElementNotAccessibleError",
- "ElementNotInteractableError",
- "InsecureCertificateError",
- "InvalidArgumentError",
- "InvalidElementStateError",
- "InvalidSelectorError",
- "InvalidSessionIDError",
- "JavaScriptError",
- "MoveTargetOutOfBoundsError",
- "NoAlertOpenError",
- "NoSuchElementError",
- "NoSuchFrameError",
- "NoSuchWindowError",
- "ScriptTimeoutError",
- "SessionNotCreatedError",
- "StaleElementReferenceError",
- "TimeoutError",
- "UnableToSetCookieError",
- "UnknownCommandError",
- "UnknownError",
- "UnsupportedOperationError",
- "WebDriverError",
- ]);
- const BUILTIN_ERRORS = new Set([
- "Error",
- "EvalError",
- "InternalError",
- "RangeError",
- "ReferenceError",
- "SyntaxError",
- "TypeError",
- "URIError",
- ]);
- this.EXPORTED_SYMBOLS = ["error"].concat(Array.from(ERRORS));
- this.error = {};
- /**
- * Checks if obj is an instance of the Error prototype in a safe manner.
- * Prefer using this over using instanceof since the Error prototype
- * isn't unique across browsers, and XPCOM nsIException's are special
- * snowflakes.
- *
- * @param {*} val
- * Any value that should be undergo the test for errorness.
- * @return {boolean}
- * True if error, false otherwise.
- */
- error.isError = function (val) {
- if (val === null || typeof val != "object") {
- return false;
- } else if (val instanceof Ci.nsIException) {
- return true;
- } else {
- // DOMRectList errors on string comparison
- try {
- let proto = Object.getPrototypeOf(val);
- return BUILTIN_ERRORS.has(proto.toString());
- } catch (e) {
- return false;
- }
- }
- };
- /**
- * Checks if obj is an object in the WebDriverError prototypal chain.
- */
- error.isWebDriverError = function (obj) {
- return error.isError(obj) &&
- ("name" in obj && ERRORS.has(obj.name));
- };
- /**
- * Wraps any error as a WebDriverError. If the given error is already in
- * the WebDriverError prototype chain, this function returns it
- * unmodified.
- */
- error.wrap = function (err) {
- if (error.isWebDriverError(err)) {
- return err;
- }
- return new WebDriverError(err);
- };
- /**
- * Unhandled error reporter. Dumps the error and its stacktrace to console,
- * and reports error to the Browser Console.
- */
- error.report = function (err) {
- let msg = "Marionette threw an error: " + error.stringify(err);
- dump(msg + "\n");
- if (Cu.reportError) {
- Cu.reportError(msg);
- }
- };
- /**
- * Prettifies an instance of Error and its stacktrace to a string.
- */
- error.stringify = function (err) {
- try {
- let s = err.toString();
- if ("stack" in err) {
- s += "\n" + err.stack;
- }
- return s;
- } catch (e) {
- return "<unprintable error>";
- }
- };
- /**
- * Pretty-print values passed to template strings.
- *
- * Usage:
- *
- * let bool = {value: true};
- * error.pprint`Expected boolean, got ${bool}`;
- * => 'Expected boolean, got [object Object] {"value": true}'
- *
- * let htmlElement = document.querySelector("input#foo");
- * error.pprint`Expected element ${htmlElement}`;
- * => 'Expected element <input id="foo" class="bar baz">'
- */
- error.pprint = function (ss, ...values) {
- function prettyObject (obj) {
- let proto = Object.prototype.toString.call(obj);
- let s = "";
- try {
- s = JSON.stringify(obj);
- } catch (e if e instanceof TypeError) {
- s = `<${e.message}>`;
- }
- return proto + " " + s;
- }
- function prettyElement (el) {
- let ident = [];
- if (el.id) {
- ident.push(`id="${el.id}"`);
- }
- if (el.classList.length > 0) {
- ident.push(`class="${el.className}"`);
- }
- let idents = "";
- if (ident.length > 0) {
- idents = " " + ident.join(" ");
- }
- return `<${el.localName}${idents}>`;
- }
- let res = [];
- for (let i = 0; i < ss.length; i++) {
- res.push(ss[i]);
- if (i < values.length) {
- let val = values[i];
- let typ = Object.prototype.toString.call(val);
- let s;
- try {
- if (val && val.nodeType === 1) {
- s = prettyElement(val);
- } else {
- s = prettyObject(val);
- }
- } catch (e) {
- s = typeof val;
- }
- res.push(s);
- }
- }
- return res.join("");
- };
- /**
- * WebDriverError is the prototypal parent of all WebDriver errors.
- * It should not be used directly, as it does not correspond to a real
- * error in the specification.
- */
- class WebDriverError extends Error {
- /**
- * @param {(string|Error)=} x
- * Optional string describing error situation or Error instance
- * to propagate.
- */
- constructor (x) {
- super(x);
- this.name = this.constructor.name;
- this.status = "webdriver error";
- // Error's ctor does not preserve x' stack
- if (error.isError(x)) {
- this.stack = x.stack;
- }
- }
- toJSON () {
- return {
- error: this.status,
- message: this.message || "",
- stacktrace: this.stack || "",
- }
- }
- static fromJSON (json) {
- if (typeof json.error == "undefined") {
- let s = JSON.stringify(json);
- throw new TypeError("Undeserialisable error type: " + s);
- }
- if (!STATUSES.has(json.error)) {
- throw new TypeError("Not of WebDriverError descent: " + json.error);
- }
- let cls = STATUSES.get(json.error);
- let err = new cls();
- if ("message" in json) {
- err.message = json.message;
- }
- if ("stacktrace" in json) {
- err.stack = json.stacktrace;
- }
- return err;
- }
- }
- class ElementNotAccessibleError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "element not accessible";
- }
- }
- /**
- * An element click could not be completed because the element receiving
- * the events is obscuring the element that was requested clicked.
- *
- * @param {Element=} obscuredEl
- * Element obscuring the element receiving the click. Providing this
- * is not required, but will produce a nicer error message.
- * @param {Map.<string, number>} coords
- * Original click location. Providing this is not required, but
- * will produce a nicer error message.
- */
- class ElementClickInterceptedError extends WebDriverError {
- constructor (obscuredEl = undefined, coords = undefined) {
- let msg = "";
- if (obscuredEl && coords) {
- const doc = obscuredEl.ownerDocument;
- const overlayingEl = doc.elementFromPoint(coords.x, coords.y);
- switch (obscuredEl.style.pointerEvents) {
- case "none":
- msg = error.pprint`Element ${obscuredEl} is not clickable ` +
- `at point (${coords.x},${coords.y}) ` +
- `because it does not have pointer events enabled, ` +
- error.pprint`and element ${overlayingEl} ` +
- `would receive the click instead`;
- break;
- default:
- msg = error.pprint`Element ${obscuredEl} is not clickable ` +
- `at point (${coords.x},${coords.y}) ` +
- error.pprint`because another element ${overlayingEl} ` +
- `obscures it`;
- break;
- }
- }
- super(msg);
- this.status = "element click intercepted";
- }
- }
- class ElementNotInteractableError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "element not interactable";
- }
- }
- class InsecureCertificateError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "insecure certificate";
- }
- }
- class InvalidArgumentError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "invalid argument";
- }
- }
- class InvalidElementStateError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "invalid element state";
- }
- }
- class InvalidSelectorError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "invalid selector";
- }
- }
- class InvalidSessionIDError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "invalid session id";
- }
- }
- /**
- * Creates a richly annotated error for an error situation that occurred
- * whilst evaluating injected scripts.
- */
- class JavaScriptError extends WebDriverError {
- /**
- * @param {(string|Error)} x
- * An Error object instance or a string describing the error
- * situation.
- * @param {string=} fnName
- * Name of the function to use in the stack trace message.
- * @param {string=} file
- * Filename of the test file on the client.
- * @param {number=} line
- * Line number of |file|.
- * @param {string=} script
- * Script being executed, in text form.
- */
- constructor (
- x,
- fnName = undefined,
- file = undefined,
- line = undefined,
- script = undefined) {
- let msg = String(x);
- let trace = "";
- if (fnName) {
- trace += fnName;
- if (file) {
- trace += ` @${file}`;
- if (line) {
- trace += `, line ${line}`;
- }
- }
- }
- if (error.isError(x)) {
- let jsStack = x.stack.split("\n");
- let match = jsStack[0].match(/:(\d+):\d+$/);
- let jsLine = match ? parseInt(match[1]) : 0;
- if (script) {
- let src = script.split("\n")[jsLine];
- trace += "\n" +
- `inline javascript, line ${jsLine}\n` +
- `src: "${src}"`;
- }
- trace += "\nStack:\n" + x.stack;
- }
- super(msg);
- this.status = "javascript error";
- this.stack = trace;
- }
- }
- class MoveTargetOutOfBoundsError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "move target out of bounds";
- }
- }
- class NoAlertOpenError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "no such alert";
- }
- }
- class NoSuchElementError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "no such element";
- }
- }
- class NoSuchFrameError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "no such frame";
- }
- }
- class NoSuchWindowError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "no such window";
- }
- }
- class ScriptTimeoutError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "script timeout";
- }
- }
- class SessionNotCreatedError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "session not created";
- }
- }
- class StaleElementReferenceError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "stale element reference";
- }
- }
- class TimeoutError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "timeout";
- }
- }
- class UnableToSetCookieError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "unable to set cookie";
- }
- }
- class UnknownCommandError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "unknown command";
- }
- }
- class UnknownError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "unknown error";
- }
- }
- class UnsupportedOperationError extends WebDriverError {
- constructor (message) {
- super(message);
- this.status = "unsupported operation";
- }
- }
- const STATUSES = new Map([
- ["element not accessible", ElementNotAccessibleError],
- ["element not interactable", ElementNotInteractableError],
- ["element click intercepted", ElementClickInterceptedError],
- ["insecure certificate", InsecureCertificateError],
- ["invalid argument", InvalidArgumentError],
- ["invalid element state", InvalidElementStateError],
- ["invalid selector", InvalidSelectorError],
- ["invalid session id", InvalidSessionIDError],
- ["javascript error", JavaScriptError],
- ["move target out of bounds", MoveTargetOutOfBoundsError],
- ["no alert open", NoAlertOpenError],
- ["no such element", NoSuchElementError],
- ["no such frame", NoSuchFrameError],
- ["no such window", NoSuchWindowError],
- ["script timeout", ScriptTimeoutError],
- ["session not created", SessionNotCreatedError],
- ["stale element reference", StaleElementReferenceError],
- ["timeout", TimeoutError],
- ["unable to set cookie", UnableToSetCookieError],
- ["unknown command", UnknownCommandError],
- ["unknown error", UnknownError],
- ["unsupported operation", UnsupportedOperationError],
- ["webdriver error", WebDriverError],
- ]);
|