--- a/testing/marionette/accessibility.js
+++ b/testing/marionette/accessibility.js
@@ -27,16 +27,17 @@ XPCOMUtils.defineLazyGetter(this, "servi
} catch (e) {
logger.warn("Accessibility module is not present");
return undefined;
}
});
this.EXPORTED_SYMBOLS = ["accessibility"];
+/** @namespace */
this.accessibility = {
get service() {
return service;
},
};
/**
* Accessible states used to check element"s state from the accessiblity API
@@ -119,17 +120,17 @@ accessibility.Checks = class {
* Get an accessible object for an element.
*
* @param {DOMElement|XULElement} element
* Element to get the accessible object for.
* @param {boolean=} mustHaveAccessible
* Flag indicating that the element must have an accessible object.
* Defaults to not require this.
*
- * @return {Promise: nsIAccessible}
+ * @return {Promise.<nsIAccessible>}
* Promise with an accessibility object for the given element.
*/
getAccessible(element, mustHaveAccessible = false) {
if (!this.strict) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -25,16 +25,18 @@ this.EXPORTED_SYMBOLS = ["action"];
const {pprint} = error;
// TODO? With ES 2016 and Symbol you can make a safer approximation
// to an enum e.g. https://gist.github.com/xmlking/e86e4f15ec32b12c4689
/**
* Implements WebDriver Actions API: a low-level interface for providing
* virtualised device input to the web browser.
+ *
+ * @namespace
*/
this.action = {
Pause: "pause",
KeyDown: "keyDown",
KeyUp: "keyUp",
PointerDown: "pointerDown",
PointerUp: "pointerUp",
PointerMove: "pointerMove",
@@ -341,25 +343,25 @@ const KEY_CODE_LOOKUP = {
action.PointerOrigin = {
Viewport: "viewport",
Pointer: "pointer",
};
/**
* Look up a PointerOrigin.
*
- * @param {?} obj
+ * @param {(undefined|string|WebElement)} obj
* Origin for a pointerMove action.
*
- * @return {?}
+ * @return {action.PointerOrigin}
* A pointer origin that is either "viewport" (default), "pointer", or a
* web-element reference.
*
* @throws {InvalidArgumentError}
- * If |obj| is not a valid origin.
+ * If <code>obj</code> is not a valid origin.
*/
action.PointerOrigin.get = function(obj) {
let origin = obj;
if (typeof obj == "undefined") {
origin = this.Viewport;
} else if (typeof obj == "string") {
let name = capitalize(obj);
assert.in(name, this, pprint`Unknown pointer-move origin: ${obj}`);
@@ -384,17 +386,17 @@ action.PointerType = {
*
* @param {string} str
* Name of pointer type.
*
* @return {string}
* A pointer type for processing pointer parameters.
*
* @throws {InvalidArgumentError}
- * If |str| is not a valid pointer type.
+ * If <code>str</code> is not a valid pointer type.
*/
action.PointerType.get = function(str) {
let name = capitalize(str);
assert.in(name, this, pprint`Unknown pointerType: ${str}`);
return this[name];
};
/**
@@ -402,17 +404,17 @@ action.PointerType.get = function(str) {
* input ID and the device state for that input source, with one entry
* for each active input source.
*
* Initialized in listener.js.
*/
action.inputStateMap = undefined;
/**
- * List of |action.Action| associated with current session. Used to
+ * List of {@link action.Action} associated with current session. Used to
* manage dispatching events when resetting the state of the input sources.
* Reset operations are assumed to be idempotent.
*
* Initialized in listener.js
*/
action.inputsToCancel = undefined;
/**
@@ -421,42 +423,46 @@ action.inputsToCancel = undefined;
class InputState {
constructor() {
this.type = this.constructor.name.toLowerCase();
}
/**
* Check equality of this InputState object with another.
*
- * @para{?} other
+ * @param {InputState} other
* Object representing an input state.
+ *
* @return {boolean}
- * True if |this| has the same |type| as |other|.
+ * True if <code>this</code> has the same <code>type</code>
+ * as <code>other</code>.
*/
is(other) {
if (typeof other == "undefined") {
return false;
}
return this.type === other.type;
}
toString() {
return `[object ${this.constructor.name}InputState]`;
}
/**
- * @param {?} obj
- * Object with property |type| and optionally |parameters| or
- * |pointerType|, representing an action sequence or an action item.
+ * @param {Object.<string, ?>} obj
+ * Object with property <code>type</code> and optionally
+ * <code>parameters</code> or <code>pointerType</code>,
+ * representing an action sequence or an action item.
*
* @return {action.InputState}
- * An |action.InputState| object for the type of the |actionSequence|.
+ * An {@link InputState} object for the type of the
+ * {@link actionSequence}.
*
* @throws {InvalidArgumentError}
- * If |actionSequence.type| is not valid.
+ * If {@link actionSequence.type} is not valid.
*/
static fromJson(obj) {
let type = obj.type;
assert.in(type, ACTIONS, pprint`Unknown action type: ${type}`);
let name = type == "none" ? "Null" : capitalize(type);
if (name == "Pointer") {
if (!obj.pointerType &&
(!obj.parameters || !obj.parameters.pointerType)) {
@@ -623,18 +629,20 @@ action.InputState.Pointer = class Pointe
* Repesents an action for dispatch. Used in |action.Chain| and
* |action.Sequence|.
*
* @param {string} id
* Input source ID.
* @param {string} type
* Action type: none, key, pointer.
* @param {string} subtype
- * Action subtype: pause, keyUp, keyDown, pointerUp, pointerDown,
- * pointerMove, pointerCancel.
+ * Action subtype: {@link action.Pause}, {@link action.KeyUp},
+ * {@link action.KeyDown}, {@link action.PointerUp},
+ * {@link action.PointerDown}, {@link action.PointerMove}, or
+ * {@link action.PointerCancel}.
*
* @throws {InvalidArgumentError}
* If any parameters are undefined.
*/
action.Action = class {
constructor(id, type, subtype) {
if ([id, type, subtype].includes(undefined)) {
throw new InvalidArgumentError("Missing id, type or subtype");
@@ -647,28 +655,29 @@ action.Action = class {
this.subtype = subtype;
}
toString() {
return `[action ${this.type}]`;
}
/**
- * @param {?} actionSequence
+ * @param {action.Sequence} actionSequence
* Object representing sequence of actions from one input source.
- * @param {?} actionItem
+ * @param {action.Action} actionItem
* Object representing a single action from |actionSequence|.
*
* @return {action.Action}
* An action that can be dispatched; corresponds to |actionItem|.
*
* @throws {InvalidArgumentError}
- * If any |actionSequence| or |actionItem| attributes are invalid.
+ * If any <code>actionSequence</code> or <code>actionItem</code>
+ * attributes are invalid.
* @throws {UnsupportedOperationError}
- * If |actionItem.type| is |pointerCancel|.
+ * If <code>actionItem.type</code> is {@link action.PointerCancel}.
*/
static fromJson(actionSequence, actionItem) {
let type = actionSequence.type;
let id = actionSequence.id;
let subtypes = ACTIONS[type];
if (!subtypes) {
throw new InvalidArgumentError("Unknown type: " + type);
}
@@ -785,17 +794,17 @@ action.Chain = class extends Array {
* |Array.<action.Action>|.
*/
action.Sequence = class extends Array {
toString() {
return `[sequence ${super.toString()}]`;
}
/**
- * @param {?} actionSequence
+ * @param {Object.<string, ?>} actionSequence
* Object that represents a sequence action items for one input source.
*
* @return {action.Sequence}
* Sequence of actions that can be dispatched.
*
* @throws {InvalidArgumentError}
* If |actionSequence.id| is not a string or it's aleady mapped
* to an |action.InputState} incompatible with |actionSequence.type|.
@@ -838,17 +847,17 @@ action.PointerParameters = class {
this.pointerType = action.PointerType.get(pointerType);
}
toString() {
return `[pointerParameters ${this.pointerType}]`;
}
/**
- * @param {?} parametersData
+ * @param {Object.<string, ?>} parametersData
* Object that represents pointer parameters.
*
* @return {action.PointerParameters}
* Validated pointer paramters.
*/
static fromJson(parametersData) {
if (typeof parametersData == "undefined") {
return new action.PointerParameters();
@@ -949,18 +958,19 @@ action.Mouse = class {
* tick's actions are not dispatched until the Promise for the current
* tick is resolved.
*
* @param {action.Chain} chain
* Actions grouped by tick; each element in |chain| is a sequence of
* actions for one tick.
* @param {element.Store} seenEls
* Element store.
- * @param {?} container
- * Object with |frame| attribute of type |nsIDOMWindow|.
+ * @param {Object.<string, nsIDOMWindow>} container
+ * Object with <code>frame</code> property of type
+ * <code>nsIDOMWindow</code>.
*
* @return {Promise}
* Promise for dispatching all actions in |chain|.
*/
action.dispatch = function(chain, seenEls, container) {
let chainEvents = Task.spawn(function*() {
for (let tickActions of chain) {
yield action.dispatchTickActions(
@@ -985,18 +995,19 @@ action.dispatch = function(chain, seenEl
* different durations and therefore may not end in the same order.
*
* @param {Array.<action.Action>} tickActions
* List of actions for one tick.
* @param {number} tickDuration
* Duration in milliseconds of this tick.
* @param {element.Store} seenEls
* Element store.
- * @param {?} container
- * Object with |frame| attribute of type |nsIDOMWindow|.
+ * @param {Object.<string, nsIDOMWindow>} container
+ * Object with <code>frame</code> property of type
+ * <code>nsIDOMWindow</code>.
*
* @return {Promise}
* Promise for dispatching all tick-actions and pending DOM events.
*/
action.dispatchTickActions = function(
tickActions, tickDuration, seenEls, container) {
let pendingEvents = tickActions.map(
toEvents(tickDuration, seenEls, container));
@@ -1062,18 +1073,19 @@ action.computePointerDestination = funct
/**
* Create a closure to use as a map from action definitions to Promise events.
*
* @param {number} tickDuration
* Duration in milliseconds of this tick.
* @param {element.Store} seenEls
* Element store.
- * @param {?} container
- * Object with |frame| attribute of type |nsIDOMWindow|.
+ * @param {Object.<string, nsIDOMWindow>} container
+ * Object with <code>frame</code> property of type
+ * <code>nsIDOMWindow</code>.
*
* @return {function(action.Action): Promise}
* Function that takes an action and returns a Promise for dispatching
* the event that corresponds to that action.
*/
function toEvents(tickDuration, seenEls, container) {
return a => {
let inputState = action.inputStateMap.get(a.id);
@@ -1263,18 +1275,19 @@ function dispatchPointerUp(a, inputState
* with the pointer coordinates being updated around 60 times per second.
*
* @param {action.Action} a
* Action to dispatch.
* @param {action.InputState} inputState
* Input state for this action's input source.
* @param {element.Store} seenEls
* Element store.
- * @param {?} container
- * Object with |frame| attribute of type |nsIDOMWindow|.
+ * @param {Object.<string, nsIDOMWindow>} container
+ * Object with <code>frame</code> property of type
+ * <code>nsIDOMWindow</code>.
*
* @return {Promise}
* Promise to dispatch at least one pointermove event, as well as
* mousemove events as appropriate.
*/
function dispatchPointerMove(
a, inputState, tickDuration, seenEls, container) {
const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
--- a/testing/marionette/addon.js
+++ b/testing/marionette/addon.js
@@ -8,16 +8,17 @@ const {interfaces: Ci, utils: Cu} = Comp
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
const {UnknownError} = Cu.import("chrome://marionette/content/error.js", {});
this.EXPORTED_SYMBOLS = ["addon"];
+/** @namespace */
this.addon = {};
// from https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager/AddonManager#AddonInstall_errors
addon.Errors = {
[-1]: "ERROR_NETWORK_FAILURE: A network error occured.",
[-2]: "ERROR_INCORECT_HASH: The downloaded file did not match the expected hash.",
[-3]: "ERROR_CORRUPT_FILE: The file appears to be corrupt.",
[-4]: "ERROR_FILE_ACCESS: There was an error accessing the filesystem.",
@@ -38,17 +39,17 @@ function lookupError(code) {
* Temporary addons will automatically be uninstalled on shutdown and
* do not need to be signed, though they must be restartless.
*
* @param {string} path
* Full path to the extension package archive.
* @param {boolean=} temporary
* True to install the addon temporarily, false (default) otherwise.
*
- * @return {Promise: string}
+ * @return {Promise.<string>}
* Addon ID.
*
* @throws {UnknownError}
* If there is a problem installing the addon.
*/
addon.install = function(path, temporary = false) {
return new Promise((resolve, reject) => {
let file = new FileUtils.File(path);
--- a/testing/marionette/assert.js
+++ b/testing/marionette/assert.js
@@ -19,17 +19,21 @@ const {
UnsupportedOperationError,
} = Cu.import("chrome://marionette/content/error.js", {});
this.EXPORTED_SYMBOLS = ["assert"];
const isFennec = () => AppConstants.platform == "android";
const isFirefox = () => Services.appinfo.name == "Firefox";
-/** Shorthands for common assertions made in Marionette. */
+/**
+ * Shorthands for common assertions made in Marionette.
+ *
+ * @namespace
+ */
this.assert = {};
/**
* Asserts that Marionette has a session.
*
* @param {GeckoDriver} driver
* Marionette driver instance.
* @param {string=} msg
--- a/testing/marionette/atom.js
+++ b/testing/marionette/atom.js
@@ -9,16 +9,17 @@
// 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.
this.EXPORTED_SYMBOLS = ["atom"];
+/** @namespace */
this.atom = {};
// https://github.com/SeleniumHQ/selenium/blob/master/javascript/atoms/action.js#L83
atom.clearElement = function (element, window){return function(){function g(a){throw a;}var h=void 0,i=!0,k=null,l=!1;function n(a){return function(){return this[a]}}function o(a){return function(){return a}}var p,q=this;
function aa(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function r(a){return a!==h}function ba(a){var b=aa(a);return"array"==b||"object"==b&&"number"==typeof a.length}function t(a){return"string"==typeof a}function w(a){return"function"==aa(a)}function ca(a){a=aa(a);return"object"==a||"array"==a||"function"==a}var da="closure_uid_"+Math.floor(2147483648*Math.random()).toString(36),ea=0,fa=Date.now||function(){return+new Date};
function x(a,b){function c(){}c.prototype=b.prototype;a.$=b.prototype;a.prototype=new c};function ga(a,b){for(var c=1;c<arguments.length;c++)var d=(""+arguments[c]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a}function ha(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function ia(a){if(!ja.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ka,"&"));-1!=a.indexOf("<")&&(a=a.replace(la,"<"));-1!=a.indexOf(">")&&(a=a.replace(ma,">"));-1!=a.indexOf('"')&&(a=a.replace(na,"""));return a}var ka=/&/g,la=/</g,ma=/>/g,na=/\"/g,ja=/[&<>\"]/;
function oa(a,b){for(var c=0,d=ha(""+a).split("."),e=ha(""+b).split("."),f=Math.max(d.length,e.length),j=0;0==c&&j<f;j++){var m=d[j]||"",s=e[j]||"",O=RegExp("(\\d*)(\\D*)","g"),E=RegExp("(\\d*)(\\D*)","g");do{var u=O.exec(m)||["","",""],v=E.exec(s)||["","",""];if(0==u[0].length&&0==v[0].length)break;c=((0==u[1].length?0:parseInt(u[1],10))<(0==v[1].length?0:parseInt(v[1],10))?-1:(0==u[1].length?0:parseInt(u[1],10))>(0==v[1].length?0:parseInt(v[1],10))?1:0)||((0==u[2].length)<(0==v[2].length)?-1:(0==
--- a/testing/marionette/browser.js
+++ b/testing/marionette/browser.js
@@ -11,27 +11,28 @@ Cu.import("chrome://marionette/content/e
const {
NoSuchWindowError,
UnsupportedOperationError,
} = Cu.import("chrome://marionette/content/error.js", {});
Cu.import("chrome://marionette/content/frame.js");
this.EXPORTED_SYMBOLS = ["browser"];
+/** @namespace */
this.browser = {};
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/**
- * Get the <xul:browser> for the specified tab.
+ * Get the <code><xul:browser></code> for the specified tab.
*
- * @param {<xul:tab>} tab
+ * @param {Tab} tab
* The tab whose browser needs to be returned.
*
- * @return {<xul:browser>}
+ * @return {Browser}
* The linked browser for the tab or null if no browser can be found.
*/
browser.getBrowserForTab = function(tab) {
// Fennec
if ("browser" in tab) {
return tab.browser;
// Firefox
@@ -43,17 +44,17 @@ browser.getBrowserForTab = function(tab)
};
/**
* Return the tab browser for the specified chrome window.
*
* @param {nsIDOMWindow} win
* The window whose tabbrowser needs to be accessed.
*
- * @return {<xul:tabbrowser>}
+ * @return {Tab}
* Tab browser or null if it's not a browser window.
*/
browser.getTabBrowser = function(win) {
// Fennec
if ("BrowserApp" in win) {
return win.BrowserApp;
// Firefox
--- a/testing/marionette/capture.js
+++ b/testing/marionette/capture.js
@@ -9,17 +9,21 @@ Cu.importGlobalProperties(["crypto"]);
this.EXPORTED_SYMBOLS = ["capture"];
const CONTEXT_2D = "2d";
const BG_COLOUR = "rgb(255,255,255)";
const PNG_MIME = "image/png";
const XHTML_NS = "http://www.w3.org/1999/xhtml";
-/** Provides primitives to capture screenshots. */
+/**
+ * Provides primitives to capture screenshots.
+ *
+ * @namespace
+ */
this.capture = {};
capture.Format = {
Base64: 0,
Hash: 1,
};
/**
--- a/testing/marionette/cert.js
+++ b/testing/marionette/cert.js
@@ -17,17 +17,21 @@ const sss = Cc["@mozilla.org/ssservice;1
.getService(Ci.nsISiteSecurityService);
const CONTRACT_ID = "@mozilla.org/security/certoverride;1";
const CERT_PINNING_ENFORCEMENT_PREF =
"security.cert_pinning.enforcement_level";
const HSTS_PRELOAD_LIST_PREF =
"network.stricttransportsecurity.preloadlist";
-/** TLS certificate service override management for Marionette. */
+/**
+ * TLS certificate service override management for Marionette.
+ *
+ * @namespace
+ */
this.cert = {
Error: {
Untrusted: 1,
Mismatch: 2,
Time: 4,
},
currentOverride: null,
--- a/testing/marionette/cookie.js
+++ b/testing/marionette/cookie.js
@@ -13,34 +13,43 @@ const {
error,
InvalidCookieDomainError,
} = Cu.import("chrome://marionette/content/error.js", {});
this.EXPORTED_SYMBOLS = ["cookie"];
const IPV4_PORT_EXPR = /:\d+$/;
+/** @namespace */
this.cookie = {
manager: Services.cookies,
};
/**
+ * @name Cookie
+ *
+ * @return {Object.<string, (number|boolean|string)>
+ */
+
+/**
* Unmarshal a JSON Object to a cookie representation.
*
* Effectively this will run validation checks on |json|, which will
* produce the errors expected by WebDriver if the input is not valid.
*
- * @param {Map.<string, (number|boolean|string)> json
- * Cookie to be deserialised. |name| and |value| are required fields
- * which must be strings. The |path| field is optional, but must
- * be a string if provided. The |secure|, |httpOnly|, and |session|
- * fields are similarly optional, but must be booleans. Likewise,
- * the |expiry| field is optional but must be unsigned integer.
+ * @param {Object.<string, (number|boolean|string)>} json
+ * Cookie to be deserialised. <var>name</var> and <var>value</var>
+ * are required fields which must be strings. The <var>path</var>
+ * field is optional, but must be a string if provided.
+ * The <var>secure</var>, <var>httpOnly</var>, and
+ * <var>session</var>fields are similarly optional, but must be
+ * booleans. Likewise, the <var>expiry</var> field is optional but
+ * must be unsigned integer.
*
- * @return {Map.<string, (number|boolean|string)>
+ * @return {Cookie}
* Valid cookie object.
*
* @throws {InvalidArgumentError}
* If any of the properties are invalid.
*/
cookie.fromJSON = function(json) {
let newCookie = {};
@@ -66,55 +75,50 @@ cookie.fromJSON = function(json) {
}
return newCookie;
};
/**
* Insert cookie to the cookie store.
*
- * @param {Map.<string, (string|number|boolean)} newCookie
+ * @param {Cookie} newCookie
* Cookie to add.
- * @param {Map.<string, ?>} opts
- * Optional parameters:
- *
- * restrictToHost (string)
- * Perform test that |newCookie|'s domain matches this.
+ * @param {string=} restrictToHost
+ * Perform test that <var>newCookie</var>'s domain matches this.
*
* @throws {TypeError}
- * If |name|, |value|, or |domain| are not present and of the
- * correct type.
+ * If <var>name</var>, <var>value</var>, or <var>domain</var> are
+ * not present and of the correct type.
* @throws {InvalidCookieDomainError}
- * If |restrictToHost| is set and |newCookie|'s domain does not match.
+ * If <var>restrictToHost</var> is set and <var>newCookie</var>'s
+ * domain does not match.
*/
-cookie.add = function(newCookie, opts = {}) {
+cookie.add = function(newCookie, {restrictToHost = null} = {}) {
assert.string(newCookie.name, "Cookie name must be string");
assert.string(newCookie.value, "Cookie value must be string");
assert.string(newCookie.domain, "Cookie domain must be string");
if (typeof newCookie.path == "undefined") {
newCookie.path = "/";
}
if (typeof newCookie.expiry == "undefined") {
// twenty years into the future
let date = new Date();
let now = new Date(Date.now());
date.setYear(now.getFullYear() + 20);
newCookie.expiry = date.getTime() / 1000;
}
- if (opts.restrictToHost) {
- assert.in("restrictToHost", opts,
- "Missing cookie domain for host restriction test");
-
- if (newCookie.domain !== opts.restrictToHost) {
+ if (restrictToHost) {
+ if (newCookie.domain !== restrictToHost) {
throw new InvalidCookieDomainError(
`Cookies may only be set ` +
- ` for the current domain (${opts.restrictToHost})`);
+ ` for the current domain (${restrictToHost})`);
}
}
// remove port from domain, if present.
// unfortunately this catches IPv6 addresses by mistake
// TODO: Bug 814416
newCookie.domain = newCookie.domain.replace(IPV4_PORT_EXPR, "");
@@ -128,40 +132,41 @@ cookie.add = function(newCookie, opts =
newCookie.session,
newCookie.expiry,
{} /* origin attributes */);
};
/**
* Remove cookie from the cookie store.
*
- * @param {Map.<string, (string|number|boolean)} toDelete
+ * @param {Cookie} toDelete
* Cookie to remove.
*/
cookie.remove = function(toDelete) {
cookie.manager.remove(
toDelete.domain,
toDelete.name,
toDelete.path,
false,
{} /* originAttributes */);
};
/**
- * Iterates over the cookies for the current |host|. You may optionally
- * filter for specific paths on that |host| by specifying a path in
- * |currentPath|.
+ * Iterates over the cookies for the current <var>host</var>. You may
+ * optionally filter for specific paths on that <var>host</var> by
+ * specifying a path in <var>currentPath</var>.
*
* @param {string} host
* Hostname to retrieve cookies for.
- * @param {string=} currentPath
- * Optionally filter the cookies for |host| for the specific path.
- * Defautls to "/", meaning all cookies for |host| are included.
+ * @param {string=} [currentPath="/"] currentPath
+ * Optionally filter the cookies for <var>host</var> for the
+ * specific path. Defaults to "<tt>/</tt>", meaning all cookies
+ * for <var>host</var> are included.
*
- * @return {[Symbol.Iterator]}
+ * @return {Iterable.<Cookie>}
* Iterator.
*/
cookie.iter = function*(host, currentPath = "/") {
assert.string(host, "host must be string");
assert.string(currentPath, "currentPath must be string");
const isForCurrentPath = path => currentPath.indexOf(path) != -1;
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -70,59 +70,80 @@ const SUPPORTED_STRATEGIES = new Set([
element.Strategy.Anon,
element.Strategy.AnonAttribute,
]);
const logger = Log.repository.getLogger("Marionette");
const globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
+/**
+ * The Marionette WebDriver services provides a standard conforming
+ * implementation of the W3C WebDriver specification.
+ *
+ * @see {@link https://w3c.github.io/webdriver/webdriver-spec.html}
+ * @namespace driver
+ */
+
// This is used to prevent newSession from returning before the telephony
// API's are ready; see bug 792647. This assumes that marionette-server.js
// will be loaded before the 'system-message-listener-ready' message
// is fired. If this stops being true, this approach will have to change.
var systemMessageListenerReady = false;
Services.obs.addObserver(function() {
systemMessageListenerReady = true;
}, "system-message-listener-ready");
+/**
+ * @enum
+ * @memberof driver
+ */
this.Context = {
CHROME: "chrome",
CONTENT: "content",
};
+/** @memberof driver */
this.Context.fromString = function(s) {
s = s.toUpperCase();
if (s in this) {
return this[s];
}
return null;
};
/**
-* Helper function for converting an nsISimpleEnumerator to
-* a javascript iterator
-* @param{nsISimpleEnumerator} enumerator
-* enumerator to convert
-*/
+ * Helper function for converting a {@link nsISimpleEnumerator} to a
+ * JavaScript iterator.
+ *
+ * @memberof driver
+ *
+ * @param {nsISimpleEnumerator} enumerator
+ * Enumerator to turn into iterator.
+ *
+ * @return {Iterable}
+ * Iterator.
+ */
function* enumeratorIterator(enumerator) {
while (enumerator.hasMoreElements()) {
yield enumerator.getNext();
}
}
/**
* Implements (parts of) the W3C WebDriver protocol. GeckoDriver lives
* in chrome space and mediates calls to the message listener of the current
* browsing context's content frame message listener via ListenerProxy.
*
- * Throughout this prototype, functions with the argument {@code cmd}'s
- * documentation refers to the contents of the {@code cmd.parameters}
+ * Throughout this prototype, functions with the argument <var>cmd</var>'s
+ * documentation refers to the contents of the <code>cmd.parameter</code>}
* object.
*
+ * @class GeckoDriver
+ *
* @param {string} appName
* Description of the product, for example "Firefox".
* @param {MarionetteServer} server
* The instance of Marionette server.
*/
this.GeckoDriver = function(appName, server) {
this.appName = appName;
this._server = server;
@@ -313,17 +334,17 @@ GeckoDriver.prototype.switchToGlobalMess
/**
* Helper method to send async messages to the content listener.
* Correct usage is to pass in the name of a function in listener.js,
* a serialisable object, and optionally the current command's ID
* when not using the modern dispatching technique.
*
* @param {string} name
* Suffix of the targetted message listener
- * ({@code Marionette:<suffix>}).
+ * <tt>Marionette:SUFFIX</tt>.
* @param {Object=} msg
* Optional JSON serialisable object to send to the listener.
* @param {number=} commandID
* Optional command ID to ensure synchronisity.
*/
GeckoDriver.prototype.sendAsync = function(name, data, commandID) {
name = "Marionette:" + name;
let payload = copy(data);
@@ -371,19 +392,19 @@ GeckoDriver.prototype.sendTargettedAsync
throw new WebDriverError(e);
}
}
};
/**
* Get the session's current top-level browsing context.
*
- * It will return the outer {@ChromeWindow} previously selected by window
- * handle through {@code #switchToWindow}, or the first window that was
- * registered.
+ * It will return the outer {@link ChromeWindow} previously selected by
+ * window handle through {@link #switchToWindow}, or the first window that
+ * was registered.
*
* @param {Context=} forcedContext
* Optional name of the context to use for finding the window.
* It will be required if a command always needs a specific context,
* whether which context is currently set. Defaults to the current
* context.
*
* @return {ChromeWindow}
@@ -460,17 +481,17 @@ GeckoDriver.prototype.addBrowser = funct
* Registers a new browser, win, with Marionette.
*
* If we have not seen the browser content window before, the listener
* frame script will be loaded into it. If isNewSession is true, we will
* switch focus to the start frame when it registers.
*
* @param {nsIDOMWindow} win
* Window whose browser we need to access.
- * @param {boolean=false} isNewSession
+ * @param {boolean=} [false] isNewSession
* True if this is the first time we're talking to this browser.
*/
GeckoDriver.prototype.startBrowser = function(win, isNewSession = false) {
this.mainFrame = win;
this.curFrame = null;
this.addBrowser(win);
this.curBrowser.isNewSession = isNewSession;
this.whenBrowserStarted(win, isNewSession);
@@ -771,146 +792,148 @@ GeckoDriver.prototype.getContext = funct
resp.body.value = this.context.toString();
};
/**
* Executes a JavaScript function in the context of the current browsing
* context, if in content space, or in chrome space otherwise, and returns
* the return value of the function.
*
- * It is important to note that if the {@code sandboxName} parameter
+ * It is important to note that if the <var>sandboxName</var> parameter
* is left undefined, the script will be evaluated in a mutable sandbox,
* causing any change it makes on the global state of the document to have
* lasting side-effects.
*
* @param {string} script
* Script to evaluate as a function body.
* @param {Array.<(string|boolean|number|object|WebElement)>} args
- * Arguments exposed to the script in {@code arguments}. The array
- * items must be serialisable to the WebDriver protocol.
+ * Arguments exposed to the script in <code>arguments</code>.
+ * The array items must be serialisable to the WebDriver protocol.
* @param {number} scriptTimeout
* Duration in milliseconds of when to interrupt and abort the
* script evaluation.
* @param {string=} sandbox
* Name of the sandbox to evaluate the script in. The sandbox is
* cached for later re-use on the same Window object if
- * {@code newSandbox} is false. If he parameter is undefined,
+ * <var>newSandbox</var> is false. If he parameter is undefined,
* the script is evaluated in a mutable sandbox. If the parameter
* is "system", it will be evaluted in a sandbox with elevated system
* privileges, equivalent to chrome space.
* @param {boolean=} newSandbox
* Forces the script to be evaluated in a fresh sandbox. Note that if
* it is undefined, the script will normally be evaluted in a fresh
* sandbox.
* @param {string=} filename
* Filename of the client's program where this script is evaluated.
* @param {number=} line
* Line in the client's program where this script is evaluated.
* @param {boolean=} debug_script
- * Attach an {@code onerror} event handler on the Window object.
- * It does not differentiate content errors from chrome errors.
+ * Attach an <code>onerror</code> event handler on the {@link Window}
+ * object. It does not differentiate content errors from chrome errors.
* @param {boolean=} directInject
* Evaluate the script without wrapping it in a function.
*
* @return {(string|boolean|number|object|WebElement)}
* Return value from the script, or null which signifies either the
* JavaScript notion of null or undefined.
*
- * @throws ScriptTimeoutError
- * If the script was interrupted due to reaching the {@code
- * scriptTimeout} or default timeout.
- * @throws JavaScriptError
- * If an Error was thrown whilst evaluating the script.
+ * @throws {ScriptTimeoutError}
+ * If the script was interrupted due to reaching the
+ * <var>scriptTimeout</var> or default timeout.
+ * @throws {JavaScriptError}
+ * If an {@link Error} was thrown whilst evaluating the script.
*/
GeckoDriver.prototype.executeScript = function*(cmd, resp) {
assert.window(this.getCurrentWindow());
let {script, args, scriptTimeout} = cmd.parameters;
scriptTimeout = scriptTimeout || this.timeouts.script;
let opts = {
sandboxName: cmd.parameters.sandbox,
newSandbox: !!(typeof cmd.parameters.newSandbox == "undefined") ||
cmd.parameters.newSandbox,
- filename: cmd.parameters.filename,
+ file: cmd.parameters.filename,
line: cmd.parameters.line,
debug: cmd.parameters.debug_script,
};
resp.body.value = yield this.execute_(script, args, scriptTimeout, opts);
};
/**
* Executes a JavaScript function in the context of the current browsing
* context, if in content space, or in chrome space otherwise, and returns
* the object passed to the callback.
*
- * The callback is always the last argument to the {@code arguments}
+ * The callback is always the last argument to the <var>arguments</var>
* list passed to the function scope of the script. It can be retrieved
* as such:
*
+ * <pre><code>
* let callback = arguments[arguments.length - 1];
* callback("foo");
* // "foo" is returned
+ * </code></pre>
*
- * It is important to note that if the {@code sandboxName} parameter
+ * It is important to note that if the <var>sandboxName</var> parameter
* is left undefined, the script will be evaluated in a mutable sandbox,
* causing any change it makes on the global state of the document to have
* lasting side-effects.
*
* @param {string} script
* Script to evaluate as a function body.
* @param {Array.<(string|boolean|number|object|WebElement)>} args
- * Arguments exposed to the script in {@code arguments}. The array
- * items must be serialisable to the WebDriver protocol.
+ * Arguments exposed to the script in <code>arguments</code>.
+ * The array items must be serialisable to the WebDriver protocol.
* @param {number} scriptTimeout
* Duration in milliseconds of when to interrupt and abort the
* script evaluation.
* @param {string=} sandbox
* Name of the sandbox to evaluate the script in. The sandbox is
* cached for later re-use on the same Window object if
- * {@code newSandbox} is false. If the parameter is undefined,
+ * <var>newSandbox</var> is false. If the parameter is undefined,
* the script is evaluated in a mutable sandbox. If the parameter
* is "system", it will be evaluted in a sandbox with elevated system
* privileges, equivalent to chrome space.
* @param {boolean=} newSandbox
* Forces the script to be evaluated in a fresh sandbox. Note that if
* it is undefined, the script will normally be evaluted in a fresh
* sandbox.
* @param {string=} filename
* Filename of the client's program where this script is evaluated.
* @param {number=} line
* Line in the client's program where this script is evaluated.
* @param {boolean=} debug_script
- * Attach an {@code onerror} event handler on the Window object.
- * It does not differentiate content errors from chrome errors.
+ * Attach an <code>onerror</code> event handler on the {@link Window}
+ * object. It does not differentiate content errors from chrome errors.
* @param {boolean=} directInject
* Evaluate the script without wrapping it in a function.
*
* @return {(string|boolean|number|object|WebElement)}
* Return value from the script, or null which signifies either the
* JavaScript notion of null or undefined.
*
- * @throws ScriptTimeoutError
- * If the script was interrupted due to reaching the {@code
- * scriptTimeout} or default timeout.
- * @throws JavaScriptError
+ * @throws {ScriptTimeoutError}
+ * If the script was interrupted due to reaching the
+ * <var>scriptTimeout</var> or default timeout.
+ * @throws {JavaScriptError}
* If an Error was thrown whilst evaluating the script.
*/
GeckoDriver.prototype.executeAsyncScript = function* (cmd, resp) {
assert.window(this.getCurrentWindow());
let {script, args, scriptTimeout} = cmd.parameters;
scriptTimeout = scriptTimeout || this.timeouts.script;
let opts = {
sandboxName: cmd.parameters.sandbox,
newSandbox: !!(typeof cmd.parameters.newSandbox == "undefined") ||
cmd.parameters.newSandbox,
- filename: cmd.parameters.filename,
+ file: cmd.parameters.filename,
line: cmd.parameters.line,
debug: cmd.parameters.debug_script,
async: true,
};
resp.body.value = yield this.execute_(script, args, scriptTimeout, opts);
};
@@ -2500,18 +2523,18 @@ GeckoDriver.prototype.switchToShadowRoot
*
* @throws {UnsupportedOperationError}
* Not available in current context.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
* @throws {InvalidCookieDomainError}
- * If |cookie| is for a different domain than the active document's
- * host.
+ * If <var>cookie</var> is for a different domain than the active
+ * document's host.
*/
GeckoDriver.prototype.addCookie = function(cmd, resp) {
assert.content(this.context);
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
let {protocol, hostname} = this.currentURL;
@@ -2526,18 +2549,18 @@ GeckoDriver.prototype.addCookie = functi
}
cookie.add(newCookie, {restrictToHost: hostname});
};
/**
* Get all the cookies for the current domain.
*
- * This is the equivalent of calling {@code document.cookie} and parsing
- * the result.
+ * This is the equivalent of calling <code>document.cookie</code> and
+ * parsing the result.
*
* @throws {UnsupportedOperationError}
* Not available in current context.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
@@ -2756,27 +2779,27 @@ GeckoDriver.prototype.deleteSession = fu
*
* @param {string=} id
* Optional web element reference to take a screenshot of.
* If undefined, a screenshot will be taken of the document element.
* @param {Array.<string>=} highlights
* List of web elements to highlight.
* @param {boolean} full
* True to take a screenshot of the entire document element. Is not
- * considered if {@code id} is not defined. Defaults to true.
+ * considered if <var>id</var> is not defined. Defaults to true.
* @param {boolean=} hash
* True if the user requests a hash of the image data.
* @param {boolean=} scroll
* Scroll to element if |id| is provided. If undefined, it will
* scroll to the element.
*
* @return {string}
- * If |hash| is false, PNG image encoded as Base64 encoded string.
- * If |hash| is True, hex digest of the SHA-256 hash of the base64
- * encoded string.
+ * If <var>hash</var> is false, PNG image encoded as Base64 encoded
+ * string. If <var>hash</var> is true, hex digest of the SHA-256
+ * hash of the Base64 encoded string.
*/
GeckoDriver.prototype.takeScreenshot = function(cmd, resp) {
let win = assert.window(this.getCurrentWindow());
let {id, highlights, full, hash} = cmd.parameters;
highlights = highlights || [];
let format = hash ? capture.Format.Hash : capture.Format.Base64;
@@ -3045,17 +3068,17 @@ GeckoDriver.prototype.acceptConnections
}
/**
* Quits the application with the provided flags.
*
* Marionette will stop accepting new connections before ending the
* current session, and finally attempting to quit the application.
*
- * Optional {@code nsIAppStartup} flags may be provided as
+ * Optional {@link nsIAppStartup} flags may be provided as
* an array of masks, and these will be combined by ORing
* them with a bitmask. The available masks are defined in
* https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIAppStartup.
*
* Crucially, only one of the *Quit flags can be specified. The |eRestart|
* flag may be bit-wise combined with one of the *Quit flags to cause
* the application to restart after it quits.
*
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -17,41 +17,42 @@ const {
JavaScriptError,
NoSuchElementError,
StaleElementReferenceError,
} = Cu.import("chrome://marionette/content/error.js", {});
Cu.import("chrome://marionette/content/wait.js");
const logger = Log.repository.getLogger("Marionette");
+this.EXPORTED_SYMBOLS = ["element"];
+
+const DOCUMENT_POSITION_DISCONNECTED = 1;
+const XMLNS = "http://www.w3.org/1999/xhtml";
+
+const uuidGen = Cc["@mozilla.org/uuid-generator;1"]
+ .getService(Ci.nsIUUIDGenerator);
+
/**
* This module provides shared functionality for dealing with DOM-
* and web elements in Marionette.
*
* A web element is an abstraction used to identify an element when it
* is transported across the protocol, between remote- and local ends.
*
* Each element has an associated web element reference (a UUID) that
* uniquely identifies the the element across all browsing contexts. The
* web element reference for every element representing the same element
* is the same.
*
- * The @code{element.Store} provides a mapping between web element
+ * The {@link element.Store} provides a mapping between web element
* references and DOM elements for each browsing context. It also provides
* functionality for looking up and retrieving elements.
+ *
+ * @namespace
*/
-
-this.EXPORTED_SYMBOLS = ["element"];
-
-const DOCUMENT_POSITION_DISCONNECTED = 1;
-const XMLNS = "http://www.w3.org/1999/xhtml";
-
-const uuidGen = Cc["@mozilla.org/uuid-generator;1"]
- .getService(Ci.nsIUUIDGenerator);
-
this.element = {};
element.Key = "element-6066-11e4-a52e-4f735466cecf";
element.LegacyKey = "ELEMENT";
element.Strategy = {
ClassName: "class name",
Selector: "css selector",
@@ -66,16 +67,19 @@ element.Strategy = {
};
/**
* Stores known/seen elements and their associated web element
* references.
*
* Elements are added by calling |add(el)| or |addAll(elements)|, and
* may be queried by their web element reference using |get(element)|.
+ *
+ * @class
+ * @memberof element
*/
element.Store = class {
constructor() {
this.els = {};
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
}
clear() {
@@ -231,17 +235,17 @@ element.Store = class {
* @param {string} strategy
* Search strategy whereby to locate the element(s).
* @param {string} selector
* Selector search pattern. The selector must be compatible with
* the chosen search |strategy|.
* @param {Object.<string, ?>} opts
* Options.
*
- * @return {Promise: (nsIDOMElement|Array<nsIDOMElement>)}
+ * @return {Promise.<(nsIDOMElement|Array.<nsIDOMElement>)>}
* Single element or a sequence of elements.
*
* @throws InvalidSelectorError
* If |strategy| is unknown.
* @throws InvalidSelectorError
* If |selector| is malformed.
* @throws NoSuchElementError
* If a single element is requested, this error will throw if the
@@ -607,17 +611,17 @@ element.makeWebElement = function(uuid)
[element.LegacyKey]: uuid,
};
};
/**
* Checks if |ref| has either |element.Key| or |element.LegacyKey|
* as properties.
*
- * @param {?} ref
+ * @param {Object.<string, string>} ref
* Object that represents a web element reference.
* @return {boolean}
* True if |ref| has either expected property.
*/
element.isWebElementReference = function(ref) {
let properties = Object.getOwnPropertyNames(ref);
return properties.includes(element.Key) ||
properties.includes(element.LegacyKey);
--- a/testing/marionette/error.js
+++ b/testing/marionette/error.js
@@ -42,16 +42,17 @@ const BUILTIN_ERRORS = new Set([
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError",
]);
this.EXPORTED_SYMBOLS = ["error", "error.pprint"].concat(Array.from(ERRORS));
+/** @namespace */
this.error = {};
/**
* Check if |val| is an instance of the |Error| prototype.
*
* Because error objects may originate from different globals, comparing
* the prototype of the left hand side with the prototype property from
* the right hand side, which is what |instanceof| does, will not work.
@@ -228,24 +229,38 @@ class WebDriverError extends Error {
this.status = "webdriver error";
// Error's ctor does not preserve x' stack
if (error.isError(x)) {
this.stack = x.stack;
}
}
+ /**
+ * @return {Object.<string, string>}
+ * JSON serialisation of error prototype.
+ */
toJSON() {
return {
error: this.status,
message: this.message || "",
stacktrace: this.stack || "",
}
}
+ /**
+ * Unmarshals a JSON error representation to the appropriate Marionette
+ * error type.
+ *
+ * @param {Object.<string, string>} json
+ * Error object.
+ *
+ * @return {Error}
+ * Error prototype.
+ */
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);
}
@@ -257,16 +272,17 @@ class WebDriverError extends Error {
}
if ("stacktrace" in json) {
err.stack = json.stacktrace;
}
return err;
}
}
+/** The Gecko a11y API indicates that the element is not accessible. */
class ElementNotAccessibleError extends WebDriverError {
constructor(message) {
super(message);
this.status = "element not accessible";
}
}
/**
@@ -305,30 +321,39 @@ class ElementClickInterceptedError exten
}
}
super(msg);
this.status = "element click intercepted";
}
}
+/**
+ * A command could not be completed because the element is not pointer-
+ * or keyboard interactable.
+ */
class ElementNotInteractableError extends WebDriverError {
constructor(message) {
super(message);
this.status = "element not interactable";
}
}
+/**
+ * Navigation caused the user agent to hit a certificate warning, which
+ * is usually the result of an expired or invalid TLS certificate.
+ */
class InsecureCertificateError extends WebDriverError {
constructor(message) {
super(message);
this.status = "insecure certificate";
}
}
+/** The arguments passed to a command are either invalid or malformed. */
class InvalidArgumentError extends WebDriverError {
constructor(message) {
super(message);
this.status = "invalid argument";
}
}
class InvalidCookieDomainError extends WebDriverError {
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -27,183 +27,185 @@ const ARGUMENTS = "__webDriverArguments"
const CALLBACK = "__webDriverCallback";
const COMPLETE = "__webDriverComplete";
const DEFAULT_TIMEOUT = 10000; // ms
const FINISH = "finish";
const MARIONETTE_SCRIPT_FINISHED = "marionetteScriptFinished";
const ELEMENT_KEY = "element";
const W3C_ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf";
+/** @namespace */
this.evaluate = {};
/**
* Evaluate a script in given sandbox.
*
- * If the option {@code directInject} is not specified, the script will
- * be executed as a function with the {@code args} argument applied.
+ * If the option var>directInject</var> is not specified, the script
+ * will be executed as a function with the <var>args</var> argument
+ * applied.
*
- * The arguments provided by the {@code args} argument are exposed through
- * the {@code arguments} object available in the script context, and if
- * the script is executed asynchronously with the {@code async}
- * option, an additional last argument that is synonymous to the
- * {@code marionetteScriptFinished} global is appended, and can be
- * accessed through {@code arguments[arguments.length - 1]}.
+ * The arguments provided by the <var>args</var> argument are exposed
+ * through the <code>arguments</code> object available in the script
+ * context, and if the script is executed asynchronously with the
+ * <var>async</var> option, an additional last argument that is synonymous
+ * to the <code>marionetteScriptFinished</code> global is appended, and
+ * can be accessed through <code>arguments[arguments.length - 1]</code>.
*
- * The {@code timeout} option specifies the duration for how long the
- * script should be allowed to run before it is interrupted and aborted.
- * An interrupted script will cause a ScriptTimeoutError to occur.
+ * The <var>timeout</var> option specifies the duration for how long
+ * the script should be allowed to run before it is interrupted and aborted.
+ * An interrupted script will cause a {@link ScriptTimeoutError} to occur.
*
- * The {@code async} option indicates that the script will not return
- * until the {@code marionetteScriptFinished} global callback is invoked,
- * which is analogous to the last argument of the {@code arguments}
- * object.
+ * The <var>async</var> option indicates that the script will
+ * not return until the <code>marionetteScriptFinished</code> global
+ * callback is invoked, which is analogous to the last argument of the
+ * <code>arguments</code> object.
*
- * The option {@code directInject} causes the script to be evaluated
+ * The option <var>directInject</var> causes the script to be evaluated
* without being wrapped in a function and the provided arguments will
* be disregarded. This will cause such things as root scope return
* statements to throw errors because they are not used inside a function.
*
- * The {@code filename} option is used in error messages to provide
+ * The <var>file</var> option is used in error messages to provide
* information on the origin script file in the local end.
*
- * The {@code line} option is used in error messages, along with
- * {@code filename}, to provide the line number in the origin script
+ * The <var>line</var> option is used in error messages, along with
+ * <var>filename</var>, to provide the line number in the origin script
* file on the local end.
*
- * @param {nsISandbox) sb
- * The sandbox the script will be evaluted in.
+ * @param {nsISandbox} sb
+ * Sandbox the script will be evaluted in.
* @param {string} script
- * The script to evaluate.
+ * Script to evaluate.
* @param {Array.<?>=} args
* A sequence of arguments to call the script with.
- * @param {Object.<string, ?>=} opts
- * Dictionary of options:
- *
- * async (boolean)
- * Indicates if the script should return immediately or wait
- * for the callback be invoked before returning.
- * debug (boolean)
- * Attaches an {@code onerror} event listener.
- * directInject (boolean)
- * Evaluates the script without wrapping it in a function.
- * filename (string)
- * File location of the program in the client.
- * line (number)
- * Line number of the program in the client.
- * sandboxName (string)
- * Name of the sandbox. Elevated system privileges, equivalent
- * to chrome space, will be given if it is "system".
- * timeout (boolean)
- * Duration in milliseconds before interrupting the script.
+ * @param {boolean=} [async=false] async
+ * Indicates if the script should return immediately or wait for
+ * the callback to be invoked before returning.
+ * @param {boolean=} [debug=false] debug
+ * Attaches an <code>onerror</code> event listener.
+ * @param {string=} [file="dummy file"] file
+ * File location of the program in the client.
+ * @param {number=} [line=0] line
+ * Line number of th eprogram in the client.
+ * @param {string=} sandboxName
+ * Name of the sandbox. Elevated system privileges, equivalent to
+ * chrome space, will be given if it is <tt>system</tt>.
+ * @param {number=} [timeout=DEFAULT_TIMEOUT] timeout
+ * Duration in milliseconds before interrupting the script.
*
* @return {Promise}
* A promise that when resolved will give you the return value from
* the script. Note that the return value requires serialisation before
* it can be sent to the client.
*
* @throws {JavaScriptError}
- * If an Error was thrown whilst evaluating the script.
+ * If an {@link Error} was thrown whilst evaluating the script.
* @throws {ScriptTimeoutError}
* If the script was interrupted due to script timeout.
*/
-evaluate.sandbox = function(sb, script, args = [], opts = {}) {
+evaluate.sandbox = function(sb, script, args = [],
+ {
+ async = false,
+ debug = false,
+ directInject = false,
+ file = "dummy file",
+ line = 0,
+ sandboxName = null,
+ timeout = DEFAULT_TIMEOUT,
+ } = {}) {
let scriptTimeoutID, timeoutHandler, unloadHandler;
let promise = new Promise((resolve, reject) => {
let src = "";
sb[COMPLETE] = resolve;
timeoutHandler = () => reject(new ScriptTimeoutError("Timed out"));
unloadHandler = sandbox.cloneInto(
() => reject(new JavaScriptError("Document was unloaded")),
sb);
// wrap in function
- if (!opts.directInject) {
- if (opts.async) {
+ if (!directInject) {
+ if (async) {
sb[CALLBACK] = sb[COMPLETE];
}
sb[ARGUMENTS] = sandbox.cloneInto(args, sb);
// callback function made private
// so that introspection is possible
// on the arguments object
- if (opts.async) {
+ if (async) {
sb[CALLBACK] = sb[COMPLETE];
src += `${ARGUMENTS}.push(rv => ${CALLBACK}(rv));`;
}
src += `(function() { ${script} }).apply(null, ${ARGUMENTS})`;
// marionetteScriptFinished is not WebDriver conformant,
// hence it is only exposed to immutable sandboxes
- if (opts.sandboxName) {
+ if (sandboxName) {
sb[MARIONETTE_SCRIPT_FINISHED] = sb[CALLBACK];
}
}
// onerror is not hooked on by default because of the inability to
// differentiate content errors from chrome errors.
//
// see bug 1128760 for more details
- if (opts.debug) {
+ if (debug) {
sb.window.onerror = (msg, url, line) => {
let err = new JavaScriptError(`${msg} at ${url}:${line}`);
reject(err);
};
}
// timeout and unload handlers
- const timeout = opts.timeout || DEFAULT_TIMEOUT;
scriptTimeoutID = setTimeout(timeoutHandler, timeout);
sb.window.onunload = unloadHandler;
- const file = opts.filename || "dummy file";
- const line = opts.line || 0;
-
let res;
try {
res = Cu.evalInSandbox(src, sb, "1.8", file, 0);
} catch (e) {
let err = new JavaScriptError(e, {
fnName: "execute_script",
file,
line,
script,
});
reject(err);
}
- if (!opts.async) {
+ if (!async) {
resolve(res);
}
});
return promise.then(res => {
clearTimeout(scriptTimeoutID);
sb.window.removeEventListener("unload", unloadHandler);
return res;
});
};
/**
* Convert any web elements in arbitrary objects to DOM elements by
* looking them up in the seen element store.
*
- * @param {?} obj
+ * @param {Object} obj
* Arbitrary object containing web elements.
* @param {element.Store} seenEls
* Element store to use for lookup of web element references.
* @param {Window} win
* Window.
* @param {ShadowRoot} shadowRoot
* Shadow root.
*
- * @return {?}
- * Same object as provided by |obj| with the web elements replaced
- * by DOM elements.
+ * @return {Object}
+ * Same object as provided by <var>obj</var> with the web elements
+ * replaced by DOM elements.
*/
evaluate.fromJSON = function(obj, seenEls, win, shadowRoot = undefined) {
switch (typeof obj) {
case "boolean":
case "number":
case "string":
default:
return obj;
@@ -241,24 +243,24 @@ evaluate.fromJSON = function(obj, seenEl
/**
* Convert arbitrary objects to JSON-safe primitives that can be
* transported over the Marionette protocol.
*
* Any DOM elements are converted to web elements by looking them up
* and/or adding them to the element store provided.
*
- * @param {?} obj
+ * @param {Object} obj
* Object to be marshaled.
* @param {element.Store} seenEls
* Element store to use for lookup of web element references.
*
- * @return {?}
- * Same object as provided by |obj| with the elements replaced by
- * web elements.
+ * @return {Object}
+ * Same object as provided by <var>obj</var> with the elements
+ * replaced by web elements.
*/
evaluate.toJSON = function(obj, seenEls) {
const t = Object.prototype.toString.call(obj);
// null
if (t == "[object Undefined]" || t == "[object Null]") {
return null;
@@ -299,23 +301,23 @@ evaluate.toJSON = function(obj, seenEls)
return rv;
};
/**
* Cu.isDeadWrapper does not return true for a dead sandbox that was
* assosciated with and extension popup. This provides a way to still
* test for a dead object.
*
- * @param {?} obj
+ * @param {Object} obj
* A potentially dead object.
* @param {string} prop
* Name of a property on the object.
*
* @returns {boolean}
- * True if |obj| is dead, false otherwise.
+ * True if <var>obj</var> is dead, false otherwise.
*/
evaluate.isDead = function(obj, prop) {
try {
obj[prop];
} catch (e) {
if (e.message.includes("dead object")) {
return true;
}
@@ -326,32 +328,32 @@ evaluate.isDead = function(obj, prop) {
this.sandbox = {};
/**
* Provides a safe way to take an object defined in a privileged scope and
* create a structured clone of it in a less-privileged scope. It returns
* a reference to the clone.
*
- * Unlike for |Components.utils.cloneInto|, |obj| may contain functions
- * and DOM elemnets.
+ * Unlike for {@link Components.utils.cloneInto}, <var>obj</var> may
+ * contain functions and DOM elemnets.
*/
sandbox.cloneInto = function(obj, sb) {
return Cu.cloneInto(obj, sb, {cloneFunctions: true, wrapReflectors: true});
};
/**
- * Augment given sandbox by an adapter that has an {@code exports}
+ * Augment given sandbox by an adapter that has an <code>exports</code>
* map property, or a normal map, of function names and function
* references.
*
* @param {Sandbox} sb
* The sandbox to augment.
* @param {Object} adapter
- * Object that holds an {@code exports} property, or a map, of
+ * Object that holds an <code>exports</code> property, or a map, of
* function names and function references.
*
* @return {Sandbox}
* The augmented sandbox.
*/
sandbox.augment = function(sb, adapter) {
function* entries(obj) {
for (let key of Object.keys(obj)) {
@@ -420,16 +422,18 @@ sandbox.createSimpleTest = function(wind
sb[FINISH] = () => sb[COMPLETE](harness.generate_results());
return sb;
};
/**
* Sandbox storage. When the user requests a sandbox by a specific name,
* if one exists in the storage this will be used as long as its window
* reference is still valid.
+ *
+ * @memberof evaluate
*/
this.Sandboxes = class {
/**
* @param {function(): Window} windowFn
* A function that returns the references to the current Window
* object.
*/
constructor(windowFn) {
@@ -445,17 +449,17 @@ this.Sandboxes = class {
* Factory function for getting a sandbox by name, or failing that,
* creating a new one.
*
* If the sandbox' window does not match the provided window, a new one
* will be created.
*
* @param {string} name
* The name of the sandbox to get or create.
- * @param {boolean} fresh
+ * @param {boolean=} [fresh=false] fresh
* Remove old sandbox by name first, if it exists.
*
* @return {Sandbox}
* A used or fresh sandbox.
*/
get(name = "default", fresh = false) {
let sb = this.boxes_.get(name);
if (sb) {
@@ -469,15 +473,13 @@ this.Sandboxes = class {
} else {
sb = sandbox.create(this.window_);
}
this.boxes_.set(name, sb);
}
return sb;
}
- /**
- * Clears cache of sandboxes.
- */
+ /** Clears cache of sandboxes. */
clear() {
this.boxes_.clear();
}
};
--- a/testing/marionette/event.js
+++ b/testing/marionette/event.js
@@ -33,16 +33,19 @@ function getDOMWindowUtils(win) {
win = window;
}
// this assumes we are operating in chrome space
return win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
}
+/** @namespace */
+this.event = {};
+
event.MouseEvents = {
click: 0,
dblclick: 1,
mousedown: 2,
mouseup: 3,
mouseover: 4,
mouseout: 5,
};
@@ -1288,17 +1291,17 @@ event.sendKeyUp = function(keyToSend, mo
delete modifiers.type;
};
/**
* Synthesize a key event for a single key.
*
* @param {string} keyToSend
* Code point or normalized key value
- * @param {?} modifiers
+ * @param {Object.<string, boolean>} modifiers
* Object with properties used in KeyboardEvent (shiftkey, repeat, ...)
* as well as, the event |type| such as keydown. All properties
* are optional.
* @param {Window=} window
* Window object. If |window| is undefined, the event is synthesized
* in current window.
*/
event.sendSingleKey = function(keyToSend, modifiers, window = undefined) {
--- a/testing/marionette/frame.js
+++ b/testing/marionette/frame.js
@@ -6,16 +6,17 @@
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
this.EXPORTED_SYMBOLS = ["frame"];
+/** @namespace */
this.frame = {};
const FRAME_SCRIPT = "chrome://marionette/content/listener.js";
// list of OOP frames that has the frame script loaded
var remoteFrames = [];
/**
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -18,19 +18,17 @@ const {
} = Cu.import("chrome://marionette/content/error.js", {});
Cu.import("chrome://marionette/content/element.js");
Cu.import("chrome://marionette/content/event.js");
Cu.importGlobalProperties(["File"]);
this.EXPORTED_SYMBOLS = ["interaction"];
-/**
- * XUL elements that support disabled attribute.
- */
+/** XUL elements that support disabled attribute. */
const DISABLED_ATTRIBUTE_SUPPORTED_XUL = new Set([
"ARROWSCROLLBOX",
"BUTTON",
"CHECKBOX",
"COLORPICKER",
"COMMAND",
"DATEPICKER",
"DESCRIPTION",
@@ -55,100 +53,98 @@ const DISABLED_ATTRIBUTE_SUPPORTED_XUL =
"TAB",
"TABS",
"TEXTBOX",
"TIMEPICKER",
"TOOLBARBUTTON",
"TREE",
]);
-/**
- * XUL elements that support checked property.
- */
+/** XUL elements that support checked property. */
const CHECKED_PROPERTY_SUPPORTED_XUL = new Set([
"BUTTON",
"CHECKBOX",
"LISTITEM",
"TOOLBARBUTTON",
]);
-/**
- * XUL elements that support selected property.
- */
+/** XUL elements that support selected property. */
const SELECTED_PROPERTY_SUPPORTED_XUL = new Set([
"LISTITEM",
"MENU",
"MENUITEM",
"MENUSEPARATOR",
"RADIO",
"RICHLISTITEM",
"TAB",
]);
/**
- * Common form controls that user can change the value property interactively.
+ * Common form controls that user can change the value property
+ * interactively.
*/
const COMMON_FORM_CONTROLS = new Set([
"input",
"textarea",
"select",
]);
/**
- * Input elements that do not fire "input" and "change" events when value
- * property changes.
+ * Input elements that do not fire <tt>input</tt> and <tt>change</tt>
+ * events when value property changes.
*/
const INPUT_TYPES_NO_EVENT = new Set([
"checkbox",
"radio",
"file",
"hidden",
"image",
"reset",
"button",
"submit",
]);
+/** @namespace */
this.interaction = {};
/**
* Interact with an element by clicking it.
*
* The element is scrolled into view before visibility- or interactability
* checks are performed.
*
- * Selenium-style visibility checks will be performed if |specCompat|
- * is false (default). Otherwise pointer-interactability checks will be
- * performed. If either of these fail an
- * {@code ElementNotInteractableError} is thrown.
+ * Selenium-style visibility checks will be performed
+ * if <var>specCompat</var> is false (default). Otherwise
+ * pointer-interactability checks will be performed. If either of these
+ * fail an {@link ElementNotInteractableError} is thrown.
*
- * If |strict| is enabled (defaults to disabled), further accessibility
- * checks will be performed, and these may result in an
- * {@code ElementNotAccessibleError} being returned.
+ * If <var>strict</var> is enabled (defaults to disabled), further
+ * accessibility checks will be performed, and these may result in an
+ * {@link ElementNotAccessibleError} being returned.
*
- * When |el| is not enabled, an {@code InvalidElementStateError}
+ * When <var>el</var> is not enabled, an {@link InvalidElementStateError}
* is returned.
*
- * @param {DOMElement|XULElement} el
+ * @param {(DOMElement|XULElement)} el
* Element to click.
- * @param {boolean=} strict
+ * @param {boolean=} [strict=false] strict
* Enforce strict accessibility tests.
- * @param {boolean=} specCompat
+ * @param {boolean=} [specCompat=false] specCompat
* Use WebDriver specification compatible interactability definition.
*
* @throws {ElementNotInteractableError}
* If either Selenium-style visibility check or
* pointer-interactability check fails.
* @throws {ElementClickInterceptedError}
- * If |el| is obscured by another element and a click would not hit,
- * in |specCompat| mode.
+ * If <var>el</var> is obscured by another element and a click would
+ * not hit, in <var>specCompat</var> mode.
* @throws {ElementNotAccessibleError}
- * If |strict| is true and element is not accessible.
+ * If <var>strict</var> is true and element is not accessible.
* @throws {InvalidElementStateError}
- * If |el| is not enabled.
+ * If <var>el</var> is not enabled.
*/
interaction.clickElement = function* (
el, strict = false, specCompat = false) {
const a11y = accessibility.get(strict);
if (element.isXULElement(el)) {
yield chromeClick(el, a11y);
} else if (specCompat) {
yield webdriverClickElement(el, a11y);
@@ -259,31 +255,34 @@ function* seleniumClickElement(el, a11y)
let rects = el.getClientRects();
let centre = element.getInViewCentrePoint(rects[0], win);
let opts = {};
event.synthesizeMouseAtPoint(centre.x, centre.y, opts, win);
}
}
/**
- * Select <option> element in a <select> list.
+ * Select <tt><option></tt> element in a <tt><select></tt>
+ * list.
*
* Because the dropdown list of select elements are implemented using
* native widget technology, our trusted synthesised events are not able
* to reach them. Dropdowns are instead handled mimicking DOM events,
* which for obvious reasons is not ideal, but at the current point in
* time considered to be good enough.
*
* @param {HTMLOptionElement} option
* Option element to select.
*
- * @throws TypeError
- * If |el| is a XUL element or not an <option> element.
- * @throws Error
- * If unable to find |el|'s parent <select> element.
+ * @throws {TypeError}
+ * If <var>el</var> is a XUL element or not an <tt><option></tt>
+ * element.
+ * @throws {Error}
+ * If unable to find <var>el</var>'s parent <tt><select></tt>
+ * element.
*/
interaction.selectOption = function(el) {
if (element.isXULElement(el)) {
throw new Error("XUL dropdowns not supported");
}
if (el.localName != "option") {
throw new TypeError("Invalid elements");
}
@@ -313,17 +312,18 @@ interaction.selectOption = function(el)
* If the document is unloaded during this request, the promise is
* rejected.
*
* @param {Window} win
* Associated window.
*
* @return {Promise}
* Promise is accepted once event queue is flushed, or rejected if
- * |win| has closed or been unloaded before the queue can be flushed.
+ * <var>win</var> has closed or been unloaded before the queue can
+ * be flushed.
*/
interaction.flushEventLoop = function* (win) {
return new Promise(resolve => {
let handleEvent = event => {
win.removeEventListener("beforeunload", this);
resolve();
};
@@ -333,20 +333,21 @@ interaction.flushEventLoop = function* (
}
win.addEventListener("beforeunload", handleEvent);
win.requestAnimationFrame(handleEvent);
});
};
/**
- * Appends |path| to an <input type=file>'s file list.
+ * Appends <var>path</var> to an <tt><input type=file></tt>'s
+ * file list.
*
* @param {HTMLInputElement} el
- * An <input type=file> element.
+ * An <tt><input type=file></tt> element.
* @param {string} path
* Full path to file.
*/
interaction.uploadFile = function* (el, path) {
let file = yield File.createFromFileName(path).then(file => {
return file;
}, () => {
return null;
@@ -377,18 +378,18 @@ interaction.uploadFile = function* (el,
/**
* Sets a form element's value.
*
* @param {DOMElement} el
* An form element, e.g. input, textarea, etc.
* @param {string} value
* The value to be set.
*
- * @throws TypeError
- * If |el| is not an supported form element.
+ * @throws {TypeError}
+ * If <var>el</var> is not an supported form element.
*/
interaction.setFormControlValue = function* (el, value) {
if (!COMMON_FORM_CONTROLS.has(el.localName)) {
throw new TypeError("This function is for form elements only");
}
el.value = value;
@@ -404,17 +405,17 @@ interaction.setFormControlValue = functi
* Send keys to element.
*
* @param {DOMElement|XULElement} el
* Element to send key events to.
* @param {Array.<string>} value
* Sequence of keystrokes to send to the element.
* @param {boolean} ignoreVisibility
* Flag to enable or disable element visibility tests.
- * @param {boolean=} strict
+ * @param {boolean=} [strict=false] strict
* Enforce strict accessibility tests.
*/
interaction.sendKeysToElement = function(
el, value, ignoreVisibility, strict = false) {
let win = getWindow(el);
let a11y = accessibility.get(strict);
return a11y.getAccessible(el, true).then(acc => {
a11y.assertActionable(acc, el);
@@ -422,17 +423,17 @@ interaction.sendKeysToElement = function
});
};
/**
* Determine the element displayedness of an element.
*
* @param {DOMElement|XULElement} el
* Element to determine displayedness of.
- * @param {boolean=} strict
+ * @param {boolean=} [strict=false] strict
* Enforce strict accessibility tests.
*
* @return {boolean}
* True if element is displayed, false otherwise.
*/
interaction.isElementDisplayed = function(el, strict = false) {
let win = getWindow(el);
let displayed = atom.isElementDisplayed(el, win);
@@ -479,17 +480,17 @@ interaction.isElementEnabled = function(
/**
* Determines if the referenced element is selected or not.
*
* This operation only makes sense on input elements of the Checkbox-
* and Radio Button states, or option elements.
*
* @param {DOMElement|XULElement} el
* Element to test if is selected.
- * @param {boolean=} strict
+ * @param {boolean=} [strict=false] strict
* Enforce strict accessibility tests.
*
* @return {boolean}
* True if element is selected, false otherwise.
*/
interaction.isElementSelected = function(el, strict = false) {
let selected = true;
let win = getWindow(el);
--- a/testing/marionette/l10n.js
+++ b/testing/marionette/l10n.js
@@ -23,16 +23,17 @@ Cu.import("resource://gre/modules/XPCOMU
XPCOMUtils.defineLazyServiceGetter(
this, "domParser", "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
const {NoSuchElementError} =
Cu.import("chrome://marionette/content/error.js", {});
this.EXPORTED_SYMBOLS = ["l10n"];
+/** @namespace */
this.l10n = {};
/**
* Retrieve the localized string for the specified entity id.
*
* Example:
* localizeEntity(["chrome://global/locale/about.dtd"], "about.version")
*
--- a/testing/marionette/legacyaction.js
+++ b/testing/marionette/legacyaction.js
@@ -13,16 +13,17 @@ Cu.import("chrome://marionette/content/e
const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms
this.EXPORTED_SYMBOLS = ["legacyaction"];
const logger = Log.repository.getLogger("Marionette");
+/** @namespace */
this.legacyaction = this.action = {};
/**
* Functionality for (single finger) action chains.
*/
action.Chain = function (checkForInterrupted) {
// for assigning unique ids to all touches
this.nextTouchId = 1000;
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -710,31 +710,31 @@ function deleteSession(msg) {
/**
* Send asynchronous reply to chrome.
*
* @param {UUID} uuid
* Unique identifier of the request.
* @param {AsyncContentSender.ResponseType} type
* Type of response.
- * @param {?=} data
+ * @param {*} [Object] data
* JSON serialisable object to accompany the message. Defaults to
* an empty dictionary.
*/
function sendToServer(uuid, data = undefined) {
let channel = new proxy.AsyncMessageChannel(
() => this,
sendAsyncMessage.bind(this));
channel.reply(uuid, data);
}
/**
* Send asynchronous reply with value to chrome.
*
- * @param {?} obj
+ * @param {Object} obj
* JSON serialisable object of arbitrary type and complexity.
* @param {UUID} uuid
* Unique identifier of the request.
*/
function sendResponse(obj, uuid) {
sendToServer(uuid, obj);
}
--- a/testing/marionette/message.js
+++ b/testing/marionette/message.js
@@ -16,33 +16,49 @@ this.EXPORTED_SYMBOLS = [
"Command",
"Message",
"MessageOrigin",
"Response",
];
const logger = Log.repository.getLogger("Marionette");
+/**
+ * Messages may originate from either the server or the client.
+ * Because the remote protocol is full duplex, both endpoints may be the
+ * origin of both commands and responses.
+ *
+ * @enum
+ * @see {@link Message}
+ */
const MessageOrigin = {
+ /** Indicates that the message originates from the client. */
Client: 0,
+ /** Indicates that the message originates from the server. */
Server: 1,
};
+/**
+ * Representation of the packets transproted over the wire.
+ *
+ * @class
+ */
this.Message = {};
/**
* Converts a data packet into a Command or Response type.
*
* @param {Array.<number, number, ?, ?>} data
* A four element array where the elements, in sequence, signifies
* message type, message ID, method name or error, and parameters
* or result.
*
- * @return {(Command,Response)}
- * Based on the message type, a Command or Response instance.
+ * @return {Message}
+ * Based on the message type, a {@link Command} or {@link Response}
+ * instance.
*
* @throws {TypeError}
* If the message type is not recognised.
*/
Message.fromMsg = function(data) {
switch (data[0]) {
case Command.TYPE:
return Command.fromMsg(data);
@@ -90,17 +106,17 @@ Message.fromMsg = function(data) {
* are called when the client returns with a response. These are
* {@code function onerror({Object})}, {@code function onresult({Object})},
* and {@code function onresult({Response})}.
*
* @param {number} msgId
* Message ID unique identifying this message.
* @param {string} name
* Command name.
- * @param {Object<string, ?>} params
+ * @param {Object.<string, ?>} params
* Command parameters.
*/
class Command {
constructor(msgID, name, params = {}) {
this.id = assert.integer(msgID);
this.name = assert.string(name);
this.parameters = assert.object(params);
@@ -185,32 +201,39 @@ const validator = {
*
* For example setting the {@code error} property on the body when
* {@code value}, {@code sessionId}, or {@code capabilities} have been
* set previously will cause an error.
*/
const ResponseBody = () => new Proxy({}, validator);
/**
+ * @callback ResponseCallback
+ *
+ * @param {Response} resp
+ * Response to handle.
+ */
+
+/**
* Represents the response returned from the remote end after execution
* of its corresponding command.
*
* The response is a mutable object passed to each command for
* modification through the available setters. To send data in a response,
* you modify the body property on the response. The body property can
* also be replaced completely.
*
* The response is sent implicitly by CommandProcessor when a command
* has finished executing, and any modifications made subsequent to that
* will have no effect.
*
* @param {number} msgID
* Message ID tied to the corresponding command request this is a
* response for.
- * @param {function(Response|Message)} respHandler
+ * @param {ResponseHandler} respHandler
* Function callback called on sending the response.
*/
class Response {
constructor(msgID, respHandler = () => {}) {
this.id = assert.integer(msgID);
this.respHandler_ = assert.callable(respHandler);
this.error = null;
--- a/testing/marionette/modal.js
+++ b/testing/marionette/modal.js
@@ -9,18 +9,18 @@ const {utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
this.EXPORTED_SYMBOLS = ["modal"];
const COMMON_DIALOG = "chrome://global/content/commonDialog.xul";
const isFirefox = () => Services.appinfo.name == "Firefox";
-this.modal = {};
-modal = {
+/** @namespace */
+this.modal = {
COMMON_DIALOG_LOADED: "common-dialog-loaded",
TABMODAL_DIALOG_LOADED: "tabmodal-dialog-loaded",
handlers: {
"common-dialog-loaded": new Set(),
"tabmodal-dialog-loaded": new Set(),
},
};
--- a/testing/marionette/navigate.js
+++ b/testing/marionette/navigate.js
@@ -5,33 +5,34 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.importGlobalProperties(["URL"]);
this.EXPORTED_SYMBOLS = ["navigate"];
+/** @namespace */
this.navigate = {};
/**
* Determines if we expect to get a DOM load event (DOMContentLoaded)
- * on navigating to the |future| URL.
+ * on navigating to the <code>future</code> URL.
*
* @param {string} current
* URL the browser is currently visiting.
* @param {string=} future
* Destination URL, if known.
*
* @return {boolean}
* Full page load would be expected if future is followed.
*
* @throws TypeError
- * If |current| is not defined, or any of |current| or |future|
- * are invalid URLs.
+ * If <code>current</code> is not defined, or any of
+ * <code>current</code> or <code>future</code> are invalid URLs.
*/
navigate.isLoadEventExpected = function(current, future = undefined) {
// assume we will go somewhere exciting
if (typeof current == "undefined") {
throw TypeError("Expected at least one URL");
}
// Assume we will go somewhere exciting
--- a/testing/marionette/packets.js
+++ b/testing/marionette/packets.js
@@ -46,16 +46,18 @@ this.EXPORTED_SYMBOLS = ["RawPacket", "P
// The transport's previous check ensured the header length did not
// exceed 20 characters. Here, we opt for the somewhat smaller, but still
// large limit of 1 TiB.
const PACKET_LENGTH_MAX = Math.pow(2, 40);
/**
* A generic Packet processing object (extended by two subtypes below).
+ *
+ * @class
*/
function Packet(transport) {
this._transport = transport;
this._length = 0;
}
/**
* Attempt to initialize a new Packet based on the incoming packet header
--- a/testing/marionette/proxy.js
+++ b/testing/marionette/proxy.js
@@ -28,16 +28,17 @@ var ownPriorityGetterTrap = {
get: (obj, prop) => {
if (obj.hasOwnProperty(prop)) {
return obj[prop];
}
return (...args) => obj.send(prop, args);
},
};
+/** @namespace */
this.proxy = {};
/**
* Creates a transparent interface between the chrome- and content
* contexts.
*
* Calls to this object will be proxied via the message manager to a
* content frame script, and responses are returend as promises.
@@ -217,31 +218,33 @@ proxy.AsyncMessageChannel = class {
/**
* Reply to an asynchronous request.
*
* Passing an WebDriverError prototype will cause the receiving channel
* to throw this error.
*
* Usage:
*
+ * <pre><code>
* let channel = proxy.AsyncMessageChannel(
* messageManager, sendAsyncMessage.bind(this));
*
* // throws in requester:
* channel.reply(uuid, new WebDriverError());
*
* // returns with value:
* channel.reply(uuid, "hello world!");
*
* // returns with undefined:
* channel.reply(uuid);
+ * </pre></code>
*
* @param {UUID} uuid
* Unique identifier of the request.
- * @param {?=} obj
+ * @param {*} obj
* Message data to reply with.
*/
reply(uuid, obj = undefined) {
// TODO(ato): Eventually the uuid will be hidden in the dispatcher
// in listener, and passing it explicitly to this function will be
// unnecessary.
if (typeof obj == "undefined") {
this.sendReply_(uuid, proxy.AsyncMessageChannel.ReplyType.Ok);
@@ -348,17 +351,17 @@ class AsyncChromeSender {
}
/**
* Call registered function in chrome context.
*
* @param {string} name
* Function to call in the chrome, e.g. for "Marionette:foo", use
* "foo".
- * @param {?} args
+ * @param {*} args
* Argument list to pass the function. Must be JSON serialisable.
*
* @return {Promise}
* A promise that resolves to the value sent back.
*/
send(name, args) {
let uuid = uuidgen.generateUUID().toString();
--- a/testing/marionette/reftest.js
+++ b/testing/marionette/reftest.js
@@ -32,20 +32,26 @@ const STATUS = {
PASS: "PASS",
FAIL: "FAIL",
ERROR: "ERROR",
TIMEOUT: "TIMEOUT",
};
/**
* Implements an fast runner for web-platform-tests format reftests
- * c.f. http://web-platform-tests.org/writing-tests/reftests.html
+ * c.f. http://web-platform-tests.org/writing-tests/reftests.html.
+ *
+ * @namespace
*/
-let reftest = {};
+this.reftest = {};
+/**
+ * @memberof reftest
+ * @class Runner
+ */
reftest.Runner = class {
constructor(driver) {
this.driver = driver;
this.canvasCache = new Map([[null, []]]);
this.windowUtils = null;
this.lastURL = null;
this.remote = Preferences.get(PREF_E10S);
}
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -32,16 +32,18 @@ const {DebuggerTransport} =
XPCOMUtils.defineLazyServiceGetter(
this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
const logger = Log.repository.getLogger("Marionette");
const {KeepWhenOffline, LoopbackOnly} = Ci.nsIServerSocket;
this.EXPORTED_SYMBOLS = ["server"];
+
+/** @namespace */
this.server = {};
const PROTOCOL_VERSION = 3;
const ENV_ENABLED = "MOZ_MARIONETTE";
const PREF_CONTENT_LISTENER = "marionette.contentListener";
const PREF_PORT = "marionette.port";
@@ -543,17 +545,17 @@ server.TCPConnection = class {
}.bind(this));
req.then(sendResponse, sendError).catch(error.report);
}
/**
* Fail-safe creation of a new instance of |message.Response|.
*
- * @param {?} msgID
+ * @param {number} msgID
* Message ID to respond to. If it is not a number, -1 is used.
*
* @return {message.Response}
* Response to the message with |msgID|.
*/
createResponse(msgID) {
if (typeof msgID != "number") {
msgID = -1;
@@ -586,17 +588,17 @@ server.TCPConnection = class {
* The message is sent over the debugger transport socket.
*
* The command ID is a unique identifier assigned to the client's request
* that is used to distinguish the asynchronous responses.
*
* Whilst responses to commands are synchronous and must be sent in the
* correct order.
*
- * @param {Command,Response} msg
+ * @param {Message} msg
* The command or response to send.
*/
send(msg) {
msg.origin = MessageOrigin.Server;
if (msg instanceof Command) {
this.commands_.set(msg.id, msg);
this.sendToEmulator(msg);
} else if (msg instanceof Response) {
@@ -615,30 +617,30 @@ server.TCPConnection = class {
sendToClient(resp) {
this.driver.responseCompleted();
this.sendMessage(resp);
}
/**
* Marshal message to the Marionette message format and send it.
*
- * @param {Command,Response} msg
+ * @param {Message} msg
* The message to send.
*/
sendMessage(msg) {
this.log_(msg);
let payload = msg.toMsg();
this.sendRaw(payload);
}
/**
* Send the given payload over the debugger transport socket to the
* connected client.
*
- * @param {Object} payload
+ * @param {Object.<string, ?>} payload
* The payload to ship.
*/
sendRaw(payload) {
this.conn.send(payload);
}
log_(msg) {
let a = (msg.origin == MessageOrigin.Client ? " -> " : " <- ");
--- a/testing/marionette/session.js
+++ b/testing/marionette/session.js
@@ -22,32 +22,37 @@ const logger = Log.repository.getLogger(
const {pprint} = error;
// Enable testing this module, as Services.appinfo.* is not available
// in xpcshell tests.
const appinfo = {name: "<missing>", version: "<missing>"};
try { appinfo.name = Services.appinfo.name.toLowerCase(); } catch (e) {}
try { appinfo.version = Services.appinfo.version; } catch (e) {}
-/** State associated with a WebDriver session. */
+/**
+ * State associated with a WebDriver session.
+ *
+ * @namespace
+ */
this.session = {};
/** Representation of WebDriver session timeouts. */
session.Timeouts = class {
constructor() {
// disabled
this.implicit = 0;
// five mintues
this.pageLoad = 300000;
// 30 seconds
this.script = 30000;
}
toString() { return "[object session.Timeouts]"; }
+ /** Marshals timeout durations to a JSON Object. */
toJSON() {
return {
implicit: this.implicit,
pageLoad: this.pageLoad,
script: this.script,
};
}
@@ -73,25 +78,39 @@ session.Timeouts = class {
throw new InvalidArgumentError("Unrecognised timeout: " + typ);
}
}
return t;
}
};
-/** Enum of page loading strategies. */
+/**
+ * Enum of page loading strategies.
+ *
+ * @enum
+ */
session.PageLoadStrategy = {
+ /** No page load strategy. Navigation will return immediately. */
None: "none",
+ /**
+ * Eager, causing navigation to complete when the document reaches
+ * the <code>interactive</code> ready state.
+ */
Eager: "eager",
+ /**
+ * Normal, causing navigation to return when the document reaches the
+ * <code>complete</code> ready state.
+ */
Normal: "normal",
};
/** Proxy configuration object representation. */
session.Proxy = class {
+ /** @class */
constructor() {
this.proxyType = null;
this.httpProxy = null;
this.httpProxyPort = null;
this.sslProxy = null;
this.sslProxyPort = null;
this.ftpProxy = null;
this.ftpProxyPort = null;
@@ -154,32 +173,40 @@ session.Proxy = class {
default:
return false;
}
}
toString() { return "[object session.Proxy]"; }
+ /**
+ * @return {Object.<string, (number|string)>}
+ * JSON serialisation of proxy object.
+ */
toJSON() {
return marshal({
proxyType: this.proxyType,
httpProxy: this.httpProxy,
httpProxyPort: this.httpProxyPort,
sslProxy: this.sslProxy,
sslProxyPort: this.sslProxyPort,
ftpProxy: this.ftpProxy,
ftpProxyPort: this.ftpProxyPort,
socksProxy: this.socksProxy,
socksProxyPort: this.socksProxyPort,
socksProxyVersion: this.socksProxyVersion,
proxyAutoconfigUrl: this.proxyAutoconfigUrl,
});
}
+ /**
+ * @param {Object.<string, ?>} json
+ * JSON Object to unmarshal.
+ */
static fromJSON(json) {
let p = new session.Proxy();
if (typeof json == "undefined" || json === null) {
return p;
}
assert.object(json);
@@ -214,16 +241,17 @@ session.Proxy = class {
}
return p;
}
};
/** WebDriver session capabilities representation. */
session.Capabilities = class extends Map {
+ /** @class */
constructor() {
super([
// webdriver
["browserName", appinfo.name],
["browserVersion", appinfo.version],
["platformName", Services.sysinfo.getProperty("name").toLowerCase()],
["platformVersion", Services.sysinfo.getProperty("version")],
["pageLoadStrategy", session.PageLoadStrategy.Normal],
@@ -237,43 +265,55 @@ session.Capabilities = class extends Map
// proprietary
["specificationLevel", 0],
["moz:processID", Services.appinfo.processID],
["moz:profile", maybeProfile()],
["moz:accessibilityChecks", false],
]);
}
+ /**
+ * @param {string} key
+ * Capability name.
+ * @param {(string|number|boolean)} value
+ * JSON-safe capability value.
+ */
set(key, value) {
if (key === "timeouts" && !(value instanceof session.Timeouts)) {
throw new TypeError();
} else if (key === "proxy" && !(value instanceof session.Proxy)) {
throw new TypeError();
}
return super.set(key, value);
}
+ /** @return {string} */
toString() { return "[object session.Capabilities]"; }
+ /**
+ * JSON serialisation of capabilities object.
+ *
+ * @return {Object.<string, ?>}
+ */
toJSON() {
return marshal(this);
}
/**
* Unmarshal a JSON object representation of WebDriver capabilities.
*
* @param {Object.<string, ?>=} json
* WebDriver capabilities.
* @param {boolean=} merge
- * If providing |json| with |desiredCapabilities| or
- * |requiredCapabilities| fields, or both, it should be set to
- * true to merge these before parsing. This indicates
- * that the input provided is from a client and not from
- * |session.Capabilities#toJSON|.
+ * If providing <var>json</var> with <tt>desiredCapabilities</tt> or
+ * <tt>requiredCapabilities</tt> fields, or both, it should be
+ * set to true to merge these before parsing. This indicates that
+ * the input provided is from a client and not from
+ * {@link session.Capabilities#toJSON}.
*
* @return {session.Capabilities}
* Internal representation of WebDriver capabilities.
*/
static fromJSON(json, {merge = false} = {}) {
if (typeof json == "undefined" || json === null) {
json = {};
}
--- a/testing/marionette/stream-utils.js
+++ b/testing/marionette/stream-utils.js
@@ -54,16 +54,17 @@ const BUFFER_SIZE = 0x8000;
* Promise is resolved when copying completes or rejected if any
* (unexpected) errors occur.
*/
function copyStream(input, output, length) {
let copier = new StreamCopier(input, output, length);
return copier.copy();
}
+/** @class */
function StreamCopier(input, output, length) {
EventEmitter.decorate(this);
this._id = StreamCopier._nextId++;
this.input = input;
// Save off the base output stream, since we know it's async as we've
// required
this.baseAsyncOutput = output;
if (IOUtil.outputStreamIsBuffered(output)) {
@@ -191,36 +192,35 @@ StreamCopier.prototype = {
},
_debug(msg) {
},
};
/**
- * Read from a stream, one byte at a time, up to the next |delimiter|
- * character, but stopping if we've read |count| without finding it.
- * Reading also terminates early if there are less than |count| bytes
- * available on the stream. In that case, we only read as many bytes as
- * the stream currently has to offer.
- *
- * TODO: This implementation could be removed if bug 984651 is fixed,
- * which provides a native version of the same idea.
+ * Read from a stream, one byte at a time, up to the next
+ * <var>delimiter</var> character, but stopping if we've read |count|
+ * without finding it. Reading also terminates early if there are less
+ * than <var>count</var> bytes available on the stream. In that case,
+ * we only read as many bytes as the stream currently has to offer.
*
* @param {nsIInputStream} stream
* Input stream to read from.
* @param {string} delimiter
* Character we're trying to find.
* @param {number} count
* Max number of characters to read while searching.
*
* @return {string}
* Collected data. If the delimiter was found, this string will
* end with it.
*/
+// TODO: This implementation could be removed if bug 984651 is fixed,
+// which provides a native version of the same idea.
function delimitedRead(stream, delimiter, count) {
let scriptableStream;
if (stream instanceof Ci.nsIScriptableInputStream) {
scriptableStream = stream;
} else {
scriptableStream = new ScriptableInputStream(stream);
}
--- a/testing/marionette/transport.js
+++ b/testing/marionette/transport.js
@@ -100,16 +100,18 @@ const PACKET_HEADER_MAX = 200;
* copied. See stream-utils.js.
*
* - onClosed(reason) - called when the connection is closed. |reason|
* is an optional nsresult or object, typically passed when the
* transport is closed due to some error in a underlying stream.
*
* See ./packets.js and the Remote Debugging Protocol specification for
* more details on the format of these packets.
+ *
+ * @class
*/
function DebuggerTransport(input, output) {
EventEmitter.decorate(this);
this._input = input;
this._scriptableInput = new ScriptableInputStream(input);
this._output = output;
@@ -712,25 +714,26 @@ LocalDebuggerTransport.prototype = {
}
},
};
/**
* A transport for the debugging protocol that uses nsIMessageManagers to
* exchange packets with servers running in child processes.
*
- * In the parent process, |mm| should be the nsIMessageSender for the
- * child process. In a child process, |mm| should be the child process
- * message manager, which sends packets to the parent.
+ * In the parent process, <var>mm</var> should be the nsIMessageSender
+ * for the child process. In a child process, |mm| should be the child
+ * process message manager, which sends packets to the parent.
*
- * |prefix| is a string included in the message names, to distinguish
- * multiple servers running in the same child process.
+ * <var>prefix</var> is a string included in the message names, to
+ * distinguish multiple servers running in the same child process.
*
- * This transport exchanges messages named 'debug:<prefix>:packet', where
- * <prefix> is |prefix|, whose data is the protocol packet.
+ * This transport exchanges messages named <tt>debug:PREFIX:packet</tt>,
+ * where <tt>PREFIX</tt> is <var>prefix</var>, whose data is the protocol
+ * packet.
*/
function ChildDebuggerTransport(mm, prefix) {
EventEmitter.decorate(this);
this._mm = mm;
this._messageName = "debug:" + prefix + ":packet";
}
--- a/testing/marionette/wait.js
+++ b/testing/marionette/wait.js
@@ -5,20 +5,38 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("chrome://marionette/content/error.js");
this.EXPORTED_SYMBOLS = ["wait"];
-/** Implicit wait utilities. */
+/**
+ * Poll-waiting utilities.
+ *
+ * @namespace
+ */
this.wait = {};
/**
+ * @callback WaitCondition
+ *
+ * @param {function(*)} resolve
+ * To be called when the condition has been met. Will return the
+ * resolved value.
+ * @param {function} reject
+ * To be called when the condition has not been met. Will cause
+ * the condition to be revaluated or time out.
+ *
+ * @return {*}
+ * The value from calling <code>resolve</code>.
+ */
+
+/**
* Runs a promise-like function off the main thread until it is resolved
* through |resolve| or |rejected| callbacks. The function is guaranteed
* to be run at least once, irregardless of the timeout.
*
* The |func| is evaluated every |interval| for as long as its runtime
* duration does not exceed |interval|. Evaluations occur sequentially,
* meaning that evaluations of |func| are queued if the runtime evaluation
* duration of |func| is greater than |interval|.
@@ -28,40 +46,42 @@ this.wait = {};
* an argument indicates that the expected wait condition was met and
* will return the passed value to the caller. Conversely, calling
* |reject| will evaluate |func| again until the |timeout| duration has
* elapsed or |func| throws. The passed value to |reject| will also be
* returned to the caller once the wait has expired.
*
* Usage:
*
+ * <pre><code>
* let els = wait.until((resolve, reject) => {
* let res = document.querySelectorAll("p");
* if (res.length > 0) {
* resolve(Array.from(res));
* } else {
* reject([]);
* }
* });
+ * </pre></code>
*
- * @param {function(resolve: function(?), reject: function(?)): ?} func
+ * @param {WaitCondition} func
* Function to run off the main thread.
* @param {number=} timeout
* Desired timeout. If 0 or less than the runtime evaluation time
* of |func|, |func| is guaranteed to run at least once. The default
* is 2000 milliseconds.
* @param {number=} interval
* Duration between each poll of |func| in milliseconds. Defaults to
* 10 milliseconds.
*
- * @return {Promise: ?}
+ * @return {Promise.<*>}
* Yields the value passed to |func|'s |resolve| or |reject|
* callbacks.
*
- * @throws {?}
+ * @throws {*}
* If |func| throws, its error is propagated.
*/
wait.until = function(func, timeout = 2000, interval = 10) {
const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
return new Promise((resolve, reject) => {
const start = new Date().getTime();
const end = start + timeout;