manifest.sys.mjs (29052B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { globals } from "resource://reftest/globals.sys.mjs"; 6 7 const { 8 NS_GFXINFO_CONTRACTID, 9 10 TYPE_REFTEST_EQUAL, 11 TYPE_REFTEST_NOTEQUAL, 12 TYPE_LOAD, 13 TYPE_SCRIPT, 14 TYPE_PRINT, 15 16 EXPECTED_PASS, 17 EXPECTED_FAIL, 18 EXPECTED_RANDOM, 19 EXPECTED_FUZZY, 20 21 PREF_BOOLEAN, 22 PREF_STRING, 23 PREF_INTEGER, 24 25 FOCUS_FILTER_NEEDS_FOCUS_TESTS, 26 FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS, 27 28 g, 29 } = globals; 30 31 import { NetUtil } from "resource://gre/modules/NetUtil.sys.mjs"; 32 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 33 34 const RE_PROTOCOL = /^\w+:/; 35 const RE_PREF_ITEM = /^(|test-|ref-)pref\((.+?),(.*)\)$/; 36 37 export function ReadTopManifest(aFileURL, aFilter, aManifestID) { 38 var url = g.ioService.newURI(aFileURL); 39 if (!url) { 40 throw new Error("Expected a file or http URL for the manifest."); 41 } 42 43 g.manifestsLoaded = {}; 44 ReadManifest(url, aFilter, aManifestID); 45 } 46 47 // Note: If you materially change the reftest manifest parsing, 48 // please keep the parser in layout/tools/reftest/__init__.py in sync. 49 // (in particular keep CONDITIONS_JS_TO_MP in sync) 50 // eslint-disable-next-line complexity 51 function ReadManifest(aURL, aFilter, aManifestID) { 52 // Ensure each manifest is only read once. This assumes that manifests that 53 // are included with filters will be read via their include before they are 54 // read directly in the case of a duplicate 55 if (g.manifestsLoaded.hasOwnProperty(aURL.spec)) { 56 if (g.manifestsLoaded[aURL.spec] === null) { 57 return; 58 } 59 aFilter = [aFilter[0], aFilter[1], true]; 60 } 61 g.manifestsLoaded[aURL.spec] = aFilter[1]; 62 63 var listURL = aURL; 64 var channel = NetUtil.newChannel({ 65 uri: aURL, 66 loadUsingSystemPrincipal: true, 67 }); 68 try { 69 var inputStream = channel.open(); 70 } catch (e) { 71 g.logger.error("failed to open manifest at : " + aURL.spec); 72 throw new Error(e); 73 } 74 if (channel instanceof Ci.nsIHttpChannel && channel.responseStatus != 200) { 75 g.logger.error("HTTP ERROR : " + channel.responseStatus); 76 } 77 var streamBuf = getStreamContent(inputStream); 78 inputStream.close(); 79 var lines = streamBuf.split(/\n|\r|\r\n/); 80 81 // The sandbox for fails-if(), etc., condition evaluation. This is not 82 // always required and so is created on demand. 83 var sandbox; 84 function GetOrCreateSandbox() { 85 if (!sandbox) { 86 sandbox = BuildConditionSandbox(aURL); 87 } 88 return sandbox; 89 } 90 91 var mozharness_test_paths = Services.prefs.getBoolPref( 92 "reftest.mozharness_test_paths" 93 ); 94 var lineNo = 0; 95 var urlprefix = ""; 96 var defaults = []; 97 var defaultTestPrefSettings = [], 98 defaultRefPrefSettings = []; 99 if (g.compareRetainedDisplayLists) { 100 AddRetainedDisplayListTestPrefs( 101 GetOrCreateSandbox(), 102 defaultTestPrefSettings, 103 defaultRefPrefSettings 104 ); 105 } 106 for (var str of lines) { 107 ++lineNo; 108 if (str.charAt(0) == "#") { 109 continue; 110 } // entire line was a comment 111 var i = str.search(/\s+#/); 112 if (i >= 0) { 113 str = str.substring(0, i); 114 } 115 // strip leading and trailing whitespace 116 str = str.replace(/^\s*/, "").replace(/\s*$/, ""); 117 if (!str || str == "") { 118 continue; 119 } 120 var items = str.split(/\s+/); // split on whitespace 121 122 if (items[0] == "url-prefix") { 123 if (items.length != 2) { 124 throw new Error( 125 "url-prefix requires one url in manifest file " + 126 aURL.spec + 127 " line " + 128 lineNo 129 ); 130 } 131 urlprefix = items[1]; 132 continue; 133 } 134 135 if (items[0] == "defaults") { 136 items.shift(); 137 defaults = items; 138 continue; 139 } 140 141 var expected_status = EXPECTED_PASS; 142 var allow_silent_fail = false; 143 var minAsserts = 0; 144 var maxAsserts = 0; 145 var needs_focus = false; 146 var slow = false; 147 var skip = false; 148 var testPrefSettings = defaultTestPrefSettings.concat(); 149 var refPrefSettings = defaultRefPrefSettings.concat(); 150 var fuzzy_delta = { min: 0, max: 2 }; 151 var fuzzy_pixels = { min: 0, max: 1 }; 152 var chaosMode = false; 153 var wrCapture = { test: false, ref: false }; 154 var nonSkipUsed = false; 155 var noAutoFuzz = false; 156 157 var origLength = items.length; 158 items = defaults.concat(items); 159 var modifiers = [...items]; 160 while ( 161 items[0].match( 162 /^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy|chaos-mode|wr-capture|wr-capture-ref|noautofuzz)/ 163 ) 164 ) { 165 var item = items.shift(); 166 var stat; 167 var cond; 168 var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/); 169 if (m) { 170 stat = m[1]; 171 // Note: m[2] contains the parentheses, and we want them. 172 cond = Cu.evalInSandbox(m[2], GetOrCreateSandbox()); 173 } else if (item.match(/^(fails|random|skip)$/)) { 174 stat = item; 175 cond = true; 176 } else if (item == "needs-focus") { 177 needs_focus = true; 178 cond = false; 179 } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) { 180 cond = false; 181 minAsserts = Number(m[1]); 182 maxAsserts = m[2] == undefined ? minAsserts : Number(m[2].substring(1)); 183 } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) { 184 cond = false; 185 if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) { 186 minAsserts = Number(m[2]); 187 maxAsserts = 188 m[3] == undefined ? minAsserts : Number(m[3].substring(1)); 189 } 190 } else if (item == "slow") { 191 cond = false; 192 slow = true; 193 } else if ((m = item.match(/^require-or\((.*?)\)$/))) { 194 var args = m[1].split(/,/); 195 if (args.length != 2) { 196 throw new Error( 197 "Error in manifest file " + 198 aURL.spec + 199 " line " + 200 lineNo + 201 ": wrong number of args to require-or" 202 ); 203 } 204 var [precondition_str, fallback_action] = args; 205 var preconditions = precondition_str.split(/&&/); 206 cond = false; 207 for (var precondition of preconditions) { 208 if (precondition === "debugMode") { 209 // Currently unimplemented. Requires asynchronous 210 // JSD call + getting an event while no JS is running 211 stat = fallback_action; 212 cond = true; 213 break; 214 } else if (precondition === "true") { 215 // For testing 216 } else { 217 // Unknown precondition. Assume it is unimplemented. 218 stat = fallback_action; 219 cond = true; 220 break; 221 } 222 } 223 } else if ((m = item.match(/^slow-if\((.*?)\)$/))) { 224 cond = false; 225 if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) { 226 slow = true; 227 } 228 } else if (item == "silentfail") { 229 cond = false; 230 allow_silent_fail = true; 231 } else if ((m = item.match(RE_PREF_ITEM))) { 232 cond = false; 233 if ( 234 !AddPrefSettings( 235 m[1], 236 m[2], 237 m[3], 238 GetOrCreateSandbox(), 239 testPrefSettings, 240 refPrefSettings 241 ) 242 ) { 243 throw new Error( 244 "Error in pref value in manifest file " + 245 aURL.spec + 246 " line " + 247 lineNo 248 ); 249 } 250 } else if ((m = item.match(/^fuzzy\((\d+)-(\d+),(\d+)-(\d+)\)$/))) { 251 cond = false; 252 expected_status = EXPECTED_FUZZY; 253 fuzzy_delta = ExtractRange(m, 1); 254 fuzzy_pixels = ExtractRange(m, 3); 255 } else if ( 256 (m = item.match(/^fuzzy-if\((.*?),(\d+)-(\d+),(\d+)-(\d+)\)$/)) 257 ) { 258 cond = false; 259 if (Cu.evalInSandbox("(" + m[1] + ")", GetOrCreateSandbox())) { 260 expected_status = EXPECTED_FUZZY; 261 fuzzy_delta = ExtractRange(m, 2); 262 fuzzy_pixels = ExtractRange(m, 4); 263 } 264 } else if (item == "chaos-mode") { 265 cond = false; 266 chaosMode = true; 267 } else if (item == "wr-capture") { 268 cond = false; 269 wrCapture.test = true; 270 } else if (item == "wr-capture-ref") { 271 cond = false; 272 wrCapture.ref = true; 273 } else if (item == "noautofuzz") { 274 cond = false; 275 noAutoFuzz = true; 276 } else { 277 throw new Error( 278 "Error in manifest file " + 279 aURL.spec + 280 " line " + 281 lineNo + 282 ": unexpected item " + 283 item 284 ); 285 } 286 287 if (stat != "skip") { 288 nonSkipUsed = true; 289 } 290 291 if (cond) { 292 if (stat == "fails") { 293 expected_status = EXPECTED_FAIL; 294 } else if (stat == "random") { 295 expected_status = EXPECTED_RANDOM; 296 } else if (stat == "skip") { 297 skip = true; 298 } else if (stat == "silentfail") { 299 allow_silent_fail = true; 300 } 301 } 302 } 303 304 if (items.length > origLength) { 305 // Implies we broke out of the loop before we finished processing 306 // defaults. This means defaults contained an invalid token. 307 throw new Error( 308 "Error in manifest file " + 309 aURL.spec + 310 " line " + 311 lineNo + 312 ": invalid defaults token '" + 313 items[0] + 314 "'" 315 ); 316 } 317 318 if (minAsserts > maxAsserts) { 319 throw new Error( 320 "Bad range in manifest file " + aURL.spec + " line " + lineNo 321 ); 322 } 323 324 var runHttp = false; 325 var httpDepth; 326 if (items[0] == "HTTP") { 327 runHttp = aURL.scheme == "file"; // We can't yet run the local HTTP server 328 // for non-local reftests. 329 httpDepth = 0; 330 items.shift(); 331 } else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) { 332 // Accept HTTP(..), HTTP(../..), HTTP(../../..), etc. 333 runHttp = aURL.scheme == "file"; // We can't yet run the local HTTP server 334 // for non-local reftests. 335 httpDepth = (items[0].length - 5) / 3; 336 items.shift(); 337 } 338 339 // do not prefix the url for include commands or urls specifying 340 // a protocol 341 if (urlprefix && items[0] != "include") { 342 if (items.length > 1 && !items[1].match(RE_PROTOCOL)) { 343 items[1] = urlprefix + items[1]; 344 } 345 if (items.length > 2 && !items[2].match(RE_PROTOCOL)) { 346 items[2] = urlprefix + items[2]; 347 } 348 } 349 350 var principal = Services.scriptSecurityManager.createContentPrincipal( 351 aURL, 352 {} 353 ); 354 355 if (items[0] == "include") { 356 if (items.length != 2) { 357 throw new Error( 358 "Error in manifest file " + 359 aURL.spec + 360 " line " + 361 lineNo + 362 ": incorrect number of arguments to include" 363 ); 364 } 365 if (runHttp) { 366 throw new Error( 367 "Error in manifest file " + 368 aURL.spec + 369 " line " + 370 lineNo + 371 ": use of include with http" 372 ); 373 } 374 375 // If the expected_status is EXPECTED_PASS (the default) then allow 376 // the include. If 'skip' is true, that means there was a skip 377 // or skip-if annotation (with a true condition) on this include 378 // statement, so we should skip the include. Any other expected_status 379 // is disallowed since it's nonintuitive as to what the intended 380 // effect is. 381 if (nonSkipUsed) { 382 throw new Error( 383 "Error in manifest file " + 384 aURL.spec + 385 " line " + 386 lineNo + 387 ": include statement with annotation other than 'skip' or 'skip-if'" 388 ); 389 } else if (skip) { 390 g.logger.info( 391 "Skipping included manifest at " + 392 aURL.spec + 393 " line " + 394 lineNo + 395 " due to matching skip condition" 396 ); 397 } else { 398 // poor man's assertion 399 if (expected_status != EXPECTED_PASS) { 400 throw new Error( 401 "Error in manifest file parsing code: we should never get expected_status=" + 402 expected_status + 403 " when nonSkipUsed=false (from " + 404 aURL.spec + 405 " line " + 406 lineNo + 407 ")" 408 ); 409 } 410 411 var incURI = g.ioService.newURI(items[1], null, listURL); 412 Services.scriptSecurityManager.checkLoadURIWithPrincipal( 413 principal, 414 incURI, 415 Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT 416 ); 417 418 // Cannot use nsIFile or similar to manipulate the manifest ID; although it appears 419 // path-like, it does not refer to an actual path in the filesystem. 420 var newManifestID = aManifestID; 421 var included = items[1]; 422 // Remove included manifest file name. 423 // eg. dir1/dir2/reftest.list -> dir1/dir2 424 var pos = included.lastIndexOf("/"); 425 if (pos <= 0) { 426 included = ""; 427 } else { 428 included = included.substring(0, pos); 429 } 430 // Simplify references to parent directories. 431 // eg. dir1/dir2/../dir3 -> dir1/dir3 432 while (included.startsWith("../")) { 433 pos = newManifestID.lastIndexOf("/"); 434 if (pos < 0) { 435 pos = 0; 436 } 437 newManifestID = newManifestID.substring(0, pos); 438 included = included.substring(3); 439 } 440 // Use a new manifest ID if the included manifest is in a different directory. 441 if (included.length) { 442 if (newManifestID.length) { 443 newManifestID = newManifestID + "/" + included; 444 } else { 445 // parent directory includes may refer to the topsrcdir 446 newManifestID = included; 447 } 448 } 449 if (mozharness_test_paths) { 450 g.logger.info( 451 "Not recursively reading when MOZHARNESS_TEST_PATHS is set: " + 452 items[1] 453 ); 454 } else { 455 ReadManifest(incURI, aFilter, newManifestID); 456 } 457 } 458 } else if (items[0] == TYPE_LOAD || items[0] == TYPE_SCRIPT) { 459 let type = items[0]; 460 if (items.length != 2) { 461 throw new Error( 462 "Error in manifest file " + 463 aURL.spec + 464 " line " + 465 lineNo + 466 ": incorrect number of arguments to " + 467 type 468 ); 469 } 470 if (type == TYPE_LOAD && expected_status != EXPECTED_PASS) { 471 throw new Error( 472 "Error in manifest file " + 473 aURL.spec + 474 " line " + 475 lineNo + 476 ": incorrect known failure type for load test" 477 ); 478 } 479 AddTestItem( 480 { 481 type, 482 expected: expected_status, 483 manifest: aURL.spec, 484 manifestID: TestIdentifier(aURL.spec, aManifestID), 485 allowSilentFail: allow_silent_fail, 486 minAsserts, 487 maxAsserts, 488 needsFocus: needs_focus, 489 slow, 490 skip, 491 prefSettings1: testPrefSettings, 492 prefSettings2: refPrefSettings, 493 fuzzyMinDelta: fuzzy_delta.min, 494 fuzzyMaxDelta: fuzzy_delta.max, 495 fuzzyMinPixels: fuzzy_pixels.min, 496 fuzzyMaxPixels: fuzzy_pixels.max, 497 runHttp, 498 httpDepth, 499 url1: items[1], 500 url2: null, 501 chaosMode, 502 wrCapture, 503 noAutoFuzz, 504 modifiers, 505 }, 506 aFilter, 507 aManifestID 508 ); 509 } else if ( 510 items[0] == TYPE_REFTEST_EQUAL || 511 items[0] == TYPE_REFTEST_NOTEQUAL || 512 items[0] == TYPE_PRINT 513 ) { 514 if (items.length != 3) { 515 throw new Error( 516 "Error in manifest file " + 517 aURL.spec + 518 " line " + 519 lineNo + 520 ": incorrect number of arguments to " + 521 items[0] 522 ); 523 } 524 525 if ( 526 items[0] == TYPE_REFTEST_NOTEQUAL && 527 expected_status == EXPECTED_FUZZY && 528 (fuzzy_delta.min > 0 || fuzzy_pixels.min > 0) 529 ) { 530 throw new Error( 531 "Error in manifest file " + 532 aURL.spec + 533 " line " + 534 lineNo + 535 ": minimum fuzz must be zero for tests of type " + 536 items[0] 537 ); 538 } 539 540 let type = items[0]; 541 if (g.compareRetainedDisplayLists) { 542 type = TYPE_REFTEST_EQUAL; 543 544 // We expect twice as many assertion failures when comparing 545 // tests because we run each test twice. 546 minAsserts *= 2; 547 maxAsserts *= 2; 548 549 // Skip the test if it is expected to fail in both modes. 550 // It would unexpectedly "pass" in comparison mode mode when 551 // comparing the two failures, which is not a useful result. 552 if ( 553 expected_status === EXPECTED_FAIL || 554 expected_status === EXPECTED_RANDOM 555 ) { 556 skip = true; 557 } 558 } 559 560 AddTestItem( 561 { 562 type, 563 expected: expected_status, 564 manifest: aURL.spec, 565 manifestID: TestIdentifier(aURL.spec, aManifestID), 566 allowSilentFail: allow_silent_fail, 567 minAsserts, 568 maxAsserts, 569 needsFocus: needs_focus, 570 slow, 571 skip, 572 prefSettings1: testPrefSettings, 573 prefSettings2: refPrefSettings, 574 fuzzyMinDelta: fuzzy_delta.min, 575 fuzzyMaxDelta: fuzzy_delta.max, 576 fuzzyMinPixels: fuzzy_pixels.min, 577 fuzzyMaxPixels: fuzzy_pixels.max, 578 runHttp, 579 httpDepth, 580 url1: items[1], 581 url2: items[2], 582 chaosMode, 583 wrCapture, 584 noAutoFuzz, 585 modifiers, 586 }, 587 aFilter, 588 aManifestID 589 ); 590 } else { 591 throw new Error( 592 "Error in manifest file " + 593 aURL.spec + 594 " line " + 595 lineNo + 596 ": unknown test type " + 597 items[0] 598 ); 599 } 600 } 601 } 602 603 // Read all available data from an input stream and return it 604 // as a string. 605 function getStreamContent(inputStream) { 606 var streamBuf = ""; 607 var sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( 608 Ci.nsIScriptableInputStream 609 ); 610 sis.init(inputStream); 611 612 var available; 613 while ((available = sis.available()) != 0) { 614 streamBuf += sis.read(available); 615 } 616 617 return streamBuf; 618 } 619 620 // Build the sandbox for fails-if(), etc., condition evaluation. 621 function BuildConditionSandbox(aURL) { 622 var sandbox = new Cu.Sandbox(aURL.spec); 623 sandbox.mozinfo = Services.prefs.getStringPref("sandbox.mozinfo", {}); 624 let mozinfo = JSON.parse(sandbox.mozinfo); 625 626 // Shortcuts for widget toolkits. 627 sandbox.Android = mozinfo.os == "android"; 628 sandbox.cocoaWidget = mozinfo.toolkit == "cocoa"; 629 sandbox.gtkWidget = mozinfo.toolkit == "gtk"; 630 sandbox.winWidget = mozinfo.toolkit == "windows"; 631 632 // arch 633 sandbox.is64Bit = mozinfo.bits == "64"; // to be replaced by x86_64 or aarch64 634 sandbox.x86 = mozinfo.processor == "x86"; 635 sandbox.x86_64 = mozinfo.processor == "x86_64"; 636 sandbox.aarch64 = mozinfo.processor == "aarch64"; 637 638 // build type 639 sandbox.mingw = mozinfo.mingw; 640 sandbox.isDebugBuild = mozinfo.debug; 641 sandbox.isCoverageBuild = mozinfo.ccov; 642 sandbox.AddressSanitizer = mozinfo.asan; 643 sandbox.ThreadSanitizer = mozinfo.tsan; 644 sandbox.optimized = 645 !sandbox.isDebugBuild && 646 !sandbox.isCoverageBuild && 647 !sandbox.AddressSanitizer && 648 !sandbox.ThreadSanitizer; 649 650 sandbox.release_or_beta = mozinfo.release_or_beta; 651 652 // config specific prefs 653 sandbox.appleSilicon = mozinfo.apple_silicon; 654 sandbox.os_version = mozinfo.os_version; 655 sandbox.wayland = mozinfo.display == "wayland"; 656 657 // data not using mozinfo 658 sandbox.xulRuntime = Cu.cloneInto({}, sandbox); 659 660 // Do we *not* have a dedicated gpu process. 661 sandbox.nogpu = 662 sandbox.wayland || 663 !( 664 Services.prefs.getBoolPref("layers.gpu-process.enabled") && 665 Services.prefs.getBoolPref("layers.gpu-process.force-enabled") 666 ); 667 668 var gfxInfo = 669 NS_GFXINFO_CONTRACTID in Cc && 670 Cc[NS_GFXINFO_CONTRACTID].getService(Ci.nsIGfxInfo); 671 let readGfxInfo = function (obj, key) { 672 if (g.contentGfxInfo && key in g.contentGfxInfo) { 673 return g.contentGfxInfo[key]; 674 } 675 return obj[key]; 676 }; 677 678 sandbox.swgl = g.windowUtils.layerManagerType.startsWith( 679 "WebRender (Software" 680 ); 681 // These detect if each SVG filter primitive is enabled in WebRender 682 sandbox.gfxSVGFE = 683 Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects") && 684 !g.useDrawSnapshot; 685 sandbox.gfxSVGFEBlend = 686 Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feblend") && 687 sandbox.gfxSVGFE; 688 sandbox.gfxSVGFEColorMatrix = 689 Services.prefs.getBoolPref( 690 "gfx.webrender.svg-filter-effects.fecolormatrix" 691 ) && sandbox.gfxSVGFE; 692 sandbox.gfxSVGFEComponentTransfer = 693 Services.prefs.getBoolPref( 694 "gfx.webrender.svg-filter-effects.fecomponenttransfer" 695 ) && sandbox.gfxSVGFE; 696 sandbox.gfxSVGFEComposite = 697 Services.prefs.getBoolPref( 698 "gfx.webrender.svg-filter-effects.fecomposite" 699 ) && sandbox.gfxSVGFE; 700 sandbox.gfxSVGFEDropShadow = 701 Services.prefs.getBoolPref( 702 "gfx.webrender.svg-filter-effects.fedropshadow" 703 ) && sandbox.gfxSVGFE; 704 sandbox.gfxSVGFEFlood = 705 Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feflood") && 706 sandbox.gfxSVGFE; 707 sandbox.gfxSVGFEGaussianBlur = 708 Services.prefs.getBoolPref( 709 "gfx.webrender.svg-filter-effects.fegaussianblur" 710 ) && sandbox.gfxSVGFE; 711 sandbox.gfxSVGFEOffset = 712 Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feoffset") && 713 sandbox.gfxSVGFE; 714 715 // Use this to annotate reftests that fail in drawSnapshot, but 716 // the reason hasn't been investigated (or fixed) yet. 717 sandbox.useDrawSnapshot = g.useDrawSnapshot; 718 719 // GeckoView is currently uniquely identified by "android + e10s" but 720 // we might want to make this condition more precise in the future. 721 sandbox.geckoview = sandbox.Android && g.browserIsRemote; 722 sandbox.isolated_process = sandbox.Android && mozinfo.isolated_process; 723 724 if (sandbox.Android) { 725 sandbox.AndroidVersion = Services.sysinfo.getPropertyAsInt32("version"); 726 727 sandbox.emulator = readGfxInfo(gfxInfo, "adapterDeviceID").includes( 728 "Android Emulator" 729 ); 730 sandbox.device = !sandbox.emulator; 731 } 732 733 // Some reftests need extra fuzz on the Android 13 Pixel 5 devices. 734 sandbox.Android13 = sandbox.AndroidVersion == "33"; 735 736 // always true except for windows mingwclang builds 737 sandbox.webrtc = AppConstants.MOZ_WEBRTC; 738 739 sandbox.prefs = Cu.cloneInto( 740 { 741 getBoolPref(p) { 742 return Services.prefs.getBoolPref(p); 743 }, 744 getIntPref(p) { 745 return Services.prefs.getIntPref(p); 746 }, 747 }, 748 sandbox, 749 { cloneFunctions: true } 750 ); 751 752 // Running with a variant enabled? 753 sandbox.fission = Services.appinfo.fissionAutostart; 754 755 sandbox.incOriginInit = 756 Services.env.get("MOZ_ENABLE_INC_ORIGIN_INIT") === "1"; 757 758 if (!g.dumpedConditionSandbox) { 759 g.logger.info( 760 "Dumping representation of sandbox which can be used for expectation annotations" 761 ); 762 for (let entry of Object.entries(Cu.waiveXrays(sandbox)).sort((a, b) => 763 a[0].localeCompare(b[0]) 764 )) { 765 let value = 766 typeof entry[1] === "object" ? JSON.stringify(entry[1]) : entry[1]; 767 g.logger.info(` ${entry[0]}: ${value}`); 768 } 769 g.dumpedConditionSandbox = true; 770 } 771 772 return sandbox; 773 } 774 775 function AddRetainedDisplayListTestPrefs( 776 aSandbox, 777 aTestPrefSettings, 778 aRefPrefSettings 779 ) { 780 AddPrefSettings( 781 "test-", 782 "layout.display-list.retain", 783 "true", 784 aSandbox, 785 aTestPrefSettings, 786 aRefPrefSettings 787 ); 788 AddPrefSettings( 789 "ref-", 790 "layout.display-list.retain", 791 "false", 792 aSandbox, 793 aTestPrefSettings, 794 aRefPrefSettings 795 ); 796 } 797 798 function AddPrefSettings( 799 aWhere, 800 aPrefName, 801 aPrefValExpression, 802 aSandbox, 803 aTestPrefSettings, 804 aRefPrefSettings 805 ) { 806 var prefVal = Cu.evalInSandbox("(" + aPrefValExpression + ")", aSandbox); 807 var prefType; 808 var valType = typeof prefVal; 809 if (valType == "boolean") { 810 prefType = PREF_BOOLEAN; 811 } else if (valType == "string") { 812 prefType = PREF_STRING; 813 } else if (valType == "number" && parseInt(prefVal) == prefVal) { 814 prefType = PREF_INTEGER; 815 } else { 816 return false; 817 } 818 var setting = { name: aPrefName, type: prefType, value: prefVal }; 819 820 if ( 821 g.compareRetainedDisplayLists && 822 aPrefName != "layout.display-list.retain" 823 ) { 824 // ref-pref() is ignored, test-pref() and pref() are added to both 825 if (aWhere != "ref-") { 826 aTestPrefSettings.push(setting); 827 aRefPrefSettings.push(setting); 828 } 829 } else { 830 if (aWhere != "ref-") { 831 aTestPrefSettings.push(setting); 832 } 833 if (aWhere != "test-") { 834 aRefPrefSettings.push(setting); 835 } 836 } 837 return true; 838 } 839 840 function ExtractRange(matches, startIndex) { 841 return { 842 min: Number(matches[startIndex]), 843 max: Number(matches[startIndex + 1]), 844 }; 845 } 846 847 function ServeTestBase(aURL, depth) { 848 var listURL = aURL.QueryInterface(Ci.nsIFileURL); 849 var directory = listURL.file.parent; 850 851 // Allow serving a tree that's an ancestor of the directory containing 852 // the files so that they can use resources in ../ (etc.). 853 var dirPath = "/"; 854 while (depth > 0) { 855 dirPath = "/" + directory.leafName + dirPath; 856 directory = directory.parent; 857 --depth; 858 } 859 860 g.count++; 861 var path = "/" + Date.now() + "/" + g.count; 862 g.server.registerDirectory(path + "/", directory); 863 // this one is needed so tests can use example.org urls for cross origin testing 864 g.server.registerDirectory("/", directory); 865 866 return g.ioService.newURI( 867 "http://localhost:" + g.httpServerPort + path + dirPath 868 ); 869 } 870 871 export function CreateUrls(test) { 872 let manifestURL = g.ioService.newURI(test.manifest); 873 874 let testbase = manifestURL; 875 if (test.runHttp) { 876 testbase = ServeTestBase(manifestURL, test.httpDepth); 877 } 878 879 let testbasePrincipal = Services.scriptSecurityManager.createContentPrincipal( 880 testbase, 881 {} 882 ); 883 Services.perms.addFromPrincipal( 884 testbasePrincipal, 885 "allowXULXBL", 886 Services.perms.ALLOW_ACTION 887 ); 888 889 function FileToURI(file) { 890 if (file === null) { 891 return file; 892 } 893 894 var testURI = g.ioService.newURI(file, null, testbase); 895 let isChromeOrViewSource = 896 testURI.scheme == "chrome" || testURI.scheme == "view-source"; 897 let principal = isChromeOrViewSource 898 ? Services.scriptSecurityManager.getSystemPrincipal() 899 : Services.scriptSecurityManager.createContentPrincipal(manifestURL, {}); 900 Services.scriptSecurityManager.checkLoadURIWithPrincipal( 901 principal, 902 testURI, 903 Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT 904 ); 905 return testURI; 906 } 907 908 let files = [test.url1, test.url2]; 909 [test.url1, test.url2] = files.map(FileToURI); 910 911 return test; 912 } 913 914 function TestIdentifier(aUrl, aManifestID) { 915 // Construct a platform-independent and location-independent test identifier for 916 // a url; normally the identifier looks like a posix-compliant relative file 917 // path. 918 // Test urls may be simple file names, chrome: urls with full paths, about:blank, etc. 919 if (aUrl.startsWith("about:") || aUrl.startsWith("data:")) { 920 return aUrl; 921 } 922 var pos = aUrl.lastIndexOf("/"); 923 var url = pos < 0 ? aUrl : aUrl.substring(pos + 1); 924 return aManifestID + "/" + url; 925 } 926 927 function AddTestItem(aTest, aFilter, aManifestID) { 928 if (!aFilter) { 929 aFilter = [null, [], false]; 930 } 931 932 var identifier = TestIdentifier(aTest.url1, aManifestID); 933 if (aTest.url2 !== null) { 934 identifier = [ 935 identifier, 936 aTest.type, 937 TestIdentifier(aTest.url2, aManifestID), 938 ]; 939 } 940 941 var { url1, url2 } = CreateUrls(Object.assign({}, aTest)); 942 943 var globalFilter = aFilter[0]; 944 var manifestFilter = aFilter[1]; 945 var invertManifest = aFilter[2]; 946 if (globalFilter && !globalFilter.test(url1.spec)) { 947 if (url2 === null) { 948 return; 949 } 950 if (globalFilter && !globalFilter.test(url2.spec)) { 951 return; 952 } 953 } 954 if (manifestFilter && !(invertManifest ^ manifestFilter.test(url1.spec))) { 955 if (url2 === null) { 956 return; 957 } 958 if (manifestFilter && !(invertManifest ^ manifestFilter.test(url2.spec))) { 959 return; 960 } 961 } 962 if ( 963 g.focusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS && 964 !aTest.needsFocus 965 ) { 966 return; 967 } 968 if ( 969 g.focusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS && 970 aTest.needsFocus 971 ) { 972 return; 973 } 974 975 aTest.identifier = identifier; 976 g.urls.push(aTest); 977 // Periodically log progress to avoid no-output timeout on slow platforms. 978 // No-output timeouts during manifest parsing have been a problem for 979 // jsreftests on Android/debug. Any logging resets the no-output timer, 980 // even debug logging which is normally not displayed. 981 if (g.urls.length % 5000 == 0) { 982 g.logger.debug(g.urls.length + " tests found..."); 983 } 984 }