forked from mirrors/gecko-dev
This mechanically replaces nsILocalFile with nsIFile in *.js, *.jsm, *.sjs, *.html, *.xul, *.xml, and *.py. MozReview-Commit-ID: 4ecl3RZhOwC --HG-- extra : rebase_source : 412880ea27766118c38498d021331a3df6bccc70
451 lines
12 KiB
JavaScript
451 lines
12 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
var EXPORTED_SYMBOLS = ["applicationName", "assert", "Copy", "getBrowserObject",
|
|
"getChromeWindow", "getWindows", "getWindowByTitle",
|
|
"getWindowByType", "getWindowId", "getMethodInWindows",
|
|
"getPreference", "saveDataURL", "setPreference",
|
|
"sleep", "startTimer", "stopTimer", "takeScreenshot",
|
|
"unwrapNode", "waitFor"
|
|
];
|
|
|
|
var Cc = Components.classes;
|
|
var Ci = Components.interfaces;
|
|
var Cu = Components.utils;
|
|
|
|
|
|
Cu.import("resource://gre/modules/NetUtil.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
const applicationIdMap = {
|
|
'{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'Firefox'
|
|
}
|
|
const applicationName = applicationIdMap[Services.appinfo.ID] || Services.appinfo.name;
|
|
|
|
var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
|
|
var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
|
|
var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
|
|
|
|
var assert = new assertions.Assert();
|
|
|
|
var hwindow = Services.appShell.hiddenDOMWindow;
|
|
|
|
var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
|
|
|
function Copy (obj) {
|
|
for (var n in obj) {
|
|
this[n] = obj[n];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the browser object of the specified window
|
|
*
|
|
* @param {Window} aWindow
|
|
* Window to get the browser element from.
|
|
*
|
|
* @returns {Object} The browser element
|
|
*/
|
|
function getBrowserObject(aWindow) {
|
|
return aWindow.gBrowser;
|
|
}
|
|
|
|
function getChromeWindow(aWindow) {
|
|
var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
|
.rootTreeItem
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindow)
|
|
.QueryInterface(Ci.nsIDOMChromeWindow);
|
|
|
|
return chromeWin;
|
|
}
|
|
|
|
function getWindows(type) {
|
|
if (type == undefined) {
|
|
type = "";
|
|
}
|
|
|
|
var windows = [];
|
|
var enumerator = Services.wm.getEnumerator(type);
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
windows.push(enumerator.getNext());
|
|
}
|
|
|
|
if (type == "") {
|
|
windows.push(hwindow);
|
|
}
|
|
|
|
return windows;
|
|
}
|
|
|
|
function getMethodInWindows(methodName) {
|
|
for (var w of getWindows()) {
|
|
if (w[methodName] != undefined) {
|
|
return w[methodName];
|
|
}
|
|
}
|
|
|
|
throw new Error("Method with name: '" + methodName + "' is not in any open window.");
|
|
}
|
|
|
|
function getWindowByTitle(title) {
|
|
for (var w of getWindows()) {
|
|
if (w.document.title && w.document.title == title) {
|
|
return w;
|
|
}
|
|
}
|
|
|
|
throw new Error("Window with title: '" + title + "' not found.");
|
|
}
|
|
|
|
function getWindowByType(type) {
|
|
return Services.wm.getMostRecentWindow(type);
|
|
}
|
|
|
|
/**
|
|
* Retrieve the outer window id for the given window.
|
|
*
|
|
* @param {Number} aWindow
|
|
* Window to retrieve the id from.
|
|
* @returns {Boolean} The outer window id
|
|
**/
|
|
function getWindowId(aWindow) {
|
|
try {
|
|
// Normally we can retrieve the id via window utils
|
|
return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
getInterface(Ci.nsIDOMWindowUtils).
|
|
outerWindowID;
|
|
} catch (e) {
|
|
// ... but for observer notifications we need another interface
|
|
return aWindow.QueryInterface(Ci.nsISupportsPRUint64).data;
|
|
}
|
|
}
|
|
|
|
var checkChrome = function () {
|
|
var loc = window.document.location.href;
|
|
try {
|
|
loc = window.top.document.location.href;
|
|
} catch (e) {
|
|
}
|
|
|
|
return /^chrome:\/\//.test(loc);
|
|
}
|
|
|
|
/**
|
|
* Called to get the state of an individual preference.
|
|
*
|
|
* @param aPrefName string The preference to get the state of.
|
|
* @param aDefaultValue any The default value if preference was not found.
|
|
*
|
|
* @returns any The value of the requested preference
|
|
*
|
|
* @see setPref
|
|
* Code by Henrik Skupin: <hskupin@gmail.com>
|
|
*/
|
|
function getPreference(aPrefName, aDefaultValue) {
|
|
try {
|
|
var branch = Services.prefs;
|
|
|
|
switch (typeof aDefaultValue) {
|
|
case ('boolean'):
|
|
return branch.getBoolPref(aPrefName);
|
|
case ('string'):
|
|
return branch.getCharPref(aPrefName);
|
|
case ('number'):
|
|
return branch.getIntPref(aPrefName);
|
|
default:
|
|
return branch.getComplexValue(aPrefName);
|
|
}
|
|
} catch (e) {
|
|
return aDefaultValue;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called to set the state of an individual preference.
|
|
*
|
|
* @param aPrefName string The preference to set the state of.
|
|
* @param aValue any The value to set the preference to.
|
|
*
|
|
* @returns boolean Returns true if value was successfully set.
|
|
*
|
|
* @see getPref
|
|
* Code by Henrik Skupin: <hskupin@gmail.com>
|
|
*/
|
|
function setPreference(aName, aValue) {
|
|
try {
|
|
var branch = Services.prefs;
|
|
|
|
switch (typeof aValue) {
|
|
case ('boolean'):
|
|
branch.setBoolPref(aName, aValue);
|
|
break;
|
|
case ('string'):
|
|
branch.setCharPref(aName, aValue);
|
|
break;
|
|
case ('number'):
|
|
branch.setIntPref(aName, aValue);
|
|
break;
|
|
default:
|
|
branch.setComplexValue(aName, aValue);
|
|
}
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sleep for the given amount of milliseconds
|
|
*
|
|
* @param {number} milliseconds
|
|
* Sleeps the given number of milliseconds
|
|
*/
|
|
function sleep(milliseconds) {
|
|
var timeup = false;
|
|
|
|
hwindow.setTimeout(function () { timeup = true; }, milliseconds);
|
|
Services.tm.spinEventLoopUntil(() => timeup);
|
|
|
|
broker.pass({'function':'utils.sleep()'});
|
|
}
|
|
|
|
/**
|
|
* Check if the callback function evaluates to true
|
|
*/
|
|
function assert(callback, message, thisObject) {
|
|
var result = callback.call(thisObject);
|
|
|
|
if (!result) {
|
|
throw new Error(message || arguments.callee.name + ": Failed for '" + callback + "'");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unwraps a node which is wrapped into a XPCNativeWrapper or XrayWrapper
|
|
*
|
|
* @param {DOMnode} Wrapped DOM node
|
|
* @returns {DOMNode} Unwrapped DOM node
|
|
*/
|
|
function unwrapNode(aNode) {
|
|
var node = aNode;
|
|
if (node) {
|
|
// unwrap is not available on older branches (3.5 and 3.6) - Bug 533596
|
|
if ("unwrap" in XPCNativeWrapper) {
|
|
node = XPCNativeWrapper.unwrap(node);
|
|
}
|
|
else if (node.wrappedJSObject != null) {
|
|
node = node.wrappedJSObject;
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Waits for the callback evaluates to true
|
|
*/
|
|
function waitFor(callback, message, timeout, interval, thisObject) {
|
|
broker.log({'function': 'utils.waitFor() - DEPRECATED',
|
|
'message': 'utils.waitFor() is deprecated. Use assert.waitFor() instead'});
|
|
assert.waitFor(callback, message, timeout, interval, thisObject);
|
|
}
|
|
|
|
/**
|
|
* Calculates the x and y chrome offset for an element
|
|
* See https://developer.mozilla.org/en/DOM/window.innerHeight
|
|
*
|
|
* Note this function will not work if the user has custom toolbars (via extension) at the bottom or left/right of the screen
|
|
*/
|
|
function getChromeOffset(elem) {
|
|
var win = elem.ownerGlobal;
|
|
// Calculate x offset
|
|
var chromeWidth = 0;
|
|
|
|
if (win["name"] != "sidebar") {
|
|
chromeWidth = win.outerWidth - win.innerWidth;
|
|
}
|
|
|
|
// Calculate y offset
|
|
var chromeHeight = win.outerHeight - win.innerHeight;
|
|
// chromeHeight == 0 means elem is already in the chrome and doesn't need the addonbar offset
|
|
if (chromeHeight > 0) {
|
|
// window.innerHeight doesn't include the addon or find bar, so account for these if present
|
|
var addonbar = win.document.getElementById("addon-bar");
|
|
if (addonbar) {
|
|
chromeHeight -= addonbar.scrollHeight;
|
|
}
|
|
|
|
var findbar = win.document.getElementById("FindToolbar");
|
|
if (findbar) {
|
|
chromeHeight -= findbar.scrollHeight;
|
|
}
|
|
}
|
|
|
|
return {'x':chromeWidth, 'y':chromeHeight};
|
|
}
|
|
|
|
/**
|
|
* Takes a screenshot of the specified DOM node
|
|
*/
|
|
function takeScreenshot(node, highlights) {
|
|
var rect, win, width, height, left, top, needsOffset;
|
|
// node can be either a window or an arbitrary DOM node
|
|
try {
|
|
// node is an arbitrary DOM node
|
|
win = node.ownerGlobal;
|
|
rect = node.getBoundingClientRect();
|
|
width = rect.width;
|
|
height = rect.height;
|
|
top = rect.top;
|
|
left = rect.left;
|
|
// offset for highlights not needed as they will be relative to this node
|
|
needsOffset = false;
|
|
} catch (e) {
|
|
// node is a window
|
|
win = node;
|
|
width = win.innerWidth;
|
|
height = win.innerHeight;
|
|
top = 0;
|
|
left = 0;
|
|
// offset needed for highlights to take 'outerHeight' of window into account
|
|
needsOffset = true;
|
|
}
|
|
|
|
var canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
|
|
var ctx = canvas.getContext("2d");
|
|
// Draws the DOM contents of the window to the canvas
|
|
ctx.drawWindow(win, left, top, width, height, "rgb(255,255,255)");
|
|
|
|
// This section is for drawing a red rectangle around each element passed in via the highlights array
|
|
if (highlights) {
|
|
ctx.lineWidth = "2";
|
|
ctx.strokeStyle = "red";
|
|
ctx.save();
|
|
|
|
for (var i = 0; i < highlights.length; ++i) {
|
|
var elem = highlights[i];
|
|
rect = elem.getBoundingClientRect();
|
|
|
|
var offsetY = 0, offsetX = 0;
|
|
if (needsOffset) {
|
|
var offset = getChromeOffset(elem);
|
|
offsetX = offset.x;
|
|
offsetY = offset.y;
|
|
} else {
|
|
// Don't need to offset the window chrome, just make relative to containing node
|
|
offsetY = -top;
|
|
offsetX = -left;
|
|
}
|
|
|
|
// Draw the rectangle
|
|
ctx.strokeRect(rect.left + offsetX, rect.top + offsetY, rect.width, rect.height);
|
|
}
|
|
}
|
|
|
|
return canvas.toDataURL("image/jpeg", 0.5);
|
|
}
|
|
|
|
/**
|
|
* Save the dataURL content to the specified file. It will be stored in either the persisted screenshot or temporary folder.
|
|
*
|
|
* @param {String} aDataURL
|
|
* The dataURL to save
|
|
* @param {String} aFilename
|
|
* Target file name without extension
|
|
*
|
|
* @returns {Object} The hash containing the path of saved file, and the failure bit
|
|
*/
|
|
function saveDataURL(aDataURL, aFilename) {
|
|
var frame = {}; Cu.import('resource://mozmill/modules/frame.js', frame);
|
|
const FILE_PERMISSIONS = parseInt("0644", 8);
|
|
|
|
var file;
|
|
file = Cc['@mozilla.org/file/local;1']
|
|
.createInstance(Ci.nsIFile);
|
|
file.initWithPath(frame.persisted['screenshots']['path']);
|
|
file.append(aFilename + ".jpg");
|
|
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FILE_PERMISSIONS);
|
|
|
|
// Create an output stream to write to file
|
|
let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
|
|
.createInstance(Ci.nsIFileOutputStream);
|
|
foStream.init(file, 0x02 | 0x08 | 0x10, FILE_PERMISSIONS, foStream.DEFER_OPEN);
|
|
|
|
let dataURI = NetUtil.newURI(aDataURL, "UTF8");
|
|
if (!dataURI.schemeIs("data")) {
|
|
throw TypeError("aDataURL parameter has to have 'data'" +
|
|
" scheme instead of '" + dataURI.scheme + "'");
|
|
}
|
|
|
|
// Write asynchronously to buffer;
|
|
// Input and output streams are closed after write
|
|
|
|
let ready = false;
|
|
let failure = false;
|
|
|
|
function sync(aStatus) {
|
|
if (!Components.isSuccessCode(aStatus)) {
|
|
failure = true;
|
|
}
|
|
ready = true;
|
|
}
|
|
|
|
NetUtil.asyncFetch(dataURI, function (aInputStream, aAsyncFetchResult) {
|
|
if (!Components.isSuccessCode(aAsyncFetchResult)) {
|
|
// An error occurred!
|
|
sync(aAsyncFetchResult);
|
|
} else {
|
|
// Consume the input stream.
|
|
NetUtil.asyncCopy(aInputStream, foStream, function (aAsyncCopyResult) {
|
|
sync(aAsyncCopyResult);
|
|
});
|
|
}
|
|
});
|
|
|
|
assert.waitFor(function () {
|
|
return ready;
|
|
}, "DataURL has been saved to '" + file.path + "'");
|
|
|
|
return {filename: file.path, failure: failure};
|
|
}
|
|
|
|
/**
|
|
* Some very brain-dead timer functions useful for performance optimizations
|
|
* This is only enabled in debug mode
|
|
*
|
|
**/
|
|
var gutility_mzmltimer = 0;
|
|
/**
|
|
* Starts timer initializing with current EPOC time in milliseconds
|
|
*
|
|
* @returns none
|
|
**/
|
|
function startTimer(){
|
|
dump("TIMERCHECK:: starting now: " + Date.now() + "\n");
|
|
gutility_mzmltimer = Date.now();
|
|
}
|
|
|
|
/**
|
|
* Checks the timer and outputs current elapsed time since start of timer. It
|
|
* will print out a message you provide with its "time check" so you can
|
|
* correlate in the log file and figure out elapsed time of specific functions.
|
|
*
|
|
* @param aMsg string The debug message to print with the timer check
|
|
*
|
|
* @returns none
|
|
**/
|
|
function checkTimer(aMsg){
|
|
var end = Date.now();
|
|
dump("TIMERCHECK:: at " + aMsg + " is: " + (end - gutility_mzmltimer) + "\n");
|
|
}
|