1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
<!DOCTYPE html>
<link rel="author" title="Dom Farolino" href="mailto:dom@chromium.org">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-link-element">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script>
function getTimeoutPromise(t, msg) {
return new Promise((resolve, reject) => {
t.step_timeout(() => reject(`Timeout reached: ${msg}`), 2000);
});
}
// The tests generated from this loop are important to ensure that a link's load
// or error event handler never incurs infinite recursion by virtue of
// re-setting a link element's attribute to the value it already has. This is
// what thwarted the first attempt at fixing https://crbug.com/41436016 in
// Chromium.
const attributes_to_test = ['rel', 'href', 'type', 'media'];
for (const attribute of attributes_to_test) {
promise_test(async t => {
const id = token();
const link = document.createElement('link');
let count = 0;
let response = null;
link.onerror = t.unreached_func('Sheet should load successfully');
const firstLoadPromise = new Promise(resolve => {
link.onload = resolve;
});
// Set all attributes to a sensible default, so when we re-assign one of
// them (`attribute`) idempotently later, the value doesn't change.
link.rel = 'stylesheet';
link.media = 'all';
link.type = 'text/css';
link.href = new URL(`stylesheet.py?id=${id}`, location.href);
document.head.append(link);
t.add_cleanup(() => {
link.remove();
});
await Promise.race([firstLoadPromise, getTimeoutPromise(t, 'first resource')]);
response = await fetch(`stylesheet.py?id=${id}&count=foo`);
count = await response.text();
assert_equals(count, '1', "server sees first style sheet request");
const secondLoadPromise = new Promise((resolve, reject) => {
link.onload = () => reject('second load event unexpectedly fired');
});
const expectedTimeoutPromise = new Promise(resolve => {
t.step_timeout(resolve, 2000);
});
link[attribute] = link[attribute];
await Promise.race([expectedTimeoutPromise, secondLoadPromise]);
response = await fetch(`stylesheet.py?id=${id}&count=foo`);
count = await response.text();
assert_equals(count, '0',
"server does not see second request to the same style sheet");
}, `<link> cannot request the same resource twice by touching the ` +
`'${attribute}' attribute, if its value never changes`);
}
promise_test(async t => {
const id = token();
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'style';
document.head.append(link);
t.add_cleanup(() => {
link.remove();
});
let count = 0;
let response = null;
link.onerror = t.unreached_func('Sheet should load successfully');
const firstLoadPromise = new Promise(resolve => {
link.onload = resolve;
});
link.href = `stylesheet.py?id=${id}`;
await Promise.race([firstLoadPromise, getTimeoutPromise(t, 'first resource')]);
response = await fetch(`stylesheet.py?id=${id}&count=foo`);
count = await response.text();
assert_equals(count, '1', "server sees preload request");
const secondLoadPromise = new Promise(resolve => {
link.onload = resolve;
});
// Switching from `rel=preload` => `rel=stylesheet` triggers the stylesheet
// processing model. The resource loads from the preload cache and never
// touches the network, but the load event does fire again.
link.rel = 'stylesheet';
await Promise.race([secondLoadPromise,
getTimeoutPromise(t, 'rel=stylesheet using preload cache')]);
response = await fetch(`stylesheet.py?id=${id}&count=foo`);
count = await response.text();
assert_equals(count, '0', "server does not see second request to the same style sheet");
}, "<link> load event can fire twice for the same href resource, based on " +
"'rel' attribute mutations");
promise_test(async t => {
const link = document.createElement('link');
link.rel = 'stylesheet';
document.head.append(link);
t.add_cleanup(() => {
link.remove();
});
link.onerror = t.unreached_func('Sheet should load successfully');
const firstLoadPromise = new Promise(resolve => {
link.onload = resolve;
});
link.href = 'style.css?first';
await Promise.race([firstLoadPromise, getTimeoutPromise(t, 'first resource')]);
const secondLoadPromise = new Promise(resolve => {
link.onload = resolve;
});
link.href = 'style.css?second';
await Promise.race([secondLoadPromise, getTimeoutPromise(t, 'second resource')]);
const thirdLoadPromise = new Promise(resolve => {
link.onload = resolve;
});
link.href = 'style.css?third';
await Promise.race([thirdLoadPromise, getTimeoutPromise(t, 'third resource')]);
}, "<link> load event fires for each DIFFERENT stylesheet it loads");
</script>
</head>
</html>
|