diff options
Diffstat (limited to 'layout/tools/reftest/manifest.jsm')
-rw-r--r-- | layout/tools/reftest/manifest.jsm | 803 |
1 files changed, 803 insertions, 0 deletions
diff --git a/layout/tools/reftest/manifest.jsm b/layout/tools/reftest/manifest.jsm new file mode 100644 index 0000000000..476b04317b --- /dev/null +++ b/layout/tools/reftest/manifest.jsm @@ -0,0 +1,803 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- / +/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ +/* 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/. */ +"use strict"; + +var EXPORTED_SYMBOLS = ["ReadTopManifest", "CreateUrls"]; + +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, +} = ChromeUtils.import("resource://reftest/globals.jsm"); +const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +const NS_SCRIPTSECURITYMANAGER_CONTRACTID = "@mozilla.org/scriptsecuritymanager;1"; +const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX = "@mozilla.org/network/protocol;1?name="; +const NS_XREAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; + +const RE_PROTOCOL = /^\w+:/; +const RE_PREF_ITEM = /^(|test-|ref-)pref\((.+?),(.*)\)$/; + + +function ReadTopManifest(aFileURL, aFilter, aManifestID) +{ + var url = g.ioService.newURI(aFileURL); + if (!url) + throw "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. +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; + else + aFilter = [aFilter[0], aFilter[1], true]; + } + g.manifestsLoaded[aURL.spec] = aFilter[1]; + + var secMan = Cc[NS_SCRIPTSECURITYMANAGER_CONTRACTID] + .getService(Ci.nsIScriptSecurityManager); + + 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 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 "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 "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 "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 "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 "Error in manifest file " + aURL.spec + " line " + lineNo + ": invalid defaults token '" + items[0] + "'"; + } + + if (minAsserts > maxAsserts) { + throw "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 = secMan.createContentPrincipal(aURL, {}); + + if (items[0] == "include") { + if (items.length != 2) + throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to include"; + if (runHttp) + throw "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 "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 "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); + secMan.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 > 0) { + if (newManifestID.length > 0) { + 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) { + var type = items[0]; + if (items.length != 2) + throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to " + type; + if (type == TYPE_LOAD && expected_status != EXPECTED_PASS) + throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect known failure type for load test"; + AddTestItem({ type: type, + expected: expected_status, + manifest: aURL.spec, + manifestID: TestIdentifier(aURL.spec, aManifestID), + allowSilentFail: allow_silent_fail, + minAsserts: minAsserts, + maxAsserts: maxAsserts, + needsFocus: needs_focus, + slow: slow, + skip: skip, + prefSettings1: testPrefSettings, + prefSettings2: refPrefSettings, + fuzzyMinDelta: fuzzy_delta.min, + fuzzyMaxDelta: fuzzy_delta.max, + fuzzyMinPixels: fuzzy_pixels.min, + fuzzyMaxPixels: fuzzy_pixels.max, + runHttp: runHttp, + httpDepth: httpDepth, + url1: items[1], + url2: null, + chaosMode: chaosMode, + wrCapture: wrCapture, + noAutoFuzz: noAutoFuzz }, aFilter, aManifestID); + } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL || items[0] == TYPE_PRINT) { + if (items.length != 3) + throw "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 "Error in manifest file " + aURL.spec + " line " + lineNo + ": minimum fuzz must be zero for tests of type " + items[0]; + } + + var 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: type, + expected: expected_status, + manifest: aURL.spec, + manifestID: TestIdentifier(aURL.spec, aManifestID), + allowSilentFail: allow_silent_fail, + minAsserts: minAsserts, + maxAsserts: maxAsserts, + needsFocus: needs_focus, + slow: slow, + skip: skip, + prefSettings1: testPrefSettings, + prefSettings2: refPrefSettings, + fuzzyMinDelta: fuzzy_delta.min, + fuzzyMaxDelta: fuzzy_delta.max, + fuzzyMinPixels: fuzzy_pixels.min, + fuzzyMaxPixels: fuzzy_pixels.max, + runHttp: runHttp, + httpDepth: httpDepth, + url1: items[1], + url2: items[2], + chaosMode: chaosMode, + wrCapture: wrCapture, + noAutoFuzz: noAutoFuzz }, aFilter, aManifestID); + } else { + throw "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); + var xr = Cc[NS_XREAPPINFO_CONTRACTID].getService(Ci.nsIXULRuntime); + var appInfo = Cc[NS_XREAPPINFO_CONTRACTID].getService(Ci.nsIXULAppInfo); + sandbox.isDebugBuild = g.debug.isDebugBuild; + sandbox.isCoverageBuild = g.isCoverageBuild; + var prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + + sandbox.xulRuntime = Cu.cloneInto({widgetToolkit: xr.widgetToolkit, OS: xr.OS, XPCOMABI: xr.XPCOMABI}, sandbox); + + var testRect = g.browser.getBoundingClientRect(); + 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"); + + sandbox.remoteCanvas = 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 == true; + + // Shortcuts for widget toolkits. + sandbox.Android = xr.OS == "Android"; + sandbox.cocoaWidget = xr.widgetToolkit == "cocoa"; + sandbox.gtkWidget = xr.widgetToolkit == "gtk"; + sandbox.qtWidget = xr.widgetToolkit == "qt"; + sandbox.winWidget = xr.widgetToolkit == "windows"; + + sandbox.is64Bit = xr.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 = + 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 && 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 = xr.widgetToolkit == "gtk"; + + var sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); + if (sandbox.Android) { + // This is currently used to distinguish Android 4.0.3 (SDK version 15) + // and later from Android 2.x + sandbox.AndroidVersion = sysInfo.getPropertyAsInt32("version"); + + sandbox.emulator = readGfxInfo(gfxInfo, "adapterDeviceID").includes("Android Emulator"); + sandbox.device = !sandbox.emulator; + } + + sandbox.MinGW = sandbox.winWidget && 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", "platform", + "oscpu", "language", "misc"]; + sandbox.http = new sandbox.Object(); + httpProps.forEach((x) => sandbox.http[x] = hh[x]); + + // set to specific Android13 version (Pixel 5 in CI) + sandbox.Android13 = sandbox.Android && (sandbox.http["platform"] == "Android 13"); + + // 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 = prefs.getBoolPref("sandbox.apple_silicon", false); + + // Set a flag on sandbox if the windows default theme is active + sandbox.windowsDefaultTheme = g.containingWindow.matchMedia("(-moz-windows-default-theme)").matches; + sandbox.gpuProcessForceEnabled = prefs.getBoolPref("layers.gpu-process.force-enabled", false); + + sandbox.prefs = Cu.cloneInto({ + getBoolPref: function(p) { return prefs.getBoolPref(p); }, + getIntPref: function(p) { return 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 = 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); +} + +function CreateUrls(test) { + let secMan = Cc[NS_SCRIPTSECURITYMANAGER_CONTRACTID] + .getService(Ci.nsIScriptSecurityManager); + + let manifestURL = g.ioService.newURI(test.manifest); + + let testbase = manifestURL; + if (test.runHttp) { + testbase = ServeTestBase(manifestURL, test.httpDepth) + } + + let testbasePrincipal = secMan.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 ? secMan.getSystemPrincipal() : + secMan.createContentPrincipal(manifestURL, {}); + secMan.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..."); +} |