enumerateDevices-with-navigation.https.html (3646B)
1 <!doctype html> 2 <title>enumerateDevices() with navigation</title> 3 <script src=/resources/testharness.js></script> 4 <script src=/resources/testharnessreport.js></script> 5 <script src="/resources/testdriver.js"></script> 6 <script src="/resources/testdriver-vendor.js"></script> 7 <body></body> 8 <script> 9 'use strict'; 10 const blank_url = '/common/blank.html'; 11 const search2 = '?2'; 12 13 function promise_new_task(t) { 14 return new Promise(resolve => t.step_timeout(resolve, 0)); 15 } 16 function promise_event(target, name) { 17 return new Promise(resolve => target[`on${name}`] = resolve); 18 } 19 20 promise_test(async t => { 21 // Gecko persists only toplevel documents, so load documents in a toplevel. 22 await test_driver.bless('window.open()'); 23 const proxy = window.open(blank_url); 24 t.add_cleanup(() => proxy.close()); 25 await promise_event(proxy, 'pageshow'); 26 const devices = proxy.navigator.mediaDevices; 27 // Use another task so that another load creates a new session history entry. 28 await promise_new_task(t); 29 30 proxy.location = blank_url + search2; 31 await promise_event(proxy, 'pagehide'); 32 // Use another task to ensure the first subdocument is no longer fully 33 // active and proxy refers to the realm of the second document. 34 await promise_new_task(t); 35 assert_equals(proxy.location.search, search2, 'navigated search'); 36 // Enumerate from the inactive first Window. 37 const promise_enumerate = devices.enumerateDevices(); 38 // `then()` is used rather than static Promise methods because microtasks 39 // for `PromiseResolve()` do not run when Promises from inactive realms are 40 // involved. Whether microtasks for `then()` run depends on the realm of 41 // the handler rather than the realm of the Promise. 42 // Don't use `finally()`, because it uses `PromiseResolve()` and so 43 // microtasks don't run. 44 // See https://github.com/whatwg/html/issues/5319. 45 let promise_state = 'pending'; 46 promise_enumerate.then(() => promise_state = 'resolved', 47 () => promise_state = 'rejected'); 48 // Enumerate in the active second Window to provide enough time to check 49 // that the Promise from the inactive Window does not settle. 50 await proxy.navigator.mediaDevices.enumerateDevices(); 51 52 proxy.history.back(); 53 await promise_event(proxy, 'pagehide'); 54 // enumerateDevices() Promise resolution is triggered only in parallel 55 // steps, so manipulation of the Promise (if the first document was 56 // persisted) would occur through a queued task, which would run after 57 // the pagehide event is dispatched and so after the associated 58 // microtask that runs the following assert. 59 // https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-for-spec-authors 60 assert_equals(promise_state, 'pending', 'Promise state while inactive'); 61 // If the first document is restored, then that will occur immediately after 62 // pagehide (and associated microtasks), before the next global task is run. 63 // https://html.spec.whatwg.org/multipage/history.html#traverse-the-history-by-a-delta 64 await promise_new_task(t); 65 if (proxy.navigator.mediaDevices == devices) { 66 // The first document was persisted and restored. 67 assert_equals(proxy.location.search, '', 'history search'); 68 await promise_enumerate; 69 } else { 70 // The first document was not restored, but gets re-fetched. 71 await t.step_wait(() => proxy.location.search == '', 'navigation'); 72 assert_not_equals(proxy.navigator.mediaDevices, devices, 'new realm') 73 await proxy.navigator.mediaDevices.enumerateDevices(); 74 assert_equals(promise_state, 'pending', 'Promise state after discard'); 75 } 76 }, 'enumerateDevices with navigation'); 77 </script>