1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/tps/extensions/mozmill/resource/driver/mozelement.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1163 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, you can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +var EXPORTED_SYMBOLS = ["Elem", "Selector", "ID", "Link", "XPath", "Name", "Lookup", 1.9 + "MozMillElement", "MozMillCheckBox", "MozMillRadio", "MozMillDropList", 1.10 + "MozMillTextBox", "subclasses" 1.11 + ]; 1.12 + 1.13 +const NAMESPACE_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.14 + 1.15 +const Cc = Components.classes; 1.16 +const Ci = Components.interfaces; 1.17 +const Cu = Components.utils; 1.18 + 1.19 +var EventUtils = {}; Cu.import('resource://mozmill/stdlib/EventUtils.js', EventUtils); 1.20 + 1.21 +var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions); 1.22 +var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker); 1.23 +var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib); 1.24 +var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils); 1.25 + 1.26 +var assert = new assertions.Assert(); 1.27 + 1.28 +// A list of all the subclasses available. Shared modules can push their own subclasses onto this list 1.29 +var subclasses = [MozMillCheckBox, MozMillRadio, MozMillDropList, MozMillTextBox]; 1.30 + 1.31 +/** 1.32 + * createInstance() 1.33 + * 1.34 + * Returns an new instance of a MozMillElement 1.35 + * The type of the element is automatically determined 1.36 + */ 1.37 +function createInstance(locatorType, locator, elem, document) { 1.38 + var args = { "document": document, "element": elem }; 1.39 + 1.40 + // If we already have an element lets determine the best MozMillElement type 1.41 + if (elem) { 1.42 + for (var i = 0; i < subclasses.length; ++i) { 1.43 + if (subclasses[i].isType(elem)) { 1.44 + return new subclasses[i](locatorType, locator, args); 1.45 + } 1.46 + } 1.47 + } 1.48 + 1.49 + // By default we create a base MozMillElement 1.50 + if (MozMillElement.isType(elem)) { 1.51 + return new MozMillElement(locatorType, locator, args); 1.52 + } 1.53 + 1.54 + throw new Error("Unsupported element type " + locatorType + ": " + locator); 1.55 +} 1.56 + 1.57 +var Elem = function (node) { 1.58 + return createInstance("Elem", node, node); 1.59 +}; 1.60 + 1.61 +var Selector = function (document, selector, index) { 1.62 + return createInstance("Selector", selector, elementslib.Selector(document, selector, index), document); 1.63 +}; 1.64 + 1.65 +var ID = function (document, nodeID) { 1.66 + return createInstance("ID", nodeID, elementslib.ID(document, nodeID), document); 1.67 +}; 1.68 + 1.69 +var Link = function (document, linkName) { 1.70 + return createInstance("Link", linkName, elementslib.Link(document, linkName), document); 1.71 +}; 1.72 + 1.73 +var XPath = function (document, expr) { 1.74 + return createInstance("XPath", expr, elementslib.XPath(document, expr), document); 1.75 +}; 1.76 + 1.77 +var Name = function (document, nName) { 1.78 + return createInstance("Name", nName, elementslib.Name(document, nName), document); 1.79 +}; 1.80 + 1.81 +var Lookup = function (document, expression) { 1.82 + var elem = createInstance("Lookup", expression, elementslib.Lookup(document, expression), document); 1.83 + 1.84 + // Bug 864268 - Expose the expression property to maintain backwards compatibility 1.85 + elem.expression = elem._locator; 1.86 + 1.87 + return elem; 1.88 +}; 1.89 + 1.90 +/** 1.91 + * MozMillElement 1.92 + * The base class for all mozmill elements 1.93 + */ 1.94 +function MozMillElement(locatorType, locator, args) { 1.95 + args = args || {}; 1.96 + this._locatorType = locatorType; 1.97 + this._locator = locator; 1.98 + this._element = args["element"]; 1.99 + this._owner = args["owner"]; 1.100 + 1.101 + this._document = this._element ? this._element.ownerDocument : args["document"]; 1.102 + this._defaultView = this._document ? this._document.defaultView : null; 1.103 + 1.104 + // Used to maintain backwards compatibility with controller.js 1.105 + this.isElement = true; 1.106 +} 1.107 + 1.108 +// Static method that returns true if node is of this element type 1.109 +MozMillElement.isType = function (node) { 1.110 + return true; 1.111 +}; 1.112 + 1.113 +// This getter is the magic behind lazy loading (note distinction between _element and element) 1.114 +MozMillElement.prototype.__defineGetter__("element", function () { 1.115 + // If the document is invalid (e.g. reload of the page), invalidate the cached 1.116 + // element and update the document cache 1.117 + if (this._defaultView && this._defaultView.document !== this._document) { 1.118 + this._document = this._defaultView.document; 1.119 + this._element = undefined; 1.120 + } 1.121 + 1.122 + if (this._element == undefined) { 1.123 + if (elementslib[this._locatorType]) { 1.124 + this._element = elementslib[this._locatorType](this._document, this._locator); 1.125 + } else if (this._locatorType == "Elem") { 1.126 + this._element = this._locator; 1.127 + } else { 1.128 + throw new Error("Unknown locator type: " + this._locatorType); 1.129 + } 1.130 + } 1.131 + 1.132 + return this._element; 1.133 +}); 1.134 + 1.135 +/** 1.136 + * Drag an element to the specified offset on another element, firing mouse and 1.137 + * drag events. Adapted from ChromeUtils.js synthesizeDrop() 1.138 + * 1.139 + * By default it will drag the source element over the destination's element 1.140 + * center with a "move" dropEffect. 1.141 + * 1.142 + * @param {MozElement} aElement 1.143 + * Destination element over which the drop occurs 1.144 + * @param {Number} [aOffsetX=aElement.width/2] 1.145 + * Relative x offset for dropping on aElement 1.146 + * @param {Number} [aOffsetY=aElement.height/2] 1.147 + * Relative y offset for dropping on aElement 1.148 + * @param {DOMWindow} [aSourceWindow=this.element.ownerDocument.defaultView] 1.149 + * Custom source Window to be used. 1.150 + * @param {DOMWindow} [aDestWindow=aElement.getNode().ownerDocument.defaultView] 1.151 + * Custom destination Window to be used. 1.152 + * @param {String} [aDropEffect="move"] 1.153 + * Possible values: copy, move, link, none 1.154 + * @param {Object[]} [aDragData] 1.155 + * An array holding custom drag data to be used during the drag event 1.156 + * Format: [{ type: "text/plain", "Text to drag"}, ...] 1.157 + * 1.158 + * @returns {String} the captured dropEffect 1.159 + */ 1.160 +MozMillElement.prototype.dragToElement = function(aElement, aOffsetX, aOffsetY, 1.161 + aSourceWindow, aDestWindow, 1.162 + aDropEffect, aDragData) { 1.163 + if (!this.element) { 1.164 + throw new Error("Could not find element " + this.getInfo()); 1.165 + } 1.166 + if (!aElement) { 1.167 + throw new Error("Missing destination element"); 1.168 + } 1.169 + 1.170 + var srcNode = this.element; 1.171 + var destNode = aElement.getNode(); 1.172 + var srcWindow = aSourceWindow || 1.173 + (srcNode.ownerDocument ? srcNode.ownerDocument.defaultView 1.174 + : srcNode); 1.175 + var destWindow = aDestWindow || 1.176 + (destNode.ownerDocument ? destNode.ownerDocument.defaultView 1.177 + : destNode); 1.178 + 1.179 + var srcRect = srcNode.getBoundingClientRect(); 1.180 + var srcCoords = { 1.181 + x: srcRect.width / 2, 1.182 + y: srcRect.height / 2 1.183 + }; 1.184 + var destRect = destNode.getBoundingClientRect(); 1.185 + var destCoords = { 1.186 + x: (!aOffsetX || isNaN(aOffsetX)) ? (destRect.width / 2) : aOffsetX, 1.187 + y: (!aOffsetY || isNaN(aOffsetY)) ? (destRect.height / 2) : aOffsetY 1.188 + }; 1.189 + 1.190 + var windowUtils = destWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.191 + .getInterface(Ci.nsIDOMWindowUtils); 1.192 + var ds = Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService); 1.193 + 1.194 + var dataTransfer; 1.195 + var trapDrag = function (event) { 1.196 + srcWindow.removeEventListener("dragstart", trapDrag, true); 1.197 + dataTransfer = event.dataTransfer; 1.198 + 1.199 + if (!aDragData) { 1.200 + return; 1.201 + } 1.202 + 1.203 + for (var i = 0; i < aDragData.length; i++) { 1.204 + var item = aDragData[i]; 1.205 + for (var j = 0; j < item.length; j++) { 1.206 + dataTransfer.mozSetDataAt(item[j].type, item[j].data, i); 1.207 + } 1.208 + } 1.209 + 1.210 + dataTransfer.dropEffect = aDropEffect || "move"; 1.211 + event.preventDefault(); 1.212 + event.stopPropagation(); 1.213 + } 1.214 + 1.215 + ds.startDragSession(); 1.216 + 1.217 + try { 1.218 + srcWindow.addEventListener("dragstart", trapDrag, true); 1.219 + EventUtils.synthesizeMouse(srcNode, srcCoords.x, srcCoords.y, 1.220 + { type: "mousedown" }, srcWindow); 1.221 + EventUtils.synthesizeMouse(destNode, destCoords.x, destCoords.y, 1.222 + { type: "mousemove" }, destWindow); 1.223 + 1.224 + var event = destWindow.document.createEvent("DragEvents"); 1.225 + event.initDragEvent("dragenter", true, true, destWindow, 0, 0, 0, 0, 0, 1.226 + false, false, false, false, 0, null, dataTransfer); 1.227 + event.initDragEvent("dragover", true, true, destWindow, 0, 0, 0, 0, 0, 1.228 + false, false, false, false, 0, null, dataTransfer); 1.229 + event.initDragEvent("drop", true, true, destWindow, 0, 0, 0, 0, 0, 1.230 + false, false, false, false, 0, null, dataTransfer); 1.231 + windowUtils.dispatchDOMEventViaPresShell(destNode, event, true); 1.232 + 1.233 + EventUtils.synthesizeMouse(destNode, destCoords.x, destCoords.y, 1.234 + { type: "mouseup" }, destWindow); 1.235 + 1.236 + return dataTransfer.dropEffect; 1.237 + } finally { 1.238 + ds.endDragSession(true); 1.239 + } 1.240 + 1.241 +}; 1.242 + 1.243 +// Returns the actual wrapped DOM node 1.244 +MozMillElement.prototype.getNode = function () { 1.245 + return this.element; 1.246 +}; 1.247 + 1.248 +MozMillElement.prototype.getInfo = function () { 1.249 + return this._locatorType + ": " + this._locator; 1.250 +}; 1.251 + 1.252 +/** 1.253 + * Sometimes an element which once existed will no longer exist in the DOM 1.254 + * This function re-searches for the element 1.255 + */ 1.256 +MozMillElement.prototype.exists = function () { 1.257 + this._element = undefined; 1.258 + if (this.element) { 1.259 + return true; 1.260 + } 1.261 + 1.262 + return false; 1.263 +}; 1.264 + 1.265 +/** 1.266 + * Synthesize a keypress event on the given element 1.267 + * 1.268 + * @param {string} aKey 1.269 + * Key to use for synthesizing the keypress event. It can be a simple 1.270 + * character like "k" or a string like "VK_ESCAPE" for command keys 1.271 + * @param {object} aModifiers 1.272 + * Information about the modifier keys to send 1.273 + * Elements: accelKey - Hold down the accelerator key (ctrl/meta) 1.274 + * [optional - default: false] 1.275 + * altKey - Hold down the alt key 1.276 + * [optional - default: false] 1.277 + * ctrlKey - Hold down the ctrl key 1.278 + * [optional - default: false] 1.279 + * metaKey - Hold down the meta key (command key on Mac) 1.280 + * [optional - default: false] 1.281 + * shiftKey - Hold down the shift key 1.282 + * [optional - default: false] 1.283 + * @param {object} aExpectedEvent 1.284 + * Information about the expected event to occur 1.285 + * Elements: target - Element which should receive the event 1.286 + * [optional - default: current element] 1.287 + * type - Type of the expected key event 1.288 + */ 1.289 +MozMillElement.prototype.keypress = function (aKey, aModifiers, aExpectedEvent) { 1.290 + if (!this.element) { 1.291 + throw new Error("Could not find element " + this.getInfo()); 1.292 + } 1.293 + 1.294 + var win = this.element.ownerDocument ? this.element.ownerDocument.defaultView 1.295 + : this.element; 1.296 + this.element.focus(); 1.297 + 1.298 + if (aExpectedEvent) { 1.299 + if (!aExpectedEvent.type) { 1.300 + throw new Error(arguments.callee.name + ": Expected event type not specified"); 1.301 + } 1.302 + 1.303 + var target = aExpectedEvent.target ? aExpectedEvent.target.getNode() 1.304 + : this.element; 1.305 + EventUtils.synthesizeKeyExpectEvent(aKey, aModifiers || {}, target, aExpectedEvent.type, 1.306 + "MozMillElement.keypress()", win); 1.307 + } else { 1.308 + EventUtils.synthesizeKey(aKey, aModifiers || {}, win); 1.309 + } 1.310 + 1.311 + broker.pass({'function':'MozMillElement.keypress()'}); 1.312 + 1.313 + return true; 1.314 +}; 1.315 + 1.316 + 1.317 +/** 1.318 + * Synthesize a general mouse event on the given element 1.319 + * 1.320 + * @param {number} aOffsetX 1.321 + * Relative x offset in the elements bounds to click on 1.322 + * @param {number} aOffsetY 1.323 + * Relative y offset in the elements bounds to click on 1.324 + * @param {object} aEvent 1.325 + * Information about the event to send 1.326 + * Elements: accelKey - Hold down the accelerator key (ctrl/meta) 1.327 + * [optional - default: false] 1.328 + * altKey - Hold down the alt key 1.329 + * [optional - default: false] 1.330 + * button - Mouse button to use 1.331 + * [optional - default: 0] 1.332 + * clickCount - Number of counts to click 1.333 + * [optional - default: 1] 1.334 + * ctrlKey - Hold down the ctrl key 1.335 + * [optional - default: false] 1.336 + * metaKey - Hold down the meta key (command key on Mac) 1.337 + * [optional - default: false] 1.338 + * shiftKey - Hold down the shift key 1.339 + * [optional - default: false] 1.340 + * type - Type of the mouse event ('click', 'mousedown', 1.341 + * 'mouseup', 'mouseover', 'mouseout') 1.342 + * [optional - default: 'mousedown' + 'mouseup'] 1.343 + * @param {object} aExpectedEvent 1.344 + * Information about the expected event to occur 1.345 + * Elements: target - Element which should receive the event 1.346 + * [optional - default: current element] 1.347 + * type - Type of the expected mouse event 1.348 + */ 1.349 +MozMillElement.prototype.mouseEvent = function (aOffsetX, aOffsetY, aEvent, aExpectedEvent) { 1.350 + if (!this.element) { 1.351 + throw new Error(arguments.callee.name + ": could not find element " + this.getInfo()); 1.352 + } 1.353 + 1.354 + if ("document" in this.element) { 1.355 + throw new Error("A window cannot be a target for mouse events."); 1.356 + } 1.357 + 1.358 + var rect = this.element.getBoundingClientRect(); 1.359 + 1.360 + if (!aOffsetX || isNaN(aOffsetX)) { 1.361 + aOffsetX = rect.width / 2; 1.362 + } 1.363 + 1.364 + if (!aOffsetY || isNaN(aOffsetY)) { 1.365 + aOffsetY = rect.height / 2; 1.366 + } 1.367 + 1.368 + // Scroll element into view otherwise the click will fail 1.369 + if ("scrollIntoView" in this.element) 1.370 + this.element.scrollIntoView(); 1.371 + 1.372 + if (aExpectedEvent) { 1.373 + // The expected event type has to be set 1.374 + if (!aExpectedEvent.type) { 1.375 + throw new Error(arguments.callee.name + ": Expected event type not specified"); 1.376 + } 1.377 + 1.378 + // If no target has been specified use the specified element 1.379 + var target = aExpectedEvent.target ? aExpectedEvent.target.getNode() 1.380 + : this.element; 1.381 + if (!target) { 1.382 + throw new Error(arguments.callee.name + ": could not find element " + 1.383 + aExpectedEvent.target.getInfo()); 1.384 + } 1.385 + 1.386 + EventUtils.synthesizeMouseExpectEvent(this.element, aOffsetX, aOffsetY, aEvent, 1.387 + target, aExpectedEvent.type, 1.388 + "MozMillElement.mouseEvent()", 1.389 + this.element.ownerDocument.defaultView); 1.390 + } else { 1.391 + EventUtils.synthesizeMouse(this.element, aOffsetX, aOffsetY, aEvent, 1.392 + this.element.ownerDocument.defaultView); 1.393 + } 1.394 + 1.395 + // Bug 555347 1.396 + // We don't know why this sleep is necessary but more investigation is needed 1.397 + // before it can be removed 1.398 + utils.sleep(0); 1.399 + 1.400 + return true; 1.401 +}; 1.402 + 1.403 +/** 1.404 + * Synthesize a mouse click event on the given element 1.405 + */ 1.406 +MozMillElement.prototype.click = function (aOffsetX, aOffsetY, aExpectedEvent) { 1.407 + // Handle menu items differently 1.408 + if (this.element && this.element.tagName == "menuitem") { 1.409 + this.element.click(); 1.410 + } else { 1.411 + this.mouseEvent(aOffsetX, aOffsetY, {}, aExpectedEvent); 1.412 + } 1.413 + 1.414 + broker.pass({'function':'MozMillElement.click()'}); 1.415 + 1.416 + return true; 1.417 +}; 1.418 + 1.419 +/** 1.420 + * Synthesize a double click on the given element 1.421 + */ 1.422 +MozMillElement.prototype.doubleClick = function (aOffsetX, aOffsetY, aExpectedEvent) { 1.423 + this.mouseEvent(aOffsetX, aOffsetY, {clickCount: 2}, aExpectedEvent); 1.424 + 1.425 + broker.pass({'function':'MozMillElement.doubleClick()'}); 1.426 + 1.427 + return true; 1.428 +}; 1.429 + 1.430 +/** 1.431 + * Synthesize a mouse down event on the given element 1.432 + */ 1.433 +MozMillElement.prototype.mouseDown = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) { 1.434 + this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mousedown"}, aExpectedEvent); 1.435 + 1.436 + broker.pass({'function':'MozMillElement.mouseDown()'}); 1.437 + 1.438 + return true; 1.439 +}; 1.440 + 1.441 +/** 1.442 + * Synthesize a mouse out event on the given element 1.443 + */ 1.444 +MozMillElement.prototype.mouseOut = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) { 1.445 + this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseout"}, aExpectedEvent); 1.446 + 1.447 + broker.pass({'function':'MozMillElement.mouseOut()'}); 1.448 + 1.449 + return true; 1.450 +}; 1.451 + 1.452 +/** 1.453 + * Synthesize a mouse over event on the given element 1.454 + */ 1.455 +MozMillElement.prototype.mouseOver = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) { 1.456 + this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseover"}, aExpectedEvent); 1.457 + 1.458 + broker.pass({'function':'MozMillElement.mouseOver()'}); 1.459 + 1.460 + return true; 1.461 +}; 1.462 + 1.463 +/** 1.464 + * Synthesize a mouse up event on the given element 1.465 + */ 1.466 +MozMillElement.prototype.mouseUp = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) { 1.467 + this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseup"}, aExpectedEvent); 1.468 + 1.469 + broker.pass({'function':'MozMillElement.mouseUp()'}); 1.470 + 1.471 + return true; 1.472 +}; 1.473 + 1.474 +/** 1.475 + * Synthesize a mouse middle click event on the given element 1.476 + */ 1.477 +MozMillElement.prototype.middleClick = function (aOffsetX, aOffsetY, aExpectedEvent) { 1.478 + this.mouseEvent(aOffsetX, aOffsetY, {button: 1}, aExpectedEvent); 1.479 + 1.480 + broker.pass({'function':'MozMillElement.middleClick()'}); 1.481 + 1.482 + return true; 1.483 +}; 1.484 + 1.485 +/** 1.486 + * Synthesize a mouse right click event on the given element 1.487 + */ 1.488 +MozMillElement.prototype.rightClick = function (aOffsetX, aOffsetY, aExpectedEvent) { 1.489 + this.mouseEvent(aOffsetX, aOffsetY, {type : "contextmenu", button: 2 }, aExpectedEvent); 1.490 + 1.491 + broker.pass({'function':'MozMillElement.rightClick()'}); 1.492 + 1.493 + return true; 1.494 +}; 1.495 + 1.496 +/** 1.497 + * Synthesize a general touch event on the given element 1.498 + * 1.499 + * @param {Number} [aOffsetX=aElement.width / 2] 1.500 + * Relative x offset in the elements bounds to click on 1.501 + * @param {Number} [aOffsetY=aElement.height / 2] 1.502 + * Relative y offset in the elements bounds to click on 1.503 + * @param {Object} [aEvent] 1.504 + * Information about the event to send 1.505 + * @param {Boolean} [aEvent.altKey=false] 1.506 + * A Boolean value indicating whether or not the alt key was down when 1.507 + * the touch event was fired 1.508 + * @param {Number} [aEvent.angle=0] 1.509 + * The angle (in degrees) that the ellipse described by rx and 1.510 + * ry must be rotated, clockwise, to most accurately cover the area 1.511 + * of contact between the user and the surface. 1.512 + * @param {Touch[]} [aEvent.changedTouches] 1.513 + * A TouchList of all the Touch objects representing individual points of 1.514 + * contact whose states changed between the previous touch event and 1.515 + * this one 1.516 + * @param {Boolean} [aEvent.ctrlKey] 1.517 + * A Boolean value indicating whether or not the control key was down 1.518 + * when the touch event was fired 1.519 + * @param {Number} [aEvent.force=1] 1.520 + * The amount of pressure being applied to the surface by the user, as a 1.521 + * float between 0.0 (no pressure) and 1.0 (maximum pressure) 1.522 + * @param {Number} [aEvent.id=0] 1.523 + * A unique identifier for this Touch object. A given touch (say, by a 1.524 + * finger) will have the same identifier for the duration of its movement 1.525 + * around the surface. This lets you ensure that you're tracking the same 1.526 + * touch all the time 1.527 + * @param {Boolean} [aEvent.metaKey] 1.528 + * A Boolean value indicating whether or not the meta key was down when 1.529 + * the touch event was fired. 1.530 + * @param {Number} [aEvent.rx=1] 1.531 + * The X radius of the ellipse that most closely circumscribes the area 1.532 + * of contact with the screen. 1.533 + * @param {Number} [aEvent.ry=1] 1.534 + * The Y radius of the ellipse that most closely circumscribes the area 1.535 + * of contact with the screen. 1.536 + * @param {Boolean} [aEvent.shiftKey] 1.537 + * A Boolean value indicating whether or not the shift key was down when 1.538 + * the touch event was fired 1.539 + * @param {Touch[]} [aEvent.targetTouches] 1.540 + * A TouchList of all the Touch objects that are both currently in 1.541 + * contact with the touch surface and were also started on the same 1.542 + * element that is the target of the event 1.543 + * @param {Touch[]} [aEvent.touches] 1.544 + * A TouchList of all the Touch objects representing all current points 1.545 + * of contact with the surface, regardless of target or changed status 1.546 + * @param {Number} [aEvent.type=*|touchstart|touchend|touchmove|touchenter|touchleave|touchcancel] 1.547 + * The type of touch event that occurred 1.548 + * @param {Element} [aEvent.target] 1.549 + * The target of the touches associated with this event. This target 1.550 + * corresponds to the target of all the touches in the targetTouches 1.551 + * attribute, but note that other touches in this event may have a 1.552 + * different target. To be careful, you should use the target associated 1.553 + * with individual touches 1.554 + */ 1.555 +MozMillElement.prototype.touchEvent = function (aOffsetX, aOffsetY, aEvent) { 1.556 + if (!this.element) { 1.557 + throw new Error(arguments.callee.name + ": could not find element " + this.getInfo()); 1.558 + } 1.559 + 1.560 + if ("document" in this.element) { 1.561 + throw new Error("A window cannot be a target for touch events."); 1.562 + } 1.563 + 1.564 + var rect = this.element.getBoundingClientRect(); 1.565 + 1.566 + if (!aOffsetX || isNaN(aOffsetX)) { 1.567 + aOffsetX = rect.width / 2; 1.568 + } 1.569 + 1.570 + if (!aOffsetY || isNaN(aOffsetY)) { 1.571 + aOffsetY = rect.height / 2; 1.572 + } 1.573 + 1.574 + // Scroll element into view otherwise the click will fail 1.575 + if ("scrollIntoView" in this.element) { 1.576 + this.element.scrollIntoView(); 1.577 + } 1.578 + 1.579 + EventUtils.synthesizeTouch(this.element, aOffsetX, aOffsetY, aEvent, 1.580 + this.element.ownerDocument.defaultView); 1.581 + 1.582 + return true; 1.583 +}; 1.584 + 1.585 +/** 1.586 + * Synthesize a touch tap event on the given element 1.587 + * 1.588 + * @param {Number} [aOffsetX=aElement.width / 2] 1.589 + * Left offset in px where the event is triggered 1.590 + * @param {Number} [aOffsetY=aElement.height / 2] 1.591 + * Top offset in px where the event is triggered 1.592 + * @param {Object} [aExpectedEvent] 1.593 + * Information about the expected event to occur 1.594 + * @param {MozMillElement} [aExpectedEvent.target=this.element] 1.595 + * Element which should receive the event 1.596 + * @param {MozMillElement} [aExpectedEvent.type] 1.597 + * Type of the expected mouse event 1.598 + */ 1.599 +MozMillElement.prototype.tap = function (aOffsetX, aOffsetY, aExpectedEvent) { 1.600 + this.mouseEvent(aOffsetX, aOffsetY, { 1.601 + clickCount: 1, 1.602 + inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH 1.603 + }, aExpectedEvent); 1.604 + 1.605 + broker.pass({'function':'MozMillElement.tap()'}); 1.606 + 1.607 + return true; 1.608 +}; 1.609 + 1.610 +/** 1.611 + * Synthesize a double tap on the given element 1.612 + * 1.613 + * @param {Number} [aOffsetX=aElement.width / 2] 1.614 + * Left offset in px where the event is triggered 1.615 + * @param {Number} [aOffsetY=aElement.height / 2] 1.616 + * Top offset in px where the event is triggered 1.617 + * @param {Object} [aExpectedEvent] 1.618 + * Information about the expected event to occur 1.619 + * @param {MozMillElement} [aExpectedEvent.target=this.element] 1.620 + * Element which should receive the event 1.621 + * @param {MozMillElement} [aExpectedEvent.type] 1.622 + * Type of the expected mouse event 1.623 + */ 1.624 +MozMillElement.prototype.doubleTap = function (aOffsetX, aOffsetY, aExpectedEvent) { 1.625 + this.mouseEvent(aOffsetX, aOffsetY, { 1.626 + clickCount: 2, 1.627 + inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH 1.628 + }, aExpectedEvent); 1.629 + 1.630 + broker.pass({'function':'MozMillElement.doubleTap()'}); 1.631 + 1.632 + return true; 1.633 +}; 1.634 + 1.635 +/** 1.636 + * Synthesize a long press 1.637 + * 1.638 + * @param {Number} aOffsetX 1.639 + * Left offset in px where the event is triggered 1.640 + * @param {Number} aOffsetY 1.641 + * Top offset in px where the event is triggered 1.642 + * @param {Number} [aTime=1000] 1.643 + * Duration of the "press" event in ms 1.644 + */ 1.645 +MozMillElement.prototype.longPress = function (aOffsetX, aOffsetY, aTime) { 1.646 + var time = aTime || 1000; 1.647 + 1.648 + this.touchStart(aOffsetX, aOffsetY); 1.649 + utils.sleep(time); 1.650 + this.touchEnd(aOffsetX, aOffsetY); 1.651 + 1.652 + broker.pass({'function':'MozMillElement.longPress()'}); 1.653 + 1.654 + return true; 1.655 +}; 1.656 + 1.657 +/** 1.658 + * Synthesize a touch & drag event on the given element 1.659 + * 1.660 + * @param {Number} aOffsetX1 1.661 + * Left offset of the start position 1.662 + * @param {Number} aOffsetY1 1.663 + * Top offset of the start position 1.664 + * @param {Number} aOffsetX2 1.665 + * Left offset of the end position 1.666 + * @param {Number} aOffsetY2 1.667 + * Top offset of the end position 1.668 + */ 1.669 +MozMillElement.prototype.touchDrag = function (aOffsetX1, aOffsetY1, aOffsetX2, aOffsetY2) { 1.670 + this.touchStart(aOffsetX1, aOffsetY1); 1.671 + this.touchMove(aOffsetX2, aOffsetY2); 1.672 + this.touchEnd(aOffsetX2, aOffsetY2); 1.673 + 1.674 + broker.pass({'function':'MozMillElement.move()'}); 1.675 + 1.676 + return true; 1.677 +}; 1.678 + 1.679 +/** 1.680 + * Synthesize a press / touchstart event on the given element 1.681 + * 1.682 + * @param {Number} aOffsetX 1.683 + * Left offset where the event is triggered 1.684 + * @param {Number} aOffsetY 1.685 + * Top offset where the event is triggered 1.686 + */ 1.687 +MozMillElement.prototype.touchStart = function (aOffsetX, aOffsetY) { 1.688 + this.touchEvent(aOffsetX, aOffsetY, { type: "touchstart" }); 1.689 + 1.690 + broker.pass({'function':'MozMillElement.touchStart()'}); 1.691 + 1.692 + return true; 1.693 +}; 1.694 + 1.695 +/** 1.696 + * Synthesize a release / touchend event on the given element 1.697 + * 1.698 + * @param {Number} aOffsetX 1.699 + * Left offset where the event is triggered 1.700 + * @param {Number} aOffsetY 1.701 + * Top offset where the event is triggered 1.702 + */ 1.703 +MozMillElement.prototype.touchEnd = function (aOffsetX, aOffsetY) { 1.704 + this.touchEvent(aOffsetX, aOffsetY, { type: "touchend" }); 1.705 + 1.706 + broker.pass({'function':'MozMillElement.touchEnd()'}); 1.707 + 1.708 + return true; 1.709 +}; 1.710 + 1.711 +/** 1.712 + * Synthesize a touchMove event on the given element 1.713 + * 1.714 + * @param {Number} aOffsetX 1.715 + * Left offset where the event is triggered 1.716 + * @param {Number} aOffsetY 1.717 + * Top offset where the event is triggered 1.718 + */ 1.719 +MozMillElement.prototype.touchMove = function (aOffsetX, aOffsetY) { 1.720 + this.touchEvent(aOffsetX, aOffsetY, { type: "touchmove" }); 1.721 + 1.722 + broker.pass({'function':'MozMillElement.touchMove()'}); 1.723 + 1.724 + return true; 1.725 +}; 1.726 + 1.727 +MozMillElement.prototype.waitForElement = function (timeout, interval) { 1.728 + var elem = this; 1.729 + 1.730 + assert.waitFor(function () { 1.731 + return elem.exists(); 1.732 + }, "Element.waitForElement(): Element '" + this.getInfo() + 1.733 + "' has been found", timeout, interval); 1.734 + 1.735 + broker.pass({'function':'MozMillElement.waitForElement()'}); 1.736 +}; 1.737 + 1.738 +MozMillElement.prototype.waitForElementNotPresent = function (timeout, interval) { 1.739 + var elem = this; 1.740 + 1.741 + assert.waitFor(function () { 1.742 + return !elem.exists(); 1.743 + }, "Element.waitForElementNotPresent(): Element '" + this.getInfo() + 1.744 + "' has not been found", timeout, interval); 1.745 + 1.746 + broker.pass({'function':'MozMillElement.waitForElementNotPresent()'}); 1.747 +}; 1.748 + 1.749 +MozMillElement.prototype.waitThenClick = function (timeout, interval, 1.750 + aOffsetX, aOffsetY, aExpectedEvent) { 1.751 + this.waitForElement(timeout, interval); 1.752 + this.click(aOffsetX, aOffsetY, aExpectedEvent); 1.753 +}; 1.754 + 1.755 +/** 1.756 + * Waits for the element to be available in the DOM, then trigger a tap event 1.757 + * 1.758 + * @param {Number} [aTimeout=5000] 1.759 + * Time to wait for the element to be available 1.760 + * @param {Number} [aInterval=100] 1.761 + * Interval to check for availability 1.762 + * @param {Number} [aOffsetX=aElement.width / 2] 1.763 + * Left offset where the event is triggered 1.764 + * @param {Number} [aOffsetY=aElement.height / 2] 1.765 + * Top offset where the event is triggered 1.766 + * @param {Object} [aExpectedEvent] 1.767 + * Information about the expected event to occur 1.768 + * @param {MozMillElement} [aExpectedEvent.target=this.element] 1.769 + * Element which should receive the event 1.770 + * @param {MozMillElement} [aExpectedEvent.type] 1.771 + * Type of the expected mouse event 1.772 + */ 1.773 +MozMillElement.prototype.waitThenTap = function (aTimeout, aInterval, 1.774 + aOffsetX, aOffsetY, aExpectedEvent) { 1.775 + this.waitForElement(aTimeout, aInterval); 1.776 + this.tap(aOffsetX, aOffsetY, aExpectedEvent); 1.777 +}; 1.778 + 1.779 +// Dispatches an HTMLEvent 1.780 +MozMillElement.prototype.dispatchEvent = function (eventType, canBubble, modifiers) { 1.781 + canBubble = canBubble || true; 1.782 + modifiers = modifiers || { }; 1.783 + 1.784 + let document = 'ownerDocument' in this.element ? this.element.ownerDocument 1.785 + : this.element.document; 1.786 + 1.787 + let evt = document.createEvent('HTMLEvents'); 1.788 + evt.shiftKey = modifiers["shift"]; 1.789 + evt.metaKey = modifiers["meta"]; 1.790 + evt.altKey = modifiers["alt"]; 1.791 + evt.ctrlKey = modifiers["ctrl"]; 1.792 + evt.initEvent(eventType, canBubble, true); 1.793 + 1.794 + this.element.dispatchEvent(evt); 1.795 +}; 1.796 + 1.797 + 1.798 +/** 1.799 + * MozMillCheckBox, which inherits from MozMillElement 1.800 + */ 1.801 +function MozMillCheckBox(locatorType, locator, args) { 1.802 + MozMillElement.call(this, locatorType, locator, args); 1.803 +} 1.804 + 1.805 + 1.806 +MozMillCheckBox.prototype = Object.create(MozMillElement.prototype, { 1.807 + check : { 1.808 + /** 1.809 + * Enable/Disable a checkbox depending on the target state 1.810 + * 1.811 + * @param {boolean} state State to set 1.812 + * @return {boolean} Success state 1.813 + */ 1.814 + value : function MMCB_check(state) { 1.815 + var result = false; 1.816 + 1.817 + if (!this.element) { 1.818 + throw new Error("could not find element " + this.getInfo()); 1.819 + } 1.820 + 1.821 + // If we have a XUL element, unwrap its XPCNativeWrapper 1.822 + if (this.element.namespaceURI == NAMESPACE_XUL) { 1.823 + this.element = utils.unwrapNode(this.element); 1.824 + } 1.825 + 1.826 + state = (typeof(state) == "boolean") ? state : false; 1.827 + if (state != this.element.checked) { 1.828 + this.click(); 1.829 + var element = this.element; 1.830 + 1.831 + assert.waitFor(function () { 1.832 + return element.checked == state; 1.833 + }, "CheckBox.check(): Checkbox " + this.getInfo() + " could not be checked/unchecked", 500); 1.834 + 1.835 + result = true; 1.836 + } 1.837 + 1.838 + broker.pass({'function':'MozMillCheckBox.check(' + this.getInfo() + 1.839 + ', state: ' + state + ')'}); 1.840 + 1.841 + return result; 1.842 + } 1.843 + } 1.844 +}); 1.845 + 1.846 + 1.847 +/** 1.848 + * Returns true if node is of type MozMillCheckBox 1.849 + * 1.850 + * @static 1.851 + * @param {DOMNode} node Node to check for its type 1.852 + * @return {boolean} True if node is of type checkbox 1.853 + */ 1.854 +MozMillCheckBox.isType = function MMCB_isType(node) { 1.855 + return ((node.localName.toLowerCase() == "input" && node.getAttribute("type") == "checkbox") || 1.856 + (node.localName.toLowerCase() == 'toolbarbutton' && node.getAttribute('type') == 'checkbox') || 1.857 + (node.localName.toLowerCase() == 'checkbox')); 1.858 +}; 1.859 + 1.860 + 1.861 +/** 1.862 + * MozMillRadio, which inherits from MozMillElement 1.863 + */ 1.864 +function MozMillRadio(locatorType, locator, args) { 1.865 + MozMillElement.call(this, locatorType, locator, args); 1.866 +} 1.867 + 1.868 + 1.869 +MozMillRadio.prototype = Object.create(MozMillElement.prototype, { 1.870 + select : { 1.871 + /** 1.872 + * Select the given radio button 1.873 + * 1.874 + * @param {number} [index=0] 1.875 + * Specifies which radio button in the group to select (only 1.876 + * applicable to radiogroup elements) 1.877 + * @return {boolean} Success state 1.878 + */ 1.879 + value : function MMR_select(index) { 1.880 + if (!this.element) { 1.881 + throw new Error("could not find element " + this.getInfo()); 1.882 + } 1.883 + 1.884 + if (this.element.localName.toLowerCase() == "radiogroup") { 1.885 + var element = this.element.getElementsByTagName("radio")[index || 0]; 1.886 + new MozMillRadio("Elem", element).click(); 1.887 + } else { 1.888 + var element = this.element; 1.889 + this.click(); 1.890 + } 1.891 + 1.892 + assert.waitFor(function () { 1.893 + // If we have a XUL element, unwrap its XPCNativeWrapper 1.894 + if (element.namespaceURI == NAMESPACE_XUL) { 1.895 + element = utils.unwrapNode(element); 1.896 + return element.selected == true; 1.897 + } 1.898 + 1.899 + return element.checked == true; 1.900 + }, "Radio.select(): Radio button " + this.getInfo() + " has been selected", 500); 1.901 + 1.902 + broker.pass({'function':'MozMillRadio.select(' + this.getInfo() + ')'}); 1.903 + 1.904 + return true; 1.905 + } 1.906 + } 1.907 +}); 1.908 + 1.909 + 1.910 +/** 1.911 + * Returns true if node is of type MozMillRadio 1.912 + * 1.913 + * @static 1.914 + * @param {DOMNode} node Node to check for its type 1.915 + * @return {boolean} True if node is of type radio 1.916 + */ 1.917 +MozMillRadio.isType = function MMR_isType(node) { 1.918 + return ((node.localName.toLowerCase() == 'input' && node.getAttribute('type') == 'radio') || 1.919 + (node.localName.toLowerCase() == 'toolbarbutton' && node.getAttribute('type') == 'radio') || 1.920 + (node.localName.toLowerCase() == 'radio') || 1.921 + (node.localName.toLowerCase() == 'radiogroup')); 1.922 +}; 1.923 + 1.924 + 1.925 +/** 1.926 + * MozMillDropList, which inherits from MozMillElement 1.927 + */ 1.928 +function MozMillDropList(locatorType, locator, args) { 1.929 + MozMillElement.call(this, locatorType, locator, args); 1.930 +} 1.931 + 1.932 + 1.933 +MozMillDropList.prototype = Object.create(MozMillElement.prototype, { 1.934 + select : { 1.935 + /** 1.936 + * Select the specified option and trigger the relevant events of the element 1.937 + * @return {boolean} 1.938 + */ 1.939 + value : function MMDL_select(index, option, value) { 1.940 + if (!this.element){ 1.941 + throw new Error("Could not find element " + this.getInfo()); 1.942 + } 1.943 + 1.944 + //if we have a select drop down 1.945 + if (this.element.localName.toLowerCase() == "select"){ 1.946 + var item = null; 1.947 + 1.948 + // The selected item should be set via its index 1.949 + if (index != undefined) { 1.950 + // Resetting a menulist has to be handled separately 1.951 + if (index == -1) { 1.952 + this.dispatchEvent('focus', false); 1.953 + this.element.selectedIndex = index; 1.954 + this.dispatchEvent('change', true); 1.955 + 1.956 + broker.pass({'function':'MozMillDropList.select()'}); 1.957 + 1.958 + return true; 1.959 + } else { 1.960 + item = this.element.options.item(index); 1.961 + } 1.962 + } else { 1.963 + for (var i = 0; i < this.element.options.length; i++) { 1.964 + var entry = this.element.options.item(i); 1.965 + if (option != undefined && entry.innerHTML == option || 1.966 + value != undefined && entry.value == value) { 1.967 + item = entry; 1.968 + break; 1.969 + } 1.970 + } 1.971 + } 1.972 + 1.973 + // Click the item 1.974 + try { 1.975 + // EventUtils.synthesizeMouse doesn't work. 1.976 + this.dispatchEvent('focus', false); 1.977 + item.selected = true; 1.978 + this.dispatchEvent('change', true); 1.979 + 1.980 + var self = this; 1.981 + var selected = index || option || value; 1.982 + assert.waitFor(function () { 1.983 + switch (selected) { 1.984 + case index: 1.985 + return selected === self.element.selectedIndex; 1.986 + break; 1.987 + case option: 1.988 + return selected === item.label; 1.989 + break; 1.990 + case value: 1.991 + return selected === item.value; 1.992 + break; 1.993 + } 1.994 + }, "DropList.select(): The correct item has been selected"); 1.995 + 1.996 + broker.pass({'function':'MozMillDropList.select()'}); 1.997 + 1.998 + return true; 1.999 + } catch (e) { 1.1000 + throw new Error("No item selected for element " + this.getInfo()); 1.1001 + } 1.1002 + } 1.1003 + //if we have a xul menupopup select accordingly 1.1004 + else if (this.element.namespaceURI.toLowerCase() == NAMESPACE_XUL) { 1.1005 + var ownerDoc = this.element.ownerDocument; 1.1006 + // Unwrap the XUL element's XPCNativeWrapper 1.1007 + this.element = utils.unwrapNode(this.element); 1.1008 + // Get the list of menuitems 1.1009 + var menuitems = this.element. 1.1010 + getElementsByTagNameNS(NAMESPACE_XUL, "menupopup")[0]. 1.1011 + getElementsByTagNameNS(NAMESPACE_XUL, "menuitem"); 1.1012 + 1.1013 + var item = null; 1.1014 + 1.1015 + if (index != undefined) { 1.1016 + if (index == -1) { 1.1017 + this.dispatchEvent('focus', false); 1.1018 + this.element.boxObject.QueryInterface(Ci.nsIMenuBoxObject).activeChild = null; 1.1019 + this.dispatchEvent('change', true); 1.1020 + 1.1021 + broker.pass({'function':'MozMillDropList.select()'}); 1.1022 + 1.1023 + return true; 1.1024 + } else { 1.1025 + item = menuitems[index]; 1.1026 + } 1.1027 + } else { 1.1028 + for (var i = 0; i < menuitems.length; i++) { 1.1029 + var entry = menuitems[i]; 1.1030 + if (option != undefined && entry.label == option || 1.1031 + value != undefined && entry.value == value) { 1.1032 + item = entry; 1.1033 + break; 1.1034 + } 1.1035 + } 1.1036 + } 1.1037 + 1.1038 + // Click the item 1.1039 + try { 1.1040 + item.click(); 1.1041 + 1.1042 + var self = this; 1.1043 + var selected = index || option || value; 1.1044 + assert.waitFor(function () { 1.1045 + switch (selected) { 1.1046 + case index: 1.1047 + return selected === self.element.selectedIndex; 1.1048 + break; 1.1049 + case option: 1.1050 + return selected === self.element.label; 1.1051 + break; 1.1052 + case value: 1.1053 + return selected === self.element.value; 1.1054 + break; 1.1055 + } 1.1056 + }, "DropList.select(): The correct item has been selected"); 1.1057 + 1.1058 + broker.pass({'function':'MozMillDropList.select()'}); 1.1059 + 1.1060 + return true; 1.1061 + } catch (e) { 1.1062 + throw new Error('No item selected for element ' + this.getInfo()); 1.1063 + } 1.1064 + } 1.1065 + } 1.1066 + } 1.1067 +}); 1.1068 + 1.1069 + 1.1070 +/** 1.1071 + * Returns true if node is of type MozMillDropList 1.1072 + * 1.1073 + * @static 1.1074 + * @param {DOMNode} node Node to check for its type 1.1075 + * @return {boolean} True if node is of type dropdown list 1.1076 + */ 1.1077 +MozMillDropList.isType = function MMR_isType(node) { 1.1078 + return ((node.localName.toLowerCase() == 'toolbarbutton' && 1.1079 + (node.getAttribute('type') == 'menu' || node.getAttribute('type') == 'menu-button')) || 1.1080 + (node.localName.toLowerCase() == 'menu') || 1.1081 + (node.localName.toLowerCase() == 'menulist') || 1.1082 + (node.localName.toLowerCase() == 'select' )); 1.1083 +}; 1.1084 + 1.1085 + 1.1086 +/** 1.1087 + * MozMillTextBox, which inherits from MozMillElement 1.1088 + */ 1.1089 +function MozMillTextBox(locatorType, locator, args) { 1.1090 + MozMillElement.call(this, locatorType, locator, args); 1.1091 +} 1.1092 + 1.1093 + 1.1094 +MozMillTextBox.prototype = Object.create(MozMillElement.prototype, { 1.1095 + sendKeys : { 1.1096 + /** 1.1097 + * Synthesize keypress events for each character on the given element 1.1098 + * 1.1099 + * @param {string} aText 1.1100 + * The text to send as single keypress events 1.1101 + * @param {object} aModifiers 1.1102 + * Information about the modifier keys to send 1.1103 + * Elements: accelKey - Hold down the accelerator key (ctrl/meta) 1.1104 + * [optional - default: false] 1.1105 + * altKey - Hold down the alt key 1.1106 + * [optional - default: false] 1.1107 + * ctrlKey - Hold down the ctrl key 1.1108 + * [optional - default: false] 1.1109 + * metaKey - Hold down the meta key (command key on Mac) 1.1110 + * [optional - default: false] 1.1111 + * shiftKey - Hold down the shift key 1.1112 + * [optional - default: false] 1.1113 + * @param {object} aExpectedEvent 1.1114 + * Information about the expected event to occur 1.1115 + * Elements: target - Element which should receive the event 1.1116 + * [optional - default: current element] 1.1117 + * type - Type of the expected key event 1.1118 + * @return {boolean} Success state 1.1119 + */ 1.1120 + value : function MMTB_sendKeys(aText, aModifiers, aExpectedEvent) { 1.1121 + if (!this.element) { 1.1122 + throw new Error("could not find element " + this.getInfo()); 1.1123 + } 1.1124 + 1.1125 + var element = this.element; 1.1126 + Array.forEach(aText, function (letter) { 1.1127 + var win = element.ownerDocument ? element.ownerDocument.defaultView 1.1128 + : element; 1.1129 + element.focus(); 1.1130 + 1.1131 + if (aExpectedEvent) { 1.1132 + if (!aExpectedEvent.type) { 1.1133 + throw new Error(arguments.callee.name + ": Expected event type not specified"); 1.1134 + } 1.1135 + 1.1136 + var target = aExpectedEvent.target ? aExpectedEvent.target.getNode() 1.1137 + : element; 1.1138 + EventUtils.synthesizeKeyExpectEvent(letter, aModifiers || {}, target, 1.1139 + aExpectedEvent.type, 1.1140 + "MozMillTextBox.sendKeys()", win); 1.1141 + } else { 1.1142 + EventUtils.synthesizeKey(letter, aModifiers || {}, win); 1.1143 + } 1.1144 + }); 1.1145 + 1.1146 + broker.pass({'function':'MozMillTextBox.type()'}); 1.1147 + 1.1148 + return true; 1.1149 + } 1.1150 + } 1.1151 +}); 1.1152 + 1.1153 + 1.1154 +/** 1.1155 + * Returns true if node is of type MozMillTextBox 1.1156 + * 1.1157 + * @static 1.1158 + * @param {DOMNode} node Node to check for its type 1.1159 + * @return {boolean} True if node is of type textbox 1.1160 + */ 1.1161 +MozMillTextBox.isType = function MMR_isType(node) { 1.1162 + return ((node.localName.toLowerCase() == 'input' && 1.1163 + (node.getAttribute('type') == 'text' || node.getAttribute('type') == 'search')) || 1.1164 + (node.localName.toLowerCase() == 'textarea') || 1.1165 + (node.localName.toLowerCase() == 'textbox')); 1.1166 +};