11cb0ef41Sopenharmony_ci// `integrityValue` indicates the 'integrity' attribute value at the time of 21cb0ef41Sopenharmony_ci// #prepare-a-script. 31cb0ef41Sopenharmony_ci// 41cb0ef41Sopenharmony_ci// `integrityValueAfterPrepare` indicates how the 'integrity' attribute value 51cb0ef41Sopenharmony_ci// is modified after #prepare-a-script: 61cb0ef41Sopenharmony_ci// - `undefined` => not modified. 71cb0ef41Sopenharmony_ci// - `null` => 'integrity' attribute is removed. 81cb0ef41Sopenharmony_ci// - others => 'integrity' attribute value is set to that value. 91cb0ef41Sopenharmony_ci// 101cb0ef41Sopenharmony_ci// TODO: Make the arguments a dictionary for readability in the test files. 111cb0ef41Sopenharmony_civar SRIScriptTest = function(pass, name, src, integrityValue, crossoriginValue, nonce, integrityValueAfterPrepare) { 121cb0ef41Sopenharmony_ci this.pass = pass; 131cb0ef41Sopenharmony_ci this.name = "Script: " + name; 141cb0ef41Sopenharmony_ci this.src = src; 151cb0ef41Sopenharmony_ci this.integrityValue = integrityValue; 161cb0ef41Sopenharmony_ci this.crossoriginValue = crossoriginValue; 171cb0ef41Sopenharmony_ci this.nonce = nonce; 181cb0ef41Sopenharmony_ci this.integrityValueAfterPrepare = integrityValueAfterPrepare; 191cb0ef41Sopenharmony_ci} 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ciSRIScriptTest.prototype.execute = function() { 221cb0ef41Sopenharmony_ci var test = async_test(this.name); 231cb0ef41Sopenharmony_ci var e = document.createElement("script"); 241cb0ef41Sopenharmony_ci e.src = this.src; 251cb0ef41Sopenharmony_ci if (this.integrityValue) { 261cb0ef41Sopenharmony_ci e.setAttribute("integrity", this.integrityValue); 271cb0ef41Sopenharmony_ci } 281cb0ef41Sopenharmony_ci if(this.crossoriginValue) { 291cb0ef41Sopenharmony_ci e.setAttribute("crossorigin", this.crossoriginValue); 301cb0ef41Sopenharmony_ci } 311cb0ef41Sopenharmony_ci if(this.nonce) { 321cb0ef41Sopenharmony_ci e.setAttribute("nonce", this.nonce); 331cb0ef41Sopenharmony_ci } 341cb0ef41Sopenharmony_ci if(this.pass) { 351cb0ef41Sopenharmony_ci e.addEventListener("load", function() {test.done()}); 361cb0ef41Sopenharmony_ci e.addEventListener("error", function() { 371cb0ef41Sopenharmony_ci test.step(function(){ assert_unreached("Good load fired error handler.") }) 381cb0ef41Sopenharmony_ci }); 391cb0ef41Sopenharmony_ci } else { 401cb0ef41Sopenharmony_ci e.addEventListener("load", function() { 411cb0ef41Sopenharmony_ci test.step(function() { assert_unreached("Bad load succeeded.") }) 421cb0ef41Sopenharmony_ci }); 431cb0ef41Sopenharmony_ci e.addEventListener("error", function() {test.done()}); 441cb0ef41Sopenharmony_ci } 451cb0ef41Sopenharmony_ci document.body.appendChild(e); 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_ci if (this.integrityValueAfterPrepare === null) { 481cb0ef41Sopenharmony_ci e.removeAttribute("integrity"); 491cb0ef41Sopenharmony_ci } else if (this.integrityValueAfterPrepare !== undefined) { 501cb0ef41Sopenharmony_ci e.setAttribute("integrity", this.integrityValueAfterPrepare); 511cb0ef41Sopenharmony_ci } 521cb0ef41Sopenharmony_ci}; 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_cifunction set_extra_attributes(element, attrs) { 551cb0ef41Sopenharmony_ci // Apply the rest of the attributes, if any. 561cb0ef41Sopenharmony_ci for (const [attr_name, attr_val] of Object.entries(attrs)) { 571cb0ef41Sopenharmony_ci element[attr_name] = attr_val; 581cb0ef41Sopenharmony_ci } 591cb0ef41Sopenharmony_ci} 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_cifunction buildElementFromDestination(resource_url, destination, attrs) { 621cb0ef41Sopenharmony_ci // Assert: |destination| is a valid destination. 631cb0ef41Sopenharmony_ci let element; 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ci // The below switch is responsible for: 661cb0ef41Sopenharmony_ci // 1. Creating the correct subresource element 671cb0ef41Sopenharmony_ci // 2. Setting said element's href, src, or fetch-instigating property 681cb0ef41Sopenharmony_ci // appropriately. 691cb0ef41Sopenharmony_ci switch (destination) { 701cb0ef41Sopenharmony_ci case "script": 711cb0ef41Sopenharmony_ci element = document.createElement(destination); 721cb0ef41Sopenharmony_ci set_extra_attributes(element, attrs); 731cb0ef41Sopenharmony_ci element.src = resource_url; 741cb0ef41Sopenharmony_ci break; 751cb0ef41Sopenharmony_ci case "style": 761cb0ef41Sopenharmony_ci element = document.createElement('link'); 771cb0ef41Sopenharmony_ci set_extra_attributes(element, attrs); 781cb0ef41Sopenharmony_ci element.rel = 'stylesheet'; 791cb0ef41Sopenharmony_ci element.href = resource_url; 801cb0ef41Sopenharmony_ci break; 811cb0ef41Sopenharmony_ci case "image": 821cb0ef41Sopenharmony_ci element = document.createElement('img'); 831cb0ef41Sopenharmony_ci set_extra_attributes(element, attrs); 841cb0ef41Sopenharmony_ci element.src = resource_url; 851cb0ef41Sopenharmony_ci break; 861cb0ef41Sopenharmony_ci default: 871cb0ef41Sopenharmony_ci assert_unreached("INVALID DESTINATION"); 881cb0ef41Sopenharmony_ci } 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci return element; 911cb0ef41Sopenharmony_ci} 921cb0ef41Sopenharmony_ci 931cb0ef41Sopenharmony_ci// When using SRIPreloadTest, also include /preload/resources/preload_helper.js 941cb0ef41Sopenharmony_ci// |number_of_requests| is used to ensure that preload requests are actually 951cb0ef41Sopenharmony_ci// reused as expected. 961cb0ef41Sopenharmony_ciconst SRIPreloadTest = (preload_sri_success, subresource_sri_success, name, 971cb0ef41Sopenharmony_ci number_of_requests, destination, resource_url, 981cb0ef41Sopenharmony_ci link_attrs, subresource_attrs) => { 991cb0ef41Sopenharmony_ci const test = async_test(name); 1001cb0ef41Sopenharmony_ci const link = document.createElement('link'); 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_ci // Early-fail in UAs that do not support `preload` links. 1031cb0ef41Sopenharmony_ci test.step_func(() => { 1041cb0ef41Sopenharmony_ci assert_true(link.relList.supports('preload'), 1051cb0ef41Sopenharmony_ci "This test is automatically failing because the browser does not" + 1061cb0ef41Sopenharmony_ci "support `preload` links."); 1071cb0ef41Sopenharmony_ci })(); 1081cb0ef41Sopenharmony_ci 1091cb0ef41Sopenharmony_ci // Build up the link. 1101cb0ef41Sopenharmony_ci link.rel = 'preload'; 1111cb0ef41Sopenharmony_ci link.as = destination; 1121cb0ef41Sopenharmony_ci link.href = resource_url; 1131cb0ef41Sopenharmony_ci for (const [attr_name, attr_val] of Object.entries(link_attrs)) { 1141cb0ef41Sopenharmony_ci link[attr_name] = attr_val; // This may override `rel` to modulepreload. 1151cb0ef41Sopenharmony_ci } 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci // Preload + subresource success and failure loading functions. 1181cb0ef41Sopenharmony_ci const valid_preload_failed = test.step_func(() => 1191cb0ef41Sopenharmony_ci { assert_unreached("Valid preload fired error handler.") }); 1201cb0ef41Sopenharmony_ci const invalid_preload_succeeded = test.step_func(() => 1211cb0ef41Sopenharmony_ci { assert_unreached("Invalid preload load succeeded.") }); 1221cb0ef41Sopenharmony_ci const valid_subresource_failed = test.step_func(() => 1231cb0ef41Sopenharmony_ci { assert_unreached("Valid subresource fired error handler.") }); 1241cb0ef41Sopenharmony_ci const invalid_subresource_succeeded = test.step_func(() => 1251cb0ef41Sopenharmony_ci { assert_unreached("Invalid subresource load succeeded.") }); 1261cb0ef41Sopenharmony_ci const subresource_pass = test.step_func(() => { 1271cb0ef41Sopenharmony_ci verifyNumberOfResourceTimingEntries(resource_url, number_of_requests); 1281cb0ef41Sopenharmony_ci test.done(); 1291cb0ef41Sopenharmony_ci }); 1301cb0ef41Sopenharmony_ci const preload_pass = test.step_func(() => { 1311cb0ef41Sopenharmony_ci const subresource_element = buildElementFromDestination( 1321cb0ef41Sopenharmony_ci resource_url, 1331cb0ef41Sopenharmony_ci destination, 1341cb0ef41Sopenharmony_ci subresource_attrs 1351cb0ef41Sopenharmony_ci ); 1361cb0ef41Sopenharmony_ci 1371cb0ef41Sopenharmony_ci if (subresource_sri_success) { 1381cb0ef41Sopenharmony_ci subresource_element.onload = subresource_pass; 1391cb0ef41Sopenharmony_ci subresource_element.onerror = valid_subresource_failed; 1401cb0ef41Sopenharmony_ci } else { 1411cb0ef41Sopenharmony_ci subresource_element.onload = invalid_subresource_succeeded; 1421cb0ef41Sopenharmony_ci subresource_element.onerror = subresource_pass; 1431cb0ef41Sopenharmony_ci } 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci document.body.append(subresource_element); 1461cb0ef41Sopenharmony_ci }); 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_ci if (preload_sri_success) { 1491cb0ef41Sopenharmony_ci link.onload = preload_pass; 1501cb0ef41Sopenharmony_ci link.onerror = valid_preload_failed; 1511cb0ef41Sopenharmony_ci } else { 1521cb0ef41Sopenharmony_ci link.onload = invalid_preload_succeeded; 1531cb0ef41Sopenharmony_ci link.onerror = preload_pass; 1541cb0ef41Sopenharmony_ci } 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci document.head.append(link); 1571cb0ef41Sopenharmony_ci} 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ci// <link> tests 1601cb0ef41Sopenharmony_ci// Style tests must be done synchronously because they rely on the presence 1611cb0ef41Sopenharmony_ci// and absence of global style, which can affect later tests. Thus, instead 1621cb0ef41Sopenharmony_ci// of executing them one at a time, the style tests are implemented as a 1631cb0ef41Sopenharmony_ci// queue that builds up a list of tests, and then executes them one at a 1641cb0ef41Sopenharmony_ci// time. 1651cb0ef41Sopenharmony_civar SRIStyleTest = function(queue, pass, name, attrs, customCallback, altPassValue) { 1661cb0ef41Sopenharmony_ci this.pass = pass; 1671cb0ef41Sopenharmony_ci this.name = "Style: " + name; 1681cb0ef41Sopenharmony_ci this.customCallback = customCallback || function () {}; 1691cb0ef41Sopenharmony_ci this.attrs = attrs || {}; 1701cb0ef41Sopenharmony_ci this.passValue = altPassValue || "rgb(255, 255, 0)"; 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci this.test = async_test(this.name); 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci this.queue = queue; 1751cb0ef41Sopenharmony_ci this.queue.push(this); 1761cb0ef41Sopenharmony_ci} 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ciSRIStyleTest.prototype.execute = function() { 1791cb0ef41Sopenharmony_ci var that = this; 1801cb0ef41Sopenharmony_ci var container = document.getElementById("container"); 1811cb0ef41Sopenharmony_ci while (container.hasChildNodes()) { 1821cb0ef41Sopenharmony_ci container.removeChild(container.firstChild); 1831cb0ef41Sopenharmony_ci } 1841cb0ef41Sopenharmony_ci 1851cb0ef41Sopenharmony_ci var test = this.test; 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ci var div = document.createElement("div"); 1881cb0ef41Sopenharmony_ci div.className = "testdiv"; 1891cb0ef41Sopenharmony_ci var e = document.createElement("link"); 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci // The link relation is guaranteed to not be "preload" or "modulepreload". 1921cb0ef41Sopenharmony_ci this.attrs.rel = this.attrs.rel || "stylesheet"; 1931cb0ef41Sopenharmony_ci for (var key in this.attrs) { 1941cb0ef41Sopenharmony_ci if (this.attrs.hasOwnProperty(key)) { 1951cb0ef41Sopenharmony_ci e.setAttribute(key, this.attrs[key]); 1961cb0ef41Sopenharmony_ci } 1971cb0ef41Sopenharmony_ci } 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci if(this.pass) { 2001cb0ef41Sopenharmony_ci e.addEventListener("load", function() { 2011cb0ef41Sopenharmony_ci test.step(function() { 2021cb0ef41Sopenharmony_ci var background = window.getComputedStyle(div, null).getPropertyValue("background-color"); 2031cb0ef41Sopenharmony_ci assert_equals(background, that.passValue); 2041cb0ef41Sopenharmony_ci test.done(); 2051cb0ef41Sopenharmony_ci }); 2061cb0ef41Sopenharmony_ci }); 2071cb0ef41Sopenharmony_ci e.addEventListener("error", function() { 2081cb0ef41Sopenharmony_ci test.step(function(){ assert_unreached("Good load fired error handler.") }) 2091cb0ef41Sopenharmony_ci }); 2101cb0ef41Sopenharmony_ci } else { 2111cb0ef41Sopenharmony_ci e.addEventListener("load", function() { 2121cb0ef41Sopenharmony_ci test.step(function() { assert_unreached("Bad load succeeded.") }) 2131cb0ef41Sopenharmony_ci }); 2141cb0ef41Sopenharmony_ci e.addEventListener("error", function() { 2151cb0ef41Sopenharmony_ci test.step(function() { 2161cb0ef41Sopenharmony_ci var background = window.getComputedStyle(div, null).getPropertyValue("background-color"); 2171cb0ef41Sopenharmony_ci assert_not_equals(background, that.passValue); 2181cb0ef41Sopenharmony_ci test.done(); 2191cb0ef41Sopenharmony_ci }); 2201cb0ef41Sopenharmony_ci }); 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci container.appendChild(div); 2231cb0ef41Sopenharmony_ci container.appendChild(e); 2241cb0ef41Sopenharmony_ci this.customCallback(e, container); 2251cb0ef41Sopenharmony_ci}; 2261cb0ef41Sopenharmony_ci 227