/* -*- 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..."); }