iframe-document-preserve.window.js (5985B)
1 // META: script=/common/get-host-info.sub.js 2 3 promise_test(async t => { 4 let iframeLoadCounter = 0; 5 const div = document.createElement('div'); 6 const iframe = document.createElement('iframe'); 7 t.add_cleanup(() => iframe.remove()); 8 9 const loadPromise = new Promise((resolve) => { 10 iframe.onload = () => { 11 iframeLoadCounter++; 12 resolve() 13 }; 14 }); 15 16 div.append(iframe); 17 document.body.append(div); 18 await loadPromise; 19 assert_equals(iframeLoadCounter, 1, "iframe loads"); 20 21 const innerDocument = iframe.contentDocument; 22 assert_true(innerDocument !== null, "about:blank Document is reachable"); 23 24 iframe.onload = () => iframeLoadCounter++; 25 await new Promise(resolve => t.step_timeout(resolve, 50)); 26 document.body.moveBefore(iframe, null); 27 assert_equals(iframe.contentDocument, innerDocument, "Document is preserved"); 28 assert_equals(iframeLoadCounter, 1, "iframe does not reload"); 29 }, "moveBefore(): about:blank iframe's document is preserved"); 30 31 const kRemoveNewParent = 'remove new parent'; 32 const kRemoveSelf = 'remove self'; 33 const kRemoveSelfViaReplaceChildren = 'remove self via replaceChildren()'; 34 const kRemoveSelfViaInnerHTML = 'remove self via innerHTML'; 35 36 promise_test(async t => { 37 const div = document.createElement('div'); 38 const iframe = document.createElement('iframe'); 39 t.add_cleanup(() => iframe.remove()); 40 41 const loadPromise = new Promise(resolve => iframe.onload = resolve); 42 iframe.src = '/resources/blank.html'; 43 44 div.append(iframe); 45 document.body.append(div); 46 await loadPromise; 47 const innerDocument = iframe.contentDocument; 48 49 document.body.moveBefore(iframe, null); 50 assert_equals(iframe.contentDocument, innerDocument, "Document is preserved"); 51 }, "moveBefore(): simple same-origin document is preserved"); 52 53 // This function runs the same test with a few variations. The meat of the test 54 // loads a cross-origin iframe which asserts that it does not get reloaded. 55 // Second, we remove the iframe from the parent document in a few different ways 56 // to trigger initially crashy paths in Chromium during the implementation of 57 // this feature. 58 function runTest(removalType) { 59 promise_test(async t => { 60 let iframeLoadCounter = 0; 61 const oldParent = document.createElement('div'); 62 const newParent = document.createElement('div'); 63 const iframe = document.createElement('iframe'); 64 iframe.onload = e => iframeLoadCounter++; 65 switch (removalType) { 66 case kRemoveNewParent: 67 t.add_cleanup(() => newParent.remove()); 68 break; 69 case kRemoveSelf: 70 t.add_cleanup(() => iframe.remove()); 71 break; 72 case kRemoveSelfViaReplaceChildren: 73 t.add_cleanup(() => newParent.replaceChildren()); 74 break; 75 case kRemoveSelfViaInnerHTML: 76 t.add_cleanup(() => {newParent.innerHTML = '';}); 77 break; 78 } 79 80 const loadMessagePromise = new Promise(resolve => window.onmessage = resolve); 81 const crossOriginIframeURL = new URL('resources/moveBefore-iframe.html', 82 location.href.replace(self.origin, get_host_info().HTTP_REMOTE_ORIGIN)); 83 iframe.src = crossOriginIframeURL; 84 85 oldParent.append(iframe); 86 document.body.append(oldParent, newParent); 87 const loadMessage = await loadMessagePromise; 88 assert_equals(loadMessage.data, 'loaded'); 89 90 const messagePromise = new Promise(resolve => window.onmessage = resolve); 91 newParent.moveBefore(iframe, null); 92 iframe.contentWindow.postMessage("after moveBefore", "*"); 93 const message = await messagePromise; 94 // If `moveBefore()` behaved just like `insertBefore()`, and reloaded the 95 // document, then `message` would contain `loaded` instead of 96 // `ack after moveBefore`. 97 assert_equals(message.data, 'ack after moveBefore', 'Iframe did not load reload after moveBefore()'); 98 assert_equals(iframeLoadCounter, 1, "iframe does not fire a second load event"); 99 }, `moveBefore(): cross-origin iframe is preserved: ${removalType}`); 100 } 101 102 runTest(kRemoveNewParent); 103 runTest(kRemoveSelf); 104 runTest(kRemoveSelfViaReplaceChildren); 105 runTest(kRemoveSelfViaInnerHTML); 106 107 promise_test(async t => { 108 const iframe1 = document.createElement('iframe'); 109 iframe1.name = 'iframe1'; 110 const iframe2 = document.createElement('iframe'); 111 iframe2.name = 'iframe2'; 112 const iframe3 = document.createElement('iframe'); 113 iframe3.name = 'iframe3'; 114 115 document.body.append(iframe1, iframe2, iframe3); 116 117 // Assert that the order of iframes in the DOM matches the order of iframes in 118 // `window.frames`. 119 let iframes = document.querySelectorAll('iframe'); 120 assert_equals(iframes[0].name, "iframe1", "iframe1 comes first in DOM"); 121 assert_equals(iframes[1].name, "iframe2", "iframe2 comes second in DOM"); 122 assert_equals(iframes[2].name, "iframe3", "iframe3 comes last in DOM"); 123 assert_equals(window.frames[0].name, "iframe1", "iframe1 comes first in frames"); 124 assert_equals(window.frames[1].name, "iframe2", "iframe2 comes second in frames"); 125 assert_equals(window.frames[2].name, "iframe3", "iframe3 comes last in frames"); 126 127 // Reverse the order of iframes in the DOM. 128 document.body.moveBefore(iframe2, iframe1); 129 document.body.moveBefore(iframe3, iframe2); 130 131 // Assert that the order of iframes in the DOM is inverse the order of iframes 132 // in `window.frames`. 133 iframes = document.querySelectorAll('iframe'); 134 assert_equals(iframes[0].name, "iframe3", "iframe3 comes first in DOM after moveBefore"); 135 assert_equals(iframes[1].name, "iframe2", "iframe2 comes second in DOM after moveBefore"); 136 assert_equals(iframes[2].name, "iframe1", "iframe1 comes last in DOM after moveBefore"); 137 assert_equals(window.frames[0].name, "iframe1", "iframe1 comes first in frames after moveBefore"); 138 assert_equals(window.frames[1].name, "iframe2", "iframe2 comes second in frames after moveBefore"); 139 assert_equals(window.frames[2].name, "iframe3", "iframe3 comes last in frames afterMoveBefore"); 140 }, "window.frames ordering does not change due to moveBefore()");