// META: script=/common/get-host-info.sub.js promise_test(async t => { let iframeLoadCounter = 0; const div = document.createElement('div'); const iframe = document.createElement('iframe'); t.add_cleanup(() => iframe.remove()); const loadPromise = new Promise((resolve) => { iframe.onload = () => { iframeLoadCounter++; resolve() }; }); div.append(iframe); document.body.append(div); await loadPromise; assert_equals(iframeLoadCounter, 1, "iframe loads"); const innerDocument = iframe.contentDocument; assert_true(innerDocument !== null, "about:blank Document is reachable"); iframe.onload = () => iframeLoadCounter++; await new Promise(resolve => t.step_timeout(resolve, 50)); document.body.moveBefore(iframe, null); assert_equals(iframe.contentDocument, innerDocument, "Document is preserved"); assert_equals(iframeLoadCounter, 1, "iframe does not reload"); }, "moveBefore(): about:blank iframe's document is preserved"); const kRemoveNewParent = 'remove new parent'; const kRemoveSelf = 'remove self'; const kRemoveSelfViaReplaceChildren = 'remove self via replaceChildren()'; const kRemoveSelfViaInnerHTML = 'remove self via innerHTML'; promise_test(async t => { const div = document.createElement('div'); const iframe = document.createElement('iframe'); t.add_cleanup(() => iframe.remove()); const loadPromise = new Promise(resolve => iframe.onload = resolve); iframe.src = '/resources/blank.html'; div.append(iframe); document.body.append(div); await loadPromise; const innerDocument = iframe.contentDocument; document.body.moveBefore(iframe, null); assert_equals(iframe.contentDocument, innerDocument, "Document is preserved"); }, "moveBefore(): simple same-origin document is preserved"); // This function runs the same test with a few variations. The meat of the test // loads a cross-origin iframe which asserts that it does not get reloaded. // Second, we remove the iframe from the parent document in a few different ways // to trigger initially crashy paths in Chromium during the implementation of // this feature. function runTest(removalType) { promise_test(async t => { let iframeLoadCounter = 0; const oldParent = document.createElement('div'); const newParent = document.createElement('div'); const iframe = document.createElement('iframe'); iframe.onload = e => iframeLoadCounter++; switch (removalType) { case kRemoveNewParent: t.add_cleanup(() => newParent.remove()); break; case kRemoveSelf: t.add_cleanup(() => iframe.remove()); break; case kRemoveSelfViaReplaceChildren: t.add_cleanup(() => newParent.replaceChildren()); break; case kRemoveSelfViaInnerHTML: t.add_cleanup(() => {newParent.innerHTML = '';}); break; } const loadMessagePromise = new Promise(resolve => window.onmessage = resolve); const crossOriginIframeURL = new URL('resources/moveBefore-iframe.html', location.href.replace(self.origin, get_host_info().HTTP_REMOTE_ORIGIN)); iframe.src = crossOriginIframeURL; oldParent.append(iframe); document.body.append(oldParent, newParent); const loadMessage = await loadMessagePromise; assert_equals(loadMessage.data, 'loaded'); const messagePromise = new Promise(resolve => window.onmessage = resolve); newParent.moveBefore(iframe, null); iframe.contentWindow.postMessage("after moveBefore", "*"); const message = await messagePromise; // If `moveBefore()` behaved just like `insertBefore()`, and reloaded the // document, then `message` would contain `loaded` instead of // `ack after moveBefore`. assert_equals(message.data, 'ack after moveBefore', 'Iframe did not load reload after moveBefore()'); assert_equals(iframeLoadCounter, 1, "iframe does not fire a second load event"); }, `moveBefore(): cross-origin iframe is preserved: ${removalType}`); } runTest(kRemoveNewParent); runTest(kRemoveSelf); runTest(kRemoveSelfViaReplaceChildren); runTest(kRemoveSelfViaInnerHTML); promise_test(async t => { const iframe1 = document.createElement('iframe'); iframe1.name = 'iframe1'; const iframe2 = document.createElement('iframe'); iframe2.name = 'iframe2'; const iframe3 = document.createElement('iframe'); iframe3.name = 'iframe3'; document.body.append(iframe1, iframe2, iframe3); // Assert that the order of iframes in the DOM matches the order of iframes in // `window.frames`. let iframes = document.querySelectorAll('iframe'); assert_equals(iframes[0].name, "iframe1", "iframe1 comes first in DOM"); assert_equals(iframes[1].name, "iframe2", "iframe2 comes second in DOM"); assert_equals(iframes[2].name, "iframe3", "iframe3 comes last in DOM"); assert_equals(window.frames[0].name, "iframe1", "iframe1 comes first in frames"); assert_equals(window.frames[1].name, "iframe2", "iframe2 comes second in frames"); assert_equals(window.frames[2].name, "iframe3", "iframe3 comes last in frames"); // Reverse the order of iframes in the DOM. document.body.moveBefore(iframe2, iframe1); document.body.moveBefore(iframe3, iframe2); // Assert that the order of iframes in the DOM is inverse the order of iframes // in `window.frames`. iframes = document.querySelectorAll('iframe'); assert_equals(iframes[0].name, "iframe3", "iframe3 comes first in DOM after moveBefore"); assert_equals(iframes[1].name, "iframe2", "iframe2 comes second in DOM after moveBefore"); assert_equals(iframes[2].name, "iframe1", "iframe1 comes last in DOM after moveBefore"); assert_equals(window.frames[0].name, "iframe1", "iframe1 comes first in frames after moveBefore"); assert_equals(window.frames[1].name, "iframe2", "iframe2 comes second in frames after moveBefore"); assert_equals(window.frames[2].name, "iframe3", "iframe3 comes last in frames afterMoveBefore"); }, "window.frames ordering does not change due to moveBefore()");