browser_ManifestObtainer_obtain.js (8232B)
1 "use strict"; 2 3 add_setup(async function () { 4 await SpecialPowers.pushPrefEnv({ 5 set: [ 6 ["dom.manifest.enabled", true], 7 ["dom.security.https_first", false], 8 ], 9 }); 10 }); 11 12 const { ManifestObtainer } = ChromeUtils.importESModule( 13 "resource://gre/modules/ManifestObtainer.sys.mjs" 14 ); 15 const remoteURL = 16 "http://mochi.test:8888/browser/dom/manifest/test/resource.sjs"; 17 const defaultURL = new URL( 18 "http://example.org/browser/dom/manifest/test/resource.sjs" 19 ); 20 defaultURL.searchParams.set("Content-Type", "text/html; charset=utf-8"); 21 requestLongerTimeout(4); 22 23 const tests = [ 24 // Fetch tests. 25 { 26 body: `<!-- no manifest in document -->`, 27 run(manifest) { 28 is(manifest, null, "Manifest without a href yields a null manifest."); 29 }, 30 }, 31 { 32 body: `<link rel="manifest">`, 33 run(manifest) { 34 is(manifest, null, "Manifest without a href yields a null manifest."); 35 }, 36 }, 37 { 38 body: ` 39 <link rel="manifesto" href='resource.sjs?body={"name":"fail"}'> 40 <link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-1"}'> 41 <link rel="manifest" href='resource.sjs?body={"name":"fail"}'>`, 42 run(manifest) { 43 is( 44 manifest.name, 45 "pass-1", 46 "Manifest is first `link` where @rel contains token manifest." 47 ); 48 }, 49 }, 50 { 51 body: ` 52 <link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-2"}'> 53 <link rel="manifest" href='resource.sjs?body={"name":"fail"}'> 54 <link rel="manifest foo bar test" href='resource.sjs?body={"name":"fail"}'>`, 55 run(manifest) { 56 is( 57 manifest.name, 58 "pass-2", 59 "Manifest is first `link` where @rel contains token manifest." 60 ); 61 }, 62 }, 63 { 64 body: `<link rel="manifest" href='${remoteURL}?body={"name":"pass-3"}'>`, 65 run(err) { 66 is( 67 err.name, 68 "TypeError", 69 "By default, manifest cannot load cross-origin." 70 ); 71 }, 72 }, 73 // CORS Tests. 74 { 75 get body() { 76 const body = 'body={"name": "pass-4"}'; 77 const CORS = `Access-Control-Allow-Origin=${defaultURL.origin}`; 78 const link = `<link 79 crossorigin=anonymous 80 rel="manifest" 81 href='${remoteURL}?${body}&${CORS}'>`; 82 return link; 83 }, 84 run(manifest) { 85 is(manifest.name, "pass-4", "CORS enabled, manifest must be fetched."); 86 }, 87 }, 88 { 89 get body() { 90 const body = 'body={"name": "fail"}'; 91 const CORS = "Access-Control-Allow-Origin=http://not-here"; 92 const link = `<link 93 crossorigin 94 rel="manifest" 95 href='${remoteURL}?${body}&${CORS}'>`; 96 return link; 97 }, 98 run(err) { 99 is( 100 err.name, 101 "TypeError", 102 "Fetch blocked by CORS - origin does not match." 103 ); 104 }, 105 }, 106 { 107 body: `<link rel="manifest" href='about:whatever'>`, 108 run(err) { 109 is( 110 err.name, 111 "TypeError", 112 "Trying to load from about:whatever is TypeError." 113 ); 114 }, 115 }, 116 { 117 body: `<link rel="manifest" href='file://manifest'>`, 118 run(err) { 119 is( 120 err.name, 121 "TypeError", 122 "Trying to load from file://whatever is a TypeError." 123 ); 124 }, 125 }, 126 // URL parsing tests 127 { 128 body: `<link rel="manifest" href='http://[12.1212.21.21.12.21.12]'>`, 129 run(err) { 130 is(err.name, "TypeError", "Trying to load invalid URL is a TypeError."); 131 }, 132 }, 133 ]; 134 135 function makeTestURL({ body }) { 136 const url = new URL(defaultURL); 137 url.searchParams.set("body", encodeURIComponent(body)); 138 return url.href; 139 } 140 141 add_task(async function () { 142 const promises = tests 143 .map(test => ({ 144 gBrowser, 145 testRunner: testObtainingManifest(test), 146 url: makeTestURL(test), 147 })) 148 .reduce((collector, tabOpts) => { 149 const promise = BrowserTestUtils.withNewTab(tabOpts, tabOpts.testRunner); 150 collector.push(promise); 151 return collector; 152 }, []); 153 154 await Promise.all(promises); 155 156 function testObtainingManifest(aTest) { 157 return async function (aBrowser) { 158 try { 159 const manifest = await ManifestObtainer.browserObtainManifest(aBrowser); 160 aTest.run(manifest); 161 } catch (e) { 162 aTest.run(e); 163 } 164 }; 165 } 166 }); 167 168 add_task(async () => { 169 // This loads a generic html page. 170 const url = new URL(defaultURL); 171 // The body get injected into the page on the server. 172 const body = `<link rel="manifest" href='resource.sjs?body={"name": "conformance check"}'>`; 173 url.searchParams.set("body", encodeURIComponent(body)); 174 175 // Let's open a tab! 176 const tabOpts = { 177 gBrowser, 178 url: url.href, 179 }; 180 // Let's do the test 181 await BrowserTestUtils.withNewTab(tabOpts, async aBrowser => { 182 const obtainerOpts = { 183 checkConformance: true, // gives us back "moz_manifest_url" member 184 }; 185 const manifest = await ManifestObtainer.browserObtainManifest( 186 aBrowser, 187 obtainerOpts 188 ); 189 is(manifest.name, "conformance check"); 190 ok("moz_manifest_url" in manifest, "Has a moz_manifest_url member"); 191 const testString = defaultURL.origin + defaultURL.pathname; 192 ok( 193 manifest.moz_manifest_url.startsWith(testString), 194 `Expect to start with with the testString, but got ${manifest.moz_manifest_url} instead,` 195 ); 196 // Clean up! 197 gBrowser.removeTab(gBrowser.getTabForBrowser(aBrowser)); 198 }); 199 }); 200 201 /* 202 * e10s race condition tests 203 * Open a bunch of tabs and load manifests 204 * in each tab. They should all return pass. 205 */ 206 add_task(async function () { 207 const defaultPath = "/browser/dom/manifest/test/manifestLoader.html"; 208 const tabURLs = [ 209 `http://example.com:80${defaultPath}`, 210 `http://example.org:80${defaultPath}`, 211 `http://example.org:8000${defaultPath}`, 212 `http://mochi.test:8888${defaultPath}`, 213 `http://sub1.test1.example.com:80${defaultPath}`, 214 `http://sub1.test1.example.org:80${defaultPath}`, 215 `http://sub1.test1.example.org:8000${defaultPath}`, 216 `http://sub1.test1.mochi.test:8888${defaultPath}`, 217 `http://sub1.test2.example.com:80${defaultPath}`, 218 `http://sub1.test2.example.org:80${defaultPath}`, 219 `http://sub1.test2.example.org:8000${defaultPath}`, 220 `http://sub2.test1.example.com:80${defaultPath}`, 221 `http://sub2.test1.example.org:80${defaultPath}`, 222 `http://sub2.test1.example.org:8000${defaultPath}`, 223 `http://sub2.test2.example.com:80${defaultPath}`, 224 `http://sub2.test2.example.org:80${defaultPath}`, 225 `http://sub2.test2.example.org:8000${defaultPath}`, 226 `http://sub2.xn--lt-uia.mochi.test:8888${defaultPath}`, 227 `http://test1.example.com:80${defaultPath}`, 228 `http://test1.example.org:80${defaultPath}`, 229 `http://test1.example.org:8000${defaultPath}`, 230 `http://test1.mochi.test:8888${defaultPath}`, 231 `http://test2.example.com:80${defaultPath}`, 232 `http://test2.example.org:80${defaultPath}`, 233 `http://test2.example.org:8000${defaultPath}`, 234 `http://test2.mochi.test:8888${defaultPath}`, 235 `http://test:80${defaultPath}`, 236 `http://www.example.com:80${defaultPath}`, 237 ]; 238 // Open tabs an collect corresponding browsers 239 let browsers = tabURLs.map( 240 url => BrowserTestUtils.addTab(gBrowser, url).linkedBrowser 241 ); 242 243 // Once all the pages have loaded, run a bunch of tests in "parallel". 244 await Promise.all( 245 (function* () { 246 for (let browser of browsers) { 247 yield BrowserTestUtils.browserLoaded(browser); 248 } 249 })() 250 ); 251 // Flood random browsers with requests. Once promises settle, check that 252 // responses all pass. 253 const results = await Promise.all( 254 (function* () { 255 for (let browser of randBrowsers(browsers, 50)) { 256 yield ManifestObtainer.browserObtainManifest(browser); 257 } 258 })() 259 ); 260 const pass = results.every(manifest => manifest.name === "pass"); 261 ok(pass, "Expect every manifest to have name equal to `pass`."); 262 // cleanup 263 browsers 264 .map(browser => gBrowser.getTabForBrowser(browser)) 265 .forEach(tab => gBrowser.removeTab(tab)); 266 267 // Helper generator, spits out random browsers 268 function* randBrowsers(aBrowsers, aMax) { 269 for (let i = 0; i < aMax; i++) { 270 const randNum = Math.round(Math.random() * (aBrowsers.length - 1)); 271 yield aBrowsers[randNum]; 272 } 273 } 274 });