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