зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1415957 - Remove MozMill from TPS. r=tcsc
MozReview-Commit-ID: HyBXrNqhzIf --HG-- extra : rebase_source : da197fa045f70621d5549e961876cfc6ce15264b
This commit is contained in:
Родитель
1acca47aff
Коммит
1873a289f7
|
@ -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 ' '),
|
||||
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"]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче