1/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- / 2/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ 3/* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6"use strict"; 7 8var EXPORTED_SYMBOLS = ["ReadTopManifest", "CreateUrls"]; 9 10Cu.import("resource://reftest/globals.jsm", this); 11Cu.import("resource://reftest/reftest.jsm", this); 12Cu.import("resource://gre/modules/Services.jsm"); 13Cu.import("resource://gre/modules/NetUtil.jsm"); 14 15const NS_SCRIPTSECURITYMANAGER_CONTRACTID = "@mozilla.org/scriptsecuritymanager;1"; 16const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX = "@mozilla.org/network/protocol;1?name="; 17const NS_XREAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; 18 19const RE_PROTOCOL = /^\w+:/; 20const RE_PREF_ITEM = /^(|test-|ref-)pref\((.+?),(.*)\)$/; 21 22 23function ReadTopManifest(aFileURL, aFilter, aManifestID) 24{ 25 var url = g.ioService.newURI(aFileURL); 26 if (!url) 27 throw "Expected a file or http URL for the manifest."; 28 29 g.manifestsLoaded = {}; 30 ReadManifest(url, aFilter, aManifestID); 31} 32 33// Note: If you materially change the reftest manifest parsing, 34// please keep the parser in layout/tools/reftest/__init__.py in sync. 35function ReadManifest(aURL, aFilter, aManifestID) 36{ 37 // Ensure each manifest is only read once. This assumes that manifests that 38 // are included with filters will be read via their include before they are 39 // read directly in the case of a duplicate 40 if (g.manifestsLoaded.hasOwnProperty(aURL.spec)) { 41 if (g.manifestsLoaded[aURL.spec] === null) 42 return; 43 else 44 aFilter = [aFilter[0], aFilter[1], true]; 45 } 46 g.manifestsLoaded[aURL.spec] = aFilter[1]; 47 48 var secMan = Cc[NS_SCRIPTSECURITYMANAGER_CONTRACTID] 49 .getService(Ci.nsIScriptSecurityManager); 50 51 var listURL = aURL; 52 var channel = NetUtil.newChannel({uri: aURL, 53 loadUsingSystemPrincipal: true}); 54 try { 55 var inputStream = channel.open(); 56 } catch (e) { 57 g.logger.error("failed to open manifest at : " + aURL.spec); 58 throw e; 59 } 60 if (channel instanceof Ci.nsIHttpChannel 61 && channel.responseStatus != 200) { 62 g.logger.error("HTTP ERROR : " + channel.responseStatus); 63 } 64 var streamBuf = getStreamContent(inputStream); 65 inputStream.close(); 66 var lines = streamBuf.split(/\n|\r|\r\n/); 67 68 // The sandbox for fails-if(), etc., condition evaluation. This is not 69 // always required and so is created on demand. 70 var sandbox; 71 function GetOrCreateSandbox() { 72 if (!sandbox) { 73 sandbox = BuildConditionSandbox(aURL); 74 } 75 return sandbox; 76 } 77 78 var lineNo = 0; 79 var urlprefix = ""; 80 var defaults = []; 81 var defaultTestPrefSettings = [], defaultRefPrefSettings = []; 82 if (g.compareRetainedDisplayLists) { 83 AddRetainedDisplayListTestPrefs(GetOrCreateSandbox(), defaultTestPrefSettings, 84 defaultRefPrefSettings); 85 } 86 for (var str of lines) { 87 ++lineNo; 88 if (str.charAt(0) == "#") 89 continue; // entire line was a comment 90 var i = str.search(/\s+#/); 91 if (i >= 0) 92 str = str.substring(0, i); 93 // strip leading and trailing whitespace 94 str = str.replace(/^\s*/, '').replace(/\s*$/, ''); 95 if (!str || str == "") 96 continue; 97 var items = str.split(/\s+/); // split on whitespace 98 99 if (items[0] == "url-prefix") { 100 if (items.length != 2) 101 throw "url-prefix requires one url in manifest file " + aURL.spec + " line " + lineNo; 102 urlprefix = items[1]; 103 continue; 104 } 105 106 if (items[0] == "defaults") { 107 items.shift(); 108 defaults = items; 109 continue; 110 } 111 112 var expected_status = EXPECTED_PASS; 113 var allow_silent_fail = false; 114 var minAsserts = 0; 115 var maxAsserts = 0; 116 var needs_focus = false; 117 var slow = false; 118 var skip = false; 119 var testPrefSettings = defaultTestPrefSettings.concat(); 120 var refPrefSettings = defaultRefPrefSettings.concat(); 121 var fuzzy_delta = { min: 0, max: 2 }; 122 var fuzzy_pixels = { min: 0, max: 1 }; 123 var chaosMode = false; 124 var wrCapture = { test: false, ref: false }; 125 var nonSkipUsed = false; 126 var noAutoFuzz = false; 127 128 var origLength = items.length; 129 items = defaults.concat(items); 130 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)/)) { 131 var item = items.shift(); 132 var stat; 133 var cond; 134 var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/); 135 if (m) { 136 stat = m[1]; 137 // Note: m[2] contains the parentheses, and we want them. 138 cond = Cu.evalInSandbox(m[2], GetOrCreateSandbox()); 139 } else if (item.match(/^(fails|random|skip)$/)) { 140 stat = item; 141 cond = true; 142 } else if (item == "needs-focus") { 143 needs_focus = true; 144 cond = false; 145 } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) { 146 cond = false; 147 minAsserts = Number(m[1]); 148 maxAsserts = (m[2] == undefined) ? minAsserts 149 : Number(m[2].substring(1)); 150 } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) { 151 cond = false; 152 if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) { 153 minAsserts = Number(m[2]); 154 maxAsserts = 155 (m[3] == undefined) ? minAsserts 156 : Number(m[3].substring(1)); 157 } 158 } else if (item == "slow") { 159 cond = false; 160 slow = true; 161 } else if ((m = item.match(/^require-or\((.*?)\)$/))) { 162 var args = m[1].split(/,/); 163 if (args.length != 2) { 164 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": wrong number of args to require-or"; 165 } 166 var [precondition_str, fallback_action] = args; 167 var preconditions = precondition_str.split(/&&/); 168 cond = false; 169 for (var precondition of preconditions) { 170 if (precondition === "debugMode") { 171 // Currently unimplemented. Requires asynchronous 172 // JSD call + getting an event while no JS is running 173 stat = fallback_action; 174 cond = true; 175 break; 176 } else if (precondition === "true") { 177 // For testing 178 } else { 179 // Unknown precondition. Assume it is unimplemented. 180 stat = fallback_action; 181 cond = true; 182 break; 183 } 184 } 185 } else if ((m = item.match(/^slow-if\((.*?)\)$/))) { 186 cond = false; 187 if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) 188 slow = true; 189 } else if (item == "silentfail") { 190 cond = false; 191 allow_silent_fail = true; 192 } else if ((m = item.match(RE_PREF_ITEM))) { 193 cond = false; 194 if (!AddPrefSettings(m[1], m[2], m[3], GetOrCreateSandbox(), 195 testPrefSettings, refPrefSettings)) { 196 throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo; 197 } 198 } else if ((m = item.match(/^fuzzy\((\d+)-(\d+),(\d+)-(\d+)\)$/))) { 199 cond = false; 200 expected_status = EXPECTED_FUZZY; 201 fuzzy_delta = ExtractRange(m, 1); 202 fuzzy_pixels = ExtractRange(m, 3); 203 } else if ((m = item.match(/^fuzzy-if\((.*?),(\d+)-(\d+),(\d+)-(\d+)\)$/))) { 204 cond = false; 205 if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) { 206 expected_status = EXPECTED_FUZZY; 207 fuzzy_delta = ExtractRange(m, 2); 208 fuzzy_pixels = ExtractRange(m, 4); 209 } 210 } else if (item == "chaos-mode") { 211 cond = false; 212 chaosMode = true; 213 } else if (item == "wr-capture") { 214 cond = false; 215 wrCapture.test = true; 216 } else if (item == "wr-capture-ref") { 217 cond = false; 218 wrCapture.ref = true; 219 } else if (item == "noautofuzz") { 220 cond = false; 221 noAutoFuzz = true; 222 } else { 223 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unexpected item " + item; 224 } 225 226 if (stat != "skip") { 227 nonSkipUsed = true; 228 } 229 230 if (cond) { 231 if (stat == "fails") { 232 expected_status = EXPECTED_FAIL; 233 } else if (stat == "random") { 234 expected_status = EXPECTED_RANDOM; 235 } else if (stat == "skip") { 236 skip = true; 237 } else if (stat == "silentfail") { 238 allow_silent_fail = true; 239 } 240 } 241 } 242 243 if (items.length > origLength) { 244 // Implies we broke out of the loop before we finished processing 245 // defaults. This means defaults contained an invalid token. 246 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": invalid defaults token '" + items[0] + "'"; 247 } 248 249 if (minAsserts > maxAsserts) { 250 throw "Bad range in manifest file " + aURL.spec + " line " + lineNo; 251 } 252 253 var runHttp = false; 254 var httpDepth; 255 if (items[0] == "HTTP") { 256 runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server 257 // for non-local reftests. 258 httpDepth = 0; 259 items.shift(); 260 } else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) { 261 // Accept HTTP(..), HTTP(../..), HTTP(../../..), etc. 262 runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server 263 // for non-local reftests. 264 httpDepth = (items[0].length - 5) / 3; 265 items.shift(); 266 } 267 268 // do not prefix the url for include commands or urls specifying 269 // a protocol 270 if (urlprefix && items[0] != "include") { 271 if (items.length > 1 && !items[1].match(RE_PROTOCOL)) { 272 items[1] = urlprefix + items[1]; 273 } 274 if (items.length > 2 && !items[2].match(RE_PROTOCOL)) { 275 items[2] = urlprefix + items[2]; 276 } 277 } 278 279 var principal = secMan.createContentPrincipal(aURL, {}); 280 281 if (items[0] == "include") { 282 if (items.length != 2) 283 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to include"; 284 if (runHttp) 285 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": use of include with http"; 286 287 // If the expected_status is EXPECTED_PASS (the default) then allow 288 // the include. If 'skip' is true, that means there was a skip 289 // or skip-if annotation (with a true condition) on this include 290 // statement, so we should skip the include. Any other expected_status 291 // is disallowed since it's nonintuitive as to what the intended 292 // effect is. 293 if (nonSkipUsed) { 294 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": include statement with annotation other than 'skip' or 'skip-if'"; 295 } else if (skip) { 296 g.logger.info("Skipping included manifest at " + aURL.spec + " line " + lineNo + " due to matching skip condition"); 297 } else { 298 // poor man's assertion 299 if (expected_status != EXPECTED_PASS) { 300 throw "Error in manifest file parsing code: we should never get expected_status=" + expected_status + " when nonSkipUsed=false (from " + aURL.spec + " line " + lineNo + ")"; 301 } 302 303 var incURI = g.ioService.newURI(items[1], null, listURL); 304 secMan.checkLoadURIWithPrincipal(principal, incURI, 305 Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); 306 307 // Cannot use nsIFile or similar to manipulate the manifest ID; although it appears 308 // path-like, it does not refer to an actual path in the filesystem. 309 var newManifestID = aManifestID; 310 var included = items[1]; 311 // Remove included manifest file name. 312 // eg. dir1/dir2/reftest.list -> dir1/dir2 313 var pos = included.lastIndexOf("/"); 314 if (pos <= 0) { 315 included = ""; 316 } else { 317 included = included.substring(0, pos); 318 } 319 // Simplify references to parent directories. 320 // eg. dir1/dir2/../dir3 -> dir1/dir3 321 while (included.startsWith("../")) { 322 pos = newManifestID.lastIndexOf("/"); 323 if (pos < 0) { 324 pos = 0; 325 } 326 newManifestID = newManifestID.substring(0, pos); 327 included = included.substring(3); 328 } 329 // Use a new manifest ID if the included manifest is in a different directory. 330 if (included.length > 0) { 331 if (newManifestID.length > 0) { 332 newManifestID = newManifestID + "/" + included; 333 } else { 334 // parent directory includes may refer to the topsrcdir 335 newManifestID = included; 336 } 337 } 338 ReadManifest(incURI, aFilter, newManifestID); 339 } 340 } else if (items[0] == TYPE_LOAD || items[0] == TYPE_SCRIPT) { 341 var type = items[0]; 342 if (items.length != 2) 343 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to " + type; 344 if (type == TYPE_LOAD && expected_status != EXPECTED_PASS) 345 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect known failure type for load test"; 346 AddTestItem({ type: type, 347 expected: expected_status, 348 manifest: aURL.spec, 349 manifestID: TestIdentifier(aURL.spec, aManifestID), 350 allowSilentFail: allow_silent_fail, 351 minAsserts: minAsserts, 352 maxAsserts: maxAsserts, 353 needsFocus: needs_focus, 354 slow: slow, 355 skip: skip, 356 prefSettings1: testPrefSettings, 357 prefSettings2: refPrefSettings, 358 fuzzyMinDelta: fuzzy_delta.min, 359 fuzzyMaxDelta: fuzzy_delta.max, 360 fuzzyMinPixels: fuzzy_pixels.min, 361 fuzzyMaxPixels: fuzzy_pixels.max, 362 runHttp: runHttp, 363 httpDepth: httpDepth, 364 url1: items[1], 365 url2: null, 366 chaosMode: chaosMode, 367 wrCapture: wrCapture, 368 noAutoFuzz: noAutoFuzz }, aFilter, aManifestID); 369 } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL || items[0] == TYPE_PRINT) { 370 if (items.length != 3) 371 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to " + items[0]; 372 373 if (items[0] == TYPE_REFTEST_NOTEQUAL && 374 expected_status == EXPECTED_FUZZY && 375 (fuzzy_delta.min > 0 || fuzzy_pixels.min > 0)) { 376 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": minimum fuzz must be zero for tests of type " + items[0]; 377 } 378 379 var type = items[0]; 380 if (g.compareRetainedDisplayLists) { 381 type = TYPE_REFTEST_EQUAL; 382 383 // We expect twice as many assertion failures when comparing 384 // tests because we run each test twice. 385 minAsserts *= 2; 386 maxAsserts *= 2; 387 388 // Skip the test if it is expected to fail in both modes. 389 // It would unexpectedly "pass" in comparison mode mode when 390 // comparing the two failures, which is not a useful result. 391 if (expected_status === EXPECTED_FAIL || 392 expected_status === EXPECTED_RANDOM) { 393 skip = true; 394 } 395 } 396 397 AddTestItem({ type: type, 398 expected: expected_status, 399 manifest: aURL.spec, 400 manifestID: TestIdentifier(aURL.spec, aManifestID), 401 allowSilentFail: allow_silent_fail, 402 minAsserts: minAsserts, 403 maxAsserts: maxAsserts, 404 needsFocus: needs_focus, 405 slow: slow, 406 skip: skip, 407 prefSettings1: testPrefSettings, 408 prefSettings2: refPrefSettings, 409 fuzzyMinDelta: fuzzy_delta.min, 410 fuzzyMaxDelta: fuzzy_delta.max, 411 fuzzyMinPixels: fuzzy_pixels.min, 412 fuzzyMaxPixels: fuzzy_pixels.max, 413 runHttp: runHttp, 414 httpDepth: httpDepth, 415 url1: items[1], 416 url2: items[2], 417 chaosMode: chaosMode, 418 wrCapture: wrCapture, 419 noAutoFuzz: noAutoFuzz }, aFilter, aManifestID); 420 } else { 421 throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unknown test type " + items[0]; 422 } 423 } 424} 425 426// Read all available data from an input stream and return it 427// as a string. 428function getStreamContent(inputStream) 429{ 430 var streamBuf = ""; 431 var sis = Cc["@mozilla.org/scriptableinputstream;1"]. 432 createInstance(Ci.nsIScriptableInputStream); 433 sis.init(inputStream); 434 435 var available; 436 while ((available = sis.available()) != 0) { 437 streamBuf += sis.read(available); 438 } 439 440 return streamBuf; 441} 442 443// Build the sandbox for fails-if(), etc., condition evaluation. 444function BuildConditionSandbox(aURL) { 445 var sandbox = new Cu.Sandbox(aURL.spec); 446 var xr = Cc[NS_XREAPPINFO_CONTRACTID].getService(Ci.nsIXULRuntime); 447 var appInfo = Cc[NS_XREAPPINFO_CONTRACTID].getService(Ci.nsIXULAppInfo); 448 sandbox.isDebugBuild = g.debug.isDebugBuild; 449 sandbox.isCoverageBuild = g.isCoverageBuild; 450 var prefs = Cc["@mozilla.org/preferences-service;1"]. 451 getService(Ci.nsIPrefBranch); 452 var env = Cc["@mozilla.org/process/environment;1"]. 453 getService(Ci.nsIEnvironment); 454 455 sandbox.xulRuntime = Cu.cloneInto({widgetToolkit: xr.widgetToolkit, OS: xr.OS, XPCOMABI: xr.XPCOMABI}, sandbox); 456 457 var testRect = g.browser.getBoundingClientRect(); 458 sandbox.smallScreen = false; 459 if (g.containingWindow.innerWidth < 800 || g.containingWindow.innerHeight < 1000) { 460 sandbox.smallScreen = true; 461 } 462 463 var gfxInfo = (NS_GFXINFO_CONTRACTID in Cc) && Cc[NS_GFXINFO_CONTRACTID].getService(Ci.nsIGfxInfo); 464 let readGfxInfo = function (obj, key) { 465 if (g.contentGfxInfo && (key in g.contentGfxInfo)) { 466 return g.contentGfxInfo[key]; 467 } 468 return obj[key]; 469 } 470 471 try { 472 sandbox.d2d = readGfxInfo(gfxInfo, "D2DEnabled"); 473 sandbox.dwrite = readGfxInfo(gfxInfo, "DWriteEnabled"); 474 sandbox.embeddedInFirefoxReality = readGfxInfo(gfxInfo, "EmbeddedInFirefoxReality"); 475 } catch (e) { 476 sandbox.d2d = false; 477 sandbox.dwrite = false; 478 sandbox.embeddedInFirefoxReality = false; 479 } 480 481 var canvasBackend = readGfxInfo(gfxInfo, "AzureCanvasBackend"); 482 var contentBackend = readGfxInfo(gfxInfo, "AzureContentBackend"); 483 484 sandbox.gpuProcess = gfxInfo.usingGPUProcess; 485 sandbox.azureCairo = canvasBackend == "cairo"; 486 sandbox.azureSkia = canvasBackend == "skia"; 487 sandbox.skiaContent = contentBackend == "skia"; 488 sandbox.azureSkiaGL = false; 489 // true if we are using the same Azure backend for rendering canvas and content 490 sandbox.contentSameGfxBackendAsCanvas = contentBackend == canvasBackend 491 || (contentBackend == "none" && canvasBackend == "cairo"); 492 493 sandbox.remoteCanvas = prefs.getBoolPref("gfx.canvas.remote") && sandbox.d2d && sandbox.gpuProcess; 494 495 sandbox.layersGPUAccelerated = 496 g.windowUtils.layerManagerType != "Basic"; 497 sandbox.d3d11 = 498 g.windowUtils.layerManagerType == "Direct3D 11"; 499 sandbox.d3d9 = 500 g.windowUtils.layerManagerType == "Direct3D 9"; 501 sandbox.layersOpenGL = 502 g.windowUtils.layerManagerType == "OpenGL"; 503 sandbox.swgl = 504 g.windowUtils.layerManagerType.startsWith("WebRender (Software"); 505 sandbox.webrender = 506 g.windowUtils.layerManagerType.startsWith("WebRender"); 507 sandbox.layersOMTC = 508 g.windowUtils.layerManagerRemote == true; 509 sandbox.advancedLayers = 510 g.windowUtils.usingAdvancedLayers == true; 511 sandbox.layerChecksEnabled = !sandbox.webrender; 512 513 sandbox.usesOverlayScrollbars = g.windowUtils.usesOverlayScrollbars; 514 515 // Shortcuts for widget toolkits. 516 sandbox.Android = xr.OS == "Android"; 517 sandbox.cocoaWidget = xr.widgetToolkit == "cocoa"; 518 sandbox.gtkWidget = xr.widgetToolkit == "gtk"; 519 sandbox.qtWidget = xr.widgetToolkit == "qt"; 520 sandbox.winWidget = xr.widgetToolkit == "windows"; 521 522 sandbox.is64Bit = xr.is64Bit; 523 524 // Use this to annotate reftests that fail in drawSnapshot, but 525 // the reason hasn't been investigated (or fixed) yet. 526 sandbox.useDrawSnapshot = g.useDrawSnapshot; 527 // Use this to annotate reftests that use functionality 528 // that isn't available to drawSnapshot (like any sort of 529 // compositor feature such as async scrolling). 530 sandbox.unsupportedWithDrawSnapshot = g.useDrawSnapshot; 531 532 sandbox.retainedDisplayList = 533 prefs.getBoolPref("layout.display-list.retain") && !sandbox.useDrawSnapshot; 534 535 // GeckoView is currently uniquely identified by "android + e10s" but 536 // we might want to make this condition more precise in the future. 537 sandbox.geckoview = (sandbox.Android && g.browserIsRemote); 538 539 // Scrollbars that are semi-transparent. See bug 1169666. 540 sandbox.transparentScrollbars = xr.widgetToolkit == "gtk"; 541 542 var sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); 543 if (sandbox.Android) { 544 // This is currently used to distinguish Android 4.0.3 (SDK version 15) 545 // and later from Android 2.x 546 sandbox.AndroidVersion = sysInfo.getPropertyAsInt32("version"); 547 548 sandbox.emulator = readGfxInfo(gfxInfo, "adapterDeviceID").includes("Android Emulator"); 549 sandbox.device = !sandbox.emulator; 550 } 551 552 sandbox.MinGW = sandbox.winWidget && sysInfo.getPropertyAsBool("isMinGW"); 553 554#if MOZ_ASAN 555 sandbox.AddressSanitizer = true; 556#else 557 sandbox.AddressSanitizer = false; 558#endif 559 560#if MOZ_TSAN 561 sandbox.ThreadSanitizer = true; 562#else 563 sandbox.ThreadSanitizer = false; 564#endif 565 566#if MOZ_WEBRTC 567 sandbox.webrtc = true; 568#else 569 sandbox.webrtc = false; 570#endif 571 572#if MOZ_JXL 573 sandbox.jxl = true; 574#else 575 sandbox.jxl = false; 576#endif 577 578 let retainedDisplayListsEnabled = prefs.getBoolPref("layout.display-list.retain", false); 579 sandbox.retainedDisplayLists = retainedDisplayListsEnabled && !g.compareRetainedDisplayLists && !sandbox.useDrawSnapshot; 580 sandbox.compareRetainedDisplayLists = g.compareRetainedDisplayLists; 581 582#ifdef RELEASE_OR_BETA 583 sandbox.release_or_beta = true; 584#else 585 sandbox.release_or_beta = false; 586#endif 587 588 var hh = Cc[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"]. 589 getService(Ci.nsIHttpProtocolHandler); 590 var httpProps = ["userAgent", "appName", "appVersion", "vendor", 591 "vendorSub", "product", "productSub", "platform", 592 "oscpu", "language", "misc"]; 593 sandbox.http = new sandbox.Object(); 594 httpProps.forEach((x) => sandbox.http[x] = hh[x]); 595 596 // Set OSX to be the Mac OS X version, as an integer, or undefined 597 // for other platforms. The integer is formed by 100 times the 598 // major version plus the minor version, so 1006 for 10.6, 1010 for 599 // 10.10, etc. 600 var osxmatch = /Mac OS X (\d+).(\d+)$/.exec(hh.oscpu); 601 sandbox.OSX = osxmatch ? parseInt(osxmatch[1]) * 100 + parseInt(osxmatch[2]) : undefined; 602 603 // config specific prefs 604 sandbox.appleSilicon = prefs.getBoolPref("sandbox.apple_silicon", false); 605 606 // Set a flag on sandbox if the windows default theme is active 607 sandbox.windowsDefaultTheme = g.containingWindow.matchMedia("(-moz-windows-default-theme)").matches; 608 609 try { 610 sandbox.nativeThemePref = !prefs.getBoolPref("widget.non-native-theme.enabled"); 611 } catch (e) { 612 sandbox.nativeThemePref = true; 613 } 614 sandbox.gpuProcessForceEnabled = prefs.getBoolPref("layers.gpu-process.force-enabled", false); 615 616 sandbox.prefs = Cu.cloneInto({ 617 getBoolPref: function(p) { return prefs.getBoolPref(p); }, 618 getIntPref: function(p) { return prefs.getIntPref(p); } 619 }, sandbox, { cloneFunctions: true }); 620 621 // Tests shouldn't care about this except for when they need to 622 // crash the content process 623 sandbox.browserIsRemote = g.browserIsRemote; 624 sandbox.browserIsFission = g.browserIsFission; 625 626 try { 627 sandbox.asyncPan = g.containingWindow.docShell.asyncPanZoomEnabled && !sandbox.useDrawSnapshot; 628 } catch (e) { 629 sandbox.asyncPan = false; 630 } 631 632 // Graphics features 633 sandbox.usesRepeatResampling = sandbox.d2d; 634 635 // Running in a test-verify session? 636 sandbox.verify = prefs.getBoolPref("reftest.verify", false); 637 638 // Running with a variant enabled? 639 sandbox.fission = Services.appinfo.fissionAutostart; 640 sandbox.serviceWorkerE10s = true; 641 642 if (!g.dumpedConditionSandbox) { 643 g.logger.info("Dumping JSON representation of sandbox"); 644 g.logger.info(JSON.stringify(Cu.waiveXrays(sandbox))); 645 g.dumpedConditionSandbox = true; 646 } 647 648 return sandbox; 649} 650 651function AddRetainedDisplayListTestPrefs(aSandbox, aTestPrefSettings, 652 aRefPrefSettings) { 653 AddPrefSettings("test-", "layout.display-list.retain", "true", aSandbox, 654 aTestPrefSettings, aRefPrefSettings); 655 AddPrefSettings("ref-", "layout.display-list.retain", "false", aSandbox, 656 aTestPrefSettings, aRefPrefSettings); 657} 658 659function AddPrefSettings(aWhere, aPrefName, aPrefValExpression, aSandbox, aTestPrefSettings, aRefPrefSettings) { 660 var prefVal = Cu.evalInSandbox("(" + aPrefValExpression + ")", aSandbox); 661 var prefType; 662 var valType = typeof(prefVal); 663 if (valType == "boolean") { 664 prefType = PREF_BOOLEAN; 665 } else if (valType == "string") { 666 prefType = PREF_STRING; 667 } else if (valType == "number" && (parseInt(prefVal) == prefVal)) { 668 prefType = PREF_INTEGER; 669 } else { 670 return false; 671 } 672 var setting = { name: aPrefName, 673 type: prefType, 674 value: prefVal }; 675 676 if (g.compareRetainedDisplayLists && aPrefName != "layout.display-list.retain") { 677 // ref-pref() is ignored, test-pref() and pref() are added to both 678 if (aWhere != "ref-") { 679 aTestPrefSettings.push(setting); 680 aRefPrefSettings.push(setting); 681 } 682 } else { 683 if (aWhere != "ref-") { 684 aTestPrefSettings.push(setting); 685 } 686 if (aWhere != "test-") { 687 aRefPrefSettings.push(setting); 688 } 689 } 690 return true; 691} 692 693function ExtractRange(matches, startIndex) { 694 return { 695 min: Number(matches[startIndex]), 696 max: Number(matches[startIndex + 1]) 697 }; 698} 699 700function ServeTestBase(aURL, depth) { 701 var listURL = aURL.QueryInterface(Ci.nsIFileURL); 702 var directory = listURL.file.parent; 703 704 // Allow serving a tree that's an ancestor of the directory containing 705 // the files so that they can use resources in ../ (etc.). 706 var dirPath = "/"; 707 while (depth > 0) { 708 dirPath = "/" + directory.leafName + dirPath; 709 directory = directory.parent; 710 --depth; 711 } 712 713 g.count++; 714 var path = "/" + Date.now() + "/" + g.count; 715 g.server.registerDirectory(path + "/", directory); 716 717 var secMan = Cc[NS_SCRIPTSECURITYMANAGER_CONTRACTID] 718 .getService(Ci.nsIScriptSecurityManager); 719 720 var testbase = g.ioService.newURI("http://localhost:" + g.httpServerPort + 721 path + dirPath); 722 var testBasePrincipal = secMan.createContentPrincipal(testbase, {}); 723 724 // Give the testbase URI access to XUL and XBL 725 Services.perms.addFromPrincipal(testBasePrincipal, "allowXULXBL", Services.perms.ALLOW_ACTION); 726 return testbase; 727} 728 729function CreateUrls(test) { 730 let secMan = Cc[NS_SCRIPTSECURITYMANAGER_CONTRACTID] 731 .getService(Ci.nsIScriptSecurityManager); 732 733 let manifestURL = g.ioService.newURI(test.manifest); 734 735 let testbase = manifestURL; 736 if (test.runHttp) 737 testbase = ServeTestBase(manifestURL, test.httpDepth) 738 739 function FileToURI(file) 740 { 741 if (file === null) 742 return file; 743 744 var testURI = g.ioService.newURI(file, null, testbase); 745 let isChromeOrViewSource = testURI.scheme == "chrome" || testURI.scheme == "view-source"; 746 let principal = isChromeOrViewSource ? secMan.getSystemPrincipal() : 747 secMan.createContentPrincipal(manifestURL, {}); 748 secMan.checkLoadURIWithPrincipal(principal, testURI, 749 Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); 750 return testURI; 751 } 752 753 let files = [test.url1, test.url2]; 754 [test.url1, test.url2] = files.map(FileToURI); 755 756 return test; 757} 758 759function TestIdentifier(aUrl, aManifestID) { 760 // Construct a platform-independent and location-independent test identifier for 761 // a url; normally the identifier looks like a posix-compliant relative file 762 // path. 763 // Test urls may be simple file names, chrome: urls with full paths, about:blank, etc. 764 if (aUrl.startsWith("about:") || aUrl.startsWith("data:")) { 765 return aUrl; 766 } 767 var pos = aUrl.lastIndexOf("/"); 768 var url = (pos < 0) ? aUrl : aUrl.substring(pos + 1); 769 return (aManifestID + "/" + url); 770} 771 772function AddTestItem(aTest, aFilter, aManifestID) { 773 if (!aFilter) 774 aFilter = [null, [], false]; 775 776 var identifier = TestIdentifier(aTest.url1, aManifestID); 777 if (aTest.url2 !== null) { 778 identifier = [identifier, aTest.type, TestIdentifier(aTest.url2, aManifestID)]; 779 } 780 781 var {url1, url2} = CreateUrls(Object.assign({}, aTest)); 782 783 var globalFilter = aFilter[0]; 784 var manifestFilter = aFilter[1]; 785 var invertManifest = aFilter[2]; 786 if (globalFilter && !globalFilter.test(url1.spec)) 787 return; 788 if (manifestFilter && !(invertManifest ^ manifestFilter.test(url1.spec))) 789 return; 790 if (g.focusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS && 791 !aTest.needsFocus) 792 return; 793 if (g.focusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS && 794 aTest.needsFocus) 795 return; 796 797 aTest.identifier = identifier; 798 g.urls.push(aTest); 799 // Periodically log progress to avoid no-output timeout on slow platforms. 800 // No-output timeouts during manifest parsing have been a problem for 801 // jsreftests on Android/debug. Any logging resets the no-output timer, 802 // even debug logging which is normally not displayed. 803 if ((g.urls.length % 5000) == 0) 804 g.logger.debug(g.urls.length + " tests found..."); 805} 806