• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// `integrityValue` indicates the 'integrity' attribute value at the time of
2// #prepare-a-script.
3//
4// `integrityValueAfterPrepare` indicates how the 'integrity' attribute value
5// is modified after #prepare-a-script:
6// - `undefined` => not modified.
7// - `null` => 'integrity' attribute is removed.
8// - others => 'integrity' attribute value is set to that value.
9//
10// TODO: Make the arguments a dictionary for readability in the test files.
11var SRIScriptTest = function(pass, name, src, integrityValue, crossoriginValue, nonce, integrityValueAfterPrepare) {
12    this.pass = pass;
13    this.name = "Script: " + name;
14    this.src = src;
15    this.integrityValue = integrityValue;
16    this.crossoriginValue = crossoriginValue;
17    this.nonce = nonce;
18    this.integrityValueAfterPrepare = integrityValueAfterPrepare;
19}
20
21SRIScriptTest.prototype.execute = function() {
22    var test = async_test(this.name);
23    var e = document.createElement("script");
24    e.src = this.src;
25    if (this.integrityValue) {
26      e.setAttribute("integrity", this.integrityValue);
27    }
28    if(this.crossoriginValue) {
29        e.setAttribute("crossorigin", this.crossoriginValue);
30    }
31    if(this.nonce) {
32      e.setAttribute("nonce", this.nonce);
33    }
34    if(this.pass) {
35        e.addEventListener("load", function() {test.done()});
36        e.addEventListener("error", function() {
37            test.step(function(){ assert_unreached("Good load fired error handler.") })
38        });
39    } else {
40       e.addEventListener("load", function() {
41            test.step(function() { assert_unreached("Bad load succeeded.") })
42        });
43       e.addEventListener("error", function() {test.done()});
44    }
45    document.body.appendChild(e);
46
47    if (this.integrityValueAfterPrepare === null) {
48      e.removeAttribute("integrity");
49    } else if (this.integrityValueAfterPrepare !== undefined) {
50      e.setAttribute("integrity", this.integrityValueAfterPrepare);
51    }
52};
53
54function set_extra_attributes(element, attrs) {
55  // Apply the rest of the attributes, if any.
56  for (const [attr_name, attr_val] of Object.entries(attrs)) {
57    element[attr_name] = attr_val;
58  }
59}
60
61function buildElementFromDestination(resource_url, destination, attrs) {
62  // Assert: |destination| is a valid destination.
63  let element;
64
65  // The below switch is responsible for:
66  //   1. Creating the correct subresource element
67  //   2. Setting said element's href, src, or fetch-instigating property
68  //      appropriately.
69  switch (destination) {
70    case "script":
71      element = document.createElement(destination);
72      set_extra_attributes(element, attrs);
73      element.src = resource_url;
74      break;
75    case "style":
76      element = document.createElement('link');
77      set_extra_attributes(element, attrs);
78      element.rel = 'stylesheet';
79      element.href = resource_url;
80      break;
81    case "image":
82      element = document.createElement('img');
83      set_extra_attributes(element, attrs);
84      element.src = resource_url;
85      break;
86    default:
87      assert_unreached("INVALID DESTINATION");
88  }
89
90  return element;
91}
92
93// When using SRIPreloadTest, also include /preload/resources/preload_helper.js
94// |number_of_requests| is used to ensure that preload requests are actually
95// reused as expected.
96const SRIPreloadTest = (preload_sri_success, subresource_sri_success, name,
97                        number_of_requests, destination, resource_url,
98                        link_attrs, subresource_attrs) => {
99  const test = async_test(name);
100  const link = document.createElement('link');
101
102  // Early-fail in UAs that do not support `preload` links.
103  test.step_func(() => {
104    assert_true(link.relList.supports('preload'),
105      "This test is automatically failing because the browser does not" +
106      "support `preload` links.");
107  })();
108
109  // Build up the link.
110  link.rel = 'preload';
111  link.as = destination;
112  link.href = resource_url;
113  for (const [attr_name, attr_val] of Object.entries(link_attrs)) {
114    link[attr_name] = attr_val; // This may override `rel` to modulepreload.
115  }
116
117  // Preload + subresource success and failure loading functions.
118  const valid_preload_failed = test.step_func(() =>
119    { assert_unreached("Valid preload fired error handler.") });
120  const invalid_preload_succeeded = test.step_func(() =>
121    { assert_unreached("Invalid preload load succeeded.") });
122  const valid_subresource_failed = test.step_func(() =>
123    { assert_unreached("Valid subresource fired error handler.") });
124  const invalid_subresource_succeeded = test.step_func(() =>
125    { assert_unreached("Invalid subresource load succeeded.") });
126  const subresource_pass = test.step_func(() => {
127    verifyNumberOfResourceTimingEntries(resource_url, number_of_requests);
128    test.done();
129  });
130  const preload_pass = test.step_func(() => {
131    const subresource_element = buildElementFromDestination(
132      resource_url,
133      destination,
134      subresource_attrs
135    );
136
137    if (subresource_sri_success) {
138      subresource_element.onload = subresource_pass;
139      subresource_element.onerror = valid_subresource_failed;
140    } else {
141      subresource_element.onload = invalid_subresource_succeeded;
142      subresource_element.onerror = subresource_pass;
143    }
144
145    document.body.append(subresource_element);
146  });
147
148  if (preload_sri_success) {
149    link.onload = preload_pass;
150    link.onerror = valid_preload_failed;
151  } else {
152    link.onload = invalid_preload_succeeded;
153    link.onerror = preload_pass;
154  }
155
156  document.head.append(link);
157}
158
159// <link> tests
160// Style tests must be done synchronously because they rely on the presence
161// and absence of global style, which can affect later tests. Thus, instead
162// of executing them one at a time, the style tests are implemented as a
163// queue that builds up a list of tests, and then executes them one at a
164// time.
165var SRIStyleTest = function(queue, pass, name, attrs, customCallback, altPassValue) {
166    this.pass = pass;
167    this.name = "Style: " + name;
168    this.customCallback = customCallback || function () {};
169    this.attrs = attrs || {};
170    this.passValue = altPassValue || "rgb(255, 255, 0)";
171
172    this.test = async_test(this.name);
173
174    this.queue = queue;
175    this.queue.push(this);
176}
177
178SRIStyleTest.prototype.execute = function() {
179  var that = this;
180    var container = document.getElementById("container");
181    while (container.hasChildNodes()) {
182      container.removeChild(container.firstChild);
183    }
184
185    var test = this.test;
186
187    var div = document.createElement("div");
188    div.className = "testdiv";
189    var e = document.createElement("link");
190
191    // The link relation is guaranteed to not be "preload" or "modulepreload".
192    this.attrs.rel = this.attrs.rel || "stylesheet";
193    for (var key in this.attrs) {
194        if (this.attrs.hasOwnProperty(key)) {
195            e.setAttribute(key, this.attrs[key]);
196        }
197    }
198
199    if(this.pass) {
200        e.addEventListener("load", function() {
201            test.step(function() {
202                var background = window.getComputedStyle(div, null).getPropertyValue("background-color");
203                assert_equals(background, that.passValue);
204                test.done();
205            });
206        });
207        e.addEventListener("error", function() {
208            test.step(function(){ assert_unreached("Good load fired error handler.") })
209        });
210    } else {
211        e.addEventListener("load", function() {
212             test.step(function() { assert_unreached("Bad load succeeded.") })
213         });
214        e.addEventListener("error", function() {
215            test.step(function() {
216                var background = window.getComputedStyle(div, null).getPropertyValue("background-color");
217                assert_not_equals(background, that.passValue);
218                test.done();
219            });
220        });
221    }
222    container.appendChild(div);
223    container.appendChild(e);
224    this.customCallback(e, container);
225};
226
227