983 lines
28 KiB
JavaScript
983 lines
28 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import { globals } from "resource://reftest/globals.sys.mjs";
|
|
|
|
const {
|
|
NS_GFXINFO_CONTRACTID,
|
|
|
|
TYPE_REFTEST_EQUAL,
|
|
TYPE_REFTEST_NOTEQUAL,
|
|
TYPE_LOAD,
|
|
TYPE_SCRIPT,
|
|
TYPE_PRINT,
|
|
|
|
EXPECTED_PASS,
|
|
EXPECTED_FAIL,
|
|
EXPECTED_RANDOM,
|
|
EXPECTED_FUZZY,
|
|
|
|
PREF_BOOLEAN,
|
|
PREF_STRING,
|
|
PREF_INTEGER,
|
|
|
|
FOCUS_FILTER_NEEDS_FOCUS_TESTS,
|
|
FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS,
|
|
|
|
g,
|
|
} = globals;
|
|
|
|
import { NetUtil } from "resource://gre/modules/NetUtil.sys.mjs";
|
|
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
|
|
|
const RE_PROTOCOL = /^\w+:/;
|
|
const RE_PREF_ITEM = /^(|test-|ref-)pref\((.+?),(.*)\)$/;
|
|
|
|
export function ReadTopManifest(aFileURL, aFilter, aManifestID) {
|
|
var url = g.ioService.newURI(aFileURL);
|
|
if (!url) {
|
|
throw new Error("Expected a file or http URL for the manifest.");
|
|
}
|
|
|
|
g.manifestsLoaded = {};
|
|
ReadManifest(url, aFilter, aManifestID);
|
|
}
|
|
|
|
// Note: If you materially change the reftest manifest parsing,
|
|
// please keep the parser in layout/tools/reftest/__init__.py in sync.
|
|
// (in particular keep CONDITIONS_JS_TO_MP in sync)
|
|
// eslint-disable-next-line complexity
|
|
function ReadManifest(aURL, aFilter, aManifestID) {
|
|
// Ensure each manifest is only read once. This assumes that manifests that
|
|
// are included with filters will be read via their include before they are
|
|
// read directly in the case of a duplicate
|
|
if (g.manifestsLoaded.hasOwnProperty(aURL.spec)) {
|
|
if (g.manifestsLoaded[aURL.spec] === null) {
|
|
return;
|
|
}
|
|
aFilter = [aFilter[0], aFilter[1], true];
|
|
}
|
|
g.manifestsLoaded[aURL.spec] = aFilter[1];
|
|
|
|
var listURL = aURL;
|
|
var channel = NetUtil.newChannel({
|
|
uri: aURL,
|
|
loadUsingSystemPrincipal: true,
|
|
});
|
|
try {
|
|
var inputStream = channel.open();
|
|
} catch (e) {
|
|
g.logger.error("failed to open manifest at : " + aURL.spec);
|
|
throw new Error(e);
|
|
}
|
|
if (channel instanceof Ci.nsIHttpChannel && channel.responseStatus != 200) {
|
|
g.logger.error("HTTP ERROR : " + channel.responseStatus);
|
|
}
|
|
var streamBuf = getStreamContent(inputStream);
|
|
inputStream.close();
|
|
var lines = streamBuf.split(/\n|\r|\r\n/);
|
|
|
|
// The sandbox for fails-if(), etc., condition evaluation. This is not
|
|
// always required and so is created on demand.
|
|
var sandbox;
|
|
function GetOrCreateSandbox() {
|
|
if (!sandbox) {
|
|
sandbox = BuildConditionSandbox(aURL);
|
|
}
|
|
return sandbox;
|
|
}
|
|
|
|
var mozharness_test_paths = Services.prefs.getBoolPref(
|
|
"reftest.mozharness_test_paths"
|
|
);
|
|
var lineNo = 0;
|
|
var urlprefix = "";
|
|
var defaults = [];
|
|
var defaultTestPrefSettings = [],
|
|
defaultRefPrefSettings = [];
|
|
if (g.compareRetainedDisplayLists) {
|
|
AddRetainedDisplayListTestPrefs(
|
|
GetOrCreateSandbox(),
|
|
defaultTestPrefSettings,
|
|
defaultRefPrefSettings
|
|
);
|
|
}
|
|
for (var str of lines) {
|
|
++lineNo;
|
|
if (str.charAt(0) == "#") {
|
|
continue;
|
|
} // entire line was a comment
|
|
var i = str.search(/\s+#/);
|
|
if (i >= 0) {
|
|
str = str.substring(0, i);
|
|
}
|
|
// strip leading and trailing whitespace
|
|
str = str.replace(/^\s*/, "").replace(/\s*$/, "");
|
|
if (!str || str == "") {
|
|
continue;
|
|
}
|
|
var items = str.split(/\s+/); // split on whitespace
|
|
|
|
if (items[0] == "url-prefix") {
|
|
if (items.length != 2) {
|
|
throw new Error(
|
|
"url-prefix requires one url in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo
|
|
);
|
|
}
|
|
urlprefix = items[1];
|
|
continue;
|
|
}
|
|
|
|
if (items[0] == "defaults") {
|
|
items.shift();
|
|
defaults = items;
|
|
continue;
|
|
}
|
|
|
|
var expected_status = EXPECTED_PASS;
|
|
var allow_silent_fail = false;
|
|
var minAsserts = 0;
|
|
var maxAsserts = 0;
|
|
var needs_focus = false;
|
|
var slow = false;
|
|
var skip = false;
|
|
var testPrefSettings = defaultTestPrefSettings.concat();
|
|
var refPrefSettings = defaultRefPrefSettings.concat();
|
|
var fuzzy_delta = { min: 0, max: 2 };
|
|
var fuzzy_pixels = { min: 0, max: 1 };
|
|
var chaosMode = false;
|
|
var wrCapture = { test: false, ref: false };
|
|
var nonSkipUsed = false;
|
|
var noAutoFuzz = false;
|
|
|
|
var origLength = items.length;
|
|
items = defaults.concat(items);
|
|
var modifiers = [...items];
|
|
while (
|
|
items[0].match(
|
|
/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy|chaos-mode|wr-capture|wr-capture-ref|noautofuzz)/
|
|
)
|
|
) {
|
|
var item = items.shift();
|
|
var stat;
|
|
var cond;
|
|
var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
|
|
if (m) {
|
|
stat = m[1];
|
|
// Note: m[2] contains the parentheses, and we want them.
|
|
cond = Cu.evalInSandbox(m[2], GetOrCreateSandbox());
|
|
} else if (item.match(/^(fails|random|skip)$/)) {
|
|
stat = item;
|
|
cond = true;
|
|
} else if (item == "needs-focus") {
|
|
needs_focus = true;
|
|
cond = false;
|
|
} else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
|
|
cond = false;
|
|
minAsserts = Number(m[1]);
|
|
maxAsserts = m[2] == undefined ? minAsserts : Number(m[2].substring(1));
|
|
} else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) {
|
|
cond = false;
|
|
if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) {
|
|
minAsserts = Number(m[2]);
|
|
maxAsserts =
|
|
m[3] == undefined ? minAsserts : Number(m[3].substring(1));
|
|
}
|
|
} else if (item == "slow") {
|
|
cond = false;
|
|
slow = true;
|
|
} else if ((m = item.match(/^require-or\((.*?)\)$/))) {
|
|
var args = m[1].split(/,/);
|
|
if (args.length != 2) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": wrong number of args to require-or"
|
|
);
|
|
}
|
|
var [precondition_str, fallback_action] = args;
|
|
var preconditions = precondition_str.split(/&&/);
|
|
cond = false;
|
|
for (var precondition of preconditions) {
|
|
if (precondition === "debugMode") {
|
|
// Currently unimplemented. Requires asynchronous
|
|
// JSD call + getting an event while no JS is running
|
|
stat = fallback_action;
|
|
cond = true;
|
|
break;
|
|
} else if (precondition === "true") {
|
|
// For testing
|
|
} else {
|
|
// Unknown precondition. Assume it is unimplemented.
|
|
stat = fallback_action;
|
|
cond = true;
|
|
break;
|
|
}
|
|
}
|
|
} else if ((m = item.match(/^slow-if\((.*?)\)$/))) {
|
|
cond = false;
|
|
if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) {
|
|
slow = true;
|
|
}
|
|
} else if (item == "silentfail") {
|
|
cond = false;
|
|
allow_silent_fail = true;
|
|
} else if ((m = item.match(RE_PREF_ITEM))) {
|
|
cond = false;
|
|
if (
|
|
!AddPrefSettings(
|
|
m[1],
|
|
m[2],
|
|
m[3],
|
|
GetOrCreateSandbox(),
|
|
testPrefSettings,
|
|
refPrefSettings
|
|
)
|
|
) {
|
|
throw new Error(
|
|
"Error in pref value in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo
|
|
);
|
|
}
|
|
} else if ((m = item.match(/^fuzzy\((\d+)-(\d+),(\d+)-(\d+)\)$/))) {
|
|
cond = false;
|
|
expected_status = EXPECTED_FUZZY;
|
|
fuzzy_delta = ExtractRange(m, 1);
|
|
fuzzy_pixels = ExtractRange(m, 3);
|
|
} else if (
|
|
(m = item.match(/^fuzzy-if\((.*?),(\d+)-(\d+),(\d+)-(\d+)\)$/))
|
|
) {
|
|
cond = false;
|
|
if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) {
|
|
expected_status = EXPECTED_FUZZY;
|
|
fuzzy_delta = ExtractRange(m, 2);
|
|
fuzzy_pixels = ExtractRange(m, 4);
|
|
}
|
|
} else if (item == "chaos-mode") {
|
|
cond = false;
|
|
chaosMode = true;
|
|
} else if (item == "wr-capture") {
|
|
cond = false;
|
|
wrCapture.test = true;
|
|
} else if (item == "wr-capture-ref") {
|
|
cond = false;
|
|
wrCapture.ref = true;
|
|
} else if (item == "noautofuzz") {
|
|
cond = false;
|
|
noAutoFuzz = true;
|
|
} else {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": unexpected item " +
|
|
item
|
|
);
|
|
}
|
|
|
|
if (stat != "skip") {
|
|
nonSkipUsed = true;
|
|
}
|
|
|
|
if (cond) {
|
|
if (stat == "fails") {
|
|
expected_status = EXPECTED_FAIL;
|
|
} else if (stat == "random") {
|
|
expected_status = EXPECTED_RANDOM;
|
|
} else if (stat == "skip") {
|
|
skip = true;
|
|
} else if (stat == "silentfail") {
|
|
allow_silent_fail = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (items.length > origLength) {
|
|
// Implies we broke out of the loop before we finished processing
|
|
// defaults. This means defaults contained an invalid token.
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": invalid defaults token '" +
|
|
items[0] +
|
|
"'"
|
|
);
|
|
}
|
|
|
|
if (minAsserts > maxAsserts) {
|
|
throw new Error(
|
|
"Bad range in manifest file " + aURL.spec + " line " + lineNo
|
|
);
|
|
}
|
|
|
|
var runHttp = false;
|
|
var httpDepth;
|
|
if (items[0] == "HTTP") {
|
|
runHttp = aURL.scheme == "file"; // We can't yet run the local HTTP server
|
|
// for non-local reftests.
|
|
httpDepth = 0;
|
|
items.shift();
|
|
} else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) {
|
|
// Accept HTTP(..), HTTP(../..), HTTP(../../..), etc.
|
|
runHttp = aURL.scheme == "file"; // We can't yet run the local HTTP server
|
|
// for non-local reftests.
|
|
httpDepth = (items[0].length - 5) / 3;
|
|
items.shift();
|
|
}
|
|
|
|
// do not prefix the url for include commands or urls specifying
|
|
// a protocol
|
|
if (urlprefix && items[0] != "include") {
|
|
if (items.length > 1 && !items[1].match(RE_PROTOCOL)) {
|
|
items[1] = urlprefix + items[1];
|
|
}
|
|
if (items.length > 2 && !items[2].match(RE_PROTOCOL)) {
|
|
items[2] = urlprefix + items[2];
|
|
}
|
|
}
|
|
|
|
var principal = Services.scriptSecurityManager.createContentPrincipal(
|
|
aURL,
|
|
{}
|
|
);
|
|
|
|
if (items[0] == "include") {
|
|
if (items.length != 2) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": incorrect number of arguments to include"
|
|
);
|
|
}
|
|
if (runHttp) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": use of include with http"
|
|
);
|
|
}
|
|
|
|
// If the expected_status is EXPECTED_PASS (the default) then allow
|
|
// the include. If 'skip' is true, that means there was a skip
|
|
// or skip-if annotation (with a true condition) on this include
|
|
// statement, so we should skip the include. Any other expected_status
|
|
// is disallowed since it's nonintuitive as to what the intended
|
|
// effect is.
|
|
if (nonSkipUsed) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": include statement with annotation other than 'skip' or 'skip-if'"
|
|
);
|
|
} else if (skip) {
|
|
g.logger.info(
|
|
"Skipping included manifest at " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
" due to matching skip condition"
|
|
);
|
|
} else {
|
|
// poor man's assertion
|
|
if (expected_status != EXPECTED_PASS) {
|
|
throw new Error(
|
|
"Error in manifest file parsing code: we should never get expected_status=" +
|
|
expected_status +
|
|
" when nonSkipUsed=false (from " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
")"
|
|
);
|
|
}
|
|
|
|
var incURI = g.ioService.newURI(items[1], null, listURL);
|
|
Services.scriptSecurityManager.checkLoadURIWithPrincipal(
|
|
principal,
|
|
incURI,
|
|
Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT
|
|
);
|
|
|
|
// Cannot use nsIFile or similar to manipulate the manifest ID; although it appears
|
|
// path-like, it does not refer to an actual path in the filesystem.
|
|
var newManifestID = aManifestID;
|
|
var included = items[1];
|
|
// Remove included manifest file name.
|
|
// eg. dir1/dir2/reftest.list -> dir1/dir2
|
|
var pos = included.lastIndexOf("/");
|
|
if (pos <= 0) {
|
|
included = "";
|
|
} else {
|
|
included = included.substring(0, pos);
|
|
}
|
|
// Simplify references to parent directories.
|
|
// eg. dir1/dir2/../dir3 -> dir1/dir3
|
|
while (included.startsWith("../")) {
|
|
pos = newManifestID.lastIndexOf("/");
|
|
if (pos < 0) {
|
|
pos = 0;
|
|
}
|
|
newManifestID = newManifestID.substring(0, pos);
|
|
included = included.substring(3);
|
|
}
|
|
// Use a new manifest ID if the included manifest is in a different directory.
|
|
if (included.length) {
|
|
if (newManifestID.length) {
|
|
newManifestID = newManifestID + "/" + included;
|
|
} else {
|
|
// parent directory includes may refer to the topsrcdir
|
|
newManifestID = included;
|
|
}
|
|
}
|
|
if (mozharness_test_paths) {
|
|
g.logger.info(
|
|
"Not recursively reading when MOZHARNESS_TEST_PATHS is set: " +
|
|
items[1]
|
|
);
|
|
} else {
|
|
ReadManifest(incURI, aFilter, newManifestID);
|
|
}
|
|
}
|
|
} else if (items[0] == TYPE_LOAD || items[0] == TYPE_SCRIPT) {
|
|
let type = items[0];
|
|
if (items.length != 2) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": incorrect number of arguments to " +
|
|
type
|
|
);
|
|
}
|
|
if (type == TYPE_LOAD && expected_status != EXPECTED_PASS) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": incorrect known failure type for load test"
|
|
);
|
|
}
|
|
AddTestItem(
|
|
{
|
|
type,
|
|
expected: expected_status,
|
|
manifest: aURL.spec,
|
|
manifestID: TestIdentifier(aURL.spec, aManifestID),
|
|
allowSilentFail: allow_silent_fail,
|
|
minAsserts,
|
|
maxAsserts,
|
|
needsFocus: needs_focus,
|
|
slow,
|
|
skip,
|
|
prefSettings1: testPrefSettings,
|
|
prefSettings2: refPrefSettings,
|
|
fuzzyMinDelta: fuzzy_delta.min,
|
|
fuzzyMaxDelta: fuzzy_delta.max,
|
|
fuzzyMinPixels: fuzzy_pixels.min,
|
|
fuzzyMaxPixels: fuzzy_pixels.max,
|
|
runHttp,
|
|
httpDepth,
|
|
url1: items[1],
|
|
url2: null,
|
|
chaosMode,
|
|
wrCapture,
|
|
noAutoFuzz,
|
|
modifiers,
|
|
},
|
|
aFilter,
|
|
aManifestID
|
|
);
|
|
} else if (
|
|
items[0] == TYPE_REFTEST_EQUAL ||
|
|
items[0] == TYPE_REFTEST_NOTEQUAL ||
|
|
items[0] == TYPE_PRINT
|
|
) {
|
|
if (items.length != 3) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": incorrect number of arguments to " +
|
|
items[0]
|
|
);
|
|
}
|
|
|
|
if (
|
|
items[0] == TYPE_REFTEST_NOTEQUAL &&
|
|
expected_status == EXPECTED_FUZZY &&
|
|
(fuzzy_delta.min > 0 || fuzzy_pixels.min > 0)
|
|
) {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": minimum fuzz must be zero for tests of type " +
|
|
items[0]
|
|
);
|
|
}
|
|
|
|
let type = items[0];
|
|
if (g.compareRetainedDisplayLists) {
|
|
type = TYPE_REFTEST_EQUAL;
|
|
|
|
// We expect twice as many assertion failures when comparing
|
|
// tests because we run each test twice.
|
|
minAsserts *= 2;
|
|
maxAsserts *= 2;
|
|
|
|
// Skip the test if it is expected to fail in both modes.
|
|
// It would unexpectedly "pass" in comparison mode mode when
|
|
// comparing the two failures, which is not a useful result.
|
|
if (
|
|
expected_status === EXPECTED_FAIL ||
|
|
expected_status === EXPECTED_RANDOM
|
|
) {
|
|
skip = true;
|
|
}
|
|
}
|
|
|
|
AddTestItem(
|
|
{
|
|
type,
|
|
expected: expected_status,
|
|
manifest: aURL.spec,
|
|
manifestID: TestIdentifier(aURL.spec, aManifestID),
|
|
allowSilentFail: allow_silent_fail,
|
|
minAsserts,
|
|
maxAsserts,
|
|
needsFocus: needs_focus,
|
|
slow,
|
|
skip,
|
|
prefSettings1: testPrefSettings,
|
|
prefSettings2: refPrefSettings,
|
|
fuzzyMinDelta: fuzzy_delta.min,
|
|
fuzzyMaxDelta: fuzzy_delta.max,
|
|
fuzzyMinPixels: fuzzy_pixels.min,
|
|
fuzzyMaxPixels: fuzzy_pixels.max,
|
|
runHttp,
|
|
httpDepth,
|
|
url1: items[1],
|
|
url2: items[2],
|
|
chaosMode,
|
|
wrCapture,
|
|
noAutoFuzz,
|
|
modifiers,
|
|
},
|
|
aFilter,
|
|
aManifestID
|
|
);
|
|
} else {
|
|
throw new Error(
|
|
"Error in manifest file " +
|
|
aURL.spec +
|
|
" line " +
|
|
lineNo +
|
|
": unknown test type " +
|
|
items[0]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read all available data from an input stream and return it
|
|
// as a string.
|
|
function getStreamContent(inputStream) {
|
|
var streamBuf = "";
|
|
var sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
|
|
Ci.nsIScriptableInputStream
|
|
);
|
|
sis.init(inputStream);
|
|
|
|
var available;
|
|
while ((available = sis.available()) != 0) {
|
|
streamBuf += sis.read(available);
|
|
}
|
|
|
|
return streamBuf;
|
|
}
|
|
|
|
// Build the sandbox for fails-if(), etc., condition evaluation.
|
|
function BuildConditionSandbox(aURL) {
|
|
var sandbox = new Cu.Sandbox(aURL.spec);
|
|
sandbox.mozinfo = Services.prefs.getStringPref("sandbox.mozinfo", {});
|
|
let mozinfo = JSON.parse(sandbox.mozinfo);
|
|
|
|
// Shortcuts for widget toolkits.
|
|
sandbox.Android = mozinfo.os == "android";
|
|
sandbox.cocoaWidget = mozinfo.toolkit == "cocoa";
|
|
sandbox.gtkWidget = mozinfo.toolkit == "gtk";
|
|
sandbox.winWidget = mozinfo.toolkit == "windows";
|
|
|
|
// arch
|
|
sandbox.is64Bit = mozinfo.bits == "64"; // to be replaced by x86_64 or aarch64
|
|
sandbox.x86 = mozinfo.processor == "x86";
|
|
sandbox.x86_64 = mozinfo.processor == "x86_64";
|
|
sandbox.aarch64 = mozinfo.processor == "aarch64";
|
|
|
|
// build type
|
|
sandbox.isDebugBuild = mozinfo.debug;
|
|
sandbox.isCoverageBuild = mozinfo.ccov;
|
|
sandbox.AddressSanitizer = mozinfo.asan;
|
|
sandbox.ThreadSanitizer = mozinfo.tsan;
|
|
sandbox.optimized =
|
|
!sandbox.isDebugBuild &&
|
|
!sandbox.isCoverageBuild &&
|
|
!sandbox.AddressSanitizer &&
|
|
!sandbox.ThreadSanitizer;
|
|
|
|
sandbox.release_or_beta = mozinfo.release_or_beta;
|
|
|
|
// config specific prefs
|
|
sandbox.appleSilicon = mozinfo.apple_silicon;
|
|
sandbox.os_version = mozinfo.os_version;
|
|
sandbox.wayland = mozinfo.display == "wayland";
|
|
|
|
// data not using mozinfo
|
|
sandbox.xulRuntime = {};
|
|
|
|
// Do we *not* have a dedicated gpu process.
|
|
sandbox.nogpu =
|
|
sandbox.wayland ||
|
|
sandbox.cocoaWidget ||
|
|
!(
|
|
Services.prefs.getBoolPref("layers.gpu-process.enabled") &&
|
|
Services.prefs.getBoolPref("layers.gpu-process.force-enabled")
|
|
);
|
|
|
|
var gfxInfo =
|
|
NS_GFXINFO_CONTRACTID in Cc &&
|
|
Cc[NS_GFXINFO_CONTRACTID].getService(Ci.nsIGfxInfo);
|
|
let readGfxInfo = function (obj, key) {
|
|
if (g.contentGfxInfo && key in g.contentGfxInfo) {
|
|
return g.contentGfxInfo[key];
|
|
}
|
|
return obj[key];
|
|
};
|
|
|
|
sandbox.swgl = g.windowUtils.layerManagerType.startsWith(
|
|
"WebRender (Software"
|
|
);
|
|
// These detect if each SVG filter primitive is enabled in WebRender
|
|
sandbox.gfxSVGFE =
|
|
Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects") &&
|
|
!g.useDrawSnapshot;
|
|
sandbox.gfxSVGFEBlend =
|
|
Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feblend") &&
|
|
sandbox.gfxSVGFE;
|
|
sandbox.gfxSVGFEColorMatrix =
|
|
Services.prefs.getBoolPref(
|
|
"gfx.webrender.svg-filter-effects.fecolormatrix"
|
|
) && sandbox.gfxSVGFE;
|
|
sandbox.gfxSVGFEComponentTransfer =
|
|
Services.prefs.getBoolPref(
|
|
"gfx.webrender.svg-filter-effects.fecomponenttransfer"
|
|
) && sandbox.gfxSVGFE;
|
|
sandbox.gfxSVGFEComposite =
|
|
Services.prefs.getBoolPref(
|
|
"gfx.webrender.svg-filter-effects.fecomposite"
|
|
) && sandbox.gfxSVGFE;
|
|
sandbox.gfxSVGFEDropShadow =
|
|
Services.prefs.getBoolPref(
|
|
"gfx.webrender.svg-filter-effects.fedropshadow"
|
|
) && sandbox.gfxSVGFE;
|
|
sandbox.gfxSVGFEFlood =
|
|
Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feflood") &&
|
|
sandbox.gfxSVGFE;
|
|
sandbox.gfxSVGFEGaussianBlur =
|
|
Services.prefs.getBoolPref(
|
|
"gfx.webrender.svg-filter-effects.fegaussianblur"
|
|
) && sandbox.gfxSVGFE;
|
|
sandbox.gfxSVGFEOffset =
|
|
Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feoffset") &&
|
|
sandbox.gfxSVGFE;
|
|
|
|
// Use this to annotate reftests that fail in drawSnapshot, but
|
|
// the reason hasn't been investigated (or fixed) yet.
|
|
sandbox.useDrawSnapshot = g.useDrawSnapshot;
|
|
|
|
// GeckoView is currently uniquely identified by "android + e10s" but
|
|
// we might want to make this condition more precise in the future.
|
|
sandbox.geckoview = sandbox.Android && g.browserIsRemote;
|
|
|
|
if (sandbox.Android) {
|
|
sandbox.AndroidVersion = Services.sysinfo.getPropertyAsInt32("version");
|
|
|
|
sandbox.emulator = readGfxInfo(gfxInfo, "adapterDeviceID").includes(
|
|
"Android Emulator"
|
|
);
|
|
sandbox.device = !sandbox.emulator;
|
|
}
|
|
|
|
// Some reftests need extra fuzz on the Android 13 Pixel 5 devices.
|
|
sandbox.Android13 = sandbox.AndroidVersion == "33";
|
|
|
|
// always true except for windows mingwclang builds
|
|
sandbox.webrtc = AppConstants.MOZ_WEBRTC;
|
|
|
|
sandbox.prefs = Cu.cloneInto(
|
|
{
|
|
getBoolPref(p) {
|
|
return Services.prefs.getBoolPref(p);
|
|
},
|
|
getIntPref(p) {
|
|
return Services.prefs.getIntPref(p);
|
|
},
|
|
},
|
|
sandbox,
|
|
{ cloneFunctions: true }
|
|
);
|
|
|
|
// Running with a variant enabled?
|
|
sandbox.fission = Services.appinfo.fissionAutostart;
|
|
|
|
sandbox.incOriginInit =
|
|
Services.env.get("MOZ_ENABLE_INC_ORIGIN_INIT") === "1";
|
|
|
|
if (!g.dumpedConditionSandbox) {
|
|
g.logger.info(
|
|
"Dumping representation of sandbox which can be used for expectation annotations"
|
|
);
|
|
for (let entry of Object.entries(Cu.waiveXrays(sandbox)).sort((a, b) =>
|
|
a[0].localeCompare(b[0])
|
|
)) {
|
|
let value =
|
|
typeof entry[1] === "object" ? JSON.stringify(entry[1]) : entry[1];
|
|
g.logger.info(` ${entry[0]}: ${value}`);
|
|
}
|
|
g.dumpedConditionSandbox = true;
|
|
}
|
|
|
|
return sandbox;
|
|
}
|
|
|
|
function AddRetainedDisplayListTestPrefs(
|
|
aSandbox,
|
|
aTestPrefSettings,
|
|
aRefPrefSettings
|
|
) {
|
|
AddPrefSettings(
|
|
"test-",
|
|
"layout.display-list.retain",
|
|
"true",
|
|
aSandbox,
|
|
aTestPrefSettings,
|
|
aRefPrefSettings
|
|
);
|
|
AddPrefSettings(
|
|
"ref-",
|
|
"layout.display-list.retain",
|
|
"false",
|
|
aSandbox,
|
|
aTestPrefSettings,
|
|
aRefPrefSettings
|
|
);
|
|
}
|
|
|
|
function AddPrefSettings(
|
|
aWhere,
|
|
aPrefName,
|
|
aPrefValExpression,
|
|
aSandbox,
|
|
aTestPrefSettings,
|
|
aRefPrefSettings
|
|
) {
|
|
var prefVal = Cu.evalInSandbox("(" + aPrefValExpression + ")", aSandbox);
|
|
var prefType;
|
|
var valType = typeof prefVal;
|
|
if (valType == "boolean") {
|
|
prefType = PREF_BOOLEAN;
|
|
} else if (valType == "string") {
|
|
prefType = PREF_STRING;
|
|
} else if (valType == "number" && parseInt(prefVal) == prefVal) {
|
|
prefType = PREF_INTEGER;
|
|
} else {
|
|
return false;
|
|
}
|
|
var setting = { name: aPrefName, type: prefType, value: prefVal };
|
|
|
|
if (
|
|
g.compareRetainedDisplayLists &&
|
|
aPrefName != "layout.display-list.retain"
|
|
) {
|
|
// ref-pref() is ignored, test-pref() and pref() are added to both
|
|
if (aWhere != "ref-") {
|
|
aTestPrefSettings.push(setting);
|
|
aRefPrefSettings.push(setting);
|
|
}
|
|
} else {
|
|
if (aWhere != "ref-") {
|
|
aTestPrefSettings.push(setting);
|
|
}
|
|
if (aWhere != "test-") {
|
|
aRefPrefSettings.push(setting);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function ExtractRange(matches, startIndex) {
|
|
return {
|
|
min: Number(matches[startIndex]),
|
|
max: Number(matches[startIndex + 1]),
|
|
};
|
|
}
|
|
|
|
function ServeTestBase(aURL, depth) {
|
|
var listURL = aURL.QueryInterface(Ci.nsIFileURL);
|
|
var directory = listURL.file.parent;
|
|
|
|
// Allow serving a tree that's an ancestor of the directory containing
|
|
// the files so that they can use resources in ../ (etc.).
|
|
var dirPath = "/";
|
|
while (depth > 0) {
|
|
dirPath = "/" + directory.leafName + dirPath;
|
|
directory = directory.parent;
|
|
--depth;
|
|
}
|
|
|
|
g.count++;
|
|
var path = "/" + Date.now() + "/" + g.count;
|
|
g.server.registerDirectory(path + "/", directory);
|
|
// this one is needed so tests can use example.org urls for cross origin testing
|
|
g.server.registerDirectory("/", directory);
|
|
|
|
return g.ioService.newURI(
|
|
"http://localhost:" + g.httpServerPort + path + dirPath
|
|
);
|
|
}
|
|
|
|
export function CreateUrls(test) {
|
|
let manifestURL = g.ioService.newURI(test.manifest);
|
|
|
|
let testbase = manifestURL;
|
|
if (test.runHttp) {
|
|
testbase = ServeTestBase(manifestURL, test.httpDepth);
|
|
}
|
|
|
|
let testbasePrincipal = Services.scriptSecurityManager.createContentPrincipal(
|
|
testbase,
|
|
{}
|
|
);
|
|
Services.perms.addFromPrincipal(
|
|
testbasePrincipal,
|
|
"allowXULXBL",
|
|
Services.perms.ALLOW_ACTION
|
|
);
|
|
|
|
function FileToURI(file) {
|
|
if (file === null) {
|
|
return file;
|
|
}
|
|
|
|
var testURI = g.ioService.newURI(file, null, testbase);
|
|
let isChromeOrViewSource =
|
|
testURI.scheme == "chrome" || testURI.scheme == "view-source";
|
|
let principal = isChromeOrViewSource
|
|
? Services.scriptSecurityManager.getSystemPrincipal()
|
|
: Services.scriptSecurityManager.createContentPrincipal(manifestURL, {});
|
|
Services.scriptSecurityManager.checkLoadURIWithPrincipal(
|
|
principal,
|
|
testURI,
|
|
Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT
|
|
);
|
|
return testURI;
|
|
}
|
|
|
|
let files = [test.url1, test.url2];
|
|
[test.url1, test.url2] = files.map(FileToURI);
|
|
|
|
return test;
|
|
}
|
|
|
|
function TestIdentifier(aUrl, aManifestID) {
|
|
// Construct a platform-independent and location-independent test identifier for
|
|
// a url; normally the identifier looks like a posix-compliant relative file
|
|
// path.
|
|
// Test urls may be simple file names, chrome: urls with full paths, about:blank, etc.
|
|
if (aUrl.startsWith("about:") || aUrl.startsWith("data:")) {
|
|
return aUrl;
|
|
}
|
|
var pos = aUrl.lastIndexOf("/");
|
|
var url = pos < 0 ? aUrl : aUrl.substring(pos + 1);
|
|
return aManifestID + "/" + url;
|
|
}
|
|
|
|
function AddTestItem(aTest, aFilter, aManifestID) {
|
|
if (!aFilter) {
|
|
aFilter = [null, [], false];
|
|
}
|
|
|
|
var identifier = TestIdentifier(aTest.url1, aManifestID);
|
|
if (aTest.url2 !== null) {
|
|
identifier = [
|
|
identifier,
|
|
aTest.type,
|
|
TestIdentifier(aTest.url2, aManifestID),
|
|
];
|
|
}
|
|
|
|
var { url1, url2 } = CreateUrls(Object.assign({}, aTest));
|
|
|
|
var globalFilter = aFilter[0];
|
|
var manifestFilter = aFilter[1];
|
|
var invertManifest = aFilter[2];
|
|
if (globalFilter && !globalFilter.test(url1.spec)) {
|
|
if (url2 === null) {
|
|
return;
|
|
}
|
|
if (globalFilter && !globalFilter.test(url2.spec)) {
|
|
return;
|
|
}
|
|
}
|
|
if (manifestFilter && !(invertManifest ^ manifestFilter.test(url1.spec))) {
|
|
if (url2 === null) {
|
|
return;
|
|
}
|
|
if (manifestFilter && !(invertManifest ^ manifestFilter.test(url2.spec))) {
|
|
return;
|
|
}
|
|
}
|
|
if (
|
|
g.focusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS &&
|
|
!aTest.needsFocus
|
|
) {
|
|
return;
|
|
}
|
|
if (
|
|
g.focusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS &&
|
|
aTest.needsFocus
|
|
) {
|
|
return;
|
|
}
|
|
|
|
aTest.identifier = identifier;
|
|
g.urls.push(aTest);
|
|
// Periodically log progress to avoid no-output timeout on slow platforms.
|
|
// No-output timeouts during manifest parsing have been a problem for
|
|
// jsreftests on Android/debug. Any logging resets the no-output timer,
|
|
// even debug logging which is normally not displayed.
|
|
if (g.urls.length % 5000 == 0) {
|
|
g.logger.debug(g.urls.length + " tests found...");
|
|
}
|
|
}
|