Bug 1445551: Part 1a - Add uses-unsafe-cpows annotation to mochitest harness. r=mconley

This allows us to specifically whitelist browser mochitests which still rely
on unsafe CPOWs, and run them in a separate Sandbox global with permissive
CPOWs enabled.

The test harness and most of the in-tree tests will run with permissive CPOWs
disabled, like the rest of the browser.

MozReview-Commit-ID: CxIkuxr5PXJ

--HG--
extra : rebase_source : 897c951e5ea84db58e92c8b627679f029ebf4a42
This commit is contained in:
Kris Maglione 2018-03-13 19:11:10 -07:00
Родитель 41ec48d989
Коммит 63ee0f9a5a
5 изменённых файлов: 79 добавлений и 19 удалений

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

@ -110,6 +110,7 @@
function browserTest(aTestFile) {
this.path = aTestFile['url'];
this.expected = aTestFile['expected'];
this.usesUnsafeCPOWs = aTestFile['uses-unsafe-cpows'] || false;
this.dumper = gDumper;
this.results = [];
this.scope = null;

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

@ -379,6 +379,16 @@ function Tester(aTests, structuredLogger, aCallback) {
this._scriptLoader = Services.scriptloader;
this.EventUtils = {};
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", this.EventUtils);
// In order to allow existing tests to continue using unsafe CPOWs
// with EventUtils, we need to load a separate copy into a sandbox
// which has unsafe CPOW usage whitelisted.
this.cpowSandbox = Cu.Sandbox(window, {sandboxPrototype: window});
Cu.permitCPOWsInScope(this.cpowSandbox);
this.cpowEventUtils = new this.cpowSandbox.Object();
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", this.cpowEventUtils);
var simpleTestScope = {};
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js", simpleTestScope);
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js", simpleTestScope);
@ -970,16 +980,17 @@ Tester.prototype = {
let currentTest = this.currentTest;
// Import utils in the test scope.
this.currentTest.scope.EventUtils = this.EventUtils;
this.currentTest.scope.SimpleTest = this.SimpleTest;
this.currentTest.scope.gTestPath = this.currentTest.path;
this.currentTest.scope.Task = this.Task;
this.currentTest.scope.ContentTask = this.ContentTask;
this.currentTest.scope.BrowserTestUtils = this.BrowserTestUtils;
this.currentTest.scope.TestUtils = this.TestUtils;
this.currentTest.scope.ExtensionTestUtils = this.ExtensionTestUtils;
let {scope} = this.currentTest;
scope.EventUtils = this.currentTest.usesUnsafeCPOWs ? this.cpowEventUtils : this.EventUtils;
scope.SimpleTest = this.SimpleTest;
scope.gTestPath = this.currentTest.path;
scope.Task = this.Task;
scope.ContentTask = this.ContentTask;
scope.BrowserTestUtils = this.BrowserTestUtils;
scope.TestUtils = this.TestUtils;
scope.ExtensionTestUtils = this.ExtensionTestUtils;
// Pass a custom report function for mochitest style reporting.
this.currentTest.scope.Assert = new this.Assert(function(err, message, stack) {
scope.Assert = new this.Assert(function(err, message, stack) {
currentTest.addResult(new testResult(err ? {
name: err.message,
ex: err.stack,
@ -996,7 +1007,7 @@ Tester.prototype = {
this.ContentTask.setTestScope(currentScope);
// Allow Assert.jsm methods to be tacked to the current scope.
this.currentTest.scope.export_assertions = function() {
scope.export_assertions = function() {
for (let func in this.Assert) {
this[func] = this.Assert[func].bind(this.Assert);
}
@ -1005,11 +1016,11 @@ Tester.prototype = {
// Override SimpleTest methods with ours.
SIMPLETEST_OVERRIDES.forEach(function(m) {
this.SimpleTest[m] = this[m];
}, this.currentTest.scope);
}, scope);
//load the tools to work with chrome .jar and remote
try {
this._scriptLoader.loadSubScript("chrome://mochikit/content/chrome-harness.js", this.currentTest.scope);
this._scriptLoader.loadSubScript("chrome://mochikit/content/chrome-harness.js", scope);
} catch (ex) { /* no chrome-harness tools */ }
// Import head.js script if it exists.
@ -1017,7 +1028,7 @@ Tester.prototype = {
this.currentTest.path.substr(0, this.currentTest.path.lastIndexOf("/"));
var headPath = currentTestDirPath + "/head.js";
try {
this._scriptLoader.loadSubScript(headPath, this.currentTest.scope);
this._scriptLoader.loadSubScript(headPath, scope);
} catch (ex) {
// Ignore if no head.js exists, but report all other errors. Note this
// will also ignore an existing head.js attempting to import a missing
@ -1032,8 +1043,7 @@ Tester.prototype = {
// Import the test script.
try {
this._scriptLoader.loadSubScript(this.currentTest.path,
this.currentTest.scope);
this._scriptLoader.loadSubScript(this.currentTest.path, scope);
// Run the test
this.lastStartTime = Date.now();
if (this.currentTest.scope.__tasks) {
@ -1091,8 +1101,8 @@ Tester.prototype = {
}
this.finish();
}.bind(currentScope));
} else if (typeof this.currentTest.scope.test == "function") {
this.currentTest.scope.test();
} else if (typeof scope.test == "function") {
scope.test();
} else {
throw "This test didn't call add_task, nor did it define a generatorTest() function, nor did it define a test() function, so we don't know how to run it.";
}
@ -1372,6 +1382,18 @@ function testScope(aTester, aTest, expected) {
self.__tester.structuredLogger.activateBuffering();
})
};
// If we're running a test that requires unsafe CPOWs, create a
// separate sandbox scope, with CPOWS whitelisted, for that test, and
// mirror all of our properties onto it. Test files will be loaded
// into this sandbox.
//
// Otherwise, load test files directly into the testScope instance.
if (aTest.usesUnsafeCPOWs) {
let sandbox = this._createSandbox();
Cu.permitCPOWsInScope(sandbox);
return sandbox;
}
}
function decorateTaskFn(fn) {
@ -1400,6 +1422,29 @@ testScope.prototype = {
ExtensionTestUtils: null,
Assert: null,
_createSandbox() {
let sandbox = Cu.Sandbox(window, {sandboxPrototype: window});
for (let prop in this) {
if (typeof this[prop] == "function") {
sandbox[prop] = this[prop].bind(this);
} else {
Object.defineProperty(sandbox, prop, {
configurable: true,
enumerable: true,
get: () => {
return this[prop];
},
set: (value) => {
this[prop] = value;
}
});
}
}
return sandbox;
},
/**
* Add a test function which is a Task function.
*

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

@ -26,10 +26,10 @@ function parseTestManifest(testManifest, params, callback) {
}
if (params.testRoot != 'tests' && params.testRoot !== undefined) {
name = params.baseurl + '/' + params.testRoot + '/' + path;
links[name] = {'test': {'url': name, 'expected': obj['expected']}};
links[name] = {'test': {'url': name, 'expected': obj['expected'], 'uses-unsafe-cpows': obj['uses-unsafe-cpows']}};
} else {
name = params.testPrefix + path;
paths.push({'test': {'url': name, 'expected': obj['expected']}});
paths.push({'test': {'url': name, 'expected': obj['expected'], 'uses-unsafe-cpows': obj['uses-unsafe-cpows']}});
}
}
if (paths.length > 0) {

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

@ -1524,6 +1524,8 @@ toolbar#nav-bar {
testob['disabled'] = test['disabled']
if 'expected' in test:
testob['expected'] = test['expected']
if 'uses-unsafe-cpows' in test:
testob['uses-unsafe-cpows'] = test['uses-unsafe-cpows'] == 'true'
if 'scheme' in test:
testob['scheme'] = test['scheme']
if options.failure_pattern_file:

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

@ -176,6 +176,10 @@ function sendMouseEvent(aEvent, aTarget, aWindow) {
ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
buttonArg, relatedTargetArg);
// If documentURIObject exists or `window` is a stub object, we're in
// a chrome scope, so don't bother trying to go through SpecialPowers.
if (!window.document || window.document.documentURIObject)
return aTarget.dispatchEvent(event);
return SpecialPowers.dispatchEvent(aWindow, aTarget, event);
}
@ -1240,6 +1244,14 @@ function _getDOMWindowUtils(aWindow = window)
aWindow = window;
}
// If documentURIObject exists or `window` is a stub object, we're in
// a chrome scope, so don't bother trying to go through SpecialPowers.
if (!window.document || window.document.documentURIObject) {
return aWindow
.QueryInterface(_EU_Ci.nsIInterfaceRequestor)
.getInterface(_EU_Ci.nsIDOMWindowUtils);
}
// we need parent.SpecialPowers for:
// layout/base/tests/test_reftests_with_caret.html
// chrome: toolkit/content/tests/chrome/test_findbar.xul