Bug 1415957 - Remove MozMill from TPS. r=tcsc

MozReview-Commit-ID: HyBXrNqhzIf

--HG--
extra : rebase_source : da197fa045f70621d5549e961876cfc6ce15264b
This commit is contained in:
Edouard Oger 2017-11-09 15:34:06 -05:00
Родитель 1acca47aff
Коммит 1873a289f7
34 изменённых файлов: 2 добавлений и 13431 удалений

Просмотреть файл

@ -334,7 +334,6 @@ testing/xpcshell/node-http2/**
# Third party services
services/common/kinto-http-client.js
services/common/kinto-offline-client.js
services/sync/tps/extensions/mozmill
# toolkit/ exclusions

Просмотреть файл

@ -6,11 +6,6 @@ module.exports = {
],
globals: {
// Globals specific to mozmill
"assert": false,
"controller": false,
"findElement": false,
"mozmill": false,
// Injected into tests via tps.jsm
"Addons": false,
"Bookmarks": false,
@ -22,7 +17,6 @@ module.exports = {
"Passwords": false,
"Phase": false,
"Prefs": false,
"RunMozmillTest": false,
"STATE_DISABLED": false,
"STATE_ENABLED": false,
"Sync": false,

Просмотреть файл

@ -1,30 +0,0 @@
/* 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/. */
Components.utils.import("resource://tps/tps.jsm");
var setupModule = function(module) {
module.controller = mozmill.getBrowserController();
assert.ok(true, "SetupModule passes");
};
var setupTest = function(module) {
assert.ok(true, "SetupTest passes");
};
var testTestStep = function() {
assert.ok(true, "test Passes");
controller.open("http://www.mozilla.org");
TPS.Login();
TPS.Sync(ACTIONS.ACTION_SYNC_WIPE_CLIENT);
};
var teardownTest = function() {
assert.ok(true, "teardownTest passes");
};
var teardownModule = function() {
assert.ok(true, "teardownModule passes");
};

Просмотреть файл

@ -1,15 +0,0 @@
/* 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 setupModule = function(module) {
module.controller = mozmill.getBrowserController();
};
var testGetNode = function() {
controller.open("about:support");
controller.waitForPageLoad();
var appbox = findElement.ID(controller.tabs.activeTab, "application-box");
assert.waitFor(() => appbox.getNode().textContent == "Firefox", "correct app name");
};

Просмотреть файл

@ -1,24 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* The list of phases mapped to their corresponding profiles. The object
* here must be in strict JSON format, as it will get parsed by the Python
* testrunner (no single quotes, extra comma's, etc).
*/
var phases = { "phase1": "profile1",
"phase2": "profile2" };
/*
* Test phases
*/
Phase("phase1", [
[RunMozmillTest, "mozmill_sanity.js"],
]);
Phase("phase2", [
[Sync],
[RunMozmillTest, "mozmill_sanity2.js"],
]);

Просмотреть файл

@ -166,7 +166,7 @@ add_task(async function test_repairs_skip_if_cant_vaidate() {
};
let requestor = {
async startRepairs(validationInfo, flowID) {
assert.ok(false, "Never should start repairs");
ok(false, "Never should start repairs");
},
tryServerOnlyRepairs() {
return false;

Просмотреть файл

@ -1,2 +0,0 @@
resource mozmill resource/

Просмотреть файл

@ -1,25 +0,0 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>mozmill@mozilla.com</em:id>
<em:name>Mozmill</em:name>
<em:version>2.0.8</em:version>
<em:description>UI Automation tool for Mozilla applications</em:description>
<em:unpack>true</em:unpack>
<em:creator>Mozilla Automation and Testing Team</em:creator>
<em:contributor>Adam Christian</em:contributor>
<em:contributor>Mikeal Rogers</em:contributor>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>10.0</em:minVersion>
<em:maxVersion>38.*</em:maxVersion>
</Description>
</em:targetApplication>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
</Description>
</RDF>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,537 +0,0 @@
/* 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 = ["ID", "Link", "XPath", "Selector", "Name", "Anon", "AnonXPath",
"Lookup", "_byID", "_byName", "_byAttrib", "_byAnonAttrib",
];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var strings = {}; Cu.import('resource://mozmill/stdlib/strings.js', strings);
var arrays = {}; Cu.import('resource://mozmill/stdlib/arrays.js', arrays);
var json2 = {}; Cu.import('resource://mozmill/stdlib/json2.js', json2);
var withs = {}; Cu.import('resource://mozmill/stdlib/withs.js', withs);
var dom = {}; Cu.import('resource://mozmill/stdlib/dom.js', dom);
var objects = {}; Cu.import('resource://mozmill/stdlib/objects.js', objects);
var countQuotes = function (str) {
var count = 0;
var i = 0;
while (i < str.length) {
i = str.indexOf('"', i);
if (i != -1) {
count++;
i++;
} else {
break;
}
}
return count;
};
/**
* smartSplit()
*
* Takes a lookup string as input and returns
* a list of each node in the string
*/
var smartSplit = function (str) {
// Ensure we have an even number of quotes
if (countQuotes(str) % 2 != 0) {
throw new Error ("Invalid Lookup Expression");
}
/**
* This regex matches a single "node" in a lookup string.
* In otherwords, it matches the part between the two '/'s
*
* Regex Explanation:
* \/ - start matching at the first forward slash
* ([^\/"]*"[^"]*")* - match as many pairs of quotes as possible until we hit a slash (ignore slashes inside quotes)
* [^\/]* - match the remainder of text outside of last quote but before next slash
*/
var re = /\/([^\/"]*"[^"]*")*[^\/]*/g
var ret = []
var match = re.exec(str);
while (match != null) {
ret.push(match[0].replace(/^\//, ""));
match = re.exec(str);
}
return ret;
};
/**
* defaultDocuments()
*
* Returns a list of default documents in which to search for elements
* if no document is provided
*/
function defaultDocuments() {
var win = Services.wm.getMostRecentWindow("navigator:browser");
return [
win.document,
utils.getBrowserObject(win).selectedBrowser.contentWindow.document
];
};
/**
* nodeSearch()
*
* Takes an optional document, callback and locator string
* Returns a handle to the located element or null
*/
function nodeSearch(doc, func, string) {
if (doc != undefined) {
var documents = [doc];
} else {
var documents = defaultDocuments();
}
var e = null;
var element = null;
//inline function to recursively find the element in the DOM, cross frame.
var search = function (win, func, string) {
if (win == null) {
return;
}
//do the lookup in the current window
element = func.call(win, string);
if (!element || (element.length == 0)) {
var frames = win.frames;
for (var i = 0; i < frames.length; i++) {
search(frames[i], func, string);
}
} else {
e = element;
}
};
for (var i = 0; i < documents.length; ++i) {
var win = documents[i].defaultView;
search(win, func, string);
if (e) {
break;
}
}
return e;
};
/**
* Selector()
*
* Finds an element by selector string
*/
function Selector(_document, selector, index) {
if (selector == undefined) {
throw new Error('Selector constructor did not recieve enough arguments.');
}
this.selector = selector;
this.getNodeForDocument = function (s) {
return this.document.querySelectorAll(s);
};
var nodes = nodeSearch(_document, this.getNodeForDocument, this.selector);
return nodes ? nodes[index || 0] : null;
};
/**
* ID()
*
* Finds an element by ID
*/
function ID(_document, nodeID) {
if (nodeID == undefined) {
throw new Error('ID constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (nodeID) {
return this.document.getElementById(nodeID);
};
return nodeSearch(_document, this.getNodeForDocument, nodeID);
};
/**
* Link()
*
* Finds a link by innerHTML
*/
function Link(_document, linkName) {
if (linkName == undefined) {
throw new Error('Link constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (linkName) {
var getText = function (el) {
var text = "";
if (el.nodeType == 3) { //textNode
if (el.data != undefined) {
text = el.data;
} else {
text = el.innerHTML;
}
text = text.replace(/n|r|t/g, " ");
}
else if (el.nodeType == 1) { //elementNode
for (var i = 0; i < el.childNodes.length; i++) {
var child = el.childNodes.item(i);
text += getText(child);
}
if (el.tagName == "P" || el.tagName == "BR" ||
el.tagName == "HR" || el.tagName == "DIV") {
text += "\n";
}
}
return text;
};
//sometimes the windows won't have this function
try {
var links = this.document.getElementsByTagName('a');
} catch (e) {
// ADD LOG LINE mresults.write('Error: '+ e, 'lightred');
}
for (var i = 0; i < links.length; i++) {
var el = links[i];
//if (getText(el).indexOf(this.linkName) != -1) {
if (el.innerHTML.indexOf(linkName) != -1) {
return el;
}
}
return null;
};
return nodeSearch(_document, this.getNodeForDocument, linkName);
};
/**
* XPath()
*
* Finds an element by XPath
*/
function XPath(_document, expr) {
if (expr == undefined) {
throw new Error('XPath constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (s) {
var aNode = this.document;
var aExpr = s;
var xpe = null;
if (this.document.defaultView == null) {
xpe = new getMethodInWindows('XPathEvaluator')();
} else {
xpe = new this.document.defaultView.XPathEvaluator();
}
var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ? aNode.documentElement
: aNode.ownerDocument.documentElement);
var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
var found = [];
var res;
while (res = result.iterateNext()) {
found.push(res);
}
return found[0];
};
return nodeSearch(_document, this.getNodeForDocument, expr);
};
/**
* Name()
*
* Finds an element by Name
*/
function Name(_document, nName) {
if (nName == undefined) {
throw new Error('Name constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (s) {
try{
var els = this.document.getElementsByName(s);
if (els.length > 0) {
return els[0];
}
} catch (e) {
}
return null;
};
return nodeSearch(_document, this.getNodeForDocument, nName);
};
var _returnResult = function (results) {
if (results.length == 0) {
return null
}
else if (results.length == 1) {
return results[0];
} else {
return results;
}
}
var _forChildren = function (element, name, value) {
var results = [];
var nodes = Array.from(element.childNodes).filter(e => e);
for (var i in nodes) {
var n = nodes[i];
if (n[name] == value) {
results.push(n);
}
}
return results;
}
var _forAnonChildren = function (_document, element, name, value) {
var results = [];
var nodes = Array.from(_document.getAnoymousNodes(element)).filter(e => e);
for (var i in nodes ) {
var n = nodes[i];
if (n[name] == value) {
results.push(n);
}
}
return results;
}
var _byID = function (_document, parent, value) {
return _returnResult(_forChildren(parent, 'id', value));
}
var _byName = function (_document, parent, value) {
return _returnResult(_forChildren(parent, 'tagName', value));
}
var _byAttrib = function (parent, attributes) {
var results = [];
var nodes = parent.childNodes;
for (var i in nodes) {
var n = nodes[i];
let requirementPass = 0;
let requirementLength = 0;
for (var a in attributes) {
requirementLength++;
try {
if (n.getAttribute(a) == attributes[a]) {
requirementPass++;
}
} catch (e) {
// Workaround any bugs in custom attribute crap in XUL elements
}
}
if (requirementPass == requirementLength) {
results.push(n);
}
}
return _returnResult(results)
}
var _byAnonAttrib = function (_document, parent, attributes) {
var results = [];
if (objects.getLength(attributes) == 1) {
for (var i in attributes) {
var k = i;
var v = attributes[i];
}
var result = _document.getAnonymousElementByAttribute(parent, k, v);
if (result) {
return result;
}
}
var nodes = Array.from(_document.getAnonymousNodes(parent)).filter(n => n.getAttribute);
function resultsForNodes (nodes) {
for (var i in nodes) {
var n = nodes[i];
requirementPass = 0;
requirementLength = 0;
for (var a in attributes) {
requirementLength++;
if (n.getAttribute(a) == attributes[a]) {
requirementPass++;
}
}
if (requirementPass == requirementLength) {
results.push(n);
}
}
}
resultsForNodes(nodes);
if (results.length == 0) {
resultsForNodes(Array.from(parent.childNodes).filter(n => n != undefined && n.getAttribute));
}
return _returnResult(results)
}
var _byIndex = function (_document, parent, i) {
if (parent instanceof Array) {
return parent[i];
}
return parent.childNodes[i];
}
var _anonByName = function (_document, parent, value) {
return _returnResult(_forAnonChildren(_document, parent, 'tagName', value));
}
var _anonByAttrib = function (_document, parent, value) {
return _byAnonAttrib(_document, parent, value);
}
var _anonByIndex = function (_document, parent, i) {
return _document.getAnonymousNodes(parent)[i];
}
/**
* Lookup()
*
* Finds an element by Lookup expression
*/
function Lookup(_document, expression) {
if (expression == undefined) {
throw new Error('Lookup constructor did not recieve enough arguments.');
}
var expSplit = smartSplit(expression).filter(e => e != '');
expSplit.unshift(_document);
var nCases = {'id':_byID, 'name':_byName, 'attrib':_byAttrib, 'index':_byIndex};
var aCases = {'name':_anonByName, 'attrib':_anonByAttrib, 'index':_anonByIndex};
/**
* Reduces the lookup expression
* @param {Object} parentNode
* Parent node (previousValue of the formerly executed reduce callback)
* @param {String} exp
* Lookup expression for the parents child node
*
* @returns {Object} Node found by the given expression
*/
var reduceLookup = function (parentNode, exp) {
// Abort in case the parent node was not found
if (!parentNode) {
return false;
}
// Handle case where only index is provided
var cases = nCases;
// Handle ending index before any of the expression gets mangled
if (withs.endsWith(exp, ']')) {
var expIndex = json2.JSON.parse(strings.vslice(exp, '[', ']'));
}
// Handle anon
if (withs.startsWith(exp, 'anon')) {
exp = strings.vslice(exp, '(', ')');
cases = aCases;
}
if (withs.startsWith(exp, '[')) {
try {
var obj = json2.JSON.parse(strings.vslice(exp, '[', ']'));
} catch (e) {
throw new SyntaxError(e + '. String to be parsed was || ' +
strings.vslice(exp, '[', ']') + ' ||');
}
var r = cases['index'](_document, parentNode, obj);
if (r == null) {
throw new SyntaxError('Expression "' + exp +
'" returned null. Anonymous == ' + (cases == aCases));
}
return r;
}
for (var c in cases) {
if (withs.startsWith(exp, c)) {
try {
var obj = json2.JSON.parse(strings.vslice(exp, '(', ')'))
} catch (e) {
throw new SyntaxError(e + '. String to be parsed was || ' +
strings.vslice(exp, '(', ')') + ' ||');
}
var result = cases[c](_document, parentNode, obj);
}
}
if (!result) {
if (withs.startsWith(exp, '{')) {
try {
var obj = json2.JSON.parse(exp);
} catch (e) {
throw new SyntaxError(e + '. String to be parsed was || ' + exp + ' ||');
}
if (cases == aCases) {
var result = _anonByAttrib(_document, parentNode, obj);
} else {
var result = _byAttrib(parentNode, obj);
}
}
}
// Final return
if (expIndex) {
// TODO: Check length and raise error
return result[expIndex];
} else {
// TODO: Check length and raise error
return result;
}
// Maybe we should cause an exception here
return false;
};
return expSplit.reduce(reduceLookup);
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,283 +0,0 @@
/* 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 = ["controller", "utils", "elementslib", "os",
"getBrowserController", "newBrowserController",
"getAddonsController", "getPreferencesController",
"newMail3PaneController", "getMail3PaneController",
"wm", "platform", "getAddrbkController",
"getMsgComposeController", "getDownloadsController",
"Application", "findElement",
"getPlacesController", 'isMac', 'isLinux', 'isWindows',
"firePythonCallback", "getAddons"
];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// imports
var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
var controller = {}; Cu.import('resource://mozmill/driver/controller.js', controller);
var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib);
var findElement = {}; Cu.import('resource://mozmill/driver/mozelement.js', findElement);
var os = {}; Cu.import('resource://mozmill/stdlib/os.js', os);
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var windows = {}; Cu.import('resource://mozmill/modules/windows.js', windows);
const DEBUG = false;
// This is a useful "check" timer. See utils.js, good for debugging
if (DEBUG) {
utils.startTimer();
}
var assert = new assertions.Assert();
// platform information
var platform = os.getPlatform();
var isMac = false;
var isWindows = false;
var isLinux = false;
if (platform == "darwin"){
isMac = true;
}
if (platform == "winnt"){
isWindows = true;
}
if (platform == "linux"){
isLinux = true;
}
var wm = Services.wm;
var appInfo = Services.appinfo;
var Application = utils.applicationName;
/**
* Retrieves the list with information about installed add-ons.
*
* @returns {String} JSON data of installed add-ons
*/
function getAddons() {
var addons = null;
AddonManager.getAllAddons(function (addonList) {
var tmp_list = [ ];
addonList.forEach(function (addon) {
var tmp = { };
// We have to filter out properties of type 'function' of the addon
// object, which will break JSON.stringify() and result in incomplete
// addon information.
for (var key in addon) {
if (typeof(addon[key]) !== "function") {
tmp[key] = addon[key];
}
}
tmp_list.push(tmp);
});
addons = tmp_list;
});
try {
// Sychronize with getAllAddons so we do not return too early
assert.waitFor(function () {
return !!addons;
})
return addons;
} catch (e) {
return null;
}
}
/**
* Retrieves application details for the Mozmill report
*
* @return {String} JSON data of application details
*/
function getApplicationDetails() {
var locale = Services.locale.getAppLocaleAsLangTag();
// Put all our necessary information into JSON and return it:
// appinfo, startupinfo, and addons
var details = {
application_id: appInfo.ID,
application_name: Application,
application_version: appInfo.version,
application_locale: locale,
platform_buildid: appInfo.platformBuildID,
platform_version: appInfo.platformVersion,
addons: getAddons(),
startupinfo: getStartupInfo(),
paths: {
appdata: Services.dirsvc.get('UAppData', Ci.nsIFile).path,
profile: Services.dirsvc.get('ProfD', Ci.nsIFile).path
}
};
return JSON.stringify(details);
}
// get startup time if available
// see http://blog.mozilla.com/tglek/2011/04/26/measuring-startup-speed-correctly/
function getStartupInfo() {
var startupInfo = {};
try {
var _startupInfo = Services.startup.getStartupInfo();
for (var time in _startupInfo) {
// convert from Date object to ms since epoch
startupInfo[time] = _startupInfo[time].getTime();
}
} catch (e) {
startupInfo = null;
}
return startupInfo;
}
function newBrowserController () {
return new controller.MozMillController(utils.getMethodInWindows('OpenBrowserWindow')());
}
function getBrowserController () {
var browserWindow = wm.getMostRecentWindow("navigator:browser");
if (browserWindow == null) {
return newBrowserController();
} else {
return new controller.MozMillController(browserWindow);
}
}
function getPlacesController () {
utils.getMethodInWindows('PlacesCommandHook').showPlacesOrganizer('AllBookmarks');
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
function getAddonsController () {
if (Application == 'SeaMonkey') {
utils.getMethodInWindows('toEM')();
}
else if (Application == 'Thunderbird') {
utils.getMethodInWindows('openAddonsMgr')();
}
else if (Application == 'Sunbird') {
utils.getMethodInWindows('goOpenAddons')();
} else {
utils.getMethodInWindows('BrowserOpenAddonsMgr')();
}
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
function getDownloadsController() {
utils.getMethodInWindows('BrowserDownloadsUI')();
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
function getPreferencesController() {
if (Application == 'Thunderbird') {
utils.getMethodInWindows('openOptionsDialog')();
} else {
utils.getMethodInWindows('openPreferences')();
}
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
// Thunderbird functions
function newMail3PaneController () {
return new controller.MozMillController(utils.getMethodInWindows('toMessengerWindow')());
}
function getMail3PaneController () {
var mail3PaneWindow = wm.getMostRecentWindow("mail:3pane");
if (mail3PaneWindow == null) {
return newMail3PaneController();
} else {
return new controller.MozMillController(mail3PaneWindow);
}
}
// Thunderbird - Address book window
function newAddrbkController () {
utils.getMethodInWindows("toAddressBook")();
utils.sleep(2000);
var addyWin = wm.getMostRecentWindow("mail:addressbook");
return new controller.MozMillController(addyWin);
}
function getAddrbkController () {
var addrbkWindow = wm.getMostRecentWindow("mail:addressbook");
if (addrbkWindow == null) {
return newAddrbkController();
} else {
return new controller.MozMillController(addrbkWindow);
}
}
function firePythonCallback (filename, method, args, kwargs) {
let obj = {'filename': filename, 'method': method};
obj['args'] = args || [];
obj['kwargs'] = kwargs || {};
broker.sendMessage("firePythonCallback", obj);
}
function timer (name) {
this.name = name;
this.timers = {};
this.actions = [];
frame.timers.push(this);
}
timer.prototype.start = function (name) {
this.timers[name].startTime = (new Date).getTime();
}
timer.prototype.stop = function (name) {
var t = this.timers[name];
t.endTime = (new Date).getTime();
t.totalTime = (t.endTime - t.startTime);
}
timer.prototype.end = function () {
frame.events.fireEvent("timer", this);
frame.timers.remove(this);
}
// Initialization
/**
* Initialize Mozmill
*/
function initialize() {
windows.init();
}
initialize();

Просмотреть файл

@ -1,58 +0,0 @@
/* 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 = ['addListener', 'addObject',
'removeListener',
'sendMessage', 'log', 'pass', 'fail'];
var listeners = {};
// add a listener for a specific message type
function addListener(msgType, listener) {
if (listeners[msgType] === undefined) {
listeners[msgType] = [];
}
listeners[msgType].push(listener);
}
// add each method in an object as a message listener
function addObject(object) {
for (var msgType in object) {
addListener(msgType, object[msgType]);
}
}
// remove a listener for all message types
function removeListener(listener) {
for (var msgType in listeners) {
for (let i = 0; i < listeners.length; ++i) {
if (listeners[msgType][i] == listener) {
listeners[msgType].splice(i, 1); // remove listener from array
}
}
}
}
function sendMessage(msgType, obj) {
if (listeners[msgType] === undefined) {
return;
}
for (let i = 0; i < listeners[msgType].length; ++i) {
listeners[msgType][i](obj);
}
}
function log(obj) {
sendMessage('log', obj);
}
function pass(obj) {
sendMessage('pass', obj);
}
function fail(obj) {
sendMessage('fail', obj);
}

Просмотреть файл

@ -1,672 +0,0 @@
/* 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 = ['Assert', 'Expect'];
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
var stack = {}; Cu.import('resource://mozmill/modules/stack.js', stack);
/**
* @name assertions
* @namespace Defines expect and assert methods to be used for assertions.
*/
/**
* The Assert class implements fatal assertions, and can be used in cases
* when a failing test has to directly abort the current test function. All
* remaining tasks will not be performed.
*
*/
var Assert = function () {}
Assert.prototype = {
// The following deepEquals implementation is from Narwhal under this license:
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
_deepEqual: function (actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (actual instanceof Date && expected instanceof Date) {
return actual.getTime() === expected.getTime();
// 7.3. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (typeof actual != 'object' && typeof expected != 'object') {
return actual == expected;
// 7.4. For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return this._objEquiv(actual, expected);
}
},
_objEquiv: function (a, b) {
if (a == null || a == undefined || b == null || b == undefined)
return false;
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
function isArguments(object) {
return Object.prototype.toString.call(object) == '[object Arguments]';
}
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return this._deepEqual(a, b);
}
try {
var ka = Object.keys(a),
kb = Object.keys(b),
key, i;
} catch (e) {//happens when one is a string literal and the other isn't
return false;
}
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
return false;
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
return false;
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!this._deepEqual(a[key], b[key])) return false;
}
return true;
},
_expectedException : function Assert__expectedException(actual, expected) {
if (!actual || !expected) {
return false;
}
if (expected instanceof RegExp) {
return expected.test(actual);
} else if (actual instanceof expected) {
return true;
} else if (expected.call({}, actual) === true) {
return true;
} else if (actual.name === expected.name) {
return true;
}
return false;
},
/**
* Log a test as failing by throwing an AssertionException.
*
* @param {object} aResult
* Test result details used for reporting.
* <dl>
* <dd>fileName</dd>
* <dt>Name of the file in which the assertion failed.</dt>
* <dd>functionName</dd>
* <dt>Function in which the assertion failed.</dt>
* <dd>lineNumber</dd>
* <dt>Line number of the file in which the assertion failed.</dt>
* <dd>message</dd>
* <dt>Message why the assertion failed.</dt>
* </dl>
* @throws {errors.AssertionError}
*
*/
_logFail: function Assert__logFail(aResult) {
throw new errors.AssertionError(aResult.message,
aResult.fileName,
aResult.lineNumber,
aResult.functionName,
aResult.name);
},
/**
* Log a test as passing by adding a pass frame.
*
* @param {object} aResult
* Test result details used for reporting.
* <dl>
* <dd>fileName</dd>
* <dt>Name of the file in which the assertion failed.</dt>
* <dd>functionName</dd>
* <dt>Function in which the assertion failed.</dt>
* <dd>lineNumber</dd>
* <dt>Line number of the file in which the assertion failed.</dt>
* <dd>message</dd>
* <dt>Message why the assertion failed.</dt>
* </dl>
*/
_logPass: function Assert__logPass(aResult) {
broker.pass({pass: aResult});
},
/**
* Test the condition and mark test as passed or failed
*
* @param {boolean} aCondition
* Condition to test.
* @param {string} aMessage
* Message to show for the test result
* @param {string} aDiagnosis
* Diagnose message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
_test: function Assert__test(aCondition, aMessage, aDiagnosis) {
let diagnosis = aDiagnosis || "";
let message = aMessage || "";
if (diagnosis)
message = aMessage ? message + " - " + diagnosis : diagnosis;
// Build result data
let frame = stack.findCallerFrame(Components.stack);
let result = {
'fileName' : frame.filename.replace(/(.*)-> /, ""),
'functionName' : frame.name,
'lineNumber' : frame.lineNumber,
'message' : message
};
// Log test result
if (aCondition) {
this._logPass(result);
}
else {
result.stack = Components.stack;
this._logFail(result);
}
return aCondition;
},
/**
* Perform an always passing test
*
* @param {string} aMessage
* Message to show for the test result.
* @returns {boolean} Always returns true.
*/
pass: function Assert_pass(aMessage) {
return this._test(true, aMessage, undefined);
},
/**
* Perform an always failing test
*
* @param {string} aMessage
* Message to show for the test result.
* @throws {errors.AssertionError}
*
* @returns {boolean} Always returns false.
*/
fail: function Assert_fail(aMessage) {
return this._test(false, aMessage, undefined);
},
/**
* Test if the value pass
*
* @param {boolean|string|number|object} aValue
* Value to test.
* @param {string} aMessage
* Message to show for the test result.
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
ok: function Assert_ok(aValue, aMessage) {
let condition = !!aValue;
let diagnosis = "got '" + aValue + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if both specified values are identical.
*
* @param {boolean|string|number|object} aValue
* Value to test.
* @param {boolean|string|number|object} aExpected
* Value to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
equal: function Assert_equal(aValue, aExpected, aMessage) {
let condition = (aValue === aExpected);
let diagnosis = "'" + aValue + "' should equal '" + aExpected + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if both specified values are not identical.
*
* @param {boolean|string|number|object} aValue
* Value to test.
* @param {boolean|string|number|object} aExpected
* Value to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
notEqual: function Assert_notEqual(aValue, aExpected, aMessage) {
let condition = (aValue !== aExpected);
let diagnosis = "'" + aValue + "' should not equal '" + aExpected + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if an object equals another object
*
* @param {object} aValue
* The object to test.
* @param {object} aExpected
* The object to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
deepEqual: function equal(aValue, aExpected, aMessage) {
let condition = this._deepEqual(aValue, aExpected);
try {
var aValueString = JSON.stringify(aValue);
} catch (e) {
var aValueString = String(aValue);
}
try {
var aExpectedString = JSON.stringify(aExpected);
} catch (e) {
var aExpectedString = String(aExpected);
}
let diagnosis = "'" + aValueString + "' should equal '" +
aExpectedString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if an object does not equal another object
*
* @param {object} aValue
* The object to test.
* @param {object} aExpected
* The object to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
notDeepEqual: function notEqual(aValue, aExpected, aMessage) {
let condition = !this._deepEqual(aValue, aExpected);
try {
var aValueString = JSON.stringify(aValue);
} catch (e) {
var aValueString = String(aValue);
}
try {
var aExpectedString = JSON.stringify(aExpected);
} catch (e) {
var aExpectedString = String(aExpected);
}
let diagnosis = "'" + aValueString + "' should not equal '" +
aExpectedString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if the regular expression matches the string.
*
* @param {string} aString
* String to test.
* @param {RegEx} aRegex
* Regular expression to use for testing that a match exists.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
match: function Assert_match(aString, aRegex, aMessage) {
// XXX Bug 634948
// Regex objects are transformed to strings when evaluated in a sandbox
// For now lets re-create the regex from its string representation
let pattern = "";
let flags = "";
try {
let matches = aRegex.toString().match(/\/(.*)\/(.*)/);
pattern = matches[1];
flags = matches[2];
} catch (e) {
}
let regex = new RegExp(pattern, flags);
let condition = (aString.match(regex) !== null);
let diagnosis = "'" + regex + "' matches for '" + aString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if the regular expression does not match the string.
*
* @param {string} aString
* String to test.
* @param {RegEx} aRegex
* Regular expression to use for testing that a match does not exist.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
notMatch: function Assert_notMatch(aString, aRegex, aMessage) {
// XXX Bug 634948
// Regex objects are transformed to strings when evaluated in a sandbox
// For now lets re-create the regex from its string representation
let pattern = flags = "";
try {
let matches = aRegex.toString().match(/\/(.*)\/(.*)/);
pattern = matches[1];
flags = matches[2];
} catch (e) {
}
let regex = new RegExp(pattern, flags);
let condition = (aString.match(regex) === null);
let diagnosis = "'" + regex + "' doesn't match for '" + aString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if a code block throws an exception.
*
* @param {string} block
* function to call to test for exception
* @param {RegEx} error
* the expected error class
* @param {string} message
* message to present if assertion fails
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
throws : function Assert_throws(block, /*optional*/error, /*optional*/message) {
return this._throws.apply(this, [true].concat(Array.prototype.slice.call(arguments)));
},
/**
* Test if a code block doesn't throw an exception.
*
* @param {string} block
* function to call to test for exception
* @param {RegEx} error
* the expected error class
* @param {string} message
* message to present if assertion fails
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
doesNotThrow : function Assert_doesNotThrow(block, /*optional*/error, /*optional*/message) {
return this._throws.apply(this, [false].concat(Array.prototype.slice.call(arguments)));
},
/* Tests whether a code block throws the expected exception
class. helper for throws() and doesNotThrow()
adapted from node.js's assert._throws()
https://github.com/joyent/node/blob/master/lib/assert.js
*/
_throws : function Assert__throws(shouldThrow, block, expected, message) {
var actual;
if (typeof expected === 'string') {
message = expected;
expected = null;
}
try {
block();
} catch (e) {
actual = e;
}
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
(message ? ' ' + message : '.');
if (shouldThrow && !actual) {
return this._test(false, message, 'Missing expected exception');
}
if (!shouldThrow && this._expectedException(actual, expected)) {
return this._test(false, message, 'Got unwanted exception');
}
if ((shouldThrow && actual && expected &&
!this._expectedException(actual, expected)) || (!shouldThrow && actual)) {
throw actual;
}
return this._test(true, message);
},
/**
* Test if the string contains the pattern.
*
* @param {String} aString String to test.
* @param {String} aPattern Pattern to look for in the string
* @param {String} aMessage Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {Boolean} Result of the test.
*/
contain: function Assert_contain(aString, aPattern, aMessage) {
let condition = (aString.indexOf(aPattern) !== -1);
let diagnosis = "'" + aString + "' should contain '" + aPattern + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if the string does not contain the pattern.
*
* @param {String} aString String to test.
* @param {String} aPattern Pattern to look for in the string
* @param {String} aMessage Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {Boolean} Result of the test.
*/
notContain: function Assert_notContain(aString, aPattern, aMessage) {
let condition = (aString.indexOf(aPattern) === -1);
let diagnosis = "'" + aString + "' should not contain '" + aPattern + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Waits for the callback evaluates to true
*
* @param {Function} aCallback
* Callback for evaluation
* @param {String} aMessage
* Message to show for result
* @param {Number} aTimeout
* Timeout in waiting for evaluation
* @param {Number} aInterval
* Interval between evaluation attempts
* @param {Object} aThisObject
* this object
* @throws {errors.AssertionError}
*
* @returns {Boolean} Result of the test.
*/
waitFor: function Assert_waitFor(aCallback, aMessage, aTimeout, aInterval, aThisObject) {
var timeout = aTimeout || 5000;
var interval = aInterval || 100;
var self = {
timeIsUp: false,
result: aCallback.call(aThisObject)
};
var deadline = Date.now() + timeout;
function wait() {
if (self.result !== true) {
self.result = aCallback.call(aThisObject);
self.timeIsUp = Date.now() > deadline;
}
}
var hwindow = Services.appShell.hiddenDOMWindow;
var timeoutInterval = hwindow.setInterval(wait, interval);
var thread = Services.tm.currentThread;
Services.tm.spinEventLoopUntil(() => {
let type = typeof(self.result);
if (type !== 'boolean') {
throw TypeError("waitFor() callback has to return a boolean" +
" instead of '" + type + "'");
}
return self.result === true || self.timeIsUp;
});
hwindow.clearInterval(timeoutInterval);
if (self.result !== true && self.timeIsUp) {
aMessage = aMessage || arguments.callee.name + ": Timeout exceeded for '" + aCallback + "'";
throw new errors.TimeoutError(aMessage);
}
broker.pass({'function':'assert.waitFor()'});
return true;
}
}
/* non-fatal assertions */
var Expect = function () {}
Expect.prototype = new Assert();
/**
* Log a test as failing by adding a fail frame.
*
* @param {object} aResult
* Test result details used for reporting.
* <dl>
* <dd>fileName</dd>
* <dt>Name of the file in which the assertion failed.</dt>
* <dd>functionName</dd>
* <dt>Function in which the assertion failed.</dt>
* <dd>lineNumber</dd>
* <dt>Line number of the file in which the assertion failed.</dt>
* <dd>message</dd>
* <dt>Message why the assertion failed.</dt>
* </dl>
*/
Expect.prototype._logFail = function Expect__logFail(aResult) {
broker.fail({fail: aResult});
}
/**
* Waits for the callback evaluates to true
*
* @param {Function} aCallback
* Callback for evaluation
* @param {String} aMessage
* Message to show for result
* @param {Number} aTimeout
* Timeout in waiting for evaluation
* @param {Number} aInterval
* Interval between evaluation attempts
* @param {Object} aThisObject
* this object
*/
Expect.prototype.waitFor = function Expect_waitFor(aCallback, aMessage, aTimeout, aInterval, aThisObject) {
let condition = true;
let message = aMessage;
try {
Assert.prototype.waitFor.apply(this, arguments);
}
catch (ex) {
if (!(ex instanceof errors.AssertionError)) {
throw ex;
}
message = ex.message;
condition = false;
}
return this._test(condition, message);
}

Просмотреть файл

@ -1,290 +0,0 @@
/* 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/. */
/**
* @namespace Defines the Mozmill driver for global actions
*/
var driver = exports;
Cu.import("resource://gre/modules/Services.jsm");
// Temporarily include utils module to re-use sleep
var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
var mozmill = {}; Cu.import("resource://mozmill/driver/mozmill.js", mozmill);
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
/**
* Gets the topmost browser window. If there are none at that time, optionally
* opens one. Otherwise will raise an exception if none are found.
*
* @memberOf driver
* @param {Boolean] [aOpenIfNone=true] Open a new browser window if none are found.
* @returns {DOMWindow}
*/
function getBrowserWindow(aOpenIfNone) {
// Set default
if (typeof aOpenIfNone === 'undefined') {
aOpenIfNone = true;
}
// If implicit open is off, turn on strict checking, and vice versa.
let win = getTopmostWindowByType("navigator:browser", !aOpenIfNone);
// Can just assume automatic open here. If we didn't want it and nothing found,
// we already raised above when getTopmostWindow was called.
if (!win)
win = openBrowserWindow();
return win;
}
/**
* Retrieves the hidden window on OS X
*
* @memberOf driver
* @returns {DOMWindow} The hidden window
*/
function getHiddenWindow() {
return Services.appShell.hiddenDOMWindow;
}
/**
* Opens a new browser window
*
* @memberOf driver
* @returns {DOMWindow}
*/
function openBrowserWindow() {
// On OS X we have to be able to create a new browser window even with no other
// window open. Therefore we have to use the hidden window. On other platforms
// at least one remaining browser window has to exist.
var win = mozmill.isMac ? getHiddenWindow() :
getTopmostWindowByType("navigator:browser", true);
return win.OpenBrowserWindow();
}
/**
* Pause the test execution for the given amount of time
*
* @type utils.sleep
* @memberOf driver
*/
var sleep = utils.sleep;
/**
* Wait until the given condition via the callback returns true.
*
* @type utils.waitFor
* @memberOf driver
*/
var waitFor = assertions.Assert.waitFor;
//
// INTERNAL WINDOW ENUMERATIONS
//
/**
* Internal function to build a list of DOM windows using a given enumerator
* and filter.
*
* @private
* @memberOf driver
* @param {nsISimpleEnumerator} aEnumerator Window enumerator to use.
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow[]} The windows found, in the same order as the enumerator.
*/
function _getWindows(aEnumerator, aFilterCallback, aStrict) {
// Set default
if (typeof aStrict === 'undefined')
aStrict = true;
let windows = [];
while (aEnumerator.hasMoreElements()) {
let window = aEnumerator.getNext();
if (!aFilterCallback || aFilterCallback(window)) {
windows.push(window);
}
}
// If this list is empty and we're strict, throw an error
if (windows.length === 0 && aStrict) {
var message = 'No windows were found';
// We'll throw a more detailed error if a filter was used.
if (aFilterCallback && aFilterCallback.name)
message += ' using filter "' + aFilterCallback.name + '"';
throw new Error(message);
}
return windows;
}
//
// FILTER CALLBACKS
//
/**
* Generator of a closure to filter a window based by a method
*
* @memberOf driver
* @param {String} aName Name of the method in the window object.
* @returns {Boolean} True if the condition is met.
*/
function windowFilterByMethod(aName) {
return function byMethod(aWindow) { return (aName in aWindow); }
}
/**
* Generator of a closure to filter a window based by the its title
*
* @param {String} aTitle Title of the window.
* @returns {Boolean} True if the condition is met.
*/
function windowFilterByTitle(aTitle) {
return function byTitle(aWindow) { return (aWindow.document.title === aTitle); }
}
/**
* Generator of a closure to filter a window based by the its type
*
* @memberOf driver
* @param {String} aType Type of the window.
* @returns {Boolean} True if the condition is met.
*/
function windowFilterByType(aType) {
return function byType(aWindow) {
var type = aWindow.document.documentElement.getAttribute("windowtype");
return (type === aType);
}
}
//
// WINDOW LIST RETRIEVAL FUNCTIONS
//
/**
* Retrieves a sorted list of open windows based on their age (newest to oldest),
* optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow[]} List of windows.
*/
function getWindowsByAge(aFilterCallback, aStrict) {
var windows = _getWindows(Services.wm.getEnumerator(""),
aFilterCallback, aStrict);
// Reverse the list, since naturally comes back old->new
return windows.reverse();
}
/**
* Retrieves a sorted list of open windows based on their z order (topmost first),
* optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow[]} List of windows.
*/
function getWindowsByZOrder(aFilterCallback, aStrict) {
return _getWindows(Services.wm.getZOrderDOMWindowEnumerator("", true),
aFilterCallback, aStrict);
}
//
// SINGLE WINDOW RETRIEVAL FUNCTIONS
//
/**
* Retrieves the last opened window, optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] If true, throws error if no window found.
*
* @returns {DOMWindow} The window, or null if none found and aStrict == false
*/
function getNewestWindow(aFilterCallback, aStrict) {
var windows = getWindowsByAge(aFilterCallback, aStrict);
return windows.length ? windows[0] : null;
}
/**
* Retrieves the topmost window, optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] If true, throws error if no window found.
*
* @returns {DOMWindow} The window, or null if none found and aStrict == false
*/
function getTopmostWindow(aFilterCallback, aStrict) {
var windows = getWindowsByZOrder(aFilterCallback, aStrict);
return windows.length ? windows[0] : null;
}
/**
* Retrieves the topmost window given by the window type
*
* XXX: Bug 462222
* This function has to be used instead of getTopmostWindow until the
* underlying platform bug has been fixed.
*
* @memberOf driver
* @param {String} [aWindowType=null] Window type to query for
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow} The window, or null if none found and aStrict == false
*/
function getTopmostWindowByType(aWindowType, aStrict) {
if (typeof aStrict === 'undefined')
aStrict = true;
var win = Services.wm.getMostRecentWindow(aWindowType);
if (win === null && aStrict) {
var message = 'No windows of type "' + aWindowType + '" were found';
throw new errors.UnexpectedError(message);
}
return win;
}
// Export of functions
driver.getBrowserWindow = getBrowserWindow;
driver.getHiddenWindow = getHiddenWindow;
driver.openBrowserWindow = openBrowserWindow;
driver.sleep = sleep;
driver.waitFor = waitFor;
driver.windowFilterByMethod = windowFilterByMethod;
driver.windowFilterByTitle = windowFilterByTitle;
driver.windowFilterByType = windowFilterByType;
driver.getWindowsByAge = getWindowsByAge;
driver.getNewestWindow = getNewestWindow;
driver.getTopmostWindowByType = getTopmostWindowByType;
// XXX Bug: 462222
// Currently those functions cannot be used. So they shouldn't be exported.
//driver.getWindowsByZOrder = getWindowsByZOrder;
//driver.getTopmostWindow = getTopmostWindow;

Просмотреть файл

@ -1,102 +0,0 @@
/* 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 = ['BaseError',
'ApplicationQuitError',
'AssertionError',
'TimeoutError'];
/**
* Creates a new instance of a base error
*
* @class Represents the base for custom errors
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function BaseError(aMessage, aFileName, aLineNumber, aFunctionName) {
this.name = this.constructor.name;
var err = new Error();
if (err.stack) {
this.stack = err.stack;
}
this.message = aMessage || err.message;
this.fileName = aFileName || err.fileName;
this.lineNumber = aLineNumber || err.lineNumber;
this.functionName = aFunctionName;
}
/**
* Creates a new instance of an application quit error used by Mozmill to
* indicate that the application is going to shutdown
*
* @class Represents an error object thrown when the application is going to shutdown
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function ApplicationQuitError(aMessage, aFileName, aLineNumber, aFunctionName) {
BaseError.apply(this, arguments);
}
ApplicationQuitError.prototype = Object.create(BaseError.prototype, {
constructor : { value : ApplicationQuitError }
});
/**
* Creates a new instance of an assertion error
*
* @class Represents an error object thrown by failing assertions
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function AssertionError(aMessage, aFileName, aLineNumber, aFunctionName) {
BaseError.apply(this, arguments);
}
AssertionError.prototype = Object.create(BaseError.prototype, {
constructor : { value : AssertionError }
});
/**
* Creates a new instance of a timeout error
*
* @class Represents an error object thrown by failing assertions
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function TimeoutError(aMessage, aFileName, aLineNumber, aFunctionName) {
AssertionError.apply(this, arguments);
}
TimeoutError.prototype = Object.create(AssertionError.prototype, {
constructor : { value : TimeoutError }
});

Просмотреть файл

@ -1,787 +0,0 @@
/* 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 = ['Collector','Runner','events', 'runTestFile', 'log',
'timers', 'persisted', 'shutdownApplication'];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
const TIMEOUT_SHUTDOWN_HTTPD = 15000;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import('resource://mozmill/stdlib/httpd.js');
var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
var os = {}; Cu.import('resource://mozmill/stdlib/os.js', os);
var strings = {}; Cu.import('resource://mozmill/stdlib/strings.js', strings);
var arrays = {}; Cu.import('resource://mozmill/stdlib/arrays.js', arrays);
var withs = {}; Cu.import('resource://mozmill/stdlib/withs.js', withs);
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var securableModule = {};
Cu.import('resource://mozmill/stdlib/securable-module.js', securableModule);
var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
var httpd = null;
var persisted = {};
var assert = new assertions.Assert();
var expect = new assertions.Expect();
var mozmill = undefined;
var mozelement = undefined;
var modules = undefined;
var timers = [];
/**
* Shutdown or restart the application
*
* @param {boolean} [aFlags=undefined]
* Additional flags how to handle the shutdown or restart.
* @see https://developer.mozilla.org/nsIAppStartup#Attributes
*/
function shutdownApplication(aFlags) {
var flags = Ci.nsIAppStartup.eForceQuit;
if (aFlags) {
flags |= aFlags;
}
// Send a request to shutdown the application. That will allow us and other
// components to finish up with any shutdown code. Please note that we don't
// care if other components or add-ons want to prevent this via cancelQuit,
// we really force the shutdown.
let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
createInstance(Components.interfaces.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested");
// Use a timer to trigger the application restart, which will allow us to
// send an ACK packet via jsbridge if the method has been called via Python.
var event = {
notify: function(timer) {
Services.startup.quit(flags);
}
}
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(event, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
function stateChangeBase(possibilties, restrictions, target, cmeta, v) {
if (possibilties) {
if (!arrays.inArray(possibilties, v)) {
// TODO Error value not in this.poss
return;
}
}
if (restrictions) {
for (var i in restrictions) {
var r = restrictions[i];
if (!r(v)) {
// TODO error value did not pass restriction
return;
}
}
}
// Fire jsbridge notification, logging notification, listener notifications
events[target] = v;
events.fireEvent(cmeta, target);
}
var events = {
appQuit : false,
currentModule : null,
currentState : null,
currentTest : null,
shutdownRequested : false,
userShutdown : null,
userShutdownTimer : null,
listeners : {},
globalListeners : []
}
events.setState = function (v) {
return stateChangeBase(['dependencies', 'setupModule', 'teardownModule',
'test', 'setupTest', 'teardownTest', 'collection'],
null, 'currentState', 'setState', v);
}
events.toggleUserShutdown = function (obj){
if (!this.userShutdown) {
this.userShutdown = obj;
var event = {
notify: function(timer) {
events.toggleUserShutdown(obj);
}
}
this.userShutdownTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.userShutdownTimer.initWithCallback(event, obj.timeout, Ci.nsITimer.TYPE_ONE_SHOT);
} else {
this.userShutdownTimer.cancel();
// If the application is not going to shutdown, the user shutdown failed and
// we have to force a shutdown.
if (!events.appQuit) {
this.fail({'function':'events.toggleUserShutdown',
'message':'Shutdown expected but none detected before timeout',
'userShutdown': obj});
var flags = Ci.nsIAppStartup.eAttemptQuit;
if (events.isRestartShutdown()) {
flags |= Ci.nsIAppStartup.eRestart;
}
shutdownApplication(flags);
}
}
}
events.isUserShutdown = function () {
return this.userShutdown ? this.userShutdown["user"] : false;
}
events.isRestartShutdown = function () {
return this.userShutdown.restart;
}
events.startShutdown = function (obj) {
events.fireEvent('shutdown', obj);
if (obj["user"]) {
events.toggleUserShutdown(obj);
} else {
shutdownApplication(obj.flags);
}
}
events.setTest = function (test) {
test.__start__ = Date.now();
test.__passes__ = [];
test.__fails__ = [];
events.currentTest = test;
var obj = {'filename': events.currentModule.__file__,
'name': test.__name__}
events.fireEvent('setTest', obj);
}
events.endTest = function (test) {
// use the current test unless specified
if (test === undefined) {
test = events.currentTest;
}
// If no test is set it has already been reported. Beside that we don't want
// to report it a second time.
if (!test || test.status === 'done')
return;
// report the end of a test
test.__end__ = Date.now();
test.status = 'done';
var obj = {'filename': events.currentModule.__file__,
'passed': test.__passes__.length,
'failed': test.__fails__.length,
'passes': test.__passes__,
'fails' : test.__fails__,
'name' : test.__name__,
'time_start': test.__start__,
'time_end': test.__end__}
if (test.skipped) {
obj['skipped'] = true;
obj.skipped_reason = test.skipped_reason;
}
if (test.meta) {
obj.meta = test.meta;
}
// Report the test result only if the test is a true test or if it is failing
if (withs.startsWith(test.__name__, "test") || test.__fails__.length > 0) {
events.fireEvent('endTest', obj);
}
}
events.setModule = function (aModule) {
aModule.__start__ = Date.now();
aModule.__status__ = 'running';
var result = stateChangeBase(null,
[function (aModule) {return (aModule.__file__ != undefined)}],
'currentModule', 'setModule', aModule);
return result;
}
events.endModule = function (aModule) {
// It should only reported once, so check if it already has been done
if (aModule.__status__ === 'done')
return;
aModule.__end__ = Date.now();
aModule.__status__ = 'done';
var obj = {
'filename': aModule.__file__,
'time_start': aModule.__start__,
'time_end': aModule.__end__
}
events.fireEvent('endModule', obj);
}
events.pass = function (obj) {
// a low level event, such as a keystroke, succeeds
if (events.currentTest) {
events.currentTest.__passes__.push(obj);
}
for (var timer of timers) {
timer.actions.push(
{"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__,
"obj": obj,
"result": "pass"}
);
}
events.fireEvent('pass', obj);
}
events.fail = function (obj) {
var error = obj.exception;
if (error) {
// Error objects aren't enumerable https://bugzilla.mozilla.org/show_bug.cgi?id=637207
obj.exception = {
name: error.name,
message: error.message,
lineNumber: error.lineNumber,
fileName: error.fileName,
stack: error.stack
};
}
// a low level event, such as a keystroke, fails
if (events.currentTest) {
events.currentTest.__fails__.push(obj);
}
for (var timer of timers) {
timer.actions.push(
{"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__,
"obj": obj,
"result": "fail"}
);
}
events.fireEvent('fail', obj);
}
events.skip = function (reason) {
// this is used to report skips associated with setupModule and nothing else
events.currentTest.skipped = true;
events.currentTest.skipped_reason = reason;
for (var timer of timers) {
timer.actions.push(
{"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__,
"obj": reason,
"result": "skip"}
);
}
events.fireEvent('skip', reason);
}
events.fireEvent = function (name, obj) {
if (events.appQuit) {
// dump('* Event discarded: ' + name + ' ' + JSON.stringify(obj) + '\n');
return;
}
if (this.listeners[name]) {
for (var i in this.listeners[name]) {
this.listeners[name][i](obj);
}
}
for (var listener of this.globalListeners) {
listener(name, obj);
}
}
events.addListener = function (name, listener) {
if (this.listeners[name]) {
this.listeners[name].push(listener);
} else if (name == '') {
this.globalListeners.push(listener)
} else {
this.listeners[name] = [listener];
}
}
events.removeListener = function (listener) {
for (var listenerIndex in this.listeners) {
var e = this.listeners[listenerIndex];
for (var i in e){
if (e[i] == listener) {
this.listeners[listenerIndex] = arrays.remove(e, i);
}
}
}
for (var i in this.globalListeners) {
if (this.globalListeners[i] == listener) {
this.globalListeners = arrays.remove(this.globalListeners, i);
}
}
}
events.persist = function () {
try {
events.fireEvent('persist', persisted);
} catch (e) {
events.fireEvent('error', "persist serialization failed.")
}
}
events.firePythonCallback = function (obj) {
obj['test'] = events.currentModule.__file__;
events.fireEvent('firePythonCallback', obj);
}
events.screenshot = function (obj) {
// Find the name of the test function
for (var attr in events.currentModule) {
if (events.currentModule[attr] == events.currentTest) {
var testName = attr;
break;
}
}
obj['test_file'] = events.currentModule.__file__;
obj['test_name'] = testName;
events.fireEvent('screenshot', obj);
}
var log = function (obj) {
events.fireEvent('log', obj);
}
// Register the listeners
broker.addObject({'endTest': events.endTest,
'fail': events.fail,
'firePythonCallback': events.firePythonCallback,
'log': log,
'pass': events.pass,
'persist': events.persist,
'screenshot': events.screenshot,
'shutdown': events.startShutdown,
});
try {
Cu.import('resource://jsbridge/modules/Events.jsm');
events.addListener('', function (name, obj) {
Events.fireEvent('mozmill.' + name, obj);
});
} catch (e) {
Services.console.logStringMessage("Event module of JSBridge not available.");
}
/**
* Observer for notifications when the application is going to shutdown
*/
function AppQuitObserver() {
this.runner = null;
Services.obs.addObserver(this, "quit-application-requested");
}
AppQuitObserver.prototype = {
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "quit-application-requested":
Services.obs.removeObserver(this, "quit-application-requested");
// If we observe a quit notification make sure to send the
// results of the current test. In those cases we don't reach
// the equivalent code in runTestModule()
events.pass({'message': 'AppQuitObserver: ' + JSON.stringify(aData),
'userShutdown': events.userShutdown});
if (this.runner) {
this.runner.end();
}
if (httpd) {
httpd.stop();
}
events.appQuit = true;
break;
}
}
}
var appQuitObserver = new AppQuitObserver();
/**
* The collector handles HTTPd.js and initilizing the module
*/
function Collector() {
this.test_modules_by_filename = {};
this.testing = [];
}
Collector.prototype.addHttpResource = function (aDirectory, aPath) {
var fp = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
fp.initWithPath(os.abspath(aDirectory, this.current_file));
return httpd.addHttpResource(fp, aPath);
}
Collector.prototype.initTestModule = function (filename, testname) {
var test_module = this.loadFile(filename, this);
var has_restarted = !(testname == null);
test_module.__tests__ = [];
for (var i in test_module) {
if (typeof(test_module[i]) == "function") {
test_module[i].__name__ = i;
// Only run setupModule if we are a single test OR if we are the first
// test of a restart chain (don't run it prior to members in a restart
// chain)
if (i == "setupModule" && !has_restarted) {
test_module.__setupModule__ = test_module[i];
} else if (i == "setupTest") {
test_module.__setupTest__ = test_module[i];
} else if (i == "teardownTest") {
test_module.__teardownTest__ = test_module[i];
} else if (i == "teardownModule") {
test_module.__teardownModule__ = test_module[i];
} else if (withs.startsWith(i, "test")) {
if (testname && (i != testname)) {
continue;
}
testname = null;
test_module.__tests__.push(test_module[i]);
}
}
}
test_module.collector = this;
test_module.status = 'loaded';
this.test_modules_by_filename[filename] = test_module;
return test_module;
}
Collector.prototype.loadFile = function (path, collector) {
var moduleLoader = new securableModule.Loader({
rootPaths: ["resource://mozmill/modules/"],
defaultPrincipal: "system",
globals : { Cc: Cc,
Ci: Ci,
Cu: Cu,
Cr: Components.results}
});
// load a test module from a file and add some candy
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
var uri = Services.io.newFileURI(file).spec;
this.loadTestResources();
var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
var module = new Components.utils.Sandbox(systemPrincipal);
module.assert = assert;
module.Cc = Cc;
module.Ci = Ci;
module.Cr = Components.results;
module.Cu = Cu;
module.collector = collector;
module.driver = moduleLoader.require("driver");
module.elementslib = mozelement;
module.errors = errors;
module.expect = expect;
module.findElement = mozelement;
module.log = log;
module.mozmill = mozmill;
module.persisted = persisted;
module.require = function (mod) {
var loader = new securableModule.Loader({
rootPaths: [Services.io.newFileURI(file.parent).spec,
"resource://mozmill/modules/"],
defaultPrincipal: "system",
globals : { assert: assert,
expect: expect,
mozmill: mozmill,
elementslib: mozelement, // This a quick hack to maintain backwards compatibility with 1.5.x
findElement: mozelement,
persisted: persisted,
Cc: Cc,
Ci: Ci,
Cu: Cu,
log: log }
});
if (modules != undefined) {
loader.modules = modules;
}
var retval = loader.require(mod);
modules = loader.modules;
return retval;
}
if (collector != undefined) {
collector.current_file = file;
collector.current_path = path;
}
try {
Services.scriptloader.loadSubScript(uri, module, "UTF-8");
} catch (e) {
var obj = {
'filename': path,
'passed': 0,
'failed': 1,
'passes': [],
'fails' : [{'exception' : {
message: e.message,
filename: e.filename,
lineNumber: e.lineNumber}}],
'name' :'<TOP_LEVEL>'
};
events.fail({'exception': e});
events.fireEvent('endTest', obj);
}
module.__file__ = path;
module.__uri__ = uri;
return module;
}
Collector.prototype.loadTestResources = function () {
// load resources we want in our tests
if (mozmill === undefined) {
mozmill = {};
Cu.import("resource://mozmill/driver/mozmill.js", mozmill);
}
if (mozelement === undefined) {
mozelement = {};
Cu.import("resource://mozmill/driver/mozelement.js", mozelement);
}
}
/**
*
*/
function Httpd(aPort) {
this.http_port = aPort;
while (true) {
try {
var srv = new HttpServer();
srv.registerContentType("sjs", "sjs");
srv.identity.setPrimary("http", "localhost", this.http_port);
srv.start(this.http_port);
this._httpd = srv;
break;
}
catch (e) {
// Failure most likely due to port conflict
this.http_port++;
}
}
}
Httpd.prototype.addHttpResource = function (aDir, aPath) {
var path = aPath ? ("/" + aPath + "/") : "/";
try {
this._httpd.registerDirectory(path, aDir);
return 'http://localhost:' + this.http_port + path;
}
catch (e) {
throw Error("Failure to register directory: " + aDir.path);
}
};
Httpd.prototype.stop = function () {
if (!this._httpd) {
return;
}
var shutdown = false;
this._httpd.stop(function () { shutdown = true; });
assert.waitFor(function () {
return shutdown;
}, "Local HTTP server has been stopped", TIMEOUT_SHUTDOWN_HTTPD);
this._httpd = null;
};
function startHTTPd() {
if (!httpd) {
// Ensure that we start the HTTP server only once during a session
httpd = new Httpd(43336);
}
}
function Runner() {
this.collector = new Collector();
this.ended = false;
var m = {}; Cu.import('resource://mozmill/driver/mozmill.js', m);
this.platform = m.platform;
events.fireEvent('startRunner', true);
}
Runner.prototype.end = function () {
if (!this.ended) {
this.ended = true;
appQuitObserver.runner = null;
events.endTest();
events.endModule(events.currentModule);
events.fireEvent('endRunner', true);
events.persist();
}
};
Runner.prototype.runTestFile = function (filename, name) {
var module = this.collector.initTestModule(filename, name);
this.runTestModule(module);
};
Runner.prototype.runTestModule = function (module) {
appQuitObserver.runner = this;
events.setModule(module);
// If setupModule passes, run all the tests. Otherwise mark them as skipped.
if (this.execFunction(module.__setupModule__, module)) {
for (var test of module.__tests__) {
if (events.shutdownRequested) {
break;
}
// If setupTest passes, run the test. Otherwise mark it as skipped.
if (this.execFunction(module.__setupTest__, module)) {
this.execFunction(test);
} else {
this.skipFunction(test, module.__setupTest__.__name__ + " failed");
}
this.execFunction(module.__teardownTest__, module);
}
} else {
for (var test of module.__tests__) {
this.skipFunction(test, module.__setupModule__.__name__ + " failed");
}
}
this.execFunction(module.__teardownModule__, module);
events.endModule(module);
};
Runner.prototype.execFunction = function (func, arg) {
if (typeof func !== "function" || events.shutdownRequested) {
return true;
}
var isTest = withs.startsWith(func.__name__, "test");
events.setState(isTest ? "test" : func.__name);
events.setTest(func);
// skip excluded platforms
if (func.EXCLUDED_PLATFORMS != undefined) {
if (arrays.inArray(func.EXCLUDED_PLATFORMS, this.platform)) {
events.skip("Platform exclusion");
events.endTest(func);
return false;
}
}
// skip function if requested
if (func.__force_skip__ != undefined) {
events.skip(func.__force_skip__);
events.endTest(func);
return false;
}
// execute the test function
try {
func(arg);
} catch (e) {
if (e instanceof errors.ApplicationQuitError) {
events.shutdownRequested = true;
} else {
events.fail({'exception': e, 'test': func})
}
}
// If a user shutdown has been requested and the function already returned,
// we can assume that a shutdown will not happen anymore. We should force a
// shutdown then, to prevent the next test from being executed.
if (events.isUserShutdown()) {
events.shutdownRequested = true;
events.toggleUserShutdown(events.userShutdown);
}
events.endTest(func);
return events.currentTest.__fails__.length == 0;
};
function runTestFile(filename, name) {
var runner = new Runner();
runner.runTestFile(filename, name);
runner.end();
return true;
}
Runner.prototype.skipFunction = function (func, message) {
events.setTest(func);
events.skip(message);
events.endTest(func);
};

Просмотреть файл

@ -1,71 +0,0 @@
/* 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/. */
/**
* @namespace Defines useful methods to work with localized content
*/
var l10n = exports;
Cu.import("resource://gre/modules/Services.jsm");
/**
* Retrieve the localized content for a given DTD entity
*
* @memberOf l10n
* @param {String[]} aDTDs Array of URLs for DTD files.
* @param {String} aEntityId ID of the entity to get the localized content of.
*
* @returns {String} Localized content
*/
function getEntity(aDTDs, aEntityId) {
// Add xhtml11.dtd to prevent missing entity errors with XHTML files
aDTDs.push("resource:///res/dtd/xhtml11.dtd");
// Build a string of external entities
var references = "";
for (let i = 0; i < aDTDs.length; i++) {
var id = 'dtd' + i;
references += '<!ENTITY % ' + id + ' SYSTEM "' + aDTDs[i] + '">%' + id + ';';
}
var header = '<?xml version="1.0"?><!DOCTYPE elem [' + references + ']>';
var element = '<elem id="entity">&' + aEntityId + ';</elem>';
var content = header + element;
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc = parser.parseFromString(content, 'text/xml');
var node = doc.querySelector('elem[id="entity"]');
if (!node) {
throw new Error("Unkown entity '" + aEntityId + "'");
}
return node.textContent;
}
/**
* Retrieve the localized content for a given property
*
* @memberOf l10n
* @param {String} aURL URL of the .properties file.
* @param {String} aProperty The property to get the value of.
*
* @returns {String} Value of the requested property
*/
function getProperty(aURL, aProperty) {
var bundle = Services.strings.createBundle(aURL);
try {
return bundle.GetStringFromName(aProperty);
} catch (ex) {
throw new Error("Unkown property '" + aProperty + "'");
}
}
// Export of functions
l10n.getEntity = getEntity;
l10n.getProperty = getProperty;

Просмотреть файл

@ -1,43 +0,0 @@
/* 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 = ['findCallerFrame'];
/**
* @namespace Defines utility methods for handling stack frames
*/
/**
* Find the frame to use for logging the test result. If a start frame has
* been specified, we walk down the stack until a frame with the same filename
* as the start frame has been found. The next file in the stack will be the
* frame to use for logging the result.
*
* @memberOf stack
* @param {Object} [aStartFrame=Components.stack] Frame to start from walking up the stack.
* @returns {Object} Frame of the stack to use for logging the result.
*/
function findCallerFrame(aStartFrame) {
let frame = Components.stack;
let filename = frame.filename.replace(/(.*)-> /, "");
// If a start frame has been specified, walk up the stack until we have
// found the corresponding file
if (aStartFrame) {
filename = aStartFrame.filename.replace(/(.*)-> /, "");
while (frame.caller &&
frame.filename && (frame.filename.indexOf(filename) == -1)) {
frame = frame.caller;
}
}
// Walk even up more until the next file has been found
while (frame.caller &&
(!frame.filename || (frame.filename.indexOf(filename) != -1)))
frame = frame.caller;
return frame;
}

Просмотреть файл

@ -1,292 +0,0 @@
/* 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 = ["init", "map"];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
// imports
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
/**
* The window map is used to store information about the current state of
* open windows, e.g. loaded state
*/
var map = {
_windows : { },
/**
* Check if a given window id is contained in the map of windows
*
* @param {Number} aWindowId
* Outer ID of the window to check.
* @returns {Boolean} True if the window is part of the map, otherwise false.
*/
contains : function (aWindowId) {
return (aWindowId in this._windows);
},
/**
* Retrieve the value of the specified window's property.
*
* @param {Number} aWindowId
* Outer ID of the window to check.
* @param {String} aProperty
* Property to retrieve the value from
* @return {Object} Value of the window's property
*/
getValue : function (aWindowId, aProperty) {
if (!this.contains(aWindowId)) {
return undefined;
} else {
var win = this._windows[aWindowId];
return (aProperty in win) ? win[aProperty]
: undefined;
}
},
/**
* Remove the entry for a given window
*
* @param {Number} aWindowId
* Outer ID of the window to check.
*/
remove : function (aWindowId) {
if (this.contains(aWindowId)) {
delete this._windows[aWindowId];
}
// dump("* current map: " + JSON.stringify(this._windows) + "\n");
},
/**
* Update the property value of a given window
*
* @param {Number} aWindowId
* Outer ID of the window to check.
* @param {String} aProperty
* Property to update the value for
* @param {Object}
* Value to set
*/
update : function (aWindowId, aProperty, aValue) {
if (!this.contains(aWindowId)) {
this._windows[aWindowId] = { };
}
this._windows[aWindowId][aProperty] = aValue;
// dump("* current map: " + JSON.stringify(this._windows) + "\n");
},
/**
* Update the internal loaded state of the given content window. To identify
* an active (re)load action we make use of an uuid.
*
* @param {Window} aId - The outer id of the window to update
* @param {Boolean} aIsLoaded - Has the window been loaded
*/
updatePageLoadStatus : function (aId, aIsLoaded) {
this.update(aId, "loaded", aIsLoaded);
var uuid = this.getValue(aId, "id_load_in_transition");
// If no uuid has been set yet or when the page gets unloaded create a new id
if (!uuid || !aIsLoaded) {
uuid = uuidgen.generateUUID();
this.update(aId, "id_load_in_transition", uuid);
}
// dump("*** Page status updated: id=" + aId + ", loaded=" + aIsLoaded + ", uuid=" + uuid + "\n");
},
/**
* This method only applies to content windows, where we have to check if it has
* been successfully loaded or reloaded. An uuid allows us to wait for the next
* load action triggered by e.g. controller.open().
*
* @param {Window} aId - The outer id of the content window to check
*
* @returns {Boolean} True if the content window has been loaded
*/
hasPageLoaded : function (aId) {
var load_current = this.getValue(aId, "id_load_in_transition");
var load_handled = this.getValue(aId, "id_load_handled");
var isLoaded = this.contains(aId) && this.getValue(aId, "loaded") &&
(load_current !== load_handled);
if (isLoaded) {
// Backup the current uuid so we can check later if another page load happened.
this.update(aId, "id_load_handled", load_current);
}
// dump("** Page has been finished loading: id=" + aId + ", status=" + isLoaded + ", uuid=" + load_current + "\n");
return isLoaded;
}
};
// Observer when a new top-level window is ready
var windowReadyObserver = {
observe: function (aSubject, aTopic, aData) {
// Not in all cases we get a ChromeWindow. So ensure we really operate
// on such an instance. Otherwise load events will not be handled.
var win = utils.getChromeWindow(aSubject);
// var id = utils.getWindowId(win);
// dump("*** 'toplevel-window-ready' observer notification: id=" + id + "\n");
attachEventListeners(win);
}
};
// Observer when a top-level window is closed
var windowCloseObserver = {
observe: function (aSubject, aTopic, aData) {
var id = utils.getWindowId(aSubject);
// dump("*** 'outer-window-destroyed' observer notification: id=" + id + "\n");
map.remove(id);
}
};
// Bug 915554
// Support for the old Private Browsing Mode (eg. ESR17)
// TODO: remove once ESR17 is no longer supported
var enterLeavePrivateBrowsingObserver = {
observe: function (aSubject, aTopic, aData) {
handleAttachEventListeners();
}
};
/**
* Attach event listeners
*
* @param {ChromeWindow} aWindow
* Window to attach listeners on.
*/
function attachEventListeners(aWindow) {
// These are the event handlers
var pageShowHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
// see https://bugzilla.mozilla.org/show_bug.cgi?id=690829
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'pageshow' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
map.updatePageLoadStatus(id, true);
}
// We need to add/remove the unload/pagehide event listeners to preserve caching.
aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
aWindow.addEventListener("pagehide", pageHideHandler, true);
};
var DOMContentLoadedHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'DOMContentLoaded' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
// We only care about error pages for DOMContentLoaded
var errorRegex = /about:.+(error)|(blocked)\?/;
if (errorRegex.exec(doc.baseURI)) {
// Wait about 1s to be sure the DOM is ready
utils.sleep(1000);
map.updatePageLoadStatus(id, true);
}
// We need to add/remove the unload event listener to preserve caching.
aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
}
};
// beforeunload is still needed because pagehide doesn't fire before the page is unloaded.
// still use pagehide for cases when beforeunload doesn't get fired
var beforeUnloadHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'beforeunload' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
map.updatePageLoadStatus(id, false);
}
aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
};
var pageHideHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'pagehide' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
map.updatePageLoadStatus(id, false);
}
// If event.persisted is true the beforeUnloadHandler would never fire
// and we have to remove the event handler here to avoid memory leaks.
if (aEvent.persisted)
aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
};
var onWindowLoaded = function (aEvent) {
var id = utils.getWindowId(aWindow);
// dump("*** 'load' event: id=" + id + ", baseURI=" + aWindow.document.baseURI + "\n");
map.update(id, "loaded", true);
// Note: Error pages will never fire a "pageshow" event. For those we
// have to wait for the "DOMContentLoaded" event. That's the final state.
// Error pages will always have a baseURI starting with
// "about:" followed by "error" or "blocked".
aWindow.addEventListener("DOMContentLoaded", DOMContentLoadedHandler, true);
// Page is ready
aWindow.addEventListener("pageshow", pageShowHandler, true);
// Leave page (use caching)
aWindow.addEventListener("pagehide", pageHideHandler, true);
};
// If the window has already been finished loading, call the load handler
// directly. Otherwise attach it to the current window.
if (aWindow.document.readyState === 'complete') {
onWindowLoaded();
} else {
aWindow.addEventListener("load", onWindowLoaded);
}
}
// Attach event listeners to all already open top-level windows
function handleAttachEventListeners() {
var enumerator = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).getEnumerator("");
while (enumerator.hasMoreElements()) {
var win = enumerator.getNext();
attachEventListeners(win);
}
}
function init() {
// Activate observer for new top level windows
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
observerService.addObserver(windowReadyObserver, "toplevel-window-ready");
observerService.addObserver(windowCloseObserver, "outer-window-destroyed");
observerService.addObserver(enterLeavePrivateBrowsingObserver, "private-browsing");
handleAttachEventListeners();
}

Просмотреть файл

@ -1,823 +0,0 @@
// Export all available functions for Mozmill
var EXPORTED_SYMBOLS = ["disableNonTestMouseEvents","sendMouseEvent", "sendChar",
"sendString", "sendKey", "synthesizeMouse", "synthesizeTouch",
"synthesizeMouseAtPoint", "synthesizeTouchAtPoint",
"synthesizeMouseAtCenter", "synthesizeTouchAtCenter",
"synthesizeWheel", "synthesizeKey",
"synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent",
"synthesizeText",
"synthesizeComposition", "synthesizeQuerySelectedText"];
var Ci = Components.interfaces;
var Cc = Components.classes;
var window = Cc["@mozilla.org/appshell/appShellService;1"]
.getService(Ci.nsIAppShellService).hiddenDOMWindow;
var _EU_Ci = Ci;
var navigator = window.navigator;
var KeyEvent = window.KeyEvent;
var parent = window.parent;
function is(aExpression1, aExpression2, aMessage) {
if (aExpression1 !== aExpression2) {
throw new Error(aMessage);
}
}
/**
* EventUtils provides some utility methods for creating and sending DOM events.
* Current methods:
* sendMouseEvent
* sendChar
* sendString
* sendKey
* synthesizeMouse
* synthesizeMouseAtCenter
* synthesizeWheel
* synthesizeKey
* synthesizeMouseExpectEvent
* synthesizeKeyExpectEvent
*
* When adding methods to this file, please add a performance test for it.
*/
/**
* Send a mouse event to the node aTarget (aTarget can be an id, or an
* actual node) . The "event" passed in to aEvent is just a JavaScript
* object with the properties set that the real mouse event object should
* have. This includes the type of the mouse event.
* E.g. to send an click event to the node with id 'node' you might do this:
*
* sendMouseEvent({type:'click'}, 'node');
*/
function getElement(id) {
return ((typeof(id) == "string") ?
document.getElementById(id) : id);
};
this.$ = this.getElement;
function sendMouseEvent(aEvent, aTarget, aWindow) {
if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'");
}
if (!aWindow) {
aWindow = window;
}
if (!(aTarget instanceof aWindow.Element)) {
aTarget = aWindow.document.getElementById(aTarget);
}
var event = aWindow.document.createEvent('MouseEvent');
var typeArg = aEvent.type;
var canBubbleArg = true;
var cancelableArg = true;
var viewArg = aWindow;
var detailArg = aEvent.detail || (aEvent.type == 'click' ||
aEvent.type == 'mousedown' ||
aEvent.type == 'mouseup' ? 1 :
aEvent.type == 'dblclick'? 2 : 0);
var screenXArg = aEvent.screenX || 0;
var screenYArg = aEvent.screenY || 0;
var clientXArg = aEvent.clientX || 0;
var clientYArg = aEvent.clientY || 0;
var ctrlKeyArg = aEvent.ctrlKey || false;
var altKeyArg = aEvent.altKey || false;
var shiftKeyArg = aEvent.shiftKey || false;
var metaKeyArg = aEvent.metaKey || false;
var buttonArg = aEvent.button || 0;
var relatedTargetArg = aEvent.relatedTarget || null;
event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
screenXArg, screenYArg, clientXArg, clientYArg,
ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
buttonArg, relatedTargetArg);
SpecialPowers.dispatchEvent(aWindow, aTarget, event);
}
/**
* Send the char aChar to the focused element. This method handles casing of
* chars (sends the right charcode, and sends a shift key for uppercase chars).
* No other modifiers are handled at this point.
*
* For now this method only works for ASCII characters and emulates the shift
* key state on US keyboard layout.
*/
function sendChar(aChar, aWindow) {
var hasShift;
// Emulate US keyboard layout for the shiftKey state.
switch (aChar) {
case "!":
case "@":
case "#":
case "$":
case "%":
case "^":
case "&":
case "*":
case "(":
case ")":
case "_":
case "+":
case "{":
case "}":
case ":":
case "\"":
case "|":
case "<":
case ">":
case "?":
hasShift = true;
break;
default:
hasShift = (aChar == aChar.toUpperCase());
break;
}
synthesizeKey(aChar, { shiftKey: hasShift }, aWindow);
}
/**
* Send the string aStr to the focused element.
*
* For now this method only works for ASCII characters and emulates the shift
* key state on US keyboard layout.
*/
function sendString(aStr, aWindow) {
for (var i = 0; i < aStr.length; ++i) {
sendChar(aStr.charAt(i), aWindow);
}
}
/**
* Send the non-character key aKey to the focused node.
* The name of the key should be the part that comes after "DOM_VK_" in the
* KeyEvent constant name for this key.
* No modifiers are handled at this point.
*/
function sendKey(aKey, aWindow) {
var keyName = "VK_" + aKey.toUpperCase();
synthesizeKey(keyName, { shiftKey: false }, aWindow);
}
/**
* Parse the key modifier flags from aEvent. Used to share code between
* synthesizeMouse and synthesizeKey.
*/
function _parseModifiers(aEvent)
{
const nsIDOMWindowUtils = _EU_Ci.nsIDOMWindowUtils;
var mval = 0;
if (aEvent.shiftKey) {
mval |= nsIDOMWindowUtils.MODIFIER_SHIFT;
}
if (aEvent.ctrlKey) {
mval |= nsIDOMWindowUtils.MODIFIER_CONTROL;
}
if (aEvent.altKey) {
mval |= nsIDOMWindowUtils.MODIFIER_ALT;
}
if (aEvent.metaKey) {
mval |= nsIDOMWindowUtils.MODIFIER_META;
}
if (aEvent.accelKey) {
mval |= (navigator.platform.indexOf("Mac") >= 0) ?
nsIDOMWindowUtils.MODIFIER_META : nsIDOMWindowUtils.MODIFIER_CONTROL;
}
if (aEvent.altGrKey) {
mval |= nsIDOMWindowUtils.MODIFIER_ALTGRAPH;
}
if (aEvent.capsLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_CAPSLOCK;
}
if (aEvent.fnKey) {
mval |= nsIDOMWindowUtils.MODIFIER_FN;
}
if (aEvent.numLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_NUMLOCK;
}
if (aEvent.scrollLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_SCROLLLOCK;
}
if (aEvent.symbolLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_SYMBOLLOCK;
}
if (aEvent.osKey) {
mval |= nsIDOMWindowUtils.MODIFIER_OS;
}
return mval;
}
/**
* Synthesize a mouse event on a target. The actual client point is determined
* by taking the aTarget's client box and offseting it by aOffsetX and
* aOffsetY. This allows mouse clicks to be simulated by calling this method.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
*
* If the type is specified, an mouse event of that type is fired. Otherwise,
* a mousedown followed by a mouse up is performed.
*
* aWindow is optional, and defaults to the current window object.
*
* Returns whether the event had preventDefault() called on it.
*/
function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent, aWindow);
}
function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent, aWindow);
}
/*
* Synthesize a mouse event at a particular point in aWindow.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
*
* If the type is specified, an mouse event of that type is fired. Otherwise,
* a mousedown followed by a mouse up is performed.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
var defaultPrevented = false;
if (utils) {
var button = aEvent.button || 0;
var clickCount = aEvent.clickCount || 1;
var modifiers = _parseModifiers(aEvent);
var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
if (("type" in aEvent) && aEvent.type) {
defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers, false, pressure, inputSource);
}
else {
utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
}
}
return defaultPrevented;
}
function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (utils) {
var id = aEvent.id || 0;
var rx = aEvent.rx || 1;
var ry = aEvent.rx || 1;
var angle = aEvent.angle || 0;
var force = aEvent.force || 1;
var modifiers = _parseModifiers(aEvent);
if (("type" in aEvent) && aEvent.type) {
utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
}
else {
utils.sendTouchEvent("touchstart", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
utils.sendTouchEvent("touchend", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
}
}
}
// Call synthesizeMouse with coordinates at the center of aTarget.
function synthesizeMouseAtCenter(aTarget, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent,
aWindow);
}
function synthesizeTouchAtCenter(aTarget, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
synthesizeTouch(aTarget, rect.width / 2, rect.height / 2, aEvent,
aWindow);
}
/**
* Synthesize a wheel event on a target. The actual client point is determined
* by taking the aTarget's client box and offseting it by aOffsetX and
* aOffsetY.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, deltaX, deltaY, deltaZ,
* deltaMode, lineOrPageDeltaX, lineOrPageDeltaY, isMomentum, isPixelOnlyDevice,
* isCustomizedByPrefs, expectedOverflowDeltaX, expectedOverflowDeltaY
*
* deltaMode must be defined, others are ok even if undefined.
*
* expectedOverflowDeltaX and expectedOverflowDeltaY take integer value. The
* value is just checked as 0 or positive or negative.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return;
}
var modifiers = _parseModifiers(aEvent);
var options = 0;
if (aEvent.isPixelOnlyDevice &&
(aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL)) {
options |= utils.WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE;
}
if (aEvent.isMomentum) {
options |= utils.WHEEL_EVENT_CAUSED_BY_MOMENTUM;
}
if (aEvent.isCustomizedByPrefs) {
options |= utils.WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS;
}
if (typeof aEvent.expectedOverflowDeltaX !== "undefined") {
if (aEvent.expectedOverflowDeltaX === 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO;
} else if (aEvent.expectedOverflowDeltaX > 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE;
} else {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE;
}
}
if (typeof aEvent.expectedOverflowDeltaY !== "undefined") {
if (aEvent.expectedOverflowDeltaY === 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO;
} else if (aEvent.expectedOverflowDeltaY > 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE;
} else {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE;
}
}
var isPixelOnlyDevice =
aEvent.isPixelOnlyDevice && aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL;
// Avoid the JS warnings "reference to undefined property"
if (!aEvent.deltaX) {
aEvent.deltaX = 0;
}
if (!aEvent.deltaY) {
aEvent.deltaY = 0;
}
if (!aEvent.deltaZ) {
aEvent.deltaZ = 0;
}
var lineOrPageDeltaX =
aEvent.lineOrPageDeltaX != null ? aEvent.lineOrPageDeltaX :
aEvent.deltaX > 0 ? Math.floor(aEvent.deltaX) :
Math.ceil(aEvent.deltaX);
var lineOrPageDeltaY =
aEvent.lineOrPageDeltaY != null ? aEvent.lineOrPageDeltaY :
aEvent.deltaY > 0 ? Math.floor(aEvent.deltaY) :
Math.ceil(aEvent.deltaY);
var rect = aTarget.getBoundingClientRect();
utils.sendWheelEvent(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent.deltaX, aEvent.deltaY, aEvent.deltaZ,
aEvent.deltaMode, modifiers,
lineOrPageDeltaX, lineOrPageDeltaY, options);
}
function _computeKeyCodeFromChar(aChar)
{
if (aChar.length != 1) {
return 0;
}
const nsIDOMKeyEvent = _EU_Ci.nsIDOMKeyEvent;
if (aChar >= 'a' && aChar <= 'z') {
return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
}
if (aChar >= 'A' && aChar <= 'Z') {
return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
}
if (aChar >= '0' && aChar <= '9') {
return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
}
// returns US keyboard layout's keycode
switch (aChar) {
case '~':
case '`':
return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
case '!':
return nsIDOMKeyEvent.DOM_VK_1;
case '@':
return nsIDOMKeyEvent.DOM_VK_2;
case '#':
return nsIDOMKeyEvent.DOM_VK_3;
case '$':
return nsIDOMKeyEvent.DOM_VK_4;
case '%':
return nsIDOMKeyEvent.DOM_VK_5;
case '^':
return nsIDOMKeyEvent.DOM_VK_6;
case '&':
return nsIDOMKeyEvent.DOM_VK_7;
case '*':
return nsIDOMKeyEvent.DOM_VK_8;
case '(':
return nsIDOMKeyEvent.DOM_VK_9;
case ')':
return nsIDOMKeyEvent.DOM_VK_0;
case '-':
case '_':
return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
case '+':
case '=':
return nsIDOMKeyEvent.DOM_VK_EQUALS;
case '{':
case '[':
return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
case '}':
case ']':
return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
case '|':
case '\\':
return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
case ':':
case ';':
return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
case '\'':
case '"':
return nsIDOMKeyEvent.DOM_VK_QUOTE;
case '<':
case ',':
return nsIDOMKeyEvent.DOM_VK_COMMA;
case '>':
case '.':
return nsIDOMKeyEvent.DOM_VK_PERIOD;
case '?':
case '/':
return nsIDOMKeyEvent.DOM_VK_SLASH;
default:
return 0;
}
}
/**
* isKeypressFiredKey() returns TRUE if the given key should cause keypress
* event when widget handles the native key event. Otherwise, FALSE.
*
* aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key
* name begins with "VK_", or a character.
*/
function isKeypressFiredKey(aDOMKeyCode)
{
if (typeof(aDOMKeyCode) == "string") {
if (aDOMKeyCode.indexOf("VK_") == 0) {
aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode];
if (!aDOMKeyCode) {
throw new Error(`Unknown key: ${aDOMKeyCode}`);
}
} else {
// If the key generates a character, it must cause a keypress event.
return true;
}
}
switch (aDOMKeyCode) {
case KeyEvent.DOM_VK_SHIFT:
case KeyEvent.DOM_VK_CONTROL:
case KeyEvent.DOM_VK_ALT:
case KeyEvent.DOM_VK_CAPS_LOCK:
case KeyEvent.DOM_VK_NUM_LOCK:
case KeyEvent.DOM_VK_SCROLL_LOCK:
case KeyEvent.DOM_VK_META:
return false;
default:
return true;
}
}
/**
* Synthesize a key event. It is targeted at whatever would be targeted by an
* actual keypress by the user, typically the focused element.
*
* aKey should be either a character or a keycode starting with VK_ such as
* VK_ENTER.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, type, location
*
* Sets one of KeyboardEvent.DOM_KEY_LOCATION_* to location. Otherwise,
* DOMWindowUtils will choose good location from the keycode.
*
* If the type is specified, a key event of that type is fired. Otherwise,
* a keydown, a keypress and then a keyup event are fired in sequence.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeKey(aKey, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (utils) {
var keyCode = 0, charCode = 0;
if (aKey.indexOf("VK_") == 0) {
keyCode = KeyEvent["DOM_" + aKey];
if (!keyCode) {
throw new Error(`Unknown key: ${aKey}`);
}
} else {
charCode = aKey.charCodeAt(0);
keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
}
var modifiers = _parseModifiers(aEvent);
var flags = 0;
if (aEvent.location != undefined) {
switch (aEvent.location) {
case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
flags |= utils.KEY_FLAG_LOCATION_STANDARD;
break;
case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
flags |= utils.KEY_FLAG_LOCATION_LEFT;
break;
case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
flags |= utils.KEY_FLAG_LOCATION_RIGHT;
break;
case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
flags |= utils.KEY_FLAG_LOCATION_NUMPAD;
break;
}
}
if (!("type" in aEvent) || !aEvent.type) {
// Send keydown + (optional) keypress + keyup events.
var keyDownDefaultHappened =
utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags);
if (isKeypressFiredKey(keyCode)) {
if (!keyDownDefaultHappened) {
flags |= utils.KEY_FLAG_PREVENT_DEFAULT;
}
utils.sendKeyEvent("keypress", keyCode, charCode, modifiers, flags);
}
utils.sendKeyEvent("keyup", keyCode, 0, modifiers, flags);
} else if (aEvent.type == "keypress") {
// Send standalone keypress event.
utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers, flags);
} else {
// Send other standalone event than keypress.
utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers, flags);
}
}
}
var _gSeenEvent = false;
/**
* Indicate that an event with an original target of aExpectedTarget and
* a type of aExpectedEvent is expected to be fired, or not expected to
* be fired.
*/
function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
{
if (!aExpectedTarget || !aExpectedEvent)
return null;
_gSeenEvent = false;
var type = (aExpectedEvent.charAt(0) == "!") ?
aExpectedEvent.substring(1) : aExpectedEvent;
var eventHandler = function(event) {
var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
event.type == type);
is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : ""));
_gSeenEvent = true;
};
aExpectedTarget.addEventListener(type, eventHandler);
return eventHandler;
}
/**
* Check if the event was fired or not. The event handler aEventHandler
* will be removed.
*/
function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
{
if (aEventHandler) {
var expectEvent = (aExpectedEvent.charAt(0) != "!");
var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
aExpectedTarget.removeEventListener(type, aEventHandler);
var desc = type + " event";
if (!expectEvent)
desc += " not";
is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired");
}
_gSeenEvent = false;
}
/**
* Similar to synthesizeMouse except that a test is performed to see if an
* event is fired at the right target as a result.
*
* aExpectedTarget - the expected originalTarget of the event.
* aExpectedEvent - the expected type of the event, such as 'select'.
* aTestName - the test name when outputing results
*
* To test that an event is not fired, use an expected type preceded by an
* exclamation mark, such as '!select'. This might be used to test that a
* click on a disabled element doesn't fire certain events for instance.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
aExpectedTarget, aExpectedEvent, aTestName,
aWindow)
{
var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
_checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
}
/**
* Similar to synthesizeKey except that a test is performed to see if an
* event is fired at the right target as a result.
*
* aExpectedTarget - the expected originalTarget of the event.
* aExpectedEvent - the expected type of the event, such as 'select'.
* aTestName - the test name when outputing results
*
* To test that an event is not fired, use an expected type preceded by an
* exclamation mark, such as '!select'.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
aTestName, aWindow)
{
var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
synthesizeKey(key, aEvent, aWindow);
_checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
}
function disableNonTestMouseEvents(aDisable)
{
var domutils = _getDOMWindowUtils();
domutils.disableNonTestMouseEvents(aDisable);
}
function _getDOMWindowUtils(aWindow)
{
if (!aWindow) {
aWindow = window;
}
// we need parent.SpecialPowers for:
// layout/base/tests/test_reftests_with_caret.html
// chrome: toolkit/content/tests/chrome/test_findbar.xul
// chrome: toolkit/content/tests/chrome/test_popup_anchor.xul
if ("SpecialPowers" in window && window.SpecialPowers != undefined) {
return SpecialPowers.getDOMWindowUtils(aWindow);
}
if ("SpecialPowers" in parent && parent.SpecialPowers != undefined) {
return parent.SpecialPowers.getDOMWindowUtils(aWindow);
}
//TODO: this is assuming we are in chrome space
return aWindow.QueryInterface(_EU_Ci.nsIInterfaceRequestor).
getInterface(_EU_Ci.nsIDOMWindowUtils);
}
// Must be synchronized with nsIDOMWindowUtils.
const COMPOSITION_ATTR_RAWINPUT = 0x02;
const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
/**
* Synthesize a composition event.
*
* @param aEvent The composition event information. This must
* have |type| member. The value must be
* "compositionstart", "compositionend" or
* "compositionupdate".
* And also this may have |data| and |locale| which
* would be used for the value of each property of
* the composition event. Note that the data would
* be ignored if the event type were
* "compositionstart".
* @param aWindow Optional (If null, current |window| will be used)
*/
function synthesizeComposition(aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return;
}
utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "",
aEvent.locale ? aEvent.locale : "");
}
/**
* Synthesize a text event.
*
* @param aEvent The text event's information, this has |composition|
* and |caret| members. |composition| has |string| and
* |clauses| members. |clauses| must be array object. Each
* object has |length| and |attr|. And |caret| has |start| and
* |length|. See the following tree image.
*
* aEvent
* +-- composition
* | +-- string
* | +-- clauses[]
* | +-- length
* | +-- attr
* +-- caret
* +-- start
* +-- length
*
* Set the composition string to |composition.string|. Set its
* clauses information to the |clauses| array.
*
* When it's composing, set the each clauses' length to the
* |composition.clauses[n].length|. The sum of the all length
* values must be same as the length of |composition.string|.
* Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
* |composition.clauses[n].attr|.
*
* When it's not composing, set 0 to the
* |composition.clauses[0].length| and
* |composition.clauses[0].attr|.
*
* Set caret position to the |caret.start|. It's offset from
* the start of the composition string. Set caret length to
* |caret.length|. If it's larger than 0, it should be wide
* caret. However, current nsEditor doesn't support wide
* caret, therefore, you should always set 0 now.
*
* @param aWindow Optional (If null, current |window| will be used)
*/
function synthesizeText(aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return;
}
if (!aEvent.composition || !aEvent.composition.clauses ||
!aEvent.composition.clauses[0]) {
return;
}
var firstClauseLength = aEvent.composition.clauses[0].length;
var firstClauseAttr = aEvent.composition.clauses[0].attr;
var secondClauseLength = 0;
var secondClauseAttr = 0;
var thirdClauseLength = 0;
var thirdClauseAttr = 0;
if (aEvent.composition.clauses[1]) {
secondClauseLength = aEvent.composition.clauses[1].length;
secondClauseAttr = aEvent.composition.clauses[1].attr;
if (aEvent.composition.clauses[2]) {
thirdClauseLength = aEvent.composition.clauses[2].length;
thirdClauseAttr = aEvent.composition.clauses[2].attr;
}
}
var caretStart = -1;
var caretLength = 0;
if (aEvent.caret) {
caretStart = aEvent.caret.start;
caretLength = aEvent.caret.length;
}
utils.sendTextEvent(aEvent.composition.string,
firstClauseLength, firstClauseAttr,
secondClauseLength, secondClauseAttr,
thirdClauseLength, thirdClauseAttr,
caretStart, caretLength);
}
/**
* Synthesize a query selected text event.
*
* @param aWindow Optional (If null, current |window| will be used)
* @return An nsIQueryContentEventResult object. If this failed,
* the result might be null.
*/
function synthesizeQuerySelectedText(aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
}

Просмотреть файл

@ -1,78 +0,0 @@
/* 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 = ['inArray', 'getSet', 'indexOf',
'remove', 'rindexOf', 'compare'];
function remove(array, from, to) {
var rest = array.slice((to || from) + 1 || array.length);
array.length = from < 0 ? array.length + from : from;
return array.push.apply(array, rest);
}
function inArray(array, value) {
for (var i in array) {
if (value == array[i]) {
return true;
}
}
return false;
}
function getSet(array) {
var narray = [];
for (var i in array) {
if (!inArray(narray, array[i])) {
narray.push(array[i]);
}
}
return narray;
}
function indexOf(array, v, offset) {
for (var i in array) {
if (offset == undefined || i >= offset) {
if (!isNaN(i) && array[i] == v) {
return new Number(i);
}
}
}
return -1;
}
function rindexOf (array, v) {
var l = array.length;
for (var i in array) {
if (!isNaN(i)) {
var i = new Number(i);
}
if (!isNaN(i) && array[l - i] == v) {
return l - i;
}
}
return -1;
}
function compare (array, carray) {
if (array.length != carray.length) {
return false;
}
for (var i in array) {
if (array[i] != carray[i]) {
return false;
}
}
return true;
}

Просмотреть файл

@ -1,24 +0,0 @@
/* 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 = ['getAttributes'];
var getAttributes = function (node) {
var attributes = {};
for (var i in node.attributes) {
if (!isNaN(i)) {
try {
var attr = node.attributes[i];
attributes[attr.name] = attr.value;
}
catch (e) {
}
}
}
return attributes;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,469 +0,0 @@
/*
http://www.JSON.org/json2.js
2008-05-25
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects without a toJSON
method. It can be a function or an array.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the object holding the key.
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array, then it will be used to
select the members to be serialized. It filters the results such
that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
*/
/*jslint evil: true */
/*global JSON */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
parse, propertyIsEnumerable, prototype, push, replace, slice, stringify,
test, toJSON, toString
*/
var EXPORTED_SYMBOLS = ["JSON"];
// Create a JSON object only if one does not already exist. We create the
// object in a closure to avoid creating global variables.
JSON = function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
Date.prototype.toJSON = function (key) {
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapeable.lastIndex = 0;
return escapeable.test(string) ?
'"' + string.replace(escapeable, function (a) {
var c = meta[a];
if (typeof c === 'string') {
return c;
}
return '\\u' + ('0000' +
(+(a.charCodeAt(0))).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// If the object has a dontEnum length property, we'll treat it as an array.
if (typeof value.length === 'number' &&
!(value.propertyIsEnumerable('length'))) {
// The object is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value, rep);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value, rep);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// Return the JSON object containing the stringify and parse methods.
return {
stringify: function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
},
parse: function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' + ('0000' +
(+(a.charCodeAt(0))).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
}
};
}();

Просмотреть файл

@ -1,54 +0,0 @@
/* 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 = ['getLength', ];//'compare'];
var getLength = function (obj) {
var len = 0;
for (let i in obj) {
len++;
}
return len;
}
// var logging = {}; Components.utils.import('resource://mozmill/stdlib/logging.js', logging);
// var objectsLogger = logging.getLogger('objectsLogger');
// var compare = function (obj1, obj2, depth, recursion) {
// if (depth == undefined) {
// var depth = 4;
// }
// if (recursion == undefined) {
// var recursion = 0;
// }
//
// if (recursion > depth) {
// return true;
// }
//
// if (typeof(obj1) != typeof(obj2)) {
// return false;
// }
//
// if (typeof(obj1) == "object" && typeof(obj2) == "object") {
// if ([x for (x in obj1)].length != [x for (x in obj2)].length) {
// return false;
// }
// for (i in obj1) {
// recursion++;
// var result = compare(obj1[i], obj2[i], depth, recursion);
// objectsLogger.info(i+' in recursion '+result);
// if (result == false) {
// return false;
// }
// }
// } else {
// if (obj1 != obj2) {
// return false;
// }
// }
// return true;
// }

Просмотреть файл

@ -1,57 +0,0 @@
/* 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 = ['listDirectory', 'getFileForPath', 'abspath', 'getPlatform'];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
function listDirectory(file) {
// file is the given directory (nsIFile)
var entries = file.directoryEntries;
var array = [];
while (entries.hasMoreElements()) {
var entry = entries.getNext();
entry.QueryInterface(Ci.nsIFile);
array.push(entry);
}
return array;
}
function getFileForPath(path) {
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
return file;
}
function abspath(rel, file) {
var relSplit = rel.split('/');
if (relSplit[0] == '..' && !file.isDirectory()) {
file = file.parent;
}
for (var p of relSplit) {
if (p == '..') {
file = file.parent;
} else if (p == '.') {
if (!file.isDirectory()) {
file = file.parent;
}
} else {
file.append(p);
}
}
return file.path;
}
function getPlatform() {
return Services.appinfo.OS.toLowerCase();
}

Просмотреть файл

@ -1,370 +0,0 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Jetpack.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Atul Varma <atul@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
(function(global) {
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/NetUtil.jsm");
var exports = {};
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
function resolvePrincipal(principal, defaultPrincipal) {
if (principal === undefined)
return defaultPrincipal;
if (principal == "system")
return systemPrincipal;
return principal;
}
// The base URI to we use when we're given relative URLs, if any.
var baseURI = null;
if (global.window)
baseURI = ios.newURI(global.location.href);
exports.baseURI = baseURI;
// The "parent" chrome URI to use if we're loading code that
// needs chrome privileges but may not have a filename that
// matches any of SpiderMonkey's defined system filename prefixes.
// The latter is needed so that wrappers can be automatically
// made for the code. For more information on this, see
// bug 418356:
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=418356
var parentChromeURIString;
if (baseURI)
// We're being loaded from a chrome-privileged document, so
// use its URL as the parent string.
parentChromeURIString = baseURI.spec;
else
// We're being loaded from a chrome-privileged JS module or
// SecurableModule, so use its filename (which may itself
// contain a reference to a parent).
parentChromeURIString = Components.stack.filename;
function maybeParentifyFilename(filename) {
var doParentifyFilename = true;
try {
// TODO: Ideally we should just make
// nsIChromeRegistry.wrappersEnabled() available from script
// and use it here. Until that's in the platform, though,
// we'll play it safe and parentify the filename unless
// we're absolutely certain things will be ok if we don't.
var filenameURI = ios.newURI(filename,
null,
baseURI);
if (filenameURI.scheme == 'chrome' &&
filenameURI.pathQueryRef.indexOf('/content/') == 0)
// Content packages will always have wrappers made for them;
// if automatic wrappers have been disabled for the
// chrome package via a chrome manifest flag, then
// this still works too, to the extent that the
// content package is insecure anyways.
doParentifyFilename = false;
} catch (e) {}
if (doParentifyFilename)
return parentChromeURIString + " -> " + filename;
return filename;
}
function getRootDir(urlStr) {
// TODO: This feels hacky, and like there will be edge cases.
return urlStr.slice(0, urlStr.lastIndexOf("/") + 1);
}
exports.SandboxFactory = function SandboxFactory(defaultPrincipal) {
// Unless specified otherwise, use a principal with limited
// privileges.
this._defaultPrincipal = resolvePrincipal(defaultPrincipal,
"http://www.mozilla.org");
},
exports.SandboxFactory.prototype = {
createSandbox: function createSandbox(options) {
var principal = resolvePrincipal(options.principal,
this._defaultPrincipal);
return {
_sandbox: new Cu.Sandbox(principal),
_principal: principal,
get globalScope() {
return this._sandbox;
},
defineProperty: function defineProperty(name, value) {
this._sandbox[name] = value;
},
getProperty: function getProperty(name) {
return this._sandbox[name];
},
evaluate: function evaluate(options) {
if (typeof(options) == 'string')
options = {contents: options};
options = {__proto__: options};
if (typeof(options.contents) != 'string')
throw new Error('Expected string for options.contents');
if (options.lineNo === undefined)
options.lineNo = 1;
if (options.jsVersion === undefined)
options.jsVersion = "1.8";
if (typeof(options.filename) != 'string')
options.filename = '<string>';
if (this._principal == systemPrincipal)
options.filename = maybeParentifyFilename(options.filename);
return Cu.evalInSandbox(options.contents,
this._sandbox,
options.jsVersion,
options.filename,
options.lineNo);
}
};
}
};
exports.Loader = function Loader(options) {
options = {__proto__: options};
if (options.fs === undefined) {
var rootPaths = options.rootPath || options.rootPaths;
if (rootPaths) {
if (rootPaths.constructor.name != "Array")
rootPaths = [rootPaths];
var fses = rootPaths.map(path => new exports.LocalFileSystem(path));
options.fs = new exports.CompositeFileSystem(fses);
} else
options.fs = new exports.LocalFileSystem();
}
if (options.sandboxFactory === undefined)
options.sandboxFactory = new exports.SandboxFactory(
options.defaultPrincipal
);
if (options.modules === undefined)
options.modules = {};
if (options.globals === undefined)
options.globals = {};
this.fs = options.fs;
this.sandboxFactory = options.sandboxFactory;
this.sandboxes = {};
this.modules = options.modules;
this.globals = options.globals;
};
exports.Loader.prototype = {
_makeRequire: function _makeRequire(rootDir) {
var self = this;
return function require(module) {
if (module == "chrome") {
var chrome = { Cc: Components.classes,
Ci: Components.interfaces,
Cu: Components.utils,
Cr: Components.results,
Cm: Components.manager,
components: Components
};
return chrome;
}
var path = self.fs.resolveModule(rootDir, module);
if (!path)
throw new Error('Module "' + module + '" not found');
if (!(path in self.modules)) {
var options = self.fs.getFile(path);
if (options.filename === undefined)
options.filename = path;
var exports = {};
var sandbox = self.sandboxFactory.createSandbox(options);
self.sandboxes[path] = sandbox;
for (name in self.globals)
sandbox.defineProperty(name, self.globals[name]);
sandbox.defineProperty('require', self._makeRequire(path));
sandbox.evaluate("var exports = {};");
let ES5 = self.modules.es5;
if (ES5) {
let { Object, Array, Function } = sandbox.globalScope;
ES5.init(Object, Array, Function);
}
self.modules[path] = sandbox.getProperty("exports");
sandbox.evaluate(options);
}
return self.modules[path];
};
},
// This is only really used by unit tests and other
// development-related facilities, allowing access to symbols
// defined in the global scope of a module.
findSandboxForModule: function findSandboxForModule(module) {
var path = this.fs.resolveModule(null, module);
if (!path)
throw new Error('Module "' + module + '" not found');
if (!(path in this.sandboxes))
this.require(module);
if (!(path in this.sandboxes))
throw new Error('Internal error: path not in sandboxes: ' +
path);
return this.sandboxes[path];
},
require: function require(module) {
return (this._makeRequire(null))(module);
},
runScript: function runScript(options, extraOutput) {
if (typeof(options) == 'string')
options = {contents: options};
options = {__proto__: options};
var sandbox = this.sandboxFactory.createSandbox(options);
if (extraOutput)
extraOutput.sandbox = sandbox;
for (name in this.globals)
sandbox.defineProperty(name, this.globals[name]);
sandbox.defineProperty('require', this._makeRequire(null));
return sandbox.evaluate(options);
}
};
exports.CompositeFileSystem = function CompositeFileSystem(fses) {
this.fses = fses;
this._pathMap = {};
};
exports.CompositeFileSystem.prototype = {
resolveModule: function resolveModule(base, path) {
for (var i = 0; i < this.fses.length; i++) {
var fs = this.fses[i];
var absPath = fs.resolveModule(base, path);
if (absPath) {
this._pathMap[absPath] = fs;
return absPath;
}
}
return null;
},
getFile: function getFile(path) {
return this._pathMap[path].getFile(path);
}
};
exports.LocalFileSystem = function LocalFileSystem(root) {
if (root === undefined) {
if (!baseURI)
throw new Error("Need a root path for module filesystem");
root = baseURI;
}
if (typeof(root) == 'string')
root = ios.newURI(root, null, baseURI);
if (root instanceof Ci.nsIFile)
root = ios.newFileURI(root);
if (!(root instanceof Ci.nsIURI))
throw new Error('Expected nsIFile, nsIURI, or string for root');
this.root = root.spec;
this._rootURI = root;
this._rootURIDir = getRootDir(root.spec);
};
exports.LocalFileSystem.prototype = {
resolveModule: function resolveModule(base, path) {
path = path + ".js";
var baseURI;
if (!base)
baseURI = this._rootURI;
else
baseURI = ios.newURI(base);
var newURI = ios.newURI(path, null, baseURI);
var channel = NetUtil.newChannel({
uri: newURI,
loadUsingSystemPrincipal: true
});
try {
channel.open2().close();
} catch (e) {
if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) {
throw e;
}
return null;
}
return newURI.spec;
},
getFile: function getFile(path) {
var channel = NetUtil.newChannel({
uri: path,
loadUsingSystemPrincipal: true
});
var iStream = channel.open2();
var ciStream = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
var bufLen = 0x8000;
ciStream.init(iStream, "UTF-8", bufLen,
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var chunk = {};
var data = "";
while (ciStream.readString(bufLen, chunk) > 0)
data += chunk.value;
ciStream.close();
iStream.close();
return {contents: data};
}
};
if (global.window) {
// We're being loaded in a chrome window, or a web page with
// UniversalXPConnect privileges.
global.SecurableModule = exports;
} else if (global.exports) {
// We're being loaded in a SecurableModule.
for (name in exports) {
global.exports[name] = exports[name];
}
} else {
// We're being loaded in a JS module.
global.EXPORTED_SYMBOLS = [];
for (name in exports) {
global.EXPORTED_SYMBOLS.push(name);
global[name] = exports[name];
}
}
})(this);

Просмотреть файл

@ -1,17 +0,0 @@
/* 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 = ['trim', 'vslice'];
var arrays = {}; Components.utils.import('resource://mozmill/stdlib/arrays.js', arrays);
var trim = function (str) {
return (str.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, ""));
}
var vslice = function (str, svalue, evalue) {
var sindex = arrays.indexOf(str, svalue);
var eindex = arrays.rindexOf(str, evalue);
return str.slice(sindex + 1, eindex);
}

Просмотреть файл

@ -1,451 +0,0 @@
/* 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");
}

Просмотреть файл

@ -1,146 +0,0 @@
/*
Copyright (c) 2006 Lawrence Oluyede <l.oluyede@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
startsWith(str, prefix[, start[, end]]) -> bool
Return true if str ends with the specified prefix, false otherwise.
With optional start, test str beginning at that position.
With optional end, stop comparing str at that position.
prefix can also be an array of strings to try.
*/
var EXPORTED_SYMBOLS = ['startsWith', 'endsWith'];
function startsWith(str, prefix, start, end) {
if (arguments.length < 2) {
throw new TypeError('startsWith() requires at least 2 arguments');
}
// check if start and end are null/undefined or a 'number'
if ((start == null) || (isNaN(new Number(start)))) {
start = 0;
}
if ((end == null) || (isNaN(new Number(end)))) {
end = Number.MAX_VALUE;
}
// if it's an array
if (typeof prefix == "object") {
for (var i = 0, j = prefix.length; i < j; i++) {
var res = _stringTailMatch(str, prefix[i], start, end, true);
if (res) {
return true;
}
}
return false;
}
return _stringTailMatch(str, prefix, start, end, true);
}
/*
endsWith(str, suffix[, start[, end]]) -> bool
Return true if str ends with the specified suffix, false otherwise.
With optional start, test str beginning at that position.
With optional end, stop comparing str at that position.
suffix can also be an array of strings to try.
*/
function endsWith(str, suffix, start, end) {
if (arguments.length < 2) {
throw new TypeError('endsWith() requires at least 2 arguments');
}
// check if start and end are null/undefined or a 'number'
if ((start == null) || (isNaN(new Number(start)))) {
start = 0;
}
if ((end == null) || (isNaN(new Number(end)))) {
end = Number.MAX_VALUE;
}
// if it's an array
if (typeof suffix == "object") {
for (var i = 0, j = suffix.length; i < j; i++) {
var res = _stringTailMatch(str, suffix[i], start, end, false);
if (res) {
return true;
}
}
return false;
}
return _stringTailMatch(str, suffix, start, end, false);
}
/*
Matches the end (direction == false) or start (direction == true) of str
against substr, using the start and end arguments. Returns false
if not found and true if found.
*/
function _stringTailMatch(str, substr, start, end, fromStart) {
var len = str.length;
var slen = substr.length;
var indices = _adjustIndices(start, end, len);
start = indices[0]; end = indices[1]; len = indices[2];
if (fromStart) {
if (start + slen > len) {
return false;
}
} else {
if (end - start < slen || start > len) {
return false;
}
if (end - slen > start) {
start = end - slen;
}
}
if (end - start >= slen) {
return str.substr(start, slen) == substr;
}
return false;
}
function _adjustIndices(start, end, len)
{
if (end > len) {
end = len;
} else if (end < 0) {
end += len;
}
if (end < 0) {
end = 0;
}
if (start < 0) {
start += len;
}
if (start < 0) {
start = 0;
}
return [start, end, len];
}

Просмотреть файл

@ -51,9 +51,6 @@ var hh = Cc["@mozilla.org/network/protocol;1?name=http"]
var prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
var mozmillInit = {};
Cu.import("resource://mozmill/driver/mozmill.js", mozmillInit);
XPCOMUtils.defineLazyGetter(this, "fileProtocolHandler", () => {
let fileHandler = Services.io.getProtocolHandler("file");
return fileHandler.QueryInterface(Ci.nsIFileProtocolHandler);
@ -574,23 +571,6 @@ var TPS = {
}
},
MozmillEndTestListener: function TPS__MozmillEndTestListener(obj) {
Logger.logInfo("mozmill endTest: " + JSON.stringify(obj));
if (obj.failed > 0) {
this.DumpError("mozmill test failed, name: " + obj.name + ", reason: " + JSON.stringify(obj.fails));
} else if ("skipped" in obj && obj.skipped) {
this.DumpError("mozmill test failed, name: " + obj.name + ", reason: " + obj.skipped_reason);
} else {
CommonUtils.namedTimer(function() {
this.FinishAsyncOperation();
}, 2000, this, "postmozmilltest");
}
},
MozmillSetTestListener: function TPS__MozmillSetTestListener(obj) {
Logger.logInfo("mozmill setTest: " + obj.name);
},
async Cleanup() {
try {
await this.WipeServer();
@ -1039,25 +1019,6 @@ var TPS = {
this._enabledEngines = names;
},
RunMozmillTest: function TPS__RunMozmillTest(testfile) {
var mozmillfile = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsIFile);
if (hh.oscpu.toLowerCase().indexOf("windows") > -1) {
let re = /\/(\w)\/(.*)/;
this.config.testdir = this.config.testdir.replace(re, "$1://$2").replace(/\//g, "\\");
}
mozmillfile.initWithPath(this.config.testdir);
mozmillfile.appendRelativePath(testfile);
Logger.logInfo("Running mozmill test " + mozmillfile.path);
var frame = {};
Cu.import("resource://mozmill/modules/frame.js", frame);
frame.events.addListener("setTest", this.MozmillSetTestListener.bind(this));
frame.events.addListener("endTest", this.MozmillEndTestListener.bind(this));
this.StartAsyncOperation();
frame.runTestFile(mozmillfile.path, null);
},
/**
* Returns a promise that resolves when a specific observer notification is
* resolved. This is similar to the various waitFor* functions, although is

Просмотреть файл

@ -446,7 +446,6 @@ class TPSTestRunner(object):
# build our tps.xpi extension
self.extensions = []
self.extensions.append(os.path.join(self.extensionDir, 'tps'))
self.extensions.append(os.path.join(self.extensionDir, "mozmill"))
# build the test list
try:

Просмотреть файл

@ -10,8 +10,6 @@
"AlertsHelper.jsm": [],
"AppData.jsm": ["makeFakeAppDir"],
"AppInfo.jsm": ["newAppInfo", "getAppInfo", "updateAppInfo"],
"arrays.js": ["inArray", "getSet", "indexOf", "remove", "rindexOf", "compare"],
"assertions.js": ["Assert", "Expect"],
"async.js": ["Async"],
"AsyncSpellCheckTestHelper.jsm": ["onSpellCheck"],
"AutoMigrate.jsm": ["AutoMigrate"],
@ -39,7 +37,6 @@
"ContentCrashHandlers.jsm": ["TabCrashHandler", "PluginCrashReporter", "UnsubmittedCrashHandler"],
"ContentObservers.js": [],
"ContentPrefUtils.jsm": ["ContentPref", "cbHandleResult", "cbHandleError", "cbHandleCompletion", "safeCallback", "_methodsCallableFromChild"],
"controller.js": ["MozMillController", "globalEventRegistry", "sleep", "windowMap"],
"cookies.js": ["Cookies"],
"CoverageUtils.jsm": ["CoverageCollector"],
"CrashManagerTest.jsm": ["configureLogging", "getManager", "sleep", "TestingCrashManager"],
@ -51,17 +48,13 @@
"distribution.js": ["DistributionCustomizer"],
"DNSTypes.jsm": ["DNS_QUERY_RESPONSE_CODES", "DNS_AUTHORITATIVE_ANSWER_CODES", "DNS_CLASS_CODES", "DNS_RECORD_TYPES"],
"doctor.js": ["Doctor"],
"dom.js": ["getAttributes"],
"DOMRequestHelper.jsm": ["DOMRequestIpcHelper"],
"DownloadCore.jsm": ["Download", "DownloadSource", "DownloadTarget", "DownloadError", "DownloadSaver", "DownloadCopySaver", "DownloadLegacySaver", "DownloadPDFSaver"],
"DownloadList.jsm": ["DownloadList", "DownloadCombinedList", "DownloadSummary"],
"elementslib.js": ["ID", "Link", "XPath", "Selector", "Name", "Anon", "AnonXPath", "Lookup", "_byID", "_byName", "_byAttrib", "_byAnonAttrib"],
"engines.js": ["EngineManager", "Engine", "SyncEngine", "Tracker", "Store", "Changeset"],
"enginesync.js": ["EngineSynchronizer"],
"errors.js": ["BaseError", "ApplicationQuitError", "AssertionError", "TimeoutError"],
"evaluate.js": ["evaluate", "sandbox", "Sandboxes"],
"event-emitter.js": ["EventEmitter"],
"EventUtils.js": ["disableNonTestMouseEvents", "sendMouseEvent", "sendChar", "sendString", "sendKey", "synthesizeMouse", "synthesizeTouch", "synthesizeMouseAtPoint", "synthesizeTouchAtPoint", "synthesizeMouseAtCenter", "synthesizeTouchAtCenter", "synthesizeWheel", "synthesizeKey", "synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent", "synthesizeText", "synthesizeComposition", "synthesizeQuerySelectedText"],
"Extension.jsm": ["Extension", "ExtensionData"],
"ExtensionAPI.jsm": ["ExtensionAPI", "ExtensionAPIs"],
"ExtensionsUI.jsm": ["ExtensionsUI"],
@ -79,7 +72,6 @@
"forms.jsm": ["FormData"],
"FormAutofillHeuristics.jsm": ["FormAutofillHeuristics", "LabelUtils"],
"FormAutofillSync.jsm": ["AddressesEngine", "CreditCardsEngine"],
"frame.js": ["Collector", "Runner", "events", "runTestFile", "log", "timers", "persisted", "shutdownApplication"],
"FrameScriptManager.jsm": ["getNewLoaderID"],
"fxa_utils.js": ["initializeIdentityWithTokenServerResponse"],
"fxaccounts.jsm": ["Authentication"],
@ -135,18 +127,13 @@
"Messaging.jsm": ["sendMessageToJava", "Messaging", "EventDispatcher"],
"microformat-shiv.js": ["Microformats"],
"MigrationUtils.jsm": ["MigrationUtils", "MigratorPrototype"],
"mozelement.js": ["Elem", "Selector", "ID", "Link", "XPath", "Name", "Lookup", "MozMillElement", "MozMillCheckBox", "MozMillRadio", "MozMillDropList", "MozMillTextBox", "subclasses"],
"mozmill.js": ["controller", "utils", "elementslib", "os", "getBrowserController", "newBrowserController", "getAddonsController", "getPreferencesController", "newMail3PaneController", "getMail3PaneController", "wm", "platform", "getAddrbkController", "getMsgComposeController", "getDownloadsController", "Application", "findElement", "getPlacesController", "isMac", "isLinux", "isWindows", "firePythonCallback", "getAddons"],
"msgbroker.js": ["addListener", "addObject", "removeListener", "sendMessage", "log", "pass", "fail"],
"MulticastDNSAndroid.jsm": ["MulticastDNS"],
"NativeMessaging.jsm": ["NativeApp"],
"NotificationDB.jsm": [],
"nsFormAutoCompleteResult.jsm": ["FormAutoCompleteResult"],
"objects.js": ["getLength"],
"observers.js": ["Observers"],
"offlineAppCache.jsm": ["OfflineAppCacheHelper"],
"OrientationChangeHandler.jsm": [],
"os.js": ["listDirectory", "getFileForPath", "abspath", "getPlatform"],
"osfile.jsm": ["OS", "require"],
"osfile_async_front.jsm": ["OS"],
"osfile_native.jsm": ["read"],
@ -195,11 +182,9 @@
"SignInToWebsite.jsm": ["SignInToWebsiteController"],
"Social.jsm": ["Social", "OpenGraphBuilder", "DynamicResizeWatcher", "sizeSocialPanelToContent"],
"SpecialPowersObserver.jsm": ["SpecialPowersObserver", "SpecialPowersObserverFactory"],
"stack.js": ["findCallerFrame"],
"StateMachineHelper.jsm": ["State", "CommandType"],
"status.js": ["Status"],
"storageserver.js": ["ServerBSO", "StorageServerCallback", "StorageServerCollection", "StorageServer", "storageServerForUsers"],
"strings.js": ["trim", "vslice"],
"StructuredLog.jsm": ["StructuredLogger", "StructuredFormatter"],
"StyleEditorUtil.jsm": ["getString", "assert", "log", "text", "wire", "showFilePicker"],
"subprocess_common.jsm": ["BaseProcess", "PromiseWorker", "SubprocessConstants"],
@ -222,7 +207,7 @@
"UpdateTelemetry.jsm": ["AUSTLMY"],
"UpdateTopLevelContentWindowIDHelper.jsm": ["trackBrowserWindow"],
"util.js": ["getChromeWindow", "Utils", "Svc"],
"utils.js": ["applicationName", "assert", "Copy", "getBrowserObject", "getChromeWindow", "getWindows", "getWindowByTitle", "getWindowByType", "getWindowId", "getMethodInWindows", "getPreference", "saveDataURL", "setPreference", "sleep", "startTimer", "stopTimer", "takeScreenshot", "unwrapNode", "waitFor", "btoa", "encryptPayload", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils", "promiseZeroTimer", "promiseNamedTimer", "getLoginTelemetryScalar"],
"utils.js": ["btoa", "encryptPayload", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils", "promiseZeroTimer", "promiseNamedTimer", "getLoginTelemetryScalar"],
"Utils.jsm": ["Utils", "Logger", "PivotContext", "PrefCache"],
"VariablesView.jsm": ["VariablesView", "escapeHTML"],
"VariablesViewController.jsm": ["VariablesViewController", "StackFrameUtils"],
@ -230,10 +215,8 @@
"vtt.jsm": ["WebVTT"],
"WebChannel.jsm": ["WebChannel", "WebChannelBroker"],
"WindowDraggingUtils.jsm": ["WindowDraggingElement"],
"windows.js": ["init", "map"],
"windows.jsm": ["BrowserWindows"],
"WindowsJumpLists.jsm": ["WinTaskbarJumpList"],
"WindowsPreviewPerTab.jsm": ["AeroPeek"],
"withs.js": ["startsWith", "endsWith"],
"xul-app.jsm": ["XulApp"]
}