link-load-error-events.sub.js (5969B)
1 /** 2 * This is the guts of the load/error event tests for <link rel="stylesheet">. 3 * 4 * We have a list of tests each of which is an object containing: href value, 5 * expected load success boolean, test description. Href values are set up in 6 * such a way that we guarantee that all stylesheet URLs are unique. This 7 * avoids issues around caching of sheets based on URL. 8 */ 9 10 // Our URLs are random, so we don't use them in error messages by 11 // default, but enable doing it if someone wants to debug things. 12 const DEBUG_URLS = false; 13 14 var isHttps = location.protocol == "https:"; 15 16 var tests = [ 17 // Basic tests 18 { 19 href: existingSheet(), 20 success: true, 21 description: "Basic load of stylesheet", 22 }, 23 { 24 href: nonexistentSheet(), 25 success: false, 26 description: "Attempted load of nonexistent stylesheet", 27 }, 28 { 29 href: `data:text/css,@import url("${existingSheet()}")`, 30 success: true, 31 description: "Import of stylesheet", 32 }, 33 { 34 href: `data:text/css,@import url("${nonexistentSheet()}")`, 35 success: false, 36 description: "Import of nonexistent stylesheet", 37 }, 38 { 39 href: `data:text/css,@import url("data:text/css,@import url('${existingSheet()}')")`, 40 success: true, 41 description: "Import of import of stylesheet", 42 }, 43 { 44 href: `data:text/css,@import url("data:text/css,@import url('${nonexistentSheet()}')")`, 45 success: false, 46 description: "Import of import of nonexistent stylesheet", 47 }, 48 49 // Non-CSS-response tests. 50 { 51 href: makeUnique(""), 52 success: false, 53 description: "Load of non-CSS stylesheet", 54 }, 55 { 56 href: `data:text/css,@import url("${makeUnique("")}")`, 57 success: false, 58 description: "Import of non-CSS stylesheet", 59 }, 60 { 61 href: `data:text/css,@import url("data:text/css,@import url('${makeUnique("")}')")`, 62 success: false, 63 description: "Import of import of non-CSS stylesheet", 64 }, 65 66 // http:// tests, to test what happens with mixed content blocking. 67 { 68 href: httpSheet(), 69 success: !isHttps, 70 description: "Load of http:// stylesheet", 71 }, 72 { 73 href: `data:text/css,@import url("${httpSheet()}")`, 74 success: !isHttps, 75 description: "Import of http:// stylesheet", 76 }, 77 { 78 href: `data:text/css,@import url("data:text/css,@import url('${httpSheet()}')")`, 79 success: !isHttps, 80 description: "Import of import of http:// stylesheet", 81 }, 82 83 // https:// tests just as a control 84 { 85 href: httpsSheet(), 86 success: true, 87 description: "Load of https:// stylesheet", 88 }, 89 { 90 href: `data:text/css,@import url("${httpsSheet()}")`, 91 success: true, 92 description: "Import of https:// stylesheet", 93 }, 94 { 95 href: `data:text/css,@import url("data:text/css,@import url('${httpsSheet()}')")`, 96 success: true, 97 description: "Import of import of https:// stylesheet", 98 }, 99 100 // Tests with multiple imports some of which are slow and some are fast. 101 { 102 href: `data:text/css,@import url("${slowResponse(existingSheet())}"); @import url("${nonexistentSheet()}");`, 103 success: false, 104 description: "Slow successful import, fast failing import", 105 }, 106 { 107 href: `data:text/css,@import url("${existingSheet()}"); @import url("${slowResponse(nonexistentSheet())}");`, 108 success: false, 109 description: "Fast successful import, slow failing import", 110 } 111 ]; 112 113 // Note: Here we really do need to use "let" at least for the href, 114 // because we lazily evaluate it in the unreached cases. 115 for (var test of tests) { 116 let {href, success, description} = test; 117 var t = async_test(description); 118 var link = document.createElement("link"); 119 link.rel = "stylesheet"; 120 hrefString = DEBUG_URLS ? `: ${href}` : ""; 121 if (success) { 122 link.onload = t.step_func_done(() => {}); 123 link.onerror = t.step_func_done(() => assert_unreached(`error fired when load expected${hrefString}`) ); 124 } else { 125 link.onerror = t.step_func_done(() => {}); 126 link.onload = t.step_func_done(() => assert_unreached(`load fired when error expected${hrefString}`) ); 127 } 128 link.href = href; 129 document.head.appendChild(link); 130 } 131 132 /* Utility function */ 133 function makeUnique(url) { 134 // Make sure we copy here, even if the thing coming in is a URL, so we don't 135 // mutate our caller's data. 136 url = new URL(url, location.href); 137 // We want to generate a unique URI to avoid the various caches browsers have 138 // for stylesheets. We don't want to just use a counter, because that would 139 // not be robust to the test being reloaded or othewise run multiple times 140 // without a browser restart. We don't want to use timstamps, because those 141 // are not likely to be unique across calls to this function, especially given 142 // the degraded timer resolution browsers have due to Spectre. 143 // 144 // So just fall back on Math.random() and assume it can't duplicate values. 145 url.searchParams.append("r", Math.random()); 146 return url; 147 } 148 149 function existingSheet() { 150 return makeUnique("resources/good.css"); 151 } 152 153 /** 154 * Function the add values to the "pipe" search param. See 155 * http://wptserve.readthedocs.io/en/latest/pipes.html for why one would do 156 * this. Because this param uses a weird '|'-separated syntax instead of just 157 * using multiple params with the same name, we need some manual code to munge 158 * the value properly. 159 */ 160 function addPipe(url, pipeVal) { 161 url = new URL(url, location.href); 162 var params = url.searchParams; 163 var oldVal = params.get("pipe"); 164 if (oldVal) { 165 params.set("pipe", oldVal + "|" + pipeVal); 166 } else { 167 params.set("pipe", pipeVal); 168 } 169 return url; 170 } 171 172 function nonexistentSheet() { 173 return addPipe(existingSheet(), "status(404)"); 174 } 175 176 function httpSheet() { 177 var url = existingSheet(); 178 url.protocol = "http"; 179 url.port = {{ports[http][0]}}; 180 return url; 181 } 182 183 function httpsSheet() { 184 var url = existingSheet(); 185 url.protocol = "https"; 186 url.port = {{ports[https][0]}}; 187 return url; 188 } 189 190 function slowResponse(url) { 191 return addPipe(url, "trickle(d1)"); 192 }