summaryrefslogtreecommitdiffstats
path: root/layout/tools/reftest/manifest.sys.mjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /layout/tools/reftest/manifest.sys.mjs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/tools/reftest/manifest.sys.mjs')
-rw-r--r--layout/tools/reftest/manifest.sys.mjs1046
1 files changed, 1046 insertions, 0 deletions
diff --git a/layout/tools/reftest/manifest.sys.mjs b/layout/tools/reftest/manifest.sys.mjs
new file mode 100644
index 0000000000..4be0afde57
--- /dev/null
+++ b/layout/tools/reftest/manifest.sys.mjs
@@ -0,0 +1,1046 @@
+/* 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 NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX =
+ "@mozilla.org/network/protocol;1?name=";
+
+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.
+// 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 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);
+ 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;
+ }
+ }
+ 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,
+ },
+ 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,
+ },
+ 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.isDebugBuild = g.debug.isDebugBuild;
+ sandbox.isCoverageBuild = g.isCoverageBuild;
+
+ sandbox.xulRuntime = Cu.cloneInto(
+ {
+ widgetToolkit: Services.appinfo.widgetToolkit,
+ OS: Services.appinfo.OS,
+ XPCOMABI: Services.appinfo.XPCOMABI,
+ },
+ sandbox
+ );
+
+ sandbox.smallScreen = false;
+ if (
+ g.containingWindow.innerWidth < 800 ||
+ g.containingWindow.innerHeight < 1000
+ ) {
+ sandbox.smallScreen = true;
+ }
+
+ 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];
+ };
+
+ try {
+ sandbox.d2d = readGfxInfo(gfxInfo, "D2DEnabled");
+ sandbox.dwrite = readGfxInfo(gfxInfo, "DWriteEnabled");
+ sandbox.embeddedInFirefoxReality = readGfxInfo(
+ gfxInfo,
+ "EmbeddedInFirefoxReality"
+ );
+ } catch (e) {
+ sandbox.d2d = false;
+ sandbox.dwrite = false;
+ sandbox.embeddedInFirefoxReality = false;
+ }
+
+ var canvasBackend = readGfxInfo(gfxInfo, "AzureCanvasBackend");
+ var contentBackend = readGfxInfo(gfxInfo, "AzureContentBackend");
+
+ sandbox.gpuProcess = gfxInfo.usingGPUProcess;
+ sandbox.azureCairo = canvasBackend == "cairo";
+ sandbox.azureSkia = canvasBackend == "skia";
+ sandbox.skiaContent = contentBackend == "skia";
+ sandbox.azureSkiaGL = false;
+ // true if we are using the same Azure backend for rendering canvas and content
+ sandbox.contentSameGfxBackendAsCanvas =
+ contentBackend == canvasBackend ||
+ (contentBackend == "none" && canvasBackend == "cairo");
+
+ try {
+ var windowProtocol = readGfxInfo(gfxInfo, "windowProtocol");
+ sandbox.wayland = windowProtocol == "wayland";
+ } catch (e) {
+ sandbox.wayland = false;
+ }
+
+ sandbox.remoteCanvas =
+ Services.prefs.getBoolPref("gfx.canvas.remote") &&
+ sandbox.d2d &&
+ sandbox.gpuProcess;
+
+ sandbox.layersGPUAccelerated = g.windowUtils.layerManagerType != "Basic";
+ sandbox.d3d11 = g.windowUtils.layerManagerType == "Direct3D 11";
+ sandbox.d3d9 = g.windowUtils.layerManagerType == "Direct3D 9";
+ sandbox.layersOpenGL = g.windowUtils.layerManagerType == "OpenGL";
+ sandbox.swgl = g.windowUtils.layerManagerType.startsWith(
+ "WebRender (Software"
+ );
+ sandbox.layersOMTC = !!g.windowUtils.layerManagerRemote;
+
+ // Shortcuts for widget toolkits.
+ sandbox.Android = Services.appinfo.OS == "Android";
+ sandbox.cocoaWidget = Services.appinfo.widgetToolkit == "cocoa";
+ sandbox.gtkWidget = Services.appinfo.widgetToolkit == "gtk";
+ sandbox.qtWidget = Services.appinfo.widgetToolkit == "qt";
+ sandbox.winWidget = Services.appinfo.widgetToolkit == "windows";
+
+ sandbox.is64Bit = Services.appinfo.is64Bit;
+
+ // Use this to annotate reftests that fail in drawSnapshot, but
+ // the reason hasn't been investigated (or fixed) yet.
+ sandbox.useDrawSnapshot = g.useDrawSnapshot;
+ // Use this to annotate reftests that use functionality
+ // that isn't available to drawSnapshot (like any sort of
+ // compositor feature such as async scrolling).
+ sandbox.unsupportedWithDrawSnapshot = g.useDrawSnapshot;
+
+ sandbox.retainedDisplayList =
+ Services.prefs.getBoolPref("layout.display-list.retain") &&
+ !sandbox.useDrawSnapshot;
+
+ // Needed to specifically test the new and old behavior. This will eventually be removed.
+ sandbox.retainedDisplayListNew =
+ sandbox.retainedDisplayList &&
+ Services.prefs.getBoolPref("layout.display-list.retain.sc");
+
+ // 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;
+
+ // Scrollbars that are semi-transparent. See bug 1169666.
+ sandbox.transparentScrollbars = Services.appinfo.widgetToolkit == "gtk";
+
+ 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";
+
+ sandbox.MinGW =
+ sandbox.winWidget && Services.sysinfo.getPropertyAsBool("isMinGW");
+
+ sandbox.AddressSanitizer = AppConstants.ASAN;
+ sandbox.ThreadSanitizer = AppConstants.TSAN;
+ sandbox.webrtc = AppConstants.MOZ_WEBRTC;
+ sandbox.jxl = AppConstants.MOZ_JXL;
+
+ sandbox.compareRetainedDisplayLists = g.compareRetainedDisplayLists;
+
+ sandbox.release_or_beta = AppConstants.RELEASE_OR_BETA;
+
+ var hh = Cc[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"].getService(
+ Ci.nsIHttpProtocolHandler
+ );
+ var httpProps = [
+ "userAgent",
+ "appName",
+ "appVersion",
+ "vendor",
+ "vendorSub",
+ "product",
+ "productSub",
+ "oscpu",
+ "language",
+ "misc",
+ ];
+ sandbox.http = new sandbox.Object();
+ httpProps.forEach(x => (sandbox.http[x] = hh[x]));
+
+ // Set OSX to be the Mac OS X version, as an integer, or undefined
+ // for other platforms. The integer is formed by 100 times the
+ // major version plus the minor version, so 1006 for 10.6, 1010 for
+ // 10.10, etc.
+ var osxmatch = /Mac OS X (\d+).(\d+)$/.exec(hh.oscpu);
+ sandbox.OSX = osxmatch
+ ? parseInt(osxmatch[1]) * 100 + parseInt(osxmatch[2])
+ : undefined;
+
+ // config specific prefs
+ sandbox.appleSilicon = Services.prefs.getBoolPref(
+ "sandbox.apple_silicon",
+ false
+ );
+
+ sandbox.gpuProcessForceEnabled = Services.prefs.getBoolPref(
+ "layers.gpu-process.force-enabled",
+ false
+ );
+
+ sandbox.prefs = Cu.cloneInto(
+ {
+ getBoolPref(p) {
+ return Services.prefs.getBoolPref(p);
+ },
+ getIntPref(p) {
+ return Services.prefs.getIntPref(p);
+ },
+ },
+ sandbox,
+ { cloneFunctions: true }
+ );
+
+ // Tests shouldn't care about this except for when they need to
+ // crash the content process
+ sandbox.browserIsRemote = g.browserIsRemote;
+ sandbox.browserIsFission = g.browserIsFission;
+
+ try {
+ sandbox.asyncPan =
+ g.containingWindow.docShell.asyncPanZoomEnabled &&
+ !sandbox.useDrawSnapshot;
+ } catch (e) {
+ sandbox.asyncPan = false;
+ }
+
+ // Graphics features
+ sandbox.usesRepeatResampling = sandbox.d2d;
+
+ // Running in a test-verify session?
+ sandbox.verify = Services.prefs.getBoolPref("reftest.verify", false);
+
+ // Running with a variant enabled?
+ sandbox.fission = Services.appinfo.fissionAutostart;
+ sandbox.serviceWorkerE10s = true;
+
+ 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...");
+ }
+}