services/sync/tps/extensions/mozmill/resource/driver/controller.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.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
     5 var EXPORTED_SYMBOLS = ["MozMillController", "globalEventRegistry",
     6                         "sleep", "windowMap"];
     8 const Cc = Components.classes;
     9 const Ci = Components.interfaces;
    10 const Cu = Components.utils;
    12 var EventUtils = {}; Cu.import('resource://mozmill/stdlib/EventUtils.js', EventUtils);
    14 var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
    15 var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
    16 var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib);
    17 var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
    18 var mozelement = {}; Cu.import('resource://mozmill/driver/mozelement.js', mozelement);
    19 var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
    20 var windows = {}; Cu.import('resource://mozmill/modules/windows.js', windows);
    22 // Declare most used utils functions in the controller namespace
    23 var assert = new assertions.Assert();
    24 var waitFor = assert.waitFor;
    26 var sleep = utils.sleep;
    28 // For Mozmill 1.5 backward compatibility
    29 var windowMap = windows.map;
    31 waitForEvents = function () {
    32 }
    34 waitForEvents.prototype = {
    35   /**
    36    * Initialize list of events for given node
    37    */
    38   init: function waitForEvents_init(node, events) {
    39     if (node.getNode != undefined)
    40       node = node.getNode();
    42     this.events = events;
    43     this.node = node;
    44     node.firedEvents = {};
    45     this.registry = {};
    47     for each (var e in events) {
    48       var listener = function (event) {
    49         this.firedEvents[event.type] = true;
    50       }
    52       this.registry[e] = listener;
    53       this.registry[e].result = false;
    54       this.node.addEventListener(e, this.registry[e], true);
    55     }
    56   },
    58   /**
    59    * Wait until all assigned events have been fired
    60    */
    61   wait: function waitForEvents_wait(timeout, interval) {
    62     for (var e in this.registry) {
    63       assert.waitFor(function () {
    64         return this.node.firedEvents[e] == true;
    65       }, "waitForEvents.wait(): Event '" + ex + "' has been fired.", timeout, interval);
    67       this.node.removeEventListener(e, this.registry[e], true);
    68     }
    69   }
    70 }
    72 /**
    73  * Class to handle menus and context menus
    74  *
    75  * @constructor
    76  * @param {MozMillController} controller
    77  *        Mozmill controller of the window under test
    78  * @param {string} menuSelector
    79  *        jQuery like selector string of the element
    80  * @param {object} document
    81  *        Document to use for finding the menu
    82  *        [optional - default: aController.window.document]
    83  */
    84 var Menu = function (controller, menuSelector, document) {
    85   this._controller = controller;
    86   this._menu = null;
    88   document = document || controller.window.document;
    89   var node = document.querySelector(menuSelector);
    90   if (node) {
    91     // We don't unwrap nodes automatically yet (Bug 573185)
    92     node = node.wrappedJSObject || node;
    93     this._menu = new mozelement.Elem(node);
    94   } else {
    95     throw new Error("Menu element '" + menuSelector + "' not found.");
    96   }
    97 }
    99 Menu.prototype = {
   101   /**
   102    * Open and populate the menu
   103    *
   104    * @param {ElemBase} contextElement
   105    *        Element whose context menu has to be opened
   106    * @returns {Menu} The Menu instance
   107    */
   108   open: function Menu_open(contextElement) {
   109     // We have to open the context menu
   110     var menu = this._menu.getNode();
   111     if ((menu.localName == "popup" || menu.localName == "menupopup") &&
   112         contextElement && contextElement.exists()) {
   113       this._controller.rightClick(contextElement);
   114       assert.waitFor(function () {
   115         return menu.state == "open";
   116       }, "Context menu has been opened.");
   117     }
   119     // Run through the entire menu and populate with dynamic entries
   120     this._buildMenu(menu);
   122     return this;
   123   },
   125   /**
   126    * Close the menu
   127    *
   128    * @returns {Menu} The Menu instance
   129    */
   130   close: function Menu_close() {
   131     var menu = this._menu.getNode();
   133     this._controller.keypress(this._menu, "VK_ESCAPE", {});
   134     assert.waitFor(function () {
   135       return menu.state == "closed";
   136     }, "Context menu has been closed.");
   138     return this;
   139   },
   141   /**
   142    * Retrieve the specified menu entry
   143    *
   144    * @param {string} itemSelector
   145    *        jQuery like selector string of the menu item
   146    * @returns {ElemBase} Menu element
   147    * @throws Error If menu element has not been found
   148    */
   149   getItem: function Menu_getItem(itemSelector) {
   150     // Run through the entire menu and populate with dynamic entries
   151     this._buildMenu(this._menu.getNode());
   153     var node = this._menu.getNode().querySelector(itemSelector);
   155     if (!node) {
   156       throw new Error("Menu entry '" + itemSelector + "' not found.");
   157     }
   159     return new mozelement.Elem(node);
   160   },
   162   /**
   163    * Click the specified menu entry
   164    *
   165    * @param {string} itemSelector
   166    *        jQuery like selector string of the menu item
   167    *
   168    * @returns {Menu} The Menu instance
   169    */
   170   click: function Menu_click(itemSelector) {
   171     this._controller.click(this.getItem(itemSelector));
   173     return this;
   174   },
   176   /**
   177    * Synthesize a keypress against the menu
   178    *
   179    * @param {string} key
   180    *        Key to press
   181    * @param {object} modifier
   182    *        Key modifiers
   183    * @see MozMillController#keypress
   184    *
   185    * @returns {Menu} The Menu instance
   186    */
   187   keypress: function Menu_keypress(key, modifier) {
   188     this._controller.keypress(this._menu, key, modifier);
   190     return this;
   191   },
   193   /**
   194    * Opens the context menu, click the specified entry and
   195    * make sure that the menu has been closed.
   196    *
   197    * @param {string} itemSelector
   198    *        jQuery like selector string of the element
   199    * @param {ElemBase} contextElement
   200    *        Element whose context menu has to be opened
   201    *
   202    * @returns {Menu} The Menu instance
   203    */
   204   select: function Menu_select(itemSelector, contextElement) {
   205     this.open(contextElement);
   206     this.click(itemSelector);
   207     this.close();
   208   },
   210   /**
   211    * Recursive function which iterates through all menu elements and
   212    * populates the menus with dynamic menu entries.
   213    *
   214    * @param {node} menu
   215    *        Top menu node whose elements have to be populated
   216    */
   217   _buildMenu: function Menu__buildMenu(menu) {
   218     var items = menu ? menu.childNodes : null;
   220     Array.forEach(items, function (item) {
   221       // When we have a menu node, fake a click onto it to populate
   222       // the sub menu with dynamic entries
   223       if (item.tagName == "menu") {
   224         var popup = item.querySelector("menupopup");
   226         if (popup) {
   227           var popupEvent = this._controller.window.document.createEvent("MouseEvent");
   228           popupEvent.initMouseEvent("popupshowing", true, true,
   229                                     this._controller.window, 0, 0, 0, 0, 0,
   230                                     false, false, false, false, 0, null);
   231           popup.dispatchEvent(popupEvent);
   233           this._buildMenu(popup);
   234         }
   235       }
   236     }, this);
   237   }
   238 };
   240 var MozMillController = function (window) {
   241   this.window = window;
   243   this.mozmillModule = {};
   244   Cu.import('resource://mozmill/driver/mozmill.js', this.mozmillModule);
   246   var self = this;
   247   assert.waitFor(function () {
   248     return window != null && self.isLoaded();
   249   }, "controller(): Window has been initialized.");
   251   // Ensure to focus the window which will move it virtually into the foreground
   252   // when focusmanager.testmode is set enabled.
   253   this.window.focus();
   255   var windowType = window.document.documentElement.getAttribute('windowtype');
   256   if (controllerAdditions[windowType] != undefined ) {
   257     this.prototype = new utils.Copy(this.prototype);
   258     controllerAdditions[windowType](this);
   259     this.windowtype = windowType;
   260   }
   261 }
   263 /**
   264  * Returns the global browser object of the window
   265  *
   266  * @returns {Object} The browser object
   267  */
   268 MozMillController.prototype.__defineGetter__("browserObject", function () {
   269   return utils.getBrowserObject(this.window);
   270 });
   272 // constructs a MozMillElement from the controller's window
   273 MozMillController.prototype.__defineGetter__("rootElement", function () {
   274   if (this._rootElement == undefined) {
   275     let docElement = this.window.document.documentElement;
   276     this._rootElement = new mozelement.MozMillElement("Elem", docElement);
   277   }
   279   return this._rootElement;
   280 });
   282 MozMillController.prototype.sleep = utils.sleep;
   283 MozMillController.prototype.waitFor = assert.waitFor;
   285 // Open the specified url in the current tab
   286 MozMillController.prototype.open = function (url) {
   287   switch (this.mozmillModule.Application) {
   288     case "Firefox":
   289     case "MetroFirefox":
   290       // Stop a running page load to not overlap requests
   291       if (this.browserObject.selectedBrowser) {
   292         this.browserObject.selectedBrowser.stop();
   293       }
   295       this.browserObject.loadURI(url);
   296       break;
   298     default:
   299       throw new Error("MozMillController.open not supported.");
   300   }
   302   broker.pass({'function':'Controller.open()'});
   303 }
   305 /**
   306  * Take a screenshot of specified node
   307  *
   308  * @param {Element} node
   309  *        The window or DOM element to capture
   310  * @param {String} name
   311  *        The name of the screenshot used in reporting and as filename
   312  * @param {Boolean} save
   313  *        If true saves the screenshot as 'name.jpg' in tempdir,
   314  *        otherwise returns a dataURL
   315  * @param {Element[]} highlights
   316  *        A list of DOM elements to highlight by drawing a red rectangle around them
   317  *
   318  * @returns {Object} Object which contains properties like filename, dataURL,
   319  *          name and timestamp of the screenshot
   320  */
   321 MozMillController.prototype.screenshot = function (node, name, save, highlights) {
   322   if (!node) {
   323     throw new Error("node is undefined");
   324   }
   326   // Unwrap the node and highlights
   327   if ("getNode" in node) {
   328     node = node.getNode();
   329   }
   331   if (highlights) {
   332     for (var i = 0; i < highlights.length; ++i) {
   333       if ("getNode" in highlights[i]) {
   334         highlights[i] = highlights[i].getNode();
   335       }
   336     }
   337   }
   339   // If save is false, a dataURL is used
   340   // Include both in the report anyway to avoid confusion and make the report easier to parse
   341   var screenshot = {"filename": undefined,
   342                     "dataURL": utils.takeScreenshot(node, highlights),
   343                     "name": name,
   344                     "timestamp": new Date().toLocaleString()};
   346   if (!save) {
   347     return screenshot;
   348   }
   350   // Save the screenshot to disk
   352   let {filename, failure} = utils.saveDataURL(screenshot.dataURL, name);
   353   screenshot.filename = filename;
   354   screenshot.failure = failure;
   356   if (failure) {
   357     broker.log({'function': 'controller.screenshot()',
   358                 'message': 'Error writing to file: ' + screenshot.filename});
   359   } else {
   360     // Send the screenshot object to python over jsbridge
   361     broker.sendMessage("screenshot", screenshot);
   362     broker.pass({'function': 'controller.screenshot()'});
   363   }
   365   return screenshot;
   366 }
   368 /**
   369  * Checks if the specified window has been loaded
   370  *
   371  * @param {DOMWindow} [aWindow=this.window] Window object to check for loaded state
   372  */
   373 MozMillController.prototype.isLoaded = function (aWindow) {
   374   var win = aWindow || this.window;
   376   return windows.map.getValue(utils.getWindowId(win), "loaded") || false;
   377 };
   379 MozMillController.prototype.__defineGetter__("waitForEvents", function () {
   380   if (this._waitForEvents == undefined) {
   381     this._waitForEvents = new waitForEvents();
   382   }
   384   return this._waitForEvents;
   385 });
   387 /**
   388  * Wrapper function to create a new instance of a menu
   389  * @see Menu
   390  */
   391 MozMillController.prototype.getMenu = function (menuSelector, document) {
   392   return new Menu(this, menuSelector, document);
   393 };
   395 MozMillController.prototype.__defineGetter__("mainMenu", function () {
   396   return this.getMenu("menubar");
   397 });
   399 MozMillController.prototype.__defineGetter__("menus", function () {
   400   logDeprecated('controller.menus', 'Use controller.mainMenu instead');
   401 });
   403 MozMillController.prototype.waitForImage = function (aElement, timeout, interval) {
   404   this.waitFor(function () {
   405     return aElement.getNode().complete == true;
   406   }, "timeout exceeded for waitForImage " + aElement.getInfo(), timeout, interval);
   408   broker.pass({'function':'Controller.waitForImage()'});
   409 }
   411 MozMillController.prototype.startUserShutdown = function (timeout, restart, next, resetProfile) {
   412   if (restart && resetProfile) {
   413     throw new Error("You can't have a user-restart and reset the profile; there is a race condition");
   414   }
   416   let shutdownObj = {
   417     'user': true,
   418     'restart': Boolean(restart),
   419     'next': next,
   420     'resetProfile': Boolean(resetProfile),
   421     'timeout': timeout
   422   };
   424   broker.sendMessage('shutdown', shutdownObj);
   425 }
   427 /**
   428  * Restart the application
   429  *
   430  * @param {string} aNext
   431  *        Name of the next test function to run after restart
   432  * @param {boolean} [aFlags=undefined]
   433  *        Additional flags how to handle the shutdown or restart. The attributes
   434  *        eRestarti386 (0x20) and eRestartx86_64 (0x30) have not been documented yet.
   435  * @see https://developer.mozilla.org/nsIAppStartup#Attributes
   436  */
   437 MozMillController.prototype.restartApplication = function (aNext, aFlags) {
   438   var flags = Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart;
   440   if (aFlags) {
   441     flags |= aFlags;
   442   }
   444   broker.sendMessage('shutdown', {'user': false,
   445                                   'restart': true,
   446                                   'flags': flags,
   447                                   'next': aNext,
   448                                   'timeout': 0 });
   450   // We have to ensure to stop the test from continuing until the application is
   451   // shutting down. The only way to do that is by throwing an exception.
   452   throw new errors.ApplicationQuitError();
   453 }
   455 /**
   456  * Stop the application
   457  *
   458  * @param {boolean} [aResetProfile=false]
   459  *        Whether to reset the profile during restart
   460  * @param {boolean} [aFlags=undefined]
   461  *        Additional flags how to handle the shutdown or restart. The attributes
   462  *        eRestarti386 and eRestartx86_64 have not been documented yet.
   463  * @see https://developer.mozilla.org/nsIAppStartup#Attributes
   464  */
   465 MozMillController.prototype.stopApplication = function (aResetProfile, aFlags) {
   466   var flags = Ci.nsIAppStartup.eAttemptQuit;
   468   if (aFlags) {
   469     flags |= aFlags;
   470   }
   472   broker.sendMessage('shutdown', {'user': false,
   473                                   'restart': false,
   474                                   'flags': flags,
   475                                   'resetProfile': aResetProfile,
   476                                   'timeout': 0 });
   478   // We have to ensure to stop the test from continuing until the application is
   479   // shutting down. The only way to do that is by throwing an exception.
   480   throw new errors.ApplicationQuitError();
   481 }
   483 //Browser navigation functions
   484 MozMillController.prototype.goBack = function () {
   485   this.window.content.history.back();
   486   broker.pass({'function':'Controller.goBack()'});
   488   return true;
   489 }
   491 MozMillController.prototype.goForward = function () {
   492   this.window.content.history.forward();
   493   broker.pass({'function':'Controller.goForward()'});
   495   return true;
   496 }
   498 MozMillController.prototype.refresh = function () {
   499   this.window.content.location.reload(true);
   500   broker.pass({'function':'Controller.refresh()'});
   502   return true;
   503 }
   505 function logDeprecated(funcName, message) {
   506   broker.log({'function': funcName + '() - DEPRECATED',
   507               'message': funcName + '() is deprecated. ' + message});
   508 }
   510 function logDeprecatedAssert(funcName) {
   511    logDeprecated('controller.' + funcName,
   512                  '. Use the generic `assertion` module instead.');
   513 }
   515 MozMillController.prototype.assertText = function (el, text) {
   516   logDeprecatedAssert("assertText");
   518   var n = el.getNode();
   520   if (n && n.innerHTML == text) {
   521     broker.pass({'function': 'Controller.assertText()'});
   522   } else {
   523     throw new Error("could not validate element " + el.getInfo() +
   524                     " with text "+ text);
   525   }
   527   return true;
   528 };
   530 /**
   531  * Assert that a specified node exists
   532  */
   533 MozMillController.prototype.assertNode = function (el) {
   534   logDeprecatedAssert("assertNode");
   536   //this.window.focus();
   537   var element = el.getNode();
   538   if (!element) {
   539     throw new Error("could not find element " + el.getInfo());
   540   }
   542   broker.pass({'function': 'Controller.assertNode()'});
   543   return true;
   544 };
   546 /**
   547  * Assert that a specified node doesn't exist
   548  */
   549 MozMillController.prototype.assertNodeNotExist = function (el) {
   550   logDeprecatedAssert("assertNodeNotExist");
   552   try {
   553     var element = el.getNode();
   554   } catch (e) {
   555     broker.pass({'function': 'Controller.assertNodeNotExist()'});
   556   }
   558   if (element) {
   559     throw new Error("Unexpectedly found element " + el.getInfo());
   560   } else {
   561     broker.pass({'function':'Controller.assertNodeNotExist()'});
   562   }
   564   return true;
   565 };
   567 /**
   568  * Assert that a form element contains the expected value
   569  */
   570 MozMillController.prototype.assertValue = function (el, value) {
   571   logDeprecatedAssert("assertValue");
   573   var n = el.getNode();
   575   if (n && n.value == value) {
   576     broker.pass({'function': 'Controller.assertValue()'});
   577   } else {
   578     throw new Error("could not validate element " + el.getInfo() +
   579                     " with value " + value);
   580   }
   582   return false;
   583 };
   585 /**
   586  * Check if the callback function evaluates to true
   587  */
   588 MozMillController.prototype.assert = function (callback, message, thisObject) {
   589   logDeprecatedAssert("assert");
   591   utils.assert(callback, message, thisObject);
   592   broker.pass({'function': ": controller.assert('" + callback + "')"});
   594   return true;
   595 }
   597 /**
   598  * Assert that a provided value is selected in a select element
   599  */
   600 MozMillController.prototype.assertSelected = function (el, value) {
   601   logDeprecatedAssert("assertSelected");
   603   var n = el.getNode();
   604   var validator = value;
   606   if (n && n.options[n.selectedIndex].value == validator) {
   607     broker.pass({'function':'Controller.assertSelected()'});
   608   } else {
   609     throw new Error("could not assert value for element " + el.getInfo() +
   610                     " with value " + value);
   611   }
   613   return true;
   614 };
   616 /**
   617  * Assert that a provided checkbox is checked
   618  */
   619 MozMillController.prototype.assertChecked = function (el) {
   620   logDeprecatedAssert("assertChecked");
   622   var element = el.getNode();
   624   if (element && element.checked == true) {
   625     broker.pass({'function':'Controller.assertChecked()'});
   626   } else {
   627     throw new Error("assert failed for checked element " + el.getInfo());
   628   }
   630   return true;
   631 };
   633 /**
   634  * Assert that a provided checkbox is not checked
   635  */
   636 MozMillController.prototype.assertNotChecked = function (el) {
   637   logDeprecatedAssert("assertNotChecked");
   639   var element = el.getNode();
   641   if (!element) {
   642     throw new Error("Could not find element" + el.getInfo());
   643   }
   645   if (!element.hasAttribute("checked") || element.checked != true) {
   646     broker.pass({'function': 'Controller.assertNotChecked()'});
   647   } else {
   648     throw new Error("assert failed for not checked element " + el.getInfo());
   649   }
   651   return true;
   652 };
   654 /**
   655  * Assert that an element's javascript property exists or has a particular value
   656  *
   657  * if val is undefined, will return true if the property exists.
   658  * if val is specified, will return true if the property exists and has the correct value
   659  */
   660 MozMillController.prototype.assertJSProperty = function (el, attrib, val) {
   661   logDeprecatedAssert("assertJSProperty");
   663   var element = el.getNode();
   665   if (!element){
   666     throw new Error("could not find element " + el.getInfo());
   667   }
   669   var value = element[attrib];
   670   var res = (value !== undefined && (val === undefined ? true :
   671                                                          String(value) == String(val)));
   672   if (res) {
   673     broker.pass({'function':'Controller.assertJSProperty("' + el.getInfo() + '") : ' + val});
   674   } else {
   675     throw new Error("Controller.assertJSProperty(" + el.getInfo() + ") : " +
   676                     (val === undefined ? "property '" + attrib +
   677                     "' doesn't exist" : val + " == " + value));
   678   }
   680   return true;
   681 };
   683 /**
   684  * Assert that an element's javascript property doesn't exist or doesn't have a particular value
   685  *
   686  * if val is undefined, will return true if the property doesn't exist.
   687  * if val is specified, will return true if the property doesn't exist or doesn't have the specified value
   688  */
   689 MozMillController.prototype.assertNotJSProperty = function (el, attrib, val) {
   690   logDeprecatedAssert("assertNotJSProperty");
   692   var element = el.getNode();
   694   if (!element){
   695     throw new Error("could not find element " + el.getInfo());
   696   }
   698   var value = element[attrib];
   699   var res = (val === undefined ? value === undefined : String(value) != String(val));
   700   if (res) {
   701     broker.pass({'function':'Controller.assertNotProperty("' + el.getInfo() + '") : ' + val});
   702   } else {
   703     throw new Error("Controller.assertNotJSProperty(" + el.getInfo() + ") : " +
   704                     (val === undefined ? "property '" + attrib +
   705                     "' exists" : val + " != " + value));
   706   }
   708   return true;
   709 };
   711 /**
   712  * Assert that an element's dom property exists or has a particular value
   713  *
   714  * if val is undefined, will return true if the property exists.
   715  * if val is specified, will return true if the property exists and has the correct value
   716  */
   717 MozMillController.prototype.assertDOMProperty = function (el, attrib, val) {
   718   logDeprecatedAssert("assertDOMProperty");
   720   var element = el.getNode();
   722   if (!element){
   723     throw new Error("could not find element " + el.getInfo());
   724   }
   726   var value, res = element.hasAttribute(attrib);
   727   if (res && val !== undefined) {
   728     value = element.getAttribute(attrib);
   729     res = (String(value) == String(val));
   730   }
   732   if (res) {
   733     broker.pass({'function':'Controller.assertDOMProperty("' + el.getInfo() + '") : ' + val});
   734   } else {
   735     throw new Error("Controller.assertDOMProperty(" + el.getInfo() + ") : " +
   736                     (val === undefined ? "property '" + attrib +
   737                     "' doesn't exist" : val + " == " + value));
   738   }
   740   return true;
   741 };
   743 /**
   744  * Assert that an element's dom property doesn't exist or doesn't have a particular value
   745  *
   746  * if val is undefined, will return true if the property doesn't exist.
   747  * if val is specified, will return true if the property doesn't exist or doesn't have the specified value
   748  */
   749 MozMillController.prototype.assertNotDOMProperty = function (el, attrib, val) {
   750   logDeprecatedAssert("assertNotDOMProperty");
   752   var element = el.getNode();
   754   if (!element) {
   755     throw new Error("could not find element " + el.getInfo());
   756   }
   758   var value, res = element.hasAttribute(attrib);
   759   if (res && val !== undefined) {
   760     value = element.getAttribute(attrib);
   761     res = (String(value) == String(val));
   762   }
   764   if (!res) {
   765     broker.pass({'function':'Controller.assertNotDOMProperty("' + el.getInfo() + '") : ' + val});
   766   } else {
   767     throw new Error("Controller.assertNotDOMProperty(" + el.getInfo() + ") : " +
   768                     (val == undefined ? "property '" + attrib +
   769                     "' exists" : val + " == " + value));
   770   }
   772   return true;
   773 };
   775 /**
   776  * Assert that a specified image has actually loaded. The Safari workaround results
   777  * in additional requests for broken images (in Safari only) but works reliably
   778  */
   779 MozMillController.prototype.assertImageLoaded = function (el) {
   780   logDeprecatedAssert("assertImageLoaded");
   782   var img = el.getNode();
   784   if (!img || img.tagName != 'IMG') {
   785     throw new Error('Controller.assertImageLoaded() failed.')
   786     return false;
   787   }
   789   var comp = img.complete;
   790   var ret = null; // Return value
   792   // Workaround for Safari -- it only supports the
   793   // complete attrib on script-created images
   794   if (typeof comp == 'undefined') {
   795     test = new Image();
   796     // If the original image was successfully loaded,
   797     // src for new one should be pulled from cache
   798     test.src = img.src;
   799     comp = test.complete;
   800   }
   802   // Check the complete attrib. Note the strict
   803   // equality check -- we don't want undefined, null, etc.
   804   // --------------------------
   805   if (comp === false) {
   806     // False -- Img failed to load in IE/Safari, or is
   807     // still trying to load in FF
   808     ret = false;
   809   } else if (comp === true && img.naturalWidth == 0) {
   810     // True, but image has no size -- image failed to
   811     // load in FF
   812     ret = false;
   813   } else {
   814     // Otherwise all we can do is assume everything's
   815     // hunky-dory
   816    ret = true;
   817   }
   819   if (ret) {
   820     broker.pass({'function':'Controller.assertImageLoaded'});
   821   } else {
   822     throw new Error('Controller.assertImageLoaded() failed.')
   823   }
   825   return true;
   826 };
   828 /**
   829  * Drag one element to the top x,y coords of another specified element
   830  */
   831 MozMillController.prototype.mouseMove = function (doc, start, dest) {
   832   // if one of these elements couldn't be looked up
   833   if (typeof start != 'object'){
   834     throw new Error("received bad coordinates");
   835   }
   837   if (typeof dest != 'object'){
   838     throw new Error("received bad coordinates");
   839   }
   841   var triggerMouseEvent = function (element, clientX, clientY) {
   842     clientX = clientX ? clientX: 0;
   843     clientY = clientY ? clientY: 0;
   845     // make the mouse understand where it is on the screen
   846     var screenX = element.boxObject.screenX ? element.boxObject.screenX : 0;
   847     var screenY = element.boxObject.screenY ? element.boxObject.screenY : 0;
   849     var evt = element.ownerDocument.createEvent('MouseEvents');
   850     if (evt.initMouseEvent) {
   851       evt.initMouseEvent('mousemove', true, true, element.ownerDocument.defaultView,
   852                          1, screenX, screenY, clientX, clientY);
   853     } else {
   854       evt.initEvent('mousemove', true, true);
   855     }
   857     element.dispatchEvent(evt);
   858   };
   860   // Do the initial move to the drag element position
   861   triggerMouseEvent(doc.body, start[0], start[1]);
   862   triggerMouseEvent(doc.body, dest[0], dest[1]);
   864   broker.pass({'function':'Controller.mouseMove()'});
   865   return true;
   866 }
   868 /**
   869  * Drag an element to the specified offset on another element, firing mouse and
   870  * drag events. Adapted from ChromeUtils.js synthesizeDrop()
   871  *
   872  * @deprecated Use the MozMillElement object
   873  *
   874  * @param {MozElement} aSrc
   875  *        Source element to be dragged
   876  * @param {MozElement} aDest
   877  *        Destination element over which the drop occurs
   878  * @param {Number} [aOffsetX=element.width/2]
   879  *        Relative x offset for dropping on the aDest element
   880  * @param {Number} [aOffsetY=element.height/2]
   881  *        Relative y offset for dropping on the aDest element
   882  * @param {DOMWindow} [aSourceWindow=this.element.ownerDocument.defaultView]
   883  *        Custom source Window to be used.
   884  * @param {String} [aDropEffect="move"]
   885  *        Effect used for the drop event
   886  * @param {Object[]} [aDragData]
   887  *        An array holding custom drag data to be used during the drag event
   888  *        Format: [{ type: "text/plain", "Text to drag"}, ...]
   889  *
   890  * @returns {String} the captured dropEffect
   891  */
   892 MozMillController.prototype.dragToElement = function (aSrc, aDest, aOffsetX,
   893                                                       aOffsetY, aSourceWindow,
   894                                                       aDropEffect, aDragData) {
   895   logDeprecated("controller.dragToElement", "Use the MozMillElement object.");
   896   return aSrc.dragToElement(aDest, aOffsetX, aOffsetY, aSourceWindow, null,
   897                             aDropEffect, aDragData);
   898 };
   900 function Tabs(controller) {
   901   this.controller = controller;
   902 }
   904 Tabs.prototype.getTab = function (index) {
   905   return this.controller.browserObject.browsers[index].contentDocument;
   906 }
   908 Tabs.prototype.__defineGetter__("activeTab", function () {
   909   return this.controller.browserObject.selectedBrowser.contentDocument;
   910 });
   912 Tabs.prototype.selectTab = function (index) {
   913   // GO in to tab manager and grab the tab by index and call focus.
   914 }
   916 Tabs.prototype.findWindow = function (doc) {
   917   for (var i = 0; i <= (this.controller.window.frames.length - 1); i++) {
   918     if (this.controller.window.frames[i].document == doc) {
   919       return this.controller.window.frames[i];
   920     }
   921   }
   923   throw new Error("Cannot find window for document. Doc title == " + doc.title);
   924 }
   926 Tabs.prototype.getTabWindow = function (index) {
   927   return this.findWindow(this.getTab(index));
   928 }
   930 Tabs.prototype.__defineGetter__("activeTabWindow", function () {
   931   return this.findWindow(this.activeTab);
   932 });
   934 Tabs.prototype.__defineGetter__("length", function () {
   935   return this.controller.browserObject.browsers.length;
   936 });
   938 Tabs.prototype.__defineGetter__("activeTabIndex", function () {
   939   var browser = this.controller.browserObject;
   941   switch(this.controller.mozmillModule.Application) {
   942     case "MetroFirefox":
   943       return browser.tabs.indexOf(browser.selectedTab);
   944     case "Firefox":
   945     default:
   946       return browser.tabContainer.selectedIndex;
   947   }
   948 });
   950 Tabs.prototype.selectTabIndex = function (aIndex) {
   951   var browser = this.controller.browserObject;
   953   switch(this.controller.mozmillModule.Application) {
   954     case "MetroFirefox":
   955       browser.selectedTab = browser.tabs[aIndex];
   956       break;
   957     case "Firefox":
   958     default:
   959       browser.selectTabAtIndex(aIndex);
   960   }
   961 }
   963 function browserAdditions (controller) {
   964   controller.tabs = new Tabs(controller);
   966   controller.waitForPageLoad = function (aDocument, aTimeout, aInterval) {
   967     var timeout = aTimeout || 30000;
   968     var win = null;
   969     var timed_out = false;
   971     // If a user tries to do waitForPageLoad(2000), this will assign the
   972     // interval the first arg which is most likely what they were expecting
   973     if (typeof(aDocument) == "number"){
   974       timeout = aDocument;
   975     }
   977     // If we have a real document use its default view
   978     if (aDocument && (typeof(aDocument) === "object") &&
   979         "defaultView" in aDocument)
   980       win = aDocument.defaultView;
   982     // If no document has been specified, fallback to the default view of the
   983     // currently selected tab browser
   984     win = win || this.browserObject.selectedBrowser.contentWindow;
   986     // Wait until the content in the tab has been loaded
   987     try {
   988       this.waitFor(function () {
   989         return windows.map.hasPageLoaded(utils.getWindowId(win));
   990       }, "Timeout", timeout, aInterval);
   991     }
   992     catch (ex if ex instanceof errors.TimeoutError) {
   993       timed_out = true;
   994     }
   995     finally {
   996       state = 'URI=' + win.document.location.href +
   997               ', readyState=' + win.document.readyState;
   998       message = "controller.waitForPageLoad(" + state + ")";
  1000       if (timed_out) {
  1001         throw new errors.AssertionError(message);
  1004       broker.pass({'function': message});
  1009 var controllerAdditions = {
  1010   'navigator:browser'  :browserAdditions
  1011 };
  1013 /**
  1014  *  DEPRECATION WARNING
  1016  * The following methods have all been DEPRECATED as of Mozmill 2.0
  1017  */
  1018 MozMillController.prototype.assertProperty = function (el, attrib, val) {
  1019   logDeprecatedAssert("assertProperty");
  1021   return this.assertJSProperty(el, attrib, val);
  1022 };
  1024 MozMillController.prototype.assertPropertyNotExist = function (el, attrib) {
  1025   logDeprecatedAssert("assertPropertyNotExist");
  1026   return this.assertNotJSProperty(el, attrib);
  1027 };
  1029 /**
  1030  *  DEPRECATION WARNING
  1032  * The following methods have all been DEPRECATED as of Mozmill 2.0
  1033  * Use the MozMillElement object instead (https://developer.mozilla.org/en/Mozmill/Mozmill_Element_Object)
  1034  */
  1035 MozMillController.prototype.select = function (aElement, index, option, value) {
  1036   logDeprecated("controller.select", "Use the MozMillElement object.");
  1038   return aElement.select(index, option, value);
  1039 };
  1041 MozMillController.prototype.keypress = function (aElement, aKey, aModifiers, aExpectedEvent) {
  1042   logDeprecated("controller.keypress", "Use the MozMillElement object.");
  1044   if (!aElement) {
  1045     aElement = new mozelement.MozMillElement("Elem", this.window);
  1048   return aElement.keypress(aKey, aModifiers, aExpectedEvent);
  1051 MozMillController.prototype.type = function (aElement, aText, aExpectedEvent) {
  1052   logDeprecated("controller.type", "Use the MozMillElement object.");
  1054   if (!aElement) {
  1055     aElement = new mozelement.MozMillElement("Elem", this.window);
  1058   var that = this;
  1059   var retval = true;
  1060   Array.forEach(aText, function (letter) {
  1061     if (!that.keypress(aElement, letter, {}, aExpectedEvent)) {
  1062       retval = false; }
  1063   });
  1065   return retval;
  1068 MozMillController.prototype.mouseEvent = function (aElement, aOffsetX, aOffsetY, aEvent, aExpectedEvent) {
  1069   logDeprecated("controller.mouseEvent", "Use the MozMillElement object.");
  1071   return aElement.mouseEvent(aOffsetX, aOffsetY, aEvent, aExpectedEvent);
  1074 MozMillController.prototype.click = function (aElement, left, top, expectedEvent) {
  1075   logDeprecated("controller.click", "Use the MozMillElement object.");
  1077   return aElement.click(left, top, expectedEvent);
  1080 MozMillController.prototype.doubleClick = function (aElement, left, top, expectedEvent) {
  1081   logDeprecated("controller.doubleClick", "Use the MozMillElement object.");
  1083   return aElement.doubleClick(left, top, expectedEvent);
  1086 MozMillController.prototype.mouseDown = function (aElement, button, left, top, expectedEvent) {
  1087   logDeprecated("controller.mouseDown", "Use the MozMillElement object.");
  1089   return aElement.mouseDown(button, left, top, expectedEvent);
  1090 };
  1092 MozMillController.prototype.mouseOut = function (aElement, button, left, top, expectedEvent) {
  1093   logDeprecated("controller.mouseOut", "Use the MozMillElement object.");
  1095   return aElement.mouseOut(button, left, top, expectedEvent);
  1096 };
  1098 MozMillController.prototype.mouseOver = function (aElement, button, left, top, expectedEvent) {
  1099   logDeprecated("controller.mouseOver", "Use the MozMillElement object.");
  1101   return aElement.mouseOver(button, left, top, expectedEvent);
  1102 };
  1104 MozMillController.prototype.mouseUp = function (aElement, button, left, top, expectedEvent) {
  1105   logDeprecated("controller.mouseUp", "Use the MozMillElement object.");
  1107   return aElement.mouseUp(button, left, top, expectedEvent);
  1108 };
  1110 MozMillController.prototype.middleClick = function (aElement, left, top, expectedEvent) {
  1111   logDeprecated("controller.middleClick", "Use the MozMillElement object.");
  1113   return aElement.middleClick(aElement, left, top, expectedEvent);
  1116 MozMillController.prototype.rightClick = function (aElement, left, top, expectedEvent) {
  1117   logDeprecated("controller.rightClick", "Use the MozMillElement object.");
  1119   return aElement.rightClick(left, top, expectedEvent);
  1122 MozMillController.prototype.check = function (aElement, state) {
  1123   logDeprecated("controller.check", "Use the MozMillElement object.");
  1125   return aElement.check(state);
  1128 MozMillController.prototype.radio = function (aElement) {
  1129   logDeprecated("controller.radio", "Use the MozMillElement object.");
  1131   return aElement.select();
  1134 MozMillController.prototype.waitThenClick = function (aElement, timeout, interval) {
  1135   logDeprecated("controller.waitThenClick", "Use the MozMillElement object.");
  1137   return aElement.waitThenClick(timeout, interval);
  1140 MozMillController.prototype.waitForElement = function (aElement, timeout, interval) {
  1141   logDeprecated("controller.waitForElement", "Use the MozMillElement object.");
  1143   return aElement.waitForElement(timeout, interval);
  1146 MozMillController.prototype.waitForElementNotPresent = function (aElement, timeout, interval) {
  1147   logDeprecated("controller.waitForElementNotPresent", "Use the MozMillElement object.");
  1149   return aElement.waitForElementNotPresent(timeout, interval);

mercurial