services/sync/tps/extensions/mozmill/resource/stdlib/utils.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 = ["applicationName", "assert", "Copy", "getBrowserObject",
     6                         "getChromeWindow", "getWindows", "getWindowByTitle",
     7                         "getWindowByType", "getWindowId", "getMethodInWindows",
     8                         "getPreference", "saveDataURL", "setPreference",
     9                         "sleep", "startTimer", "stopTimer", "takeScreenshot",
    10                         "unwrapNode", "waitFor"
    11                        ];
    13 const Cc = Components.classes;
    14 const Ci = Components.interfaces;
    15 const Cu = Components.utils;
    18 Cu.import("resource://gre/modules/NetUtil.jsm");
    19 Cu.import("resource://gre/modules/Services.jsm");
    21 const applicationIdMap = {
    22   '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'Firefox',
    23   '{99bceaaa-e3c6-48c1-b981-ef9b46b67d60}': 'MetroFirefox'
    24 }
    25 const applicationName = applicationIdMap[Services.appinfo.ID] || Services.appinfo.name;
    27 var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
    28 var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
    29 var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
    31 var assert = new assertions.Assert();
    33 var hwindow = Services.appShell.hiddenDOMWindow;
    35 var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
    37 function Copy (obj) {
    38   for (var n in obj) {
    39     this[n] = obj[n];
    40   }
    41 }
    43 /**
    44  * Returns the browser object of the specified window
    45  *
    46  * @param {Window} aWindow
    47  *        Window to get the browser element from.
    48  *
    49  * @returns {Object} The browser element
    50  */
    51 function getBrowserObject(aWindow) {
    52   switch(applicationName) {
    53     case "MetroFirefox":
    54       return aWindow.Browser;
    55     case "Firefox":
    56     default:
    57       return aWindow.gBrowser;
    58   }
    59 }
    61 function getChromeWindow(aWindow) {
    62   var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
    63                   .getInterface(Ci.nsIWebNavigation)
    64                   .QueryInterface(Ci.nsIDocShellTreeItem)
    65                   .rootTreeItem
    66                   .QueryInterface(Ci.nsIInterfaceRequestor)
    67                   .getInterface(Ci.nsIDOMWindow)
    68                   .QueryInterface(Ci.nsIDOMChromeWindow);
    70   return chromeWin;
    71 }
    73 function getWindows(type) {
    74   if (type == undefined) {
    75     type = "";
    76   }
    78   var windows = [];
    79   var enumerator = Services.wm.getEnumerator(type);
    81   while (enumerator.hasMoreElements()) {
    82     windows.push(enumerator.getNext());
    83   }
    85   if (type == "") {
    86     windows.push(hwindow);
    87   }
    89   return windows;
    90 }
    92 function getMethodInWindows(methodName) {
    93   for each (var w in getWindows()) {
    94     if (w[methodName] != undefined) {
    95       return w[methodName];
    96     }
    97   }
    99   throw new Error("Method with name: '" + methodName + "' is not in any open window.");
   100 }
   102 function getWindowByTitle(title) {
   103   for each (var w in getWindows()) {
   104     if (w.document.title && w.document.title == title) {
   105       return w;
   106     }
   107   }
   109   throw new Error("Window with title: '" + title + "' not found.");
   110 }
   112 function getWindowByType(type) {
   113   return Services.wm.getMostRecentWindow(type);
   114 }
   116 /**
   117  * Retrieve the outer window id for the given window.
   118  *
   119  * @param {Number} aWindow
   120  *        Window to retrieve the id from.
   121  * @returns {Boolean} The outer window id
   122  **/
   123 function getWindowId(aWindow) {
   124   try {
   125     // Normally we can retrieve the id via window utils
   126     return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
   127                    getInterface(Ci.nsIDOMWindowUtils).
   128                    outerWindowID;
   129   } catch (e) {
   130     // ... but for observer notifications we need another interface
   131     return aWindow.QueryInterface(Ci.nsISupportsPRUint64).data;
   132   }
   133 }
   135 var checkChrome = function () {
   136   var loc = window.document.location.href;
   137   try {
   138     loc = window.top.document.location.href;
   139   } catch (e) {
   140   }
   142   return /^chrome:\/\//.test(loc);
   143 }
   145 /**
   146  * Called to get the state of an individual preference.
   147  *
   148  * @param aPrefName     string The preference to get the state of.
   149  * @param aDefaultValue any    The default value if preference was not found.
   150  *
   151  * @returns any The value of the requested preference
   152  *
   153  * @see setPref
   154  * Code by Henrik Skupin: <hskupin@gmail.com>
   155  */
   156 function getPreference(aPrefName, aDefaultValue) {
   157   try {
   158     var branch = Services.prefs;
   160     switch (typeof aDefaultValue) {
   161       case ('boolean'):
   162         return branch.getBoolPref(aPrefName);
   163       case ('string'):
   164         return branch.getCharPref(aPrefName);
   165       case ('number'):
   166         return branch.getIntPref(aPrefName);
   167       default:
   168         return branch.getComplexValue(aPrefName);
   169     }
   170   } catch (e) {
   171     return aDefaultValue;
   172   }
   173 }
   175 /**
   176  * Called to set the state of an individual preference.
   177  *
   178  * @param aPrefName string The preference to set the state of.
   179  * @param aValue    any    The value to set the preference to.
   180  *
   181  * @returns boolean Returns true if value was successfully set.
   182  *
   183  * @see getPref
   184  * Code by Henrik Skupin: <hskupin@gmail.com>
   185  */
   186 function setPreference(aName, aValue) {
   187   try {
   188     var branch = Services.prefs;
   190     switch (typeof aValue) {
   191       case ('boolean'):
   192         branch.setBoolPref(aName, aValue);
   193         break;
   194       case ('string'):
   195         branch.setCharPref(aName, aValue);
   196         break;
   197       case ('number'):
   198         branch.setIntPref(aName, aValue);
   199         break;
   200       default:
   201         branch.setComplexValue(aName, aValue);
   202     }
   203   } catch (e) {
   204     return false;
   205   }
   207   return true;
   208 }
   210 /**
   211  * Sleep for the given amount of milliseconds
   212  *
   213  * @param {number} milliseconds
   214  *        Sleeps the given number of milliseconds
   215  */
   216 function sleep(milliseconds) {
   217   var timeup = false;
   219   hwindow.setTimeout(function () { timeup = true; }, milliseconds);
   220   var thread = Services.tm.currentThread;
   222   while (!timeup) {
   223     thread.processNextEvent(true);
   224   }
   226   broker.pass({'function':'utils.sleep()'});
   227 }
   229 /**
   230  * Check if the callback function evaluates to true
   231  */
   232 function assert(callback, message, thisObject) {
   233   var result = callback.call(thisObject);
   235   if (!result) {
   236     throw new Error(message || arguments.callee.name + ": Failed for '" + callback + "'");
   237   }
   239   return true;
   240 }
   242 /**
   243  * Unwraps a node which is wrapped into a XPCNativeWrapper or XrayWrapper
   244  *
   245  * @param {DOMnode} Wrapped DOM node
   246  * @returns {DOMNode} Unwrapped DOM node
   247  */
   248 function unwrapNode(aNode) {
   249   var node = aNode;
   250   if (node) {
   251     // unwrap is not available on older branches (3.5 and 3.6) - Bug 533596
   252     if ("unwrap" in XPCNativeWrapper) {
   253       node = XPCNativeWrapper.unwrap(node);
   254     }
   255     else if (node.wrappedJSObject != null) {
   256       node = node.wrappedJSObject;
   257     }
   258   }
   260   return node;
   261 }
   263 /**
   264  * Waits for the callback evaluates to true
   265  */
   266 function waitFor(callback, message, timeout, interval, thisObject) {
   267   broker.log({'function': 'utils.waitFor() - DEPRECATED',
   268               'message': 'utils.waitFor() is deprecated. Use assert.waitFor() instead'});
   269   assert.waitFor(callback, message, timeout, interval, thisObject);
   270 }
   272 /**
   273  * Calculates the x and y chrome offset for an element
   274  * See https://developer.mozilla.org/en/DOM/window.innerHeight
   275  *
   276  * Note this function will not work if the user has custom toolbars (via extension) at the bottom or left/right of the screen
   277  */
   278 function getChromeOffset(elem) {
   279   var win = elem.ownerDocument.defaultView;
   280   // Calculate x offset
   281   var chromeWidth = 0;
   283   if (win["name"] != "sidebar") {
   284     chromeWidth = win.outerWidth - win.innerWidth;
   285   }
   287   // Calculate y offset
   288   var chromeHeight = win.outerHeight - win.innerHeight;
   289   // chromeHeight == 0 means elem is already in the chrome and doesn't need the addonbar offset
   290   if (chromeHeight > 0) {
   291     // window.innerHeight doesn't include the addon or find bar, so account for these if present
   292     var addonbar = win.document.getElementById("addon-bar");
   293     if (addonbar) {
   294       chromeHeight -= addonbar.scrollHeight;
   295     }
   297     var findbar = win.document.getElementById("FindToolbar");
   298     if (findbar) {
   299       chromeHeight -= findbar.scrollHeight;
   300     }
   301   }
   303   return {'x':chromeWidth, 'y':chromeHeight};
   304 }
   306 /**
   307  * Takes a screenshot of the specified DOM node
   308  */
   309 function takeScreenshot(node, highlights) {
   310   var rect, win, width, height, left, top, needsOffset;
   311   // node can be either a window or an arbitrary DOM node
   312   try {
   313     // node is an arbitrary DOM node
   314     win = node.ownerDocument.defaultView;
   315     rect = node.getBoundingClientRect();
   316     width = rect.width;
   317     height = rect.height;
   318     top = rect.top;
   319     left = rect.left;
   320     // offset for highlights not needed as they will be relative to this node
   321     needsOffset = false;
   322   } catch (e) {
   323     // node is a window
   324     win = node;
   325     width = win.innerWidth;
   326     height = win.innerHeight;
   327     top = 0;
   328     left = 0;
   329     // offset needed for highlights to take 'outerHeight' of window into account
   330     needsOffset = true;
   331   }
   333   var canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
   334   canvas.width = width;
   335   canvas.height = height;
   337   var ctx = canvas.getContext("2d");
   338   // Draws the DOM contents of the window to the canvas
   339   ctx.drawWindow(win, left, top, width, height, "rgb(255,255,255)");
   341   // This section is for drawing a red rectangle around each element passed in via the highlights array
   342   if (highlights) {
   343     ctx.lineWidth = "2";
   344     ctx.strokeStyle = "red";
   345     ctx.save();
   347     for (var i = 0; i < highlights.length; ++i) {
   348       var elem = highlights[i];
   349       rect = elem.getBoundingClientRect();
   351       var offsetY = 0, offsetX = 0;
   352       if (needsOffset) {
   353         var offset = getChromeOffset(elem);
   354         offsetX = offset.x;
   355         offsetY = offset.y;
   356       } else {
   357         // Don't need to offset the window chrome, just make relative to containing node
   358         offsetY = -top;
   359         offsetX = -left;
   360       }
   362       // Draw the rectangle
   363       ctx.strokeRect(rect.left + offsetX, rect.top + offsetY, rect.width, rect.height);
   364     }
   365   }
   367   return canvas.toDataURL("image/jpeg", 0.5);
   368 }
   370 /**
   371  * Save the dataURL content to the specified file. It will be stored in either the persisted screenshot or temporary folder.
   372  *
   373  * @param {String} aDataURL
   374  *        The dataURL to save
   375  * @param {String} aFilename
   376  *        Target file name without extension
   377  *
   378  * @returns {Object} The hash containing the path of saved file, and the failure bit
   379  */
   380 function saveDataURL(aDataURL, aFilename) {
   381   var frame = {}; Cu.import('resource://mozmill/modules/frame.js', frame);
   382   const FILE_PERMISSIONS = parseInt("0644", 8);
   384   var file;
   385   file = Cc['@mozilla.org/file/local;1']
   386          .createInstance(Ci.nsILocalFile);
   387   file.initWithPath(frame.persisted['screenshots']['path']);
   388   file.append(aFilename + ".jpg");
   389   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FILE_PERMISSIONS);
   391   // Create an output stream to write to file
   392   let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
   393                  .createInstance(Ci.nsIFileOutputStream);
   394   foStream.init(file, 0x02 | 0x08 | 0x10, FILE_PERMISSIONS, foStream.DEFER_OPEN);
   396   let dataURI = NetUtil.newURI(aDataURL, "UTF8", null);
   397   if (!dataURI.schemeIs("data")) {
   398     throw TypeError("aDataURL parameter has to have 'data'" +
   399                     " scheme instead of '" + dataURI.scheme + "'");
   400   }
   402   // Write asynchronously to buffer;
   403   // Input and output streams are closed after write
   405   let ready = false;
   406   let failure = false;
   408   function sync(aStatus) {
   409     if (!Components.isSuccessCode(aStatus)) {
   410       failure = true;
   411     }
   412     ready = true;
   413   }
   415   NetUtil.asyncFetch(dataURI, function (aInputStream, aAsyncFetchResult) {
   416     if (!Components.isSuccessCode(aAsyncFetchResult)) {
   417         // An error occurred!
   418         sync(aAsyncFetchResult);
   419     } else {
   420       // Consume the input stream.
   421       NetUtil.asyncCopy(aInputStream, foStream, function (aAsyncCopyResult) {
   422         sync(aAsyncCopyResult);
   423       });
   424     }
   425   });
   427   assert.waitFor(function () {
   428     return ready;
   429   }, "DataURL has been saved to '" + file.path + "'");
   431   return {filename: file.path, failure: failure};
   432 }
   434 /**
   435  * Some very brain-dead timer functions useful for performance optimizations
   436  * This is only enabled in debug mode
   437  *
   438  **/
   439 var gutility_mzmltimer = 0;
   440 /**
   441  * Starts timer initializing with current EPOC time in milliseconds
   442  *
   443  * @returns none
   444  **/
   445 function startTimer(){
   446   dump("TIMERCHECK:: starting now: " + Date.now() + "\n");
   447   gutility_mzmltimer = Date.now();
   448 }
   450 /**
   451  * Checks the timer and outputs current elapsed time since start of timer. It
   452  * will print out a message you provide with its "time check" so you can
   453  * correlate in the log file and figure out elapsed time of specific functions.
   454  *
   455  * @param aMsg    string The debug message to print with the timer check
   456  *
   457  * @returns none
   458  **/
   459 function checkTimer(aMsg){
   460   var end = Date.now();
   461   dump("TIMERCHECK:: at " + aMsg + " is: " + (end - gutility_mzmltimer) + "\n");
   462 }

mercurial