services/sync/tps/extensions/mozmill/resource/stdlib/utils.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/services/sync/tps/extensions/mozmill/resource/stdlib/utils.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,462 @@
     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 = ["applicationName", "assert", "Copy", "getBrowserObject",
     1.9 +                        "getChromeWindow", "getWindows", "getWindowByTitle",
    1.10 +                        "getWindowByType", "getWindowId", "getMethodInWindows",
    1.11 +                        "getPreference", "saveDataURL", "setPreference",
    1.12 +                        "sleep", "startTimer", "stopTimer", "takeScreenshot",
    1.13 +                        "unwrapNode", "waitFor"
    1.14 +                       ];
    1.15 +
    1.16 +const Cc = Components.classes;
    1.17 +const Ci = Components.interfaces;
    1.18 +const Cu = Components.utils;
    1.19 +
    1.20 +
    1.21 +Cu.import("resource://gre/modules/NetUtil.jsm");
    1.22 +Cu.import("resource://gre/modules/Services.jsm");
    1.23 +
    1.24 +const applicationIdMap = {
    1.25 +  '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'Firefox',
    1.26 +  '{99bceaaa-e3c6-48c1-b981-ef9b46b67d60}': 'MetroFirefox'
    1.27 +}
    1.28 +const applicationName = applicationIdMap[Services.appinfo.ID] || Services.appinfo.name;
    1.29 +
    1.30 +var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
    1.31 +var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
    1.32 +var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
    1.33 +
    1.34 +var assert = new assertions.Assert();
    1.35 +
    1.36 +var hwindow = Services.appShell.hiddenDOMWindow;
    1.37 +
    1.38 +var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
    1.39 +
    1.40 +function Copy (obj) {
    1.41 +  for (var n in obj) {
    1.42 +    this[n] = obj[n];
    1.43 +  }
    1.44 +}
    1.45 +
    1.46 +/**
    1.47 + * Returns the browser object of the specified window
    1.48 + *
    1.49 + * @param {Window} aWindow
    1.50 + *        Window to get the browser element from.
    1.51 + *
    1.52 + * @returns {Object} The browser element
    1.53 + */
    1.54 +function getBrowserObject(aWindow) {
    1.55 +  switch(applicationName) {
    1.56 +    case "MetroFirefox":
    1.57 +      return aWindow.Browser;
    1.58 +    case "Firefox":
    1.59 +    default:
    1.60 +      return aWindow.gBrowser;
    1.61 +  }
    1.62 +}
    1.63 +
    1.64 +function getChromeWindow(aWindow) {
    1.65 +  var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
    1.66 +                  .getInterface(Ci.nsIWebNavigation)
    1.67 +                  .QueryInterface(Ci.nsIDocShellTreeItem)
    1.68 +                  .rootTreeItem
    1.69 +                  .QueryInterface(Ci.nsIInterfaceRequestor)
    1.70 +                  .getInterface(Ci.nsIDOMWindow)
    1.71 +                  .QueryInterface(Ci.nsIDOMChromeWindow);
    1.72 +
    1.73 +  return chromeWin;
    1.74 +}
    1.75 +
    1.76 +function getWindows(type) {
    1.77 +  if (type == undefined) {
    1.78 +    type = "";
    1.79 +  }
    1.80 +
    1.81 +  var windows = [];
    1.82 +  var enumerator = Services.wm.getEnumerator(type);
    1.83 +
    1.84 +  while (enumerator.hasMoreElements()) {
    1.85 +    windows.push(enumerator.getNext());
    1.86 +  }
    1.87 +
    1.88 +  if (type == "") {
    1.89 +    windows.push(hwindow);
    1.90 +  }
    1.91 +
    1.92 +  return windows;
    1.93 +}
    1.94 +
    1.95 +function getMethodInWindows(methodName) {
    1.96 +  for each (var w in getWindows()) {
    1.97 +    if (w[methodName] != undefined) {
    1.98 +      return w[methodName];
    1.99 +    }
   1.100 +  }
   1.101 +
   1.102 +  throw new Error("Method with name: '" + methodName + "' is not in any open window.");
   1.103 +}
   1.104 +
   1.105 +function getWindowByTitle(title) {
   1.106 +  for each (var w in getWindows()) {
   1.107 +    if (w.document.title && w.document.title == title) {
   1.108 +      return w;
   1.109 +    }
   1.110 +  }
   1.111 +
   1.112 +  throw new Error("Window with title: '" + title + "' not found.");
   1.113 +}
   1.114 +
   1.115 +function getWindowByType(type) {
   1.116 +  return Services.wm.getMostRecentWindow(type);
   1.117 +}
   1.118 +
   1.119 +/**
   1.120 + * Retrieve the outer window id for the given window.
   1.121 + *
   1.122 + * @param {Number} aWindow
   1.123 + *        Window to retrieve the id from.
   1.124 + * @returns {Boolean} The outer window id
   1.125 + **/
   1.126 +function getWindowId(aWindow) {
   1.127 +  try {
   1.128 +    // Normally we can retrieve the id via window utils
   1.129 +    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
   1.130 +                   getInterface(Ci.nsIDOMWindowUtils).
   1.131 +                   outerWindowID;
   1.132 +  } catch (e) {
   1.133 +    // ... but for observer notifications we need another interface
   1.134 +    return aWindow.QueryInterface(Ci.nsISupportsPRUint64).data;
   1.135 +  }
   1.136 +}
   1.137 +
   1.138 +var checkChrome = function () {
   1.139 +  var loc = window.document.location.href;
   1.140 +  try {
   1.141 +    loc = window.top.document.location.href;
   1.142 +  } catch (e) {
   1.143 +  }
   1.144 +
   1.145 +  return /^chrome:\/\//.test(loc);
   1.146 +}
   1.147 +
   1.148 +/**
   1.149 + * Called to get the state of an individual preference.
   1.150 + *
   1.151 + * @param aPrefName     string The preference to get the state of.
   1.152 + * @param aDefaultValue any    The default value if preference was not found.
   1.153 + *
   1.154 + * @returns any The value of the requested preference
   1.155 + *
   1.156 + * @see setPref
   1.157 + * Code by Henrik Skupin: <hskupin@gmail.com>
   1.158 + */
   1.159 +function getPreference(aPrefName, aDefaultValue) {
   1.160 +  try {
   1.161 +    var branch = Services.prefs;
   1.162 +
   1.163 +    switch (typeof aDefaultValue) {
   1.164 +      case ('boolean'):
   1.165 +        return branch.getBoolPref(aPrefName);
   1.166 +      case ('string'):
   1.167 +        return branch.getCharPref(aPrefName);
   1.168 +      case ('number'):
   1.169 +        return branch.getIntPref(aPrefName);
   1.170 +      default:
   1.171 +        return branch.getComplexValue(aPrefName);
   1.172 +    }
   1.173 +  } catch (e) {
   1.174 +    return aDefaultValue;
   1.175 +  }
   1.176 +}
   1.177 +
   1.178 +/**
   1.179 + * Called to set the state of an individual preference.
   1.180 + *
   1.181 + * @param aPrefName string The preference to set the state of.
   1.182 + * @param aValue    any    The value to set the preference to.
   1.183 + *
   1.184 + * @returns boolean Returns true if value was successfully set.
   1.185 + *
   1.186 + * @see getPref
   1.187 + * Code by Henrik Skupin: <hskupin@gmail.com>
   1.188 + */
   1.189 +function setPreference(aName, aValue) {
   1.190 +  try {
   1.191 +    var branch = Services.prefs;
   1.192 +
   1.193 +    switch (typeof aValue) {
   1.194 +      case ('boolean'):
   1.195 +        branch.setBoolPref(aName, aValue);
   1.196 +        break;
   1.197 +      case ('string'):
   1.198 +        branch.setCharPref(aName, aValue);
   1.199 +        break;
   1.200 +      case ('number'):
   1.201 +        branch.setIntPref(aName, aValue);
   1.202 +        break;
   1.203 +      default:
   1.204 +        branch.setComplexValue(aName, aValue);
   1.205 +    }
   1.206 +  } catch (e) {
   1.207 +    return false;
   1.208 +  }
   1.209 +
   1.210 +  return true;
   1.211 +}
   1.212 +
   1.213 +/**
   1.214 + * Sleep for the given amount of milliseconds
   1.215 + *
   1.216 + * @param {number} milliseconds
   1.217 + *        Sleeps the given number of milliseconds
   1.218 + */
   1.219 +function sleep(milliseconds) {
   1.220 +  var timeup = false;
   1.221 +
   1.222 +  hwindow.setTimeout(function () { timeup = true; }, milliseconds);
   1.223 +  var thread = Services.tm.currentThread;
   1.224 +
   1.225 +  while (!timeup) {
   1.226 +    thread.processNextEvent(true);
   1.227 +  }
   1.228 +
   1.229 +  broker.pass({'function':'utils.sleep()'});
   1.230 +}
   1.231 +
   1.232 +/**
   1.233 + * Check if the callback function evaluates to true
   1.234 + */
   1.235 +function assert(callback, message, thisObject) {
   1.236 +  var result = callback.call(thisObject);
   1.237 +
   1.238 +  if (!result) {
   1.239 +    throw new Error(message || arguments.callee.name + ": Failed for '" + callback + "'");
   1.240 +  }
   1.241 +
   1.242 +  return true;
   1.243 +}
   1.244 +
   1.245 +/**
   1.246 + * Unwraps a node which is wrapped into a XPCNativeWrapper or XrayWrapper
   1.247 + *
   1.248 + * @param {DOMnode} Wrapped DOM node
   1.249 + * @returns {DOMNode} Unwrapped DOM node
   1.250 + */
   1.251 +function unwrapNode(aNode) {
   1.252 +  var node = aNode;
   1.253 +  if (node) {
   1.254 +    // unwrap is not available on older branches (3.5 and 3.6) - Bug 533596
   1.255 +    if ("unwrap" in XPCNativeWrapper) {
   1.256 +      node = XPCNativeWrapper.unwrap(node);
   1.257 +    }
   1.258 +    else if (node.wrappedJSObject != null) {
   1.259 +      node = node.wrappedJSObject;
   1.260 +    }
   1.261 +  }
   1.262 +
   1.263 +  return node;
   1.264 +}
   1.265 +
   1.266 +/**
   1.267 + * Waits for the callback evaluates to true
   1.268 + */
   1.269 +function waitFor(callback, message, timeout, interval, thisObject) {
   1.270 +  broker.log({'function': 'utils.waitFor() - DEPRECATED',
   1.271 +              'message': 'utils.waitFor() is deprecated. Use assert.waitFor() instead'});
   1.272 +  assert.waitFor(callback, message, timeout, interval, thisObject);
   1.273 +}
   1.274 +
   1.275 +/**
   1.276 + * Calculates the x and y chrome offset for an element
   1.277 + * See https://developer.mozilla.org/en/DOM/window.innerHeight
   1.278 + *
   1.279 + * Note this function will not work if the user has custom toolbars (via extension) at the bottom or left/right of the screen
   1.280 + */
   1.281 +function getChromeOffset(elem) {
   1.282 +  var win = elem.ownerDocument.defaultView;
   1.283 +  // Calculate x offset
   1.284 +  var chromeWidth = 0;
   1.285 +
   1.286 +  if (win["name"] != "sidebar") {
   1.287 +    chromeWidth = win.outerWidth - win.innerWidth;
   1.288 +  }
   1.289 +
   1.290 +  // Calculate y offset
   1.291 +  var chromeHeight = win.outerHeight - win.innerHeight;
   1.292 +  // chromeHeight == 0 means elem is already in the chrome and doesn't need the addonbar offset
   1.293 +  if (chromeHeight > 0) {
   1.294 +    // window.innerHeight doesn't include the addon or find bar, so account for these if present
   1.295 +    var addonbar = win.document.getElementById("addon-bar");
   1.296 +    if (addonbar) {
   1.297 +      chromeHeight -= addonbar.scrollHeight;
   1.298 +    }
   1.299 +
   1.300 +    var findbar = win.document.getElementById("FindToolbar");
   1.301 +    if (findbar) {
   1.302 +      chromeHeight -= findbar.scrollHeight;
   1.303 +    }
   1.304 +  }
   1.305 +
   1.306 +  return {'x':chromeWidth, 'y':chromeHeight};
   1.307 +}
   1.308 +
   1.309 +/**
   1.310 + * Takes a screenshot of the specified DOM node
   1.311 + */
   1.312 +function takeScreenshot(node, highlights) {
   1.313 +  var rect, win, width, height, left, top, needsOffset;
   1.314 +  // node can be either a window or an arbitrary DOM node
   1.315 +  try {
   1.316 +    // node is an arbitrary DOM node
   1.317 +    win = node.ownerDocument.defaultView;
   1.318 +    rect = node.getBoundingClientRect();
   1.319 +    width = rect.width;
   1.320 +    height = rect.height;
   1.321 +    top = rect.top;
   1.322 +    left = rect.left;
   1.323 +    // offset for highlights not needed as they will be relative to this node
   1.324 +    needsOffset = false;
   1.325 +  } catch (e) {
   1.326 +    // node is a window
   1.327 +    win = node;
   1.328 +    width = win.innerWidth;
   1.329 +    height = win.innerHeight;
   1.330 +    top = 0;
   1.331 +    left = 0;
   1.332 +    // offset needed for highlights to take 'outerHeight' of window into account
   1.333 +    needsOffset = true;
   1.334 +  }
   1.335 +
   1.336 +  var canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
   1.337 +  canvas.width = width;
   1.338 +  canvas.height = height;
   1.339 +
   1.340 +  var ctx = canvas.getContext("2d");
   1.341 +  // Draws the DOM contents of the window to the canvas
   1.342 +  ctx.drawWindow(win, left, top, width, height, "rgb(255,255,255)");
   1.343 +
   1.344 +  // This section is for drawing a red rectangle around each element passed in via the highlights array
   1.345 +  if (highlights) {
   1.346 +    ctx.lineWidth = "2";
   1.347 +    ctx.strokeStyle = "red";
   1.348 +    ctx.save();
   1.349 +
   1.350 +    for (var i = 0; i < highlights.length; ++i) {
   1.351 +      var elem = highlights[i];
   1.352 +      rect = elem.getBoundingClientRect();
   1.353 +
   1.354 +      var offsetY = 0, offsetX = 0;
   1.355 +      if (needsOffset) {
   1.356 +        var offset = getChromeOffset(elem);
   1.357 +        offsetX = offset.x;
   1.358 +        offsetY = offset.y;
   1.359 +      } else {
   1.360 +        // Don't need to offset the window chrome, just make relative to containing node
   1.361 +        offsetY = -top;
   1.362 +        offsetX = -left;
   1.363 +      }
   1.364 +
   1.365 +      // Draw the rectangle
   1.366 +      ctx.strokeRect(rect.left + offsetX, rect.top + offsetY, rect.width, rect.height);
   1.367 +    }
   1.368 +  }
   1.369 +
   1.370 +  return canvas.toDataURL("image/jpeg", 0.5);
   1.371 +}
   1.372 +
   1.373 +/**
   1.374 + * Save the dataURL content to the specified file. It will be stored in either the persisted screenshot or temporary folder.
   1.375 + *
   1.376 + * @param {String} aDataURL
   1.377 + *        The dataURL to save
   1.378 + * @param {String} aFilename
   1.379 + *        Target file name without extension
   1.380 + *
   1.381 + * @returns {Object} The hash containing the path of saved file, and the failure bit
   1.382 + */
   1.383 +function saveDataURL(aDataURL, aFilename) {
   1.384 +  var frame = {}; Cu.import('resource://mozmill/modules/frame.js', frame);
   1.385 +  const FILE_PERMISSIONS = parseInt("0644", 8);
   1.386 +
   1.387 +  var file;
   1.388 +  file = Cc['@mozilla.org/file/local;1']
   1.389 +         .createInstance(Ci.nsILocalFile);
   1.390 +  file.initWithPath(frame.persisted['screenshots']['path']);
   1.391 +  file.append(aFilename + ".jpg");
   1.392 +  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FILE_PERMISSIONS);
   1.393 +
   1.394 +  // Create an output stream to write to file
   1.395 +  let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
   1.396 +                 .createInstance(Ci.nsIFileOutputStream);
   1.397 +  foStream.init(file, 0x02 | 0x08 | 0x10, FILE_PERMISSIONS, foStream.DEFER_OPEN);
   1.398 +
   1.399 +  let dataURI = NetUtil.newURI(aDataURL, "UTF8", null);
   1.400 +  if (!dataURI.schemeIs("data")) {
   1.401 +    throw TypeError("aDataURL parameter has to have 'data'" +
   1.402 +                    " scheme instead of '" + dataURI.scheme + "'");
   1.403 +  }
   1.404 +
   1.405 +  // Write asynchronously to buffer;
   1.406 +  // Input and output streams are closed after write
   1.407 +
   1.408 +  let ready = false;
   1.409 +  let failure = false;
   1.410 +
   1.411 +  function sync(aStatus) {
   1.412 +    if (!Components.isSuccessCode(aStatus)) {
   1.413 +      failure = true;
   1.414 +    }
   1.415 +    ready = true;
   1.416 +  }
   1.417 +
   1.418 +  NetUtil.asyncFetch(dataURI, function (aInputStream, aAsyncFetchResult) {
   1.419 +    if (!Components.isSuccessCode(aAsyncFetchResult)) {
   1.420 +        // An error occurred!
   1.421 +        sync(aAsyncFetchResult);
   1.422 +    } else {
   1.423 +      // Consume the input stream.
   1.424 +      NetUtil.asyncCopy(aInputStream, foStream, function (aAsyncCopyResult) {
   1.425 +        sync(aAsyncCopyResult);
   1.426 +      });
   1.427 +    }
   1.428 +  });
   1.429 +
   1.430 +  assert.waitFor(function () {
   1.431 +    return ready;
   1.432 +  }, "DataURL has been saved to '" + file.path + "'");
   1.433 +
   1.434 +  return {filename: file.path, failure: failure};
   1.435 +}
   1.436 +
   1.437 +/**
   1.438 + * Some very brain-dead timer functions useful for performance optimizations
   1.439 + * This is only enabled in debug mode
   1.440 + *
   1.441 + **/
   1.442 +var gutility_mzmltimer = 0;
   1.443 +/**
   1.444 + * Starts timer initializing with current EPOC time in milliseconds
   1.445 + *
   1.446 + * @returns none
   1.447 + **/
   1.448 +function startTimer(){
   1.449 +  dump("TIMERCHECK:: starting now: " + Date.now() + "\n");
   1.450 +  gutility_mzmltimer = Date.now();
   1.451 +}
   1.452 +
   1.453 +/**
   1.454 + * Checks the timer and outputs current elapsed time since start of timer. It
   1.455 + * will print out a message you provide with its "time check" so you can
   1.456 + * correlate in the log file and figure out elapsed time of specific functions.
   1.457 + *
   1.458 + * @param aMsg    string The debug message to print with the timer check
   1.459 + *
   1.460 + * @returns none
   1.461 + **/
   1.462 +function checkTimer(aMsg){
   1.463 +  var end = Date.now();
   1.464 +  dump("TIMERCHECK:: at " + aMsg + " is: " + (end - gutility_mzmltimer) + "\n");
   1.465 +}

mercurial