fetch-event.https.html (41615B)
1 <!DOCTYPE html> 2 <meta name=timeout content=long> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <script src="/common/get-host-info.sub.js"></script> 6 <script src="/common/utils.js"></script> 7 <script src="resources/test-helpers.sub.js"></script> 8 <body> 9 <script> 10 var worker = 'resources/fetch-event-test-worker.js'; 11 function wait(ms) { 12 return new Promise(resolve => step_timeout(resolve, ms)); 13 } 14 15 promise_test(async t => { 16 const scope = 'resources/'; 17 const registration = 18 await service_worker_unregister_and_register(t, worker, scope); 19 await wait_for_state(t, registration.installing, 'activated'); 20 21 // This will happen after all other tests 22 promise_test(t => { 23 return registration.unregister(); 24 }, 'restore global state'); 25 }, 'global setup'); 26 27 promise_test(t => { 28 const page_url = 'resources/simple.html?headers'; 29 return with_iframe(page_url) 30 .then(function(frame) { 31 t.add_cleanup(() => { frame.remove(); }); 32 const headers = JSON.parse(frame.contentDocument.body.textContent); 33 const header_names = {}; 34 for (const [name, value] of headers) { 35 header_names[name] = true; 36 } 37 38 assert_true( 39 header_names.hasOwnProperty('accept'), 40 'request includes "Accept" header as inserted by Fetch' 41 ); 42 }); 43 }, 'Service Worker headers in the request of a fetch event'); 44 45 promise_test(t => { 46 const page_url = 'resources/simple.html?string'; 47 return with_iframe(page_url) 48 .then(function(frame) { 49 t.add_cleanup(() => { frame.remove(); }); 50 assert_equals( 51 frame.contentDocument.body.textContent, 52 'Test string', 53 'Service Worker should respond to fetch with a test string'); 54 assert_equals( 55 frame.contentDocument.contentType, 56 'text/plain', 57 'The content type of the response created with a string should be text/plain'); 58 assert_equals( 59 frame.contentDocument.characterSet, 60 'UTF-8', 61 'The character set of the response created with a string should be UTF-8'); 62 }); 63 }, 'Service Worker responds to fetch event with string'); 64 65 promise_test(t => { 66 const page_url = 'resources/simple.html?string'; 67 var frame; 68 return with_iframe(page_url) 69 .then(function(f) { 70 frame = f; 71 t.add_cleanup(() => { frame.remove(); }); 72 return frame.contentWindow.fetch(page_url + "#foo") 73 }) 74 .then(function(response) { return response.text() }) 75 .then(function(text) { 76 assert_equals( 77 text, 78 'Test string', 79 'Service Worker should respond to fetch with a test string'); 80 }); 81 }, 'Service Worker responds to fetch event using request fragment with string'); 82 83 promise_test(t => { 84 const page_url = 'resources/simple.html?blob'; 85 return with_iframe(page_url) 86 .then(frame => { 87 t.add_cleanup(() => { frame.remove(); }); 88 assert_equals( 89 frame.contentDocument.body.textContent, 90 'Test blob', 91 'Service Worker should respond to fetch with a test string'); 92 }); 93 }, 'Service Worker responds to fetch event with blob body'); 94 95 promise_test(t => { 96 const page_url = 'resources/simple.html?referrer'; 97 return with_iframe(page_url) 98 .then(frame => { 99 t.add_cleanup(() => { frame.remove(); }); 100 assert_equals( 101 frame.contentDocument.body.textContent, 102 'Referrer: ' + document.location.href, 103 'Service Worker should respond to fetch with the referrer URL'); 104 }); 105 }, 'Service Worker responds to fetch event with the referrer URL'); 106 107 promise_test(t => { 108 const page_url = 'resources/simple.html?clientId'; 109 var frame; 110 return with_iframe(page_url) 111 .then(function(f) { 112 frame = f; 113 t.add_cleanup(() => { frame.remove(); }); 114 assert_equals( 115 frame.contentDocument.body.textContent, 116 'Client ID Not Found', 117 'Service Worker should respond to fetch with a client id'); 118 return frame.contentWindow.fetch('resources/other.html?clientId'); 119 }) 120 .then(function(response) { return response.text(); }) 121 .then(function(response_text) { 122 assert_equals( 123 response_text.substr(0, 15), 124 'Client ID Found', 125 'Service Worker should respond to fetch with an existing client id'); 126 }); 127 }, 'Service Worker responds to fetch event with an existing client id'); 128 129 promise_test(t => { 130 const page_url = 'resources/simple.html?resultingClientId'; 131 const expected_found = 'Resulting Client ID Found'; 132 const expected_not_found = 'Resulting Client ID Not Found'; 133 return with_iframe(page_url) 134 .then(function(frame) { 135 t.add_cleanup(() => { frame.remove(); }); 136 assert_equals( 137 frame.contentDocument.body.textContent.substr(0, expected_found.length), 138 expected_found, 139 'Service Worker should respond with an existing resulting client id for non-subresource requests'); 140 return frame.contentWindow.fetch('resources/other.html?resultingClientId'); 141 }) 142 .then(function(response) { return response.text(); }) 143 .then(function(response_text) { 144 assert_equals( 145 response_text.substr(0), 146 expected_not_found, 147 'Service Worker should respond with an empty resulting client id for subresource requests'); 148 }); 149 }, 'Service Worker responds to fetch event with the correct resulting client id'); 150 151 promise_test(t => { 152 const page_url = 'resources/simple.html?ignore'; 153 return with_iframe(page_url) 154 .then(function(frame) { 155 t.add_cleanup(() => { frame.remove(); }); 156 assert_equals(frame.contentDocument.body.textContent, 157 'Here\'s a simple html file.\n', 158 'Response should come from fallback to native fetch'); 159 }); 160 }, 'Service Worker does not respond to fetch event'); 161 162 promise_test(t => { 163 const page_url = 'resources/simple.html?null'; 164 return with_iframe(page_url) 165 .then(function(frame) { 166 t.add_cleanup(() => { frame.remove(); }); 167 assert_equals(frame.contentDocument.body.textContent, 168 '', 169 'Response should be the empty string'); 170 }); 171 }, 'Service Worker responds to fetch event with null response body'); 172 173 promise_test(t => { 174 const page_url = 'resources/simple.html?fetch'; 175 return with_iframe(page_url) 176 .then(function(frame) { 177 t.add_cleanup(() => { frame.remove(); }); 178 assert_equals(frame.contentDocument.body.textContent, 179 'Here\'s an other html file.\n', 180 'Response should come from fetched other file'); 181 }); 182 }, 'Service Worker fetches other file in fetch event'); 183 184 // Creates a form and an iframe and does a form submission that navigates the 185 // frame to |action_url|. Returns the frame after navigation. 186 function submit_form(action_url) { 187 return new Promise(resolve => { 188 const frame = document.createElement('iframe'); 189 frame.name = 'post-frame'; 190 document.body.appendChild(frame); 191 const form = document.createElement('form'); 192 form.target = frame.name; 193 form.action = action_url; 194 form.method = 'post'; 195 const input1 = document.createElement('input'); 196 input1.type = 'text'; 197 input1.value = 'testValue1'; 198 input1.name = 'testName1' 199 form.appendChild(input1); 200 const input2 = document.createElement('input'); 201 input2.type = 'text'; 202 input2.value = 'testValue2'; 203 input2.name = 'testName2' 204 form.appendChild(input2); 205 document.body.appendChild(form); 206 frame.onload = function() { 207 form.remove(); 208 resolve(frame); 209 }; 210 form.submit(); 211 }); 212 } 213 214 promise_test(t => { 215 const page_url = 'resources/simple.html?form-post'; 216 return submit_form(page_url) 217 .then(frame => { 218 t.add_cleanup(() => { frame.remove(); }); 219 assert_equals(frame.contentDocument.body.textContent, 220 'POST:application/x-www-form-urlencoded:' + 221 'testName1=testValue1&testName2=testValue2'); 222 }); 223 }, 'Service Worker responds to fetch event with POST form'); 224 225 promise_test(t => { 226 // Add '?ignore' so the service worker falls back to network. 227 const page_url = 'resources/echo-content.py?ignore'; 228 return submit_form(page_url) 229 .then(frame => { 230 t.add_cleanup(() => { frame.remove(); }); 231 assert_equals(frame.contentDocument.body.textContent, 232 'testName1=testValue1&testName2=testValue2'); 233 }); 234 }, 'Service Worker falls back to network in fetch event with POST form'); 235 236 promise_test(t => { 237 const page_url = 'resources/simple.html?multiple-respond-with'; 238 return with_iframe(page_url) 239 .then(frame => { 240 t.add_cleanup(() => { frame.remove(); }); 241 assert_equals( 242 frame.contentDocument.body.textContent, 243 '(0)(1)[InvalidStateError](2)[InvalidStateError]', 244 'Multiple calls of respondWith must throw InvalidStateErrors.'); 245 }); 246 }, 'Multiple calls of respondWith must throw InvalidStateErrors'); 247 248 promise_test(t => { 249 const page_url = 'resources/simple.html?used-check'; 250 var first_frame; 251 return with_iframe(page_url) 252 .then(function(frame) { 253 assert_equals(frame.contentDocument.body.textContent, 254 'Here\'s an other html file.\n', 255 'Response should come from fetched other file'); 256 first_frame = frame; 257 t.add_cleanup(() => { first_frame.remove(); }); 258 return with_iframe(page_url); 259 }) 260 .then(function(frame) { 261 t.add_cleanup(() => { frame.remove(); }); 262 // When we access to the page_url in the second time, the content of the 263 // response is generated inside the ServiceWorker. The body contains 264 // the value of bodyUsed of the first response which is already 265 // consumed by FetchEvent.respondWith method. 266 assert_equals( 267 frame.contentDocument.body.textContent, 268 'bodyUsed: true', 269 'event.respondWith must set the used flag.'); 270 }); 271 }, 'Service Worker event.respondWith must set the used flag'); 272 273 promise_test(t => { 274 const page_url = 'resources/simple.html?fragment-check'; 275 var fragment = '#/some/fragment'; 276 var first_frame; 277 return with_iframe(page_url + fragment) 278 .then(function(frame) { 279 t.add_cleanup(() => { frame.remove(); }); 280 assert_equals( 281 frame.contentDocument.body.textContent, 282 'Fragment Found :' + fragment, 283 'Service worker should expose URL fragments in request.'); 284 }); 285 }, 'Service Worker should expose FetchEvent URL fragments.'); 286 287 promise_test(t => { 288 const page_url = 'resources/simple.html?cache'; 289 var frame; 290 var cacheTypes = [ 291 undefined, 'default', 'no-store', 'reload', 'no-cache', 'force-cache', 'only-if-cached' 292 ]; 293 return with_iframe(page_url) 294 .then(function(f) { 295 frame = f; 296 t.add_cleanup(() => { frame.remove(); }); 297 assert_equals(frame.contentWindow.document.body.textContent, 'default'); 298 var tests = cacheTypes.map(function(type) { 299 return new Promise(function(resolve, reject) { 300 var init = {cache: type}; 301 if (type === 'only-if-cached') { 302 // For privacy reasons, for the time being, only-if-cached 303 // requires the mode to be same-origin. 304 init.mode = 'same-origin'; 305 } 306 return frame.contentWindow.fetch(page_url + '=' + type, init) 307 .then(function(response) { return response.text(); }) 308 .then(function(response_text) { 309 var expected = (type === undefined) ? 'default' : type; 310 assert_equals(response_text, expected, 311 'Service Worker should respond to fetch with the correct type'); 312 }) 313 .then(resolve) 314 .catch(reject); 315 }); 316 }); 317 return Promise.all(tests); 318 }) 319 .then(function() { 320 return new Promise(function(resolve, reject) { 321 frame.addEventListener('load', function onLoad() { 322 frame.removeEventListener('load', onLoad); 323 try { 324 assert_equals(frame.contentWindow.document.body.textContent, 325 'no-cache'); 326 resolve(); 327 } catch (e) { 328 reject(e); 329 } 330 }); 331 frame.contentWindow.location.reload(); 332 }); 333 }); 334 }, 'Service Worker responds to fetch event with the correct cache types'); 335 336 promise_test(t => { 337 const page_url = 'resources/simple.html?eventsource'; 338 var frame; 339 340 function test_eventsource(opts) { 341 return new Promise(function(resolve, reject) { 342 var eventSource = new frame.contentWindow.EventSource(page_url, opts); 343 eventSource.addEventListener('message', function(msg) { 344 eventSource.close(); 345 try { 346 var data = JSON.parse(msg.data); 347 assert_equals(data.mode, 'cors', 348 'EventSource should make CORS requests.'); 349 assert_equals(data.cache, 'no-store', 350 'EventSource should bypass the http cache.'); 351 var expectedCredentials = opts.withCredentials ? 'include' 352 : 'same-origin'; 353 assert_equals(data.credentials, expectedCredentials, 354 'EventSource should pass correct credentials mode.'); 355 resolve(); 356 } catch (e) { 357 reject(e); 358 } 359 }); 360 eventSource.addEventListener('error', function(e) { 361 eventSource.close(); 362 reject('The EventSource fired an error event.'); 363 }); 364 }); 365 } 366 367 return with_iframe(page_url) 368 .then(function(f) { 369 frame = f; 370 t.add_cleanup(() => { frame.remove(); }); 371 return test_eventsource({ withCredentials: false }); 372 }) 373 .then(function() { 374 return test_eventsource({ withCredentials: true }); 375 }); 376 }, 'Service Worker should intercept EventSource'); 377 378 promise_test(t => { 379 const page_url = 'resources/simple.html?integrity'; 380 var frame; 381 var integrity_metadata = 'gs0nqru8KbsrIt5YToQqS9fYao4GQJXtcId610g7cCU='; 382 383 return with_iframe(page_url) 384 .then(function(f) { 385 frame = f; 386 t.add_cleanup(() => { frame.remove(); }); 387 // A request has associated integrity metadata (a string). 388 // Unless stated otherwise, it is the empty string. 389 assert_equals( 390 frame.contentDocument.body.textContent, ''); 391 392 return frame.contentWindow.fetch(page_url, {'integrity': integrity_metadata}); 393 }) 394 .then(response => { 395 return response.text(); 396 }) 397 .then(response_text => { 398 assert_equals(response_text, integrity_metadata, 'integrity'); 399 }); 400 }, 'Service Worker responds to fetch event with the correct integrity_metadata'); 401 402 // Test that the service worker can read FetchEvent#body when it is a string. 403 // It responds with request body it read. 404 promise_test(t => { 405 // Set page_url to "?ignore" so the service worker falls back to network 406 // for the main resource request, and add a suffix to avoid colliding 407 // with other tests. 408 const page_url = 'resources/simple.html?ignore-for-request-body-string'; 409 let frame; 410 411 return with_iframe(page_url) 412 .then(f => { 413 frame = f; 414 t.add_cleanup(() => { frame.remove(); }); 415 return frame.contentWindow.fetch('simple.html?request-body', { 416 method: 'POST', 417 body: 'i am the request body' 418 }); 419 }) 420 .then(response => { 421 return response.text(); 422 }) 423 .then(response_text => { 424 assert_equals(response_text, 'i am the request body'); 425 }); 426 }, 'FetchEvent#body is a string'); 427 428 // Test that the service worker can read FetchEvent#body when it is made from 429 // a ReadableStream. It responds with request body it read. 430 promise_test(async t => { 431 const rs = new ReadableStream({start(c) { 432 c.enqueue('i a'); 433 c.enqueue('m the request'); 434 step_timeout(t.step_func(() => { 435 c.enqueue(' body'); 436 c.close(); 437 }, 10)); 438 }}); 439 440 // Set page_url to "?ignore" so the service worker falls back to network 441 // for the main resource request, and add a suffix to avoid colliding 442 // with other tests. 443 const page_url = `resources/simple.html?ignore&id=${token()}`; 444 445 const frame = await with_iframe(page_url); 446 t.add_cleanup(() => { frame.remove(); }); 447 const res = await frame.contentWindow.fetch('simple.html?request-body', { 448 method: 'POST', 449 body: rs.pipeThrough(new TextEncoderStream()), 450 duplex: 'half', 451 }); 452 assert_equals(await res.text(), 'i am the request body'); 453 }, 'FetchEvent#body is a ReadableStream'); 454 455 // Test that the request body is sent to network upon network fallback, 456 // for a string body. 457 promise_test(t => { 458 // Set page_url to "?ignore" so the service worker falls back to network 459 // for the main resource request, and add a suffix to avoid colliding 460 // with other tests. 461 const page_url = 'resources/?ignore-for-request-body-fallback-string'; 462 let frame; 463 464 return with_iframe(page_url) 465 .then(f => { 466 frame = f; 467 t.add_cleanup(() => { frame.remove(); }); 468 // Add "?ignore" so the service worker falls back to echo-content.py. 469 const echo_url = '/fetch/api/resources/echo-content.py?ignore'; 470 return frame.contentWindow.fetch(echo_url, { 471 method: 'POST', 472 body: 'i am the request body' 473 }); 474 }) 475 .then(response => { 476 return response.text(); 477 }) 478 .then(response_text => { 479 assert_equals( 480 response_text, 481 'i am the request body', 482 'the network fallback request should include the request body'); 483 }); 484 }, 'FetchEvent#body is a string and is passed to network fallback'); 485 486 // Test that the request body is sent to network upon network fallback, 487 // for a ReadableStream body. 488 promise_test(async t => { 489 const rs = new ReadableStream({start(c) { 490 c.enqueue('i a'); 491 c.enqueue('m the request'); 492 t.step_timeout(t.step_func(() => { 493 c.enqueue(' body'); 494 c.close(); 495 }, 10)); 496 }}); 497 // Set page_url to "?ignore" so the service worker falls back to network 498 // for the main resource request, and add a suffix to avoid colliding 499 // with other tests. 500 const page_url = 'resources/?ignore-for-request-body-fallback-string'; 501 const frame = await with_iframe(page_url); 502 t.add_cleanup(() => { frame.remove(); }); 503 // Add "?ignore" so the service worker falls back to echo-content.py. 504 const echo_url = '/fetch/api/resources/echo-content.py?ignore'; 505 const w = frame.contentWindow; 506 await promise_rejects_js(t, w.TypeError, w.fetch(echo_url, { 507 method: 'POST', 508 body: rs 509 })); 510 }, 'FetchEvent#body is a none Uint8Array ReadableStream and is passed to a service worker'); 511 512 // Test that the request body is sent to network upon network fallback even when 513 // the request body is used in the service worker, for a string body. 514 promise_test(async t => { 515 // Set page_url to "?ignore" so the service worker falls back to network 516 // for the main resource request, and add a suffix to avoid colliding 517 // with other tests. 518 const page_url = 'resources/?ignore-for-request-body-fallback-string'; 519 520 const frame = await with_iframe(page_url); 521 t.add_cleanup(() => { frame.remove(); }); 522 // Add "?use-and-ignore" so the service worker falls back to echo-content.py. 523 const echo_url = '/fetch/api/resources/echo-content.py?use-and-ignore'; 524 const response = await frame.contentWindow.fetch(echo_url, { 525 method: 'POST', 526 body: 'i am the request body' 527 }); 528 const text = await response.text(); 529 assert_equals( 530 text, 531 'i am the request body', 532 'the network fallback request should include the request body'); 533 }, 'FetchEvent#body is a string, used and passed to network fallback'); 534 535 // Test that the request body is sent to network upon network fallback even when 536 // the request body is used by clone() in the service worker, for a string body. 537 promise_test(async t => { 538 // Set page_url to "?ignore" so the service worker falls back to network 539 // for the main resource request, and add a suffix to avoid colliding 540 // with other tests. 541 const page_url = 'resources/?ignore-for-request-body-fallback-string'; 542 543 const frame = await with_iframe(page_url); 544 t.add_cleanup(() => { frame.remove(); }); 545 // Add "?clone-and-ignore" so the service worker falls back to 546 // echo-content.py. 547 const echo_url = '/fetch/api/resources/echo-content.py?clone-and-ignore'; 548 const response = await frame.contentWindow.fetch(echo_url, { 549 method: 'POST', 550 body: 'i am the request body' 551 }); 552 const text = await response.text(); 553 assert_equals( 554 text, 555 'i am the request body', 556 'the network fallback request should include the request body'); 557 }, 'FetchEvent#body is a string, cloned and passed to network fallback'); 558 559 // Test that the service worker can read FetchEvent#body when it is a blob. 560 // It responds with request body it read. 561 promise_test(t => { 562 // Set page_url to "?ignore" so the service worker falls back to network 563 // for the main resource request, and add a suffix to avoid colliding 564 // with other tests. 565 const page_url = 'resources/simple.html?ignore-for-request-body-blob'; 566 let frame; 567 568 return with_iframe(page_url) 569 .then(f => { 570 frame = f; 571 t.add_cleanup(() => { frame.remove(); }); 572 const blob = new Blob(['it\'s me the blob', ' ', 'and more blob!']); 573 return frame.contentWindow.fetch('simple.html?request-body', { 574 method: 'POST', 575 body: blob 576 }); 577 }) 578 .then(response => { 579 return response.text(); 580 }) 581 .then(response_text => { 582 assert_equals(response_text, 'it\'s me the blob and more blob!'); 583 }); 584 }, 'FetchEvent#body is a blob'); 585 586 // Test that the request body is sent to network upon network fallback, 587 // for a blob body. 588 promise_test(t => { 589 // Set page_url to "?ignore" so the service worker falls back to network 590 // for the main resource request, and add a suffix to avoid colliding 591 // with other tests. 592 const page_url = 'resources/simple.html?ignore-for-request-body-fallback-blob'; 593 let frame; 594 595 return with_iframe(page_url) 596 .then(f => { 597 frame = f; 598 t.add_cleanup(() => { frame.remove(); }); 599 const blob = new Blob(['it\'s me the blob', ' ', 'and more blob!']); 600 // Add "?ignore" so the service worker falls back to echo-content.py. 601 const echo_url = '/fetch/api/resources/echo-content.py?ignore'; 602 return frame.contentWindow.fetch(echo_url, { 603 method: 'POST', 604 body: blob 605 }); 606 }) 607 .then(response => { 608 return response.text(); 609 }) 610 .then(response_text => { 611 assert_equals( 612 response_text, 613 'it\'s me the blob and more blob!', 614 'the network fallback request should include the request body'); 615 }); 616 }, 'FetchEvent#body is a blob and is passed to network fallback'); 617 618 promise_test(async (t) => { 619 const page_url = 'resources/simple.html?keepalive'; 620 const frame = await with_iframe(page_url); 621 t.add_cleanup(() => { frame.remove(); }); 622 assert_equals(frame.contentDocument.body.textContent, 'false'); 623 const response = await frame.contentWindow.fetch(page_url, {keepalive: true}); 624 const text = await response.text(); 625 assert_equals(text, 'true'); 626 }, 'Service Worker responds to fetch event with the correct keepalive value'); 627 628 promise_test(async (t) => { 629 const page_url = 'resources/simple.html?isReloadNavigation'; 630 const frame = await with_iframe(page_url); 631 t.add_cleanup(() => { frame.remove(); }); 632 assert_equals(frame.contentDocument.body.textContent, 633 'method = GET, isReloadNavigation = false'); 634 await new Promise((resolve) => { 635 frame.addEventListener('load', resolve); 636 frame.contentWindow.location.reload(); 637 }); 638 assert_equals(frame.contentDocument.body.textContent, 639 'method = GET, isReloadNavigation = true'); 640 }, 'FetchEvent#request.isReloadNavigation is true (location.reload())'); 641 642 promise_test(async (t) => { 643 const page_url = 'resources/simple.html?isReloadNavigation'; 644 const frame = await with_iframe(page_url); 645 t.add_cleanup(() => { frame.remove(); }); 646 assert_equals(frame.contentDocument.body.textContent, 647 'method = GET, isReloadNavigation = false'); 648 await new Promise((resolve) => { 649 frame.addEventListener('load', resolve); 650 frame.contentWindow.history.go(0); 651 }); 652 assert_equals(frame.contentDocument.body.textContent, 653 'method = GET, isReloadNavigation = true'); 654 }, 'FetchEvent#request.isReloadNavigation is true (history.go(0))'); 655 656 promise_test(async (t) => { 657 const page_url = 'resources/simple.html?isReloadNavigation'; 658 const frame = await with_iframe(page_url); 659 t.add_cleanup(() => { frame.remove(); }); 660 assert_equals(frame.contentDocument.body.textContent, 661 'method = GET, isReloadNavigation = false'); 662 await new Promise((resolve) => { 663 frame.addEventListener('load', resolve); 664 const form = frame.contentDocument.createElement('form'); 665 form.method = 'POST'; 666 form.name = 'form'; 667 form.action = new Request(page_url).url; 668 frame.contentDocument.body.appendChild(form); 669 form.submit(); 670 }); 671 assert_equals(frame.contentDocument.body.textContent, 672 'method = POST, isReloadNavigation = false'); 673 await new Promise((resolve) => { 674 frame.addEventListener('load', resolve); 675 frame.contentWindow.location.reload(); 676 }); 677 assert_equals(frame.contentDocument.body.textContent, 678 'method = POST, isReloadNavigation = true'); 679 }, 'FetchEvent#request.isReloadNavigation is true (POST + location.reload())'); 680 681 promise_test(async (t) => { 682 const page_url = 'resources/simple.html?isReloadNavigation'; 683 const anotherUrl = new Request('resources/simple.html').url; 684 let frame = await with_iframe(page_url); 685 t.add_cleanup(() => { frame.remove(); }); 686 assert_equals(frame.contentDocument.body.textContent, 687 'method = GET, isReloadNavigation = false'); 688 // Use step_timeout(0) to ensure the history entry is created for Blink 689 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 690 await wait(0); 691 await new Promise((resolve) => { 692 frame.addEventListener('load', resolve); 693 frame.src = anotherUrl; 694 }); 695 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 696 await new Promise((resolve) => { 697 frame.addEventListener('load', resolve); 698 frame.contentWindow.history.go(-1); 699 }); 700 assert_equals(frame.contentDocument.body.textContent, 701 'method = GET, isReloadNavigation = false'); 702 await new Promise((resolve) => { 703 frame.addEventListener('load', resolve); 704 frame.contentWindow.history.go(0); 705 }); 706 assert_equals(frame.contentDocument.body.textContent, 707 'method = GET, isReloadNavigation = true'); 708 await new Promise((resolve) => { 709 frame.addEventListener('load', resolve); 710 frame.contentWindow.history.go(1); 711 }); 712 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 713 }, 'FetchEvent#request.isReloadNavigation is true (with history traversal)'); 714 715 promise_test(async (t) => { 716 const page_url = 'resources/simple.html?isHistoryNavigation'; 717 const anotherUrl = new Request('resources/simple.html?ignore').url; 718 const frame = await with_iframe(page_url); 719 t.add_cleanup(() => { frame.remove(); }); 720 assert_equals(frame.contentDocument.body.textContent, 721 'method = GET, isHistoryNavigation = false'); 722 // Use step_timeout(0) to ensure the history entry is created for Blink 723 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 724 await wait(0); 725 await new Promise((resolve) => { 726 frame.addEventListener('load', resolve); 727 frame.src = anotherUrl; 728 }); 729 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 730 await new Promise((resolve) => { 731 frame.addEventListener('load', resolve); 732 frame.contentWindow.history.go(-1); 733 }); 734 assert_equals(frame.contentDocument.body.textContent, 735 'method = GET, isHistoryNavigation = true'); 736 }, 'FetchEvent#request.isHistoryNavigation is true (with history.go(-1))'); 737 738 promise_test(async (t) => { 739 const page_url = 'resources/simple.html?isHistoryNavigation'; 740 const anotherUrl = new Request('resources/simple.html?ignore').url; 741 const frame = await with_iframe(anotherUrl); 742 t.add_cleanup(() => { frame.remove(); }); 743 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 744 // Use step_timeout(0) to ensure the history entry is created for Blink 745 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 746 await wait(0); 747 await new Promise((resolve) => { 748 frame.addEventListener('load', resolve); 749 frame.src = page_url; 750 }); 751 assert_equals(frame.contentDocument.body.textContent, 752 'method = GET, isHistoryNavigation = false'); 753 await new Promise((resolve) => { 754 frame.addEventListener('load', resolve); 755 frame.contentWindow.history.go(-1); 756 }); 757 await new Promise((resolve) => { 758 frame.addEventListener('load', resolve); 759 frame.contentWindow.history.go(1); 760 }); 761 assert_equals(frame.contentDocument.body.textContent, 762 'method = GET, isHistoryNavigation = true'); 763 }, 'FetchEvent#request.isHistoryNavigation is true (with history.go(1))'); 764 765 promise_test(async (t) => { 766 const page_url = 'resources/simple.html?isHistoryNavigation'; 767 const anotherUrl = new Request('resources/simple.html?ignore').url; 768 const frame = await with_iframe(anotherUrl); 769 t.add_cleanup(() => { frame.remove(); }); 770 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 771 // Use step_timeout(0) to ensure the history entry is created for Blink 772 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 773 await wait(0); 774 await new Promise((resolve) => { 775 frame.addEventListener('load', resolve); 776 frame.src = page_url; 777 }); 778 assert_equals(frame.contentDocument.body.textContent, 779 'method = GET, isHistoryNavigation = false'); 780 await new Promise((resolve) => { 781 frame.addEventListener('load', resolve); 782 frame.contentWindow.history.go(-1); 783 }); 784 await new Promise((resolve) => { 785 frame.addEventListener('load', resolve); 786 frame.contentWindow.history.go(1); 787 }); 788 assert_equals(frame.contentDocument.body.textContent, 789 'method = GET, isHistoryNavigation = true'); 790 await new Promise((resolve) => { 791 frame.addEventListener('load', resolve); 792 frame.contentWindow.history.go(0); 793 }); 794 assert_equals(frame.contentDocument.body.textContent, 795 'method = GET, isHistoryNavigation = false'); 796 }, 'FetchEvent#request.isHistoryNavigation is false (with history.go(0))'); 797 798 promise_test(async (t) => { 799 const page_url = 'resources/simple.html?isHistoryNavigation'; 800 const anotherUrl = new Request('resources/simple.html?ignore').url; 801 const frame = await with_iframe(anotherUrl); 802 t.add_cleanup(() => { frame.remove(); }); 803 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 804 // Use step_timeout(0) to ensure the history entry is created for Blink 805 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 806 await wait(0); 807 await new Promise((resolve) => { 808 frame.addEventListener('load', resolve); 809 frame.src = page_url; 810 }); 811 assert_equals(frame.contentDocument.body.textContent, 812 'method = GET, isHistoryNavigation = false'); 813 await new Promise((resolve) => { 814 frame.addEventListener('load', resolve); 815 frame.contentWindow.history.go(-1); 816 }); 817 await new Promise((resolve) => { 818 frame.addEventListener('load', resolve); 819 frame.contentWindow.history.go(1); 820 }); 821 assert_equals(frame.contentDocument.body.textContent, 822 'method = GET, isHistoryNavigation = true'); 823 await new Promise((resolve) => { 824 frame.addEventListener('load', resolve); 825 frame.contentWindow.location.reload(); 826 }); 827 assert_equals(frame.contentDocument.body.textContent, 828 'method = GET, isHistoryNavigation = false'); 829 }, 'FetchEvent#request.isHistoryNavigation is false (with location.reload)'); 830 831 promise_test(async (t) => { 832 const page_url = 'resources/simple.html?isHistoryNavigation'; 833 const anotherUrl = new Request('resources/simple.html?ignore').url; 834 const oneAnotherUrl = new Request('resources/simple.html?ignore2').url; 835 836 const frame = await with_iframe(page_url); 837 t.add_cleanup(() => { frame.remove(); }); 838 assert_equals(frame.contentDocument.body.textContent, 839 'method = GET, isHistoryNavigation = false'); 840 // Use step_timeout(0) to ensure the history entry is created for Blink 841 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 842 await wait(0); 843 await new Promise((resolve) => { 844 frame.addEventListener('load', resolve); 845 frame.src = anotherUrl; 846 }); 847 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 848 await wait(0); 849 await new Promise((resolve) => { 850 frame.addEventListener('load', resolve); 851 frame.src = oneAnotherUrl; 852 }); 853 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 854 await new Promise((resolve) => { 855 frame.addEventListener('load', resolve); 856 frame.contentWindow.history.go(-2); 857 }); 858 assert_equals(frame.contentDocument.body.textContent, 859 'method = GET, isHistoryNavigation = true'); 860 }, 'FetchEvent#request.isHistoryNavigation is true (with history.go(-2))'); 861 862 promise_test(async (t) => { 863 const page_url = 'resources/simple.html?isHistoryNavigation'; 864 const anotherUrl = new Request('resources/simple.html?ignore').url; 865 const oneAnotherUrl = new Request('resources/simple.html?ignore2').url; 866 const frame = await with_iframe(anotherUrl); 867 t.add_cleanup(() => { frame.remove(); }); 868 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 869 // Use step_timeout(0) to ensure the history entry is created for Blink 870 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 871 await wait(0); 872 await new Promise((resolve) => { 873 frame.addEventListener('load', resolve); 874 frame.src = oneAnotherUrl; 875 }); 876 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 877 await wait(0); 878 await new Promise((resolve) => { 879 frame.addEventListener('load', resolve); 880 frame.src = page_url; 881 }); 882 assert_equals(frame.contentDocument.body.textContent, 883 'method = GET, isHistoryNavigation = false'); 884 await new Promise((resolve) => { 885 frame.addEventListener('load', resolve); 886 frame.contentWindow.history.go(-2); 887 }); 888 await new Promise((resolve) => { 889 frame.addEventListener('load', resolve); 890 frame.contentWindow.history.go(2); 891 }); 892 assert_equals(frame.contentDocument.body.textContent, 893 'method = GET, isHistoryNavigation = true'); 894 }, 'FetchEvent#request.isHistoryNavigation is true (with history.go(2))'); 895 896 promise_test(async (t) => { 897 const page_url = 'resources/simple.html?isHistoryNavigation'; 898 const anotherUrl = new Request('resources/simple.html?ignore').url; 899 const frame = await with_iframe(page_url); 900 t.add_cleanup(() => { frame.remove(); }); 901 assert_equals(frame.contentDocument.body.textContent, 902 'method = GET, isHistoryNavigation = false'); 903 await new Promise((resolve) => { 904 frame.addEventListener('load', resolve); 905 const form = frame.contentDocument.createElement('form'); 906 form.method = 'POST'; 907 form.name = 'form'; 908 form.action = new Request(page_url).url; 909 frame.contentDocument.body.appendChild(form); 910 form.submit(); 911 }); 912 assert_equals(frame.contentDocument.body.textContent, 913 'method = POST, isHistoryNavigation = false'); 914 // Use step_timeout(0) to ensure the history entry is created for Blink 915 // and WebKit. See https://bugs.webkit.org/show_bug.cgi?id=42861. 916 await wait(0); 917 await new Promise((resolve) => { 918 frame.addEventListener('load', resolve); 919 frame.src = anotherUrl; 920 }); 921 assert_equals(frame.contentDocument.body.textContent, "Here's a simple html file.\n"); 922 await wait(0); 923 await new Promise((resolve) => { 924 frame.addEventListener('load', resolve); 925 frame.contentWindow.history.go(-1); 926 }); 927 assert_equals(frame.contentDocument.body.textContent, 928 'method = POST, isHistoryNavigation = true'); 929 }, 'FetchEvent#request.isHistoryNavigation is true (POST + history.go(-1))'); 930 931 // When service worker responds with a Response, no XHR upload progress 932 // events are delivered. 933 promise_test(async t => { 934 const page_url = 'resources/simple.html?ignore-for-request-body-string'; 935 const frame = await with_iframe(page_url); 936 t.add_cleanup(() => { frame.remove(); }); 937 938 const xhr = new frame.contentWindow.XMLHttpRequest(); 939 xhr.open('POST', 'simple.html?request-body'); 940 xhr.upload.addEventListener('progress', t.unreached_func('progress')); 941 xhr.upload.addEventListener('error', t.unreached_func('error')); 942 xhr.upload.addEventListener('abort', t.unreached_func('abort')); 943 xhr.upload.addEventListener('timeout', t.unreached_func('timeout')); 944 xhr.upload.addEventListener('load', t.unreached_func('load')); 945 xhr.upload.addEventListener('loadend', t.unreached_func('loadend')); 946 xhr.send('i am the request body'); 947 948 await new Promise((resolve) => xhr.addEventListener('load', resolve)); 949 }, 'XHR upload progress events for response coming from SW'); 950 951 // Upload progress events should be delivered for the network fallback case. 952 promise_test(async t => { 953 const page_url = 'resources/simple.html?ignore-for-request-body-string'; 954 const frame = await with_iframe(page_url); 955 t.add_cleanup(() => { frame.remove(); }); 956 957 let progress = false; 958 let load = false; 959 let loadend = false; 960 961 const xhr = new frame.contentWindow.XMLHttpRequest(); 962 xhr.open('POST', '/fetch/api/resources/echo-content.py?ignore'); 963 xhr.upload.addEventListener('progress', () => progress = true); 964 xhr.upload.addEventListener('error', t.unreached_func('error')); 965 xhr.upload.addEventListener('abort', t.unreached_func('abort')); 966 xhr.upload.addEventListener('timeout', t.unreached_func('timeout')); 967 xhr.upload.addEventListener('load', () => load = true); 968 xhr.upload.addEventListener('loadend', () => loadend = true); 969 xhr.send('i am the request body'); 970 971 await new Promise((resolve) => xhr.addEventListener('load', resolve)); 972 assert_true(progress, 'progress'); 973 assert_true(load, 'load'); 974 assert_true(loadend, 'loadend'); 975 }, 'XHR upload progress events for network fallback'); 976 977 promise_test(async t => { 978 // Set page_url to "?ignore" so the service worker falls back to network 979 // for the main resource request, and add a suffix to avoid colliding 980 // with other tests. 981 const page_url = 'resources/?ignore-for-request-body-fallback-string'; 982 983 const frame = await with_iframe(page_url); 984 t.add_cleanup(() => { frame.remove(); }); 985 // Add "?clone-and-ignore" so the service worker falls back to 986 // echo-content.py. 987 const echo_url = '/fetch/api/resources/echo-content.py?status=421'; 988 const response = await frame.contentWindow.fetch(echo_url, { 989 method: 'POST', 990 body: 'text body' 991 }); 992 assert_equals(response.status, 421); 993 const text = await response.text(); 994 assert_equals( 995 text, 996 'text body. Request was sent 1 times.', 997 'the network fallback request should include the request body'); 998 }, 'Fetch with POST with text on sw 421 response should not be retried.'); 999 </script> 1000 </body>