services/sync/tps/extensions/mozmill/resource/driver/mozelement.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 var EXPORTED_SYMBOLS = ["Elem", "Selector", "ID", "Link", "XPath", "Name", "Lookup",
michael@0 6 "MozMillElement", "MozMillCheckBox", "MozMillRadio", "MozMillDropList",
michael@0 7 "MozMillTextBox", "subclasses"
michael@0 8 ];
michael@0 9
michael@0 10 const NAMESPACE_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 11
michael@0 12 const Cc = Components.classes;
michael@0 13 const Ci = Components.interfaces;
michael@0 14 const Cu = Components.utils;
michael@0 15
michael@0 16 var EventUtils = {}; Cu.import('resource://mozmill/stdlib/EventUtils.js', EventUtils);
michael@0 17
michael@0 18 var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
michael@0 19 var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
michael@0 20 var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib);
michael@0 21 var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
michael@0 22
michael@0 23 var assert = new assertions.Assert();
michael@0 24
michael@0 25 // A list of all the subclasses available. Shared modules can push their own subclasses onto this list
michael@0 26 var subclasses = [MozMillCheckBox, MozMillRadio, MozMillDropList, MozMillTextBox];
michael@0 27
michael@0 28 /**
michael@0 29 * createInstance()
michael@0 30 *
michael@0 31 * Returns an new instance of a MozMillElement
michael@0 32 * The type of the element is automatically determined
michael@0 33 */
michael@0 34 function createInstance(locatorType, locator, elem, document) {
michael@0 35 var args = { "document": document, "element": elem };
michael@0 36
michael@0 37 // If we already have an element lets determine the best MozMillElement type
michael@0 38 if (elem) {
michael@0 39 for (var i = 0; i < subclasses.length; ++i) {
michael@0 40 if (subclasses[i].isType(elem)) {
michael@0 41 return new subclasses[i](locatorType, locator, args);
michael@0 42 }
michael@0 43 }
michael@0 44 }
michael@0 45
michael@0 46 // By default we create a base MozMillElement
michael@0 47 if (MozMillElement.isType(elem)) {
michael@0 48 return new MozMillElement(locatorType, locator, args);
michael@0 49 }
michael@0 50
michael@0 51 throw new Error("Unsupported element type " + locatorType + ": " + locator);
michael@0 52 }
michael@0 53
michael@0 54 var Elem = function (node) {
michael@0 55 return createInstance("Elem", node, node);
michael@0 56 };
michael@0 57
michael@0 58 var Selector = function (document, selector, index) {
michael@0 59 return createInstance("Selector", selector, elementslib.Selector(document, selector, index), document);
michael@0 60 };
michael@0 61
michael@0 62 var ID = function (document, nodeID) {
michael@0 63 return createInstance("ID", nodeID, elementslib.ID(document, nodeID), document);
michael@0 64 };
michael@0 65
michael@0 66 var Link = function (document, linkName) {
michael@0 67 return createInstance("Link", linkName, elementslib.Link(document, linkName), document);
michael@0 68 };
michael@0 69
michael@0 70 var XPath = function (document, expr) {
michael@0 71 return createInstance("XPath", expr, elementslib.XPath(document, expr), document);
michael@0 72 };
michael@0 73
michael@0 74 var Name = function (document, nName) {
michael@0 75 return createInstance("Name", nName, elementslib.Name(document, nName), document);
michael@0 76 };
michael@0 77
michael@0 78 var Lookup = function (document, expression) {
michael@0 79 var elem = createInstance("Lookup", expression, elementslib.Lookup(document, expression), document);
michael@0 80
michael@0 81 // Bug 864268 - Expose the expression property to maintain backwards compatibility
michael@0 82 elem.expression = elem._locator;
michael@0 83
michael@0 84 return elem;
michael@0 85 };
michael@0 86
michael@0 87 /**
michael@0 88 * MozMillElement
michael@0 89 * The base class for all mozmill elements
michael@0 90 */
michael@0 91 function MozMillElement(locatorType, locator, args) {
michael@0 92 args = args || {};
michael@0 93 this._locatorType = locatorType;
michael@0 94 this._locator = locator;
michael@0 95 this._element = args["element"];
michael@0 96 this._owner = args["owner"];
michael@0 97
michael@0 98 this._document = this._element ? this._element.ownerDocument : args["document"];
michael@0 99 this._defaultView = this._document ? this._document.defaultView : null;
michael@0 100
michael@0 101 // Used to maintain backwards compatibility with controller.js
michael@0 102 this.isElement = true;
michael@0 103 }
michael@0 104
michael@0 105 // Static method that returns true if node is of this element type
michael@0 106 MozMillElement.isType = function (node) {
michael@0 107 return true;
michael@0 108 };
michael@0 109
michael@0 110 // This getter is the magic behind lazy loading (note distinction between _element and element)
michael@0 111 MozMillElement.prototype.__defineGetter__("element", function () {
michael@0 112 // If the document is invalid (e.g. reload of the page), invalidate the cached
michael@0 113 // element and update the document cache
michael@0 114 if (this._defaultView && this._defaultView.document !== this._document) {
michael@0 115 this._document = this._defaultView.document;
michael@0 116 this._element = undefined;
michael@0 117 }
michael@0 118
michael@0 119 if (this._element == undefined) {
michael@0 120 if (elementslib[this._locatorType]) {
michael@0 121 this._element = elementslib[this._locatorType](this._document, this._locator);
michael@0 122 } else if (this._locatorType == "Elem") {
michael@0 123 this._element = this._locator;
michael@0 124 } else {
michael@0 125 throw new Error("Unknown locator type: " + this._locatorType);
michael@0 126 }
michael@0 127 }
michael@0 128
michael@0 129 return this._element;
michael@0 130 });
michael@0 131
michael@0 132 /**
michael@0 133 * Drag an element to the specified offset on another element, firing mouse and
michael@0 134 * drag events. Adapted from ChromeUtils.js synthesizeDrop()
michael@0 135 *
michael@0 136 * By default it will drag the source element over the destination's element
michael@0 137 * center with a "move" dropEffect.
michael@0 138 *
michael@0 139 * @param {MozElement} aElement
michael@0 140 * Destination element over which the drop occurs
michael@0 141 * @param {Number} [aOffsetX=aElement.width/2]
michael@0 142 * Relative x offset for dropping on aElement
michael@0 143 * @param {Number} [aOffsetY=aElement.height/2]
michael@0 144 * Relative y offset for dropping on aElement
michael@0 145 * @param {DOMWindow} [aSourceWindow=this.element.ownerDocument.defaultView]
michael@0 146 * Custom source Window to be used.
michael@0 147 * @param {DOMWindow} [aDestWindow=aElement.getNode().ownerDocument.defaultView]
michael@0 148 * Custom destination Window to be used.
michael@0 149 * @param {String} [aDropEffect="move"]
michael@0 150 * Possible values: copy, move, link, none
michael@0 151 * @param {Object[]} [aDragData]
michael@0 152 * An array holding custom drag data to be used during the drag event
michael@0 153 * Format: [{ type: "text/plain", "Text to drag"}, ...]
michael@0 154 *
michael@0 155 * @returns {String} the captured dropEffect
michael@0 156 */
michael@0 157 MozMillElement.prototype.dragToElement = function(aElement, aOffsetX, aOffsetY,
michael@0 158 aSourceWindow, aDestWindow,
michael@0 159 aDropEffect, aDragData) {
michael@0 160 if (!this.element) {
michael@0 161 throw new Error("Could not find element " + this.getInfo());
michael@0 162 }
michael@0 163 if (!aElement) {
michael@0 164 throw new Error("Missing destination element");
michael@0 165 }
michael@0 166
michael@0 167 var srcNode = this.element;
michael@0 168 var destNode = aElement.getNode();
michael@0 169 var srcWindow = aSourceWindow ||
michael@0 170 (srcNode.ownerDocument ? srcNode.ownerDocument.defaultView
michael@0 171 : srcNode);
michael@0 172 var destWindow = aDestWindow ||
michael@0 173 (destNode.ownerDocument ? destNode.ownerDocument.defaultView
michael@0 174 : destNode);
michael@0 175
michael@0 176 var srcRect = srcNode.getBoundingClientRect();
michael@0 177 var srcCoords = {
michael@0 178 x: srcRect.width / 2,
michael@0 179 y: srcRect.height / 2
michael@0 180 };
michael@0 181 var destRect = destNode.getBoundingClientRect();
michael@0 182 var destCoords = {
michael@0 183 x: (!aOffsetX || isNaN(aOffsetX)) ? (destRect.width / 2) : aOffsetX,
michael@0 184 y: (!aOffsetY || isNaN(aOffsetY)) ? (destRect.height / 2) : aOffsetY
michael@0 185 };
michael@0 186
michael@0 187 var windowUtils = destWindow.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 188 .getInterface(Ci.nsIDOMWindowUtils);
michael@0 189 var ds = Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService);
michael@0 190
michael@0 191 var dataTransfer;
michael@0 192 var trapDrag = function (event) {
michael@0 193 srcWindow.removeEventListener("dragstart", trapDrag, true);
michael@0 194 dataTransfer = event.dataTransfer;
michael@0 195
michael@0 196 if (!aDragData) {
michael@0 197 return;
michael@0 198 }
michael@0 199
michael@0 200 for (var i = 0; i < aDragData.length; i++) {
michael@0 201 var item = aDragData[i];
michael@0 202 for (var j = 0; j < item.length; j++) {
michael@0 203 dataTransfer.mozSetDataAt(item[j].type, item[j].data, i);
michael@0 204 }
michael@0 205 }
michael@0 206
michael@0 207 dataTransfer.dropEffect = aDropEffect || "move";
michael@0 208 event.preventDefault();
michael@0 209 event.stopPropagation();
michael@0 210 }
michael@0 211
michael@0 212 ds.startDragSession();
michael@0 213
michael@0 214 try {
michael@0 215 srcWindow.addEventListener("dragstart", trapDrag, true);
michael@0 216 EventUtils.synthesizeMouse(srcNode, srcCoords.x, srcCoords.y,
michael@0 217 { type: "mousedown" }, srcWindow);
michael@0 218 EventUtils.synthesizeMouse(destNode, destCoords.x, destCoords.y,
michael@0 219 { type: "mousemove" }, destWindow);
michael@0 220
michael@0 221 var event = destWindow.document.createEvent("DragEvents");
michael@0 222 event.initDragEvent("dragenter", true, true, destWindow, 0, 0, 0, 0, 0,
michael@0 223 false, false, false, false, 0, null, dataTransfer);
michael@0 224 event.initDragEvent("dragover", true, true, destWindow, 0, 0, 0, 0, 0,
michael@0 225 false, false, false, false, 0, null, dataTransfer);
michael@0 226 event.initDragEvent("drop", true, true, destWindow, 0, 0, 0, 0, 0,
michael@0 227 false, false, false, false, 0, null, dataTransfer);
michael@0 228 windowUtils.dispatchDOMEventViaPresShell(destNode, event, true);
michael@0 229
michael@0 230 EventUtils.synthesizeMouse(destNode, destCoords.x, destCoords.y,
michael@0 231 { type: "mouseup" }, destWindow);
michael@0 232
michael@0 233 return dataTransfer.dropEffect;
michael@0 234 } finally {
michael@0 235 ds.endDragSession(true);
michael@0 236 }
michael@0 237
michael@0 238 };
michael@0 239
michael@0 240 // Returns the actual wrapped DOM node
michael@0 241 MozMillElement.prototype.getNode = function () {
michael@0 242 return this.element;
michael@0 243 };
michael@0 244
michael@0 245 MozMillElement.prototype.getInfo = function () {
michael@0 246 return this._locatorType + ": " + this._locator;
michael@0 247 };
michael@0 248
michael@0 249 /**
michael@0 250 * Sometimes an element which once existed will no longer exist in the DOM
michael@0 251 * This function re-searches for the element
michael@0 252 */
michael@0 253 MozMillElement.prototype.exists = function () {
michael@0 254 this._element = undefined;
michael@0 255 if (this.element) {
michael@0 256 return true;
michael@0 257 }
michael@0 258
michael@0 259 return false;
michael@0 260 };
michael@0 261
michael@0 262 /**
michael@0 263 * Synthesize a keypress event on the given element
michael@0 264 *
michael@0 265 * @param {string} aKey
michael@0 266 * Key to use for synthesizing the keypress event. It can be a simple
michael@0 267 * character like "k" or a string like "VK_ESCAPE" for command keys
michael@0 268 * @param {object} aModifiers
michael@0 269 * Information about the modifier keys to send
michael@0 270 * Elements: accelKey - Hold down the accelerator key (ctrl/meta)
michael@0 271 * [optional - default: false]
michael@0 272 * altKey - Hold down the alt key
michael@0 273 * [optional - default: false]
michael@0 274 * ctrlKey - Hold down the ctrl key
michael@0 275 * [optional - default: false]
michael@0 276 * metaKey - Hold down the meta key (command key on Mac)
michael@0 277 * [optional - default: false]
michael@0 278 * shiftKey - Hold down the shift key
michael@0 279 * [optional - default: false]
michael@0 280 * @param {object} aExpectedEvent
michael@0 281 * Information about the expected event to occur
michael@0 282 * Elements: target - Element which should receive the event
michael@0 283 * [optional - default: current element]
michael@0 284 * type - Type of the expected key event
michael@0 285 */
michael@0 286 MozMillElement.prototype.keypress = function (aKey, aModifiers, aExpectedEvent) {
michael@0 287 if (!this.element) {
michael@0 288 throw new Error("Could not find element " + this.getInfo());
michael@0 289 }
michael@0 290
michael@0 291 var win = this.element.ownerDocument ? this.element.ownerDocument.defaultView
michael@0 292 : this.element;
michael@0 293 this.element.focus();
michael@0 294
michael@0 295 if (aExpectedEvent) {
michael@0 296 if (!aExpectedEvent.type) {
michael@0 297 throw new Error(arguments.callee.name + ": Expected event type not specified");
michael@0 298 }
michael@0 299
michael@0 300 var target = aExpectedEvent.target ? aExpectedEvent.target.getNode()
michael@0 301 : this.element;
michael@0 302 EventUtils.synthesizeKeyExpectEvent(aKey, aModifiers || {}, target, aExpectedEvent.type,
michael@0 303 "MozMillElement.keypress()", win);
michael@0 304 } else {
michael@0 305 EventUtils.synthesizeKey(aKey, aModifiers || {}, win);
michael@0 306 }
michael@0 307
michael@0 308 broker.pass({'function':'MozMillElement.keypress()'});
michael@0 309
michael@0 310 return true;
michael@0 311 };
michael@0 312
michael@0 313
michael@0 314 /**
michael@0 315 * Synthesize a general mouse event on the given element
michael@0 316 *
michael@0 317 * @param {number} aOffsetX
michael@0 318 * Relative x offset in the elements bounds to click on
michael@0 319 * @param {number} aOffsetY
michael@0 320 * Relative y offset in the elements bounds to click on
michael@0 321 * @param {object} aEvent
michael@0 322 * Information about the event to send
michael@0 323 * Elements: accelKey - Hold down the accelerator key (ctrl/meta)
michael@0 324 * [optional - default: false]
michael@0 325 * altKey - Hold down the alt key
michael@0 326 * [optional - default: false]
michael@0 327 * button - Mouse button to use
michael@0 328 * [optional - default: 0]
michael@0 329 * clickCount - Number of counts to click
michael@0 330 * [optional - default: 1]
michael@0 331 * ctrlKey - Hold down the ctrl key
michael@0 332 * [optional - default: false]
michael@0 333 * metaKey - Hold down the meta key (command key on Mac)
michael@0 334 * [optional - default: false]
michael@0 335 * shiftKey - Hold down the shift key
michael@0 336 * [optional - default: false]
michael@0 337 * type - Type of the mouse event ('click', 'mousedown',
michael@0 338 * 'mouseup', 'mouseover', 'mouseout')
michael@0 339 * [optional - default: 'mousedown' + 'mouseup']
michael@0 340 * @param {object} aExpectedEvent
michael@0 341 * Information about the expected event to occur
michael@0 342 * Elements: target - Element which should receive the event
michael@0 343 * [optional - default: current element]
michael@0 344 * type - Type of the expected mouse event
michael@0 345 */
michael@0 346 MozMillElement.prototype.mouseEvent = function (aOffsetX, aOffsetY, aEvent, aExpectedEvent) {
michael@0 347 if (!this.element) {
michael@0 348 throw new Error(arguments.callee.name + ": could not find element " + this.getInfo());
michael@0 349 }
michael@0 350
michael@0 351 if ("document" in this.element) {
michael@0 352 throw new Error("A window cannot be a target for mouse events.");
michael@0 353 }
michael@0 354
michael@0 355 var rect = this.element.getBoundingClientRect();
michael@0 356
michael@0 357 if (!aOffsetX || isNaN(aOffsetX)) {
michael@0 358 aOffsetX = rect.width / 2;
michael@0 359 }
michael@0 360
michael@0 361 if (!aOffsetY || isNaN(aOffsetY)) {
michael@0 362 aOffsetY = rect.height / 2;
michael@0 363 }
michael@0 364
michael@0 365 // Scroll element into view otherwise the click will fail
michael@0 366 if ("scrollIntoView" in this.element)
michael@0 367 this.element.scrollIntoView();
michael@0 368
michael@0 369 if (aExpectedEvent) {
michael@0 370 // The expected event type has to be set
michael@0 371 if (!aExpectedEvent.type) {
michael@0 372 throw new Error(arguments.callee.name + ": Expected event type not specified");
michael@0 373 }
michael@0 374
michael@0 375 // If no target has been specified use the specified element
michael@0 376 var target = aExpectedEvent.target ? aExpectedEvent.target.getNode()
michael@0 377 : this.element;
michael@0 378 if (!target) {
michael@0 379 throw new Error(arguments.callee.name + ": could not find element " +
michael@0 380 aExpectedEvent.target.getInfo());
michael@0 381 }
michael@0 382
michael@0 383 EventUtils.synthesizeMouseExpectEvent(this.element, aOffsetX, aOffsetY, aEvent,
michael@0 384 target, aExpectedEvent.type,
michael@0 385 "MozMillElement.mouseEvent()",
michael@0 386 this.element.ownerDocument.defaultView);
michael@0 387 } else {
michael@0 388 EventUtils.synthesizeMouse(this.element, aOffsetX, aOffsetY, aEvent,
michael@0 389 this.element.ownerDocument.defaultView);
michael@0 390 }
michael@0 391
michael@0 392 // Bug 555347
michael@0 393 // We don't know why this sleep is necessary but more investigation is needed
michael@0 394 // before it can be removed
michael@0 395 utils.sleep(0);
michael@0 396
michael@0 397 return true;
michael@0 398 };
michael@0 399
michael@0 400 /**
michael@0 401 * Synthesize a mouse click event on the given element
michael@0 402 */
michael@0 403 MozMillElement.prototype.click = function (aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 404 // Handle menu items differently
michael@0 405 if (this.element && this.element.tagName == "menuitem") {
michael@0 406 this.element.click();
michael@0 407 } else {
michael@0 408 this.mouseEvent(aOffsetX, aOffsetY, {}, aExpectedEvent);
michael@0 409 }
michael@0 410
michael@0 411 broker.pass({'function':'MozMillElement.click()'});
michael@0 412
michael@0 413 return true;
michael@0 414 };
michael@0 415
michael@0 416 /**
michael@0 417 * Synthesize a double click on the given element
michael@0 418 */
michael@0 419 MozMillElement.prototype.doubleClick = function (aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 420 this.mouseEvent(aOffsetX, aOffsetY, {clickCount: 2}, aExpectedEvent);
michael@0 421
michael@0 422 broker.pass({'function':'MozMillElement.doubleClick()'});
michael@0 423
michael@0 424 return true;
michael@0 425 };
michael@0 426
michael@0 427 /**
michael@0 428 * Synthesize a mouse down event on the given element
michael@0 429 */
michael@0 430 MozMillElement.prototype.mouseDown = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 431 this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mousedown"}, aExpectedEvent);
michael@0 432
michael@0 433 broker.pass({'function':'MozMillElement.mouseDown()'});
michael@0 434
michael@0 435 return true;
michael@0 436 };
michael@0 437
michael@0 438 /**
michael@0 439 * Synthesize a mouse out event on the given element
michael@0 440 */
michael@0 441 MozMillElement.prototype.mouseOut = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 442 this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseout"}, aExpectedEvent);
michael@0 443
michael@0 444 broker.pass({'function':'MozMillElement.mouseOut()'});
michael@0 445
michael@0 446 return true;
michael@0 447 };
michael@0 448
michael@0 449 /**
michael@0 450 * Synthesize a mouse over event on the given element
michael@0 451 */
michael@0 452 MozMillElement.prototype.mouseOver = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 453 this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseover"}, aExpectedEvent);
michael@0 454
michael@0 455 broker.pass({'function':'MozMillElement.mouseOver()'});
michael@0 456
michael@0 457 return true;
michael@0 458 };
michael@0 459
michael@0 460 /**
michael@0 461 * Synthesize a mouse up event on the given element
michael@0 462 */
michael@0 463 MozMillElement.prototype.mouseUp = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 464 this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseup"}, aExpectedEvent);
michael@0 465
michael@0 466 broker.pass({'function':'MozMillElement.mouseUp()'});
michael@0 467
michael@0 468 return true;
michael@0 469 };
michael@0 470
michael@0 471 /**
michael@0 472 * Synthesize a mouse middle click event on the given element
michael@0 473 */
michael@0 474 MozMillElement.prototype.middleClick = function (aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 475 this.mouseEvent(aOffsetX, aOffsetY, {button: 1}, aExpectedEvent);
michael@0 476
michael@0 477 broker.pass({'function':'MozMillElement.middleClick()'});
michael@0 478
michael@0 479 return true;
michael@0 480 };
michael@0 481
michael@0 482 /**
michael@0 483 * Synthesize a mouse right click event on the given element
michael@0 484 */
michael@0 485 MozMillElement.prototype.rightClick = function (aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 486 this.mouseEvent(aOffsetX, aOffsetY, {type : "contextmenu", button: 2 }, aExpectedEvent);
michael@0 487
michael@0 488 broker.pass({'function':'MozMillElement.rightClick()'});
michael@0 489
michael@0 490 return true;
michael@0 491 };
michael@0 492
michael@0 493 /**
michael@0 494 * Synthesize a general touch event on the given element
michael@0 495 *
michael@0 496 * @param {Number} [aOffsetX=aElement.width / 2]
michael@0 497 * Relative x offset in the elements bounds to click on
michael@0 498 * @param {Number} [aOffsetY=aElement.height / 2]
michael@0 499 * Relative y offset in the elements bounds to click on
michael@0 500 * @param {Object} [aEvent]
michael@0 501 * Information about the event to send
michael@0 502 * @param {Boolean} [aEvent.altKey=false]
michael@0 503 * A Boolean value indicating whether or not the alt key was down when
michael@0 504 * the touch event was fired
michael@0 505 * @param {Number} [aEvent.angle=0]
michael@0 506 * The angle (in degrees) that the ellipse described by rx and
michael@0 507 * ry must be rotated, clockwise, to most accurately cover the area
michael@0 508 * of contact between the user and the surface.
michael@0 509 * @param {Touch[]} [aEvent.changedTouches]
michael@0 510 * A TouchList of all the Touch objects representing individual points of
michael@0 511 * contact whose states changed between the previous touch event and
michael@0 512 * this one
michael@0 513 * @param {Boolean} [aEvent.ctrlKey]
michael@0 514 * A Boolean value indicating whether or not the control key was down
michael@0 515 * when the touch event was fired
michael@0 516 * @param {Number} [aEvent.force=1]
michael@0 517 * The amount of pressure being applied to the surface by the user, as a
michael@0 518 * float between 0.0 (no pressure) and 1.0 (maximum pressure)
michael@0 519 * @param {Number} [aEvent.id=0]
michael@0 520 * A unique identifier for this Touch object. A given touch (say, by a
michael@0 521 * finger) will have the same identifier for the duration of its movement
michael@0 522 * around the surface. This lets you ensure that you're tracking the same
michael@0 523 * touch all the time
michael@0 524 * @param {Boolean} [aEvent.metaKey]
michael@0 525 * A Boolean value indicating whether or not the meta key was down when
michael@0 526 * the touch event was fired.
michael@0 527 * @param {Number} [aEvent.rx=1]
michael@0 528 * The X radius of the ellipse that most closely circumscribes the area
michael@0 529 * of contact with the screen.
michael@0 530 * @param {Number} [aEvent.ry=1]
michael@0 531 * The Y radius of the ellipse that most closely circumscribes the area
michael@0 532 * of contact with the screen.
michael@0 533 * @param {Boolean} [aEvent.shiftKey]
michael@0 534 * A Boolean value indicating whether or not the shift key was down when
michael@0 535 * the touch event was fired
michael@0 536 * @param {Touch[]} [aEvent.targetTouches]
michael@0 537 * A TouchList of all the Touch objects that are both currently in
michael@0 538 * contact with the touch surface and were also started on the same
michael@0 539 * element that is the target of the event
michael@0 540 * @param {Touch[]} [aEvent.touches]
michael@0 541 * A TouchList of all the Touch objects representing all current points
michael@0 542 * of contact with the surface, regardless of target or changed status
michael@0 543 * @param {Number} [aEvent.type=*|touchstart|touchend|touchmove|touchenter|touchleave|touchcancel]
michael@0 544 * The type of touch event that occurred
michael@0 545 * @param {Element} [aEvent.target]
michael@0 546 * The target of the touches associated with this event. This target
michael@0 547 * corresponds to the target of all the touches in the targetTouches
michael@0 548 * attribute, but note that other touches in this event may have a
michael@0 549 * different target. To be careful, you should use the target associated
michael@0 550 * with individual touches
michael@0 551 */
michael@0 552 MozMillElement.prototype.touchEvent = function (aOffsetX, aOffsetY, aEvent) {
michael@0 553 if (!this.element) {
michael@0 554 throw new Error(arguments.callee.name + ": could not find element " + this.getInfo());
michael@0 555 }
michael@0 556
michael@0 557 if ("document" in this.element) {
michael@0 558 throw new Error("A window cannot be a target for touch events.");
michael@0 559 }
michael@0 560
michael@0 561 var rect = this.element.getBoundingClientRect();
michael@0 562
michael@0 563 if (!aOffsetX || isNaN(aOffsetX)) {
michael@0 564 aOffsetX = rect.width / 2;
michael@0 565 }
michael@0 566
michael@0 567 if (!aOffsetY || isNaN(aOffsetY)) {
michael@0 568 aOffsetY = rect.height / 2;
michael@0 569 }
michael@0 570
michael@0 571 // Scroll element into view otherwise the click will fail
michael@0 572 if ("scrollIntoView" in this.element) {
michael@0 573 this.element.scrollIntoView();
michael@0 574 }
michael@0 575
michael@0 576 EventUtils.synthesizeTouch(this.element, aOffsetX, aOffsetY, aEvent,
michael@0 577 this.element.ownerDocument.defaultView);
michael@0 578
michael@0 579 return true;
michael@0 580 };
michael@0 581
michael@0 582 /**
michael@0 583 * Synthesize a touch tap event on the given element
michael@0 584 *
michael@0 585 * @param {Number} [aOffsetX=aElement.width / 2]
michael@0 586 * Left offset in px where the event is triggered
michael@0 587 * @param {Number} [aOffsetY=aElement.height / 2]
michael@0 588 * Top offset in px where the event is triggered
michael@0 589 * @param {Object} [aExpectedEvent]
michael@0 590 * Information about the expected event to occur
michael@0 591 * @param {MozMillElement} [aExpectedEvent.target=this.element]
michael@0 592 * Element which should receive the event
michael@0 593 * @param {MozMillElement} [aExpectedEvent.type]
michael@0 594 * Type of the expected mouse event
michael@0 595 */
michael@0 596 MozMillElement.prototype.tap = function (aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 597 this.mouseEvent(aOffsetX, aOffsetY, {
michael@0 598 clickCount: 1,
michael@0 599 inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
michael@0 600 }, aExpectedEvent);
michael@0 601
michael@0 602 broker.pass({'function':'MozMillElement.tap()'});
michael@0 603
michael@0 604 return true;
michael@0 605 };
michael@0 606
michael@0 607 /**
michael@0 608 * Synthesize a double tap on the given element
michael@0 609 *
michael@0 610 * @param {Number} [aOffsetX=aElement.width / 2]
michael@0 611 * Left offset in px where the event is triggered
michael@0 612 * @param {Number} [aOffsetY=aElement.height / 2]
michael@0 613 * Top offset in px where the event is triggered
michael@0 614 * @param {Object} [aExpectedEvent]
michael@0 615 * Information about the expected event to occur
michael@0 616 * @param {MozMillElement} [aExpectedEvent.target=this.element]
michael@0 617 * Element which should receive the event
michael@0 618 * @param {MozMillElement} [aExpectedEvent.type]
michael@0 619 * Type of the expected mouse event
michael@0 620 */
michael@0 621 MozMillElement.prototype.doubleTap = function (aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 622 this.mouseEvent(aOffsetX, aOffsetY, {
michael@0 623 clickCount: 2,
michael@0 624 inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
michael@0 625 }, aExpectedEvent);
michael@0 626
michael@0 627 broker.pass({'function':'MozMillElement.doubleTap()'});
michael@0 628
michael@0 629 return true;
michael@0 630 };
michael@0 631
michael@0 632 /**
michael@0 633 * Synthesize a long press
michael@0 634 *
michael@0 635 * @param {Number} aOffsetX
michael@0 636 * Left offset in px where the event is triggered
michael@0 637 * @param {Number} aOffsetY
michael@0 638 * Top offset in px where the event is triggered
michael@0 639 * @param {Number} [aTime=1000]
michael@0 640 * Duration of the "press" event in ms
michael@0 641 */
michael@0 642 MozMillElement.prototype.longPress = function (aOffsetX, aOffsetY, aTime) {
michael@0 643 var time = aTime || 1000;
michael@0 644
michael@0 645 this.touchStart(aOffsetX, aOffsetY);
michael@0 646 utils.sleep(time);
michael@0 647 this.touchEnd(aOffsetX, aOffsetY);
michael@0 648
michael@0 649 broker.pass({'function':'MozMillElement.longPress()'});
michael@0 650
michael@0 651 return true;
michael@0 652 };
michael@0 653
michael@0 654 /**
michael@0 655 * Synthesize a touch & drag event on the given element
michael@0 656 *
michael@0 657 * @param {Number} aOffsetX1
michael@0 658 * Left offset of the start position
michael@0 659 * @param {Number} aOffsetY1
michael@0 660 * Top offset of the start position
michael@0 661 * @param {Number} aOffsetX2
michael@0 662 * Left offset of the end position
michael@0 663 * @param {Number} aOffsetY2
michael@0 664 * Top offset of the end position
michael@0 665 */
michael@0 666 MozMillElement.prototype.touchDrag = function (aOffsetX1, aOffsetY1, aOffsetX2, aOffsetY2) {
michael@0 667 this.touchStart(aOffsetX1, aOffsetY1);
michael@0 668 this.touchMove(aOffsetX2, aOffsetY2);
michael@0 669 this.touchEnd(aOffsetX2, aOffsetY2);
michael@0 670
michael@0 671 broker.pass({'function':'MozMillElement.move()'});
michael@0 672
michael@0 673 return true;
michael@0 674 };
michael@0 675
michael@0 676 /**
michael@0 677 * Synthesize a press / touchstart event on the given element
michael@0 678 *
michael@0 679 * @param {Number} aOffsetX
michael@0 680 * Left offset where the event is triggered
michael@0 681 * @param {Number} aOffsetY
michael@0 682 * Top offset where the event is triggered
michael@0 683 */
michael@0 684 MozMillElement.prototype.touchStart = function (aOffsetX, aOffsetY) {
michael@0 685 this.touchEvent(aOffsetX, aOffsetY, { type: "touchstart" });
michael@0 686
michael@0 687 broker.pass({'function':'MozMillElement.touchStart()'});
michael@0 688
michael@0 689 return true;
michael@0 690 };
michael@0 691
michael@0 692 /**
michael@0 693 * Synthesize a release / touchend event on the given element
michael@0 694 *
michael@0 695 * @param {Number} aOffsetX
michael@0 696 * Left offset where the event is triggered
michael@0 697 * @param {Number} aOffsetY
michael@0 698 * Top offset where the event is triggered
michael@0 699 */
michael@0 700 MozMillElement.prototype.touchEnd = function (aOffsetX, aOffsetY) {
michael@0 701 this.touchEvent(aOffsetX, aOffsetY, { type: "touchend" });
michael@0 702
michael@0 703 broker.pass({'function':'MozMillElement.touchEnd()'});
michael@0 704
michael@0 705 return true;
michael@0 706 };
michael@0 707
michael@0 708 /**
michael@0 709 * Synthesize a touchMove event on the given element
michael@0 710 *
michael@0 711 * @param {Number} aOffsetX
michael@0 712 * Left offset where the event is triggered
michael@0 713 * @param {Number} aOffsetY
michael@0 714 * Top offset where the event is triggered
michael@0 715 */
michael@0 716 MozMillElement.prototype.touchMove = function (aOffsetX, aOffsetY) {
michael@0 717 this.touchEvent(aOffsetX, aOffsetY, { type: "touchmove" });
michael@0 718
michael@0 719 broker.pass({'function':'MozMillElement.touchMove()'});
michael@0 720
michael@0 721 return true;
michael@0 722 };
michael@0 723
michael@0 724 MozMillElement.prototype.waitForElement = function (timeout, interval) {
michael@0 725 var elem = this;
michael@0 726
michael@0 727 assert.waitFor(function () {
michael@0 728 return elem.exists();
michael@0 729 }, "Element.waitForElement(): Element '" + this.getInfo() +
michael@0 730 "' has been found", timeout, interval);
michael@0 731
michael@0 732 broker.pass({'function':'MozMillElement.waitForElement()'});
michael@0 733 };
michael@0 734
michael@0 735 MozMillElement.prototype.waitForElementNotPresent = function (timeout, interval) {
michael@0 736 var elem = this;
michael@0 737
michael@0 738 assert.waitFor(function () {
michael@0 739 return !elem.exists();
michael@0 740 }, "Element.waitForElementNotPresent(): Element '" + this.getInfo() +
michael@0 741 "' has not been found", timeout, interval);
michael@0 742
michael@0 743 broker.pass({'function':'MozMillElement.waitForElementNotPresent()'});
michael@0 744 };
michael@0 745
michael@0 746 MozMillElement.prototype.waitThenClick = function (timeout, interval,
michael@0 747 aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 748 this.waitForElement(timeout, interval);
michael@0 749 this.click(aOffsetX, aOffsetY, aExpectedEvent);
michael@0 750 };
michael@0 751
michael@0 752 /**
michael@0 753 * Waits for the element to be available in the DOM, then trigger a tap event
michael@0 754 *
michael@0 755 * @param {Number} [aTimeout=5000]
michael@0 756 * Time to wait for the element to be available
michael@0 757 * @param {Number} [aInterval=100]
michael@0 758 * Interval to check for availability
michael@0 759 * @param {Number} [aOffsetX=aElement.width / 2]
michael@0 760 * Left offset where the event is triggered
michael@0 761 * @param {Number} [aOffsetY=aElement.height / 2]
michael@0 762 * Top offset where the event is triggered
michael@0 763 * @param {Object} [aExpectedEvent]
michael@0 764 * Information about the expected event to occur
michael@0 765 * @param {MozMillElement} [aExpectedEvent.target=this.element]
michael@0 766 * Element which should receive the event
michael@0 767 * @param {MozMillElement} [aExpectedEvent.type]
michael@0 768 * Type of the expected mouse event
michael@0 769 */
michael@0 770 MozMillElement.prototype.waitThenTap = function (aTimeout, aInterval,
michael@0 771 aOffsetX, aOffsetY, aExpectedEvent) {
michael@0 772 this.waitForElement(aTimeout, aInterval);
michael@0 773 this.tap(aOffsetX, aOffsetY, aExpectedEvent);
michael@0 774 };
michael@0 775
michael@0 776 // Dispatches an HTMLEvent
michael@0 777 MozMillElement.prototype.dispatchEvent = function (eventType, canBubble, modifiers) {
michael@0 778 canBubble = canBubble || true;
michael@0 779 modifiers = modifiers || { };
michael@0 780
michael@0 781 let document = 'ownerDocument' in this.element ? this.element.ownerDocument
michael@0 782 : this.element.document;
michael@0 783
michael@0 784 let evt = document.createEvent('HTMLEvents');
michael@0 785 evt.shiftKey = modifiers["shift"];
michael@0 786 evt.metaKey = modifiers["meta"];
michael@0 787 evt.altKey = modifiers["alt"];
michael@0 788 evt.ctrlKey = modifiers["ctrl"];
michael@0 789 evt.initEvent(eventType, canBubble, true);
michael@0 790
michael@0 791 this.element.dispatchEvent(evt);
michael@0 792 };
michael@0 793
michael@0 794
michael@0 795 /**
michael@0 796 * MozMillCheckBox, which inherits from MozMillElement
michael@0 797 */
michael@0 798 function MozMillCheckBox(locatorType, locator, args) {
michael@0 799 MozMillElement.call(this, locatorType, locator, args);
michael@0 800 }
michael@0 801
michael@0 802
michael@0 803 MozMillCheckBox.prototype = Object.create(MozMillElement.prototype, {
michael@0 804 check : {
michael@0 805 /**
michael@0 806 * Enable/Disable a checkbox depending on the target state
michael@0 807 *
michael@0 808 * @param {boolean} state State to set
michael@0 809 * @return {boolean} Success state
michael@0 810 */
michael@0 811 value : function MMCB_check(state) {
michael@0 812 var result = false;
michael@0 813
michael@0 814 if (!this.element) {
michael@0 815 throw new Error("could not find element " + this.getInfo());
michael@0 816 }
michael@0 817
michael@0 818 // If we have a XUL element, unwrap its XPCNativeWrapper
michael@0 819 if (this.element.namespaceURI == NAMESPACE_XUL) {
michael@0 820 this.element = utils.unwrapNode(this.element);
michael@0 821 }
michael@0 822
michael@0 823 state = (typeof(state) == "boolean") ? state : false;
michael@0 824 if (state != this.element.checked) {
michael@0 825 this.click();
michael@0 826 var element = this.element;
michael@0 827
michael@0 828 assert.waitFor(function () {
michael@0 829 return element.checked == state;
michael@0 830 }, "CheckBox.check(): Checkbox " + this.getInfo() + " could not be checked/unchecked", 500);
michael@0 831
michael@0 832 result = true;
michael@0 833 }
michael@0 834
michael@0 835 broker.pass({'function':'MozMillCheckBox.check(' + this.getInfo() +
michael@0 836 ', state: ' + state + ')'});
michael@0 837
michael@0 838 return result;
michael@0 839 }
michael@0 840 }
michael@0 841 });
michael@0 842
michael@0 843
michael@0 844 /**
michael@0 845 * Returns true if node is of type MozMillCheckBox
michael@0 846 *
michael@0 847 * @static
michael@0 848 * @param {DOMNode} node Node to check for its type
michael@0 849 * @return {boolean} True if node is of type checkbox
michael@0 850 */
michael@0 851 MozMillCheckBox.isType = function MMCB_isType(node) {
michael@0 852 return ((node.localName.toLowerCase() == "input" && node.getAttribute("type") == "checkbox") ||
michael@0 853 (node.localName.toLowerCase() == 'toolbarbutton' && node.getAttribute('type') == 'checkbox') ||
michael@0 854 (node.localName.toLowerCase() == 'checkbox'));
michael@0 855 };
michael@0 856
michael@0 857
michael@0 858 /**
michael@0 859 * MozMillRadio, which inherits from MozMillElement
michael@0 860 */
michael@0 861 function MozMillRadio(locatorType, locator, args) {
michael@0 862 MozMillElement.call(this, locatorType, locator, args);
michael@0 863 }
michael@0 864
michael@0 865
michael@0 866 MozMillRadio.prototype = Object.create(MozMillElement.prototype, {
michael@0 867 select : {
michael@0 868 /**
michael@0 869 * Select the given radio button
michael@0 870 *
michael@0 871 * @param {number} [index=0]
michael@0 872 * Specifies which radio button in the group to select (only
michael@0 873 * applicable to radiogroup elements)
michael@0 874 * @return {boolean} Success state
michael@0 875 */
michael@0 876 value : function MMR_select(index) {
michael@0 877 if (!this.element) {
michael@0 878 throw new Error("could not find element " + this.getInfo());
michael@0 879 }
michael@0 880
michael@0 881 if (this.element.localName.toLowerCase() == "radiogroup") {
michael@0 882 var element = this.element.getElementsByTagName("radio")[index || 0];
michael@0 883 new MozMillRadio("Elem", element).click();
michael@0 884 } else {
michael@0 885 var element = this.element;
michael@0 886 this.click();
michael@0 887 }
michael@0 888
michael@0 889 assert.waitFor(function () {
michael@0 890 // If we have a XUL element, unwrap its XPCNativeWrapper
michael@0 891 if (element.namespaceURI == NAMESPACE_XUL) {
michael@0 892 element = utils.unwrapNode(element);
michael@0 893 return element.selected == true;
michael@0 894 }
michael@0 895
michael@0 896 return element.checked == true;
michael@0 897 }, "Radio.select(): Radio button " + this.getInfo() + " has been selected", 500);
michael@0 898
michael@0 899 broker.pass({'function':'MozMillRadio.select(' + this.getInfo() + ')'});
michael@0 900
michael@0 901 return true;
michael@0 902 }
michael@0 903 }
michael@0 904 });
michael@0 905
michael@0 906
michael@0 907 /**
michael@0 908 * Returns true if node is of type MozMillRadio
michael@0 909 *
michael@0 910 * @static
michael@0 911 * @param {DOMNode} node Node to check for its type
michael@0 912 * @return {boolean} True if node is of type radio
michael@0 913 */
michael@0 914 MozMillRadio.isType = function MMR_isType(node) {
michael@0 915 return ((node.localName.toLowerCase() == 'input' && node.getAttribute('type') == 'radio') ||
michael@0 916 (node.localName.toLowerCase() == 'toolbarbutton' && node.getAttribute('type') == 'radio') ||
michael@0 917 (node.localName.toLowerCase() == 'radio') ||
michael@0 918 (node.localName.toLowerCase() == 'radiogroup'));
michael@0 919 };
michael@0 920
michael@0 921
michael@0 922 /**
michael@0 923 * MozMillDropList, which inherits from MozMillElement
michael@0 924 */
michael@0 925 function MozMillDropList(locatorType, locator, args) {
michael@0 926 MozMillElement.call(this, locatorType, locator, args);
michael@0 927 }
michael@0 928
michael@0 929
michael@0 930 MozMillDropList.prototype = Object.create(MozMillElement.prototype, {
michael@0 931 select : {
michael@0 932 /**
michael@0 933 * Select the specified option and trigger the relevant events of the element
michael@0 934 * @return {boolean}
michael@0 935 */
michael@0 936 value : function MMDL_select(index, option, value) {
michael@0 937 if (!this.element){
michael@0 938 throw new Error("Could not find element " + this.getInfo());
michael@0 939 }
michael@0 940
michael@0 941 //if we have a select drop down
michael@0 942 if (this.element.localName.toLowerCase() == "select"){
michael@0 943 var item = null;
michael@0 944
michael@0 945 // The selected item should be set via its index
michael@0 946 if (index != undefined) {
michael@0 947 // Resetting a menulist has to be handled separately
michael@0 948 if (index == -1) {
michael@0 949 this.dispatchEvent('focus', false);
michael@0 950 this.element.selectedIndex = index;
michael@0 951 this.dispatchEvent('change', true);
michael@0 952
michael@0 953 broker.pass({'function':'MozMillDropList.select()'});
michael@0 954
michael@0 955 return true;
michael@0 956 } else {
michael@0 957 item = this.element.options.item(index);
michael@0 958 }
michael@0 959 } else {
michael@0 960 for (var i = 0; i < this.element.options.length; i++) {
michael@0 961 var entry = this.element.options.item(i);
michael@0 962 if (option != undefined && entry.innerHTML == option ||
michael@0 963 value != undefined && entry.value == value) {
michael@0 964 item = entry;
michael@0 965 break;
michael@0 966 }
michael@0 967 }
michael@0 968 }
michael@0 969
michael@0 970 // Click the item
michael@0 971 try {
michael@0 972 // EventUtils.synthesizeMouse doesn't work.
michael@0 973 this.dispatchEvent('focus', false);
michael@0 974 item.selected = true;
michael@0 975 this.dispatchEvent('change', true);
michael@0 976
michael@0 977 var self = this;
michael@0 978 var selected = index || option || value;
michael@0 979 assert.waitFor(function () {
michael@0 980 switch (selected) {
michael@0 981 case index:
michael@0 982 return selected === self.element.selectedIndex;
michael@0 983 break;
michael@0 984 case option:
michael@0 985 return selected === item.label;
michael@0 986 break;
michael@0 987 case value:
michael@0 988 return selected === item.value;
michael@0 989 break;
michael@0 990 }
michael@0 991 }, "DropList.select(): The correct item has been selected");
michael@0 992
michael@0 993 broker.pass({'function':'MozMillDropList.select()'});
michael@0 994
michael@0 995 return true;
michael@0 996 } catch (e) {
michael@0 997 throw new Error("No item selected for element " + this.getInfo());
michael@0 998 }
michael@0 999 }
michael@0 1000 //if we have a xul menupopup select accordingly
michael@0 1001 else if (this.element.namespaceURI.toLowerCase() == NAMESPACE_XUL) {
michael@0 1002 var ownerDoc = this.element.ownerDocument;
michael@0 1003 // Unwrap the XUL element's XPCNativeWrapper
michael@0 1004 this.element = utils.unwrapNode(this.element);
michael@0 1005 // Get the list of menuitems
michael@0 1006 var menuitems = this.element.
michael@0 1007 getElementsByTagNameNS(NAMESPACE_XUL, "menupopup")[0].
michael@0 1008 getElementsByTagNameNS(NAMESPACE_XUL, "menuitem");
michael@0 1009
michael@0 1010 var item = null;
michael@0 1011
michael@0 1012 if (index != undefined) {
michael@0 1013 if (index == -1) {
michael@0 1014 this.dispatchEvent('focus', false);
michael@0 1015 this.element.boxObject.QueryInterface(Ci.nsIMenuBoxObject).activeChild = null;
michael@0 1016 this.dispatchEvent('change', true);
michael@0 1017
michael@0 1018 broker.pass({'function':'MozMillDropList.select()'});
michael@0 1019
michael@0 1020 return true;
michael@0 1021 } else {
michael@0 1022 item = menuitems[index];
michael@0 1023 }
michael@0 1024 } else {
michael@0 1025 for (var i = 0; i < menuitems.length; i++) {
michael@0 1026 var entry = menuitems[i];
michael@0 1027 if (option != undefined && entry.label == option ||
michael@0 1028 value != undefined && entry.value == value) {
michael@0 1029 item = entry;
michael@0 1030 break;
michael@0 1031 }
michael@0 1032 }
michael@0 1033 }
michael@0 1034
michael@0 1035 // Click the item
michael@0 1036 try {
michael@0 1037 item.click();
michael@0 1038
michael@0 1039 var self = this;
michael@0 1040 var selected = index || option || value;
michael@0 1041 assert.waitFor(function () {
michael@0 1042 switch (selected) {
michael@0 1043 case index:
michael@0 1044 return selected === self.element.selectedIndex;
michael@0 1045 break;
michael@0 1046 case option:
michael@0 1047 return selected === self.element.label;
michael@0 1048 break;
michael@0 1049 case value:
michael@0 1050 return selected === self.element.value;
michael@0 1051 break;
michael@0 1052 }
michael@0 1053 }, "DropList.select(): The correct item has been selected");
michael@0 1054
michael@0 1055 broker.pass({'function':'MozMillDropList.select()'});
michael@0 1056
michael@0 1057 return true;
michael@0 1058 } catch (e) {
michael@0 1059 throw new Error('No item selected for element ' + this.getInfo());
michael@0 1060 }
michael@0 1061 }
michael@0 1062 }
michael@0 1063 }
michael@0 1064 });
michael@0 1065
michael@0 1066
michael@0 1067 /**
michael@0 1068 * Returns true if node is of type MozMillDropList
michael@0 1069 *
michael@0 1070 * @static
michael@0 1071 * @param {DOMNode} node Node to check for its type
michael@0 1072 * @return {boolean} True if node is of type dropdown list
michael@0 1073 */
michael@0 1074 MozMillDropList.isType = function MMR_isType(node) {
michael@0 1075 return ((node.localName.toLowerCase() == 'toolbarbutton' &&
michael@0 1076 (node.getAttribute('type') == 'menu' || node.getAttribute('type') == 'menu-button')) ||
michael@0 1077 (node.localName.toLowerCase() == 'menu') ||
michael@0 1078 (node.localName.toLowerCase() == 'menulist') ||
michael@0 1079 (node.localName.toLowerCase() == 'select' ));
michael@0 1080 };
michael@0 1081
michael@0 1082
michael@0 1083 /**
michael@0 1084 * MozMillTextBox, which inherits from MozMillElement
michael@0 1085 */
michael@0 1086 function MozMillTextBox(locatorType, locator, args) {
michael@0 1087 MozMillElement.call(this, locatorType, locator, args);
michael@0 1088 }
michael@0 1089
michael@0 1090
michael@0 1091 MozMillTextBox.prototype = Object.create(MozMillElement.prototype, {
michael@0 1092 sendKeys : {
michael@0 1093 /**
michael@0 1094 * Synthesize keypress events for each character on the given element
michael@0 1095 *
michael@0 1096 * @param {string} aText
michael@0 1097 * The text to send as single keypress events
michael@0 1098 * @param {object} aModifiers
michael@0 1099 * Information about the modifier keys to send
michael@0 1100 * Elements: accelKey - Hold down the accelerator key (ctrl/meta)
michael@0 1101 * [optional - default: false]
michael@0 1102 * altKey - Hold down the alt key
michael@0 1103 * [optional - default: false]
michael@0 1104 * ctrlKey - Hold down the ctrl key
michael@0 1105 * [optional - default: false]
michael@0 1106 * metaKey - Hold down the meta key (command key on Mac)
michael@0 1107 * [optional - default: false]
michael@0 1108 * shiftKey - Hold down the shift key
michael@0 1109 * [optional - default: false]
michael@0 1110 * @param {object} aExpectedEvent
michael@0 1111 * Information about the expected event to occur
michael@0 1112 * Elements: target - Element which should receive the event
michael@0 1113 * [optional - default: current element]
michael@0 1114 * type - Type of the expected key event
michael@0 1115 * @return {boolean} Success state
michael@0 1116 */
michael@0 1117 value : function MMTB_sendKeys(aText, aModifiers, aExpectedEvent) {
michael@0 1118 if (!this.element) {
michael@0 1119 throw new Error("could not find element " + this.getInfo());
michael@0 1120 }
michael@0 1121
michael@0 1122 var element = this.element;
michael@0 1123 Array.forEach(aText, function (letter) {
michael@0 1124 var win = element.ownerDocument ? element.ownerDocument.defaultView
michael@0 1125 : element;
michael@0 1126 element.focus();
michael@0 1127
michael@0 1128 if (aExpectedEvent) {
michael@0 1129 if (!aExpectedEvent.type) {
michael@0 1130 throw new Error(arguments.callee.name + ": Expected event type not specified");
michael@0 1131 }
michael@0 1132
michael@0 1133 var target = aExpectedEvent.target ? aExpectedEvent.target.getNode()
michael@0 1134 : element;
michael@0 1135 EventUtils.synthesizeKeyExpectEvent(letter, aModifiers || {}, target,
michael@0 1136 aExpectedEvent.type,
michael@0 1137 "MozMillTextBox.sendKeys()", win);
michael@0 1138 } else {
michael@0 1139 EventUtils.synthesizeKey(letter, aModifiers || {}, win);
michael@0 1140 }
michael@0 1141 });
michael@0 1142
michael@0 1143 broker.pass({'function':'MozMillTextBox.type()'});
michael@0 1144
michael@0 1145 return true;
michael@0 1146 }
michael@0 1147 }
michael@0 1148 });
michael@0 1149
michael@0 1150
michael@0 1151 /**
michael@0 1152 * Returns true if node is of type MozMillTextBox
michael@0 1153 *
michael@0 1154 * @static
michael@0 1155 * @param {DOMNode} node Node to check for its type
michael@0 1156 * @return {boolean} True if node is of type textbox
michael@0 1157 */
michael@0 1158 MozMillTextBox.isType = function MMR_isType(node) {
michael@0 1159 return ((node.localName.toLowerCase() == 'input' &&
michael@0 1160 (node.getAttribute('type') == 'text' || node.getAttribute('type') == 'search')) ||
michael@0 1161 (node.localName.toLowerCase() == 'textarea') ||
michael@0 1162 (node.localName.toLowerCase() == 'textbox'));
michael@0 1163 };

mercurial