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.

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

mercurial