observable-constructor.window.js (4674B)
1 async function loadIframeAndReturnContentWindow() { 2 // Create and attach an iframe. 3 const iframe = document.createElement('iframe'); 4 const iframeLoadPromise = new Promise((resolve, reject) => { 5 iframe.onload = resolve; 6 iframe.onerror = reject; 7 }); 8 document.body.append(iframe); 9 await iframeLoadPromise; 10 return iframe.contentWindow; 11 } 12 13 promise_test(async t => { 14 // Hang this off of the main document's global, so the child can easily reach 15 // it. 16 window.results = []; 17 const contentWin = await loadIframeAndReturnContentWindow(); 18 19 contentWin.eval(` 20 // Get a reference to the parent result array before we detach and lose 21 // access to the parent. 22 const parentResults = parent.results; 23 24 const source = new Observable((subscriber) => { 25 parentResults.push("subscribe"); 26 // Detach the iframe and push a value to the subscriber/Observer. 27 window.frameElement.remove(); 28 parentResults.push("detached"); 29 subscriber.next("next"); 30 subscriber.complete(); 31 subscriber.error("error"); 32 }); 33 source.subscribe({ 34 next: v => { 35 // Should never run. 36 parentResults.push(v); 37 }, 38 complete: () => { 39 // Should never run. 40 parentResults.push("complete"); 41 }, 42 erorr: e => { 43 // Should never run. 44 parentResults.push(e); 45 } 46 }); 47 `); 48 49 assert_array_equals(results, ["subscribe", "detached"]); 50 }, "No observer handlers can be invoked in detached document"); 51 52 promise_test(async t => { 53 const contentWin = await loadIframeAndReturnContentWindow(); 54 55 // Set a global error handler on the iframe document's window, and verify that 56 // it is never called (because the thing triggering the error happens when the 57 // document is detached, and "reporting the exception" relies on an attached 58 // document). 59 contentWin.addEventListener("error", 60 t.unreached_func("Error should not be called"), { once: true }); 61 62 contentWin.eval(` 63 const source = new Observable((subscriber) => { 64 // Detach the iframe and push an error, which would normally "report the 65 // exception", since this subscriber did not specify an error handler. 66 window.frameElement.remove(); 67 subscriber.error("this is an error that should not be reported"); 68 }); 69 source.subscribe(); 70 `); 71 }, "Subscriber.error() does not \"report the exception\" even when an " + 72 "`error()` handler is not present, when it is invoked in a detached document"); 73 74 promise_test(async t => { 75 // Make this available off the global so the child can reach it. 76 window.results = []; 77 const contentWin = await loadIframeAndReturnContentWindow(); 78 79 // Set a global error handler on the iframe document's window, and verify that 80 // it is never called (because the thing triggering the error happens when the 81 // document is detached, and "reporting the exception" relies on an attached 82 // document). 83 contentWin.addEventListener("error", 84 t.unreached_func("Error should not be called"), { once: true }); 85 86 contentWin.eval(` 87 const parentResults = parent.results; 88 const source = new Observable((subscriber) => { 89 // This should never run. 90 parentResults.push('subscribe'); 91 }); 92 93 // Detach the iframe and try to subscribe. 94 window.frameElement.remove(); 95 parentResults.push('detached'); 96 source.subscribe(); 97 `); 98 99 assert_array_equals(results, ["detached"], "Subscribe callback is never invoked"); 100 }, "Cannot subscribe to an Observable in a detached document"); 101 102 promise_test(async t => { 103 // Make this available off the global so the child can reach it. 104 window.results = []; 105 const contentWin = await loadIframeAndReturnContentWindow(); 106 107 contentWin.eval(` 108 const parentResults = parent.results; 109 const event_target = new EventTarget(); 110 // Set up two event listeners, both of which will mutate |parentResults|: 111 // 1. A traditional event listener 112 // 2. An observable 113 event_target.addEventListener('customevent', e => parentResults.push(e)); 114 const source = event_target.when('customevent'); 115 source.subscribe(e => parentResults.push(e)); 116 117 // Detach the iframe and fire an event at the event target. The parent will 118 // confirm that the observable's next handler did not get invoked, because 119 // the window is detached. 120 const event = new Event('customevent'); 121 window.frameElement.remove(); 122 parentResults.push('detached'); 123 event_target.dispatchEvent(event); 124 `); 125 126 assert_array_equals(results, ["detached"], "Subscribe callback is never invoked"); 127 }, "Observable from EventTarget does not get notified for events in detached documents");