<?xml version="1.0"?> <?xml-stylesheet type="text/css" href="chrome://global/skin"?> <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1242644 Test swapFrameLoaders with different frame types and remoteness --> <window title="Mozilla Bug 1242644" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> <script type="application/javascript"><![CDATA[ ["SimpleTest", "SpecialPowers", "info", "is", "ok"].forEach(key => { window[key] = window.opener[key]; }) const { interfaces: Ci } = Components; const NS = { xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", html: "http://www.w3.org/1999/xhtml", } const TAG = { xul: "browser", html: "iframe", // mozbrowser } const SCENARIOS = [ ["xul", "xul"], ["xul", "html"], ["html", "xul"], ["html", "html"], ["xul", "xul", "remote"], ["xul", "html", "remote"], ["html", "xul", "remote"], ["html", "html", "remote"], ]; const HEIGHTS = [ 200, 400 ]; function frameScript() { addEventListener("load", function onLoad() { sendAsyncMessage("test:load"); }, true); } // Watch for loads in new frames window.messageManager.loadFrameScript(`data:,(${frameScript})();`, true); function once(target, eventName, useCapture = false) { info("Waiting for event: '" + eventName + "' on " + target + "."); return new Promise(resolve => { for (let [add, remove] of [ ["addEventListener", "removeEventListener"], ["addMessageListener", "removeMessageListener"], ]) { if ((add in target) && (remove in target)) { target[add](eventName, function onEvent(...aArgs) { info("Got event: '" + eventName + "' on " + target + "."); target[remove](eventName, onEvent, useCapture); resolve(aArgs); }, useCapture); break; } } }); } function* addFrame(type, remote, height) { let frame = document.createElementNS(NS[type], TAG[type]); frame.setAttribute("remote", remote); if (remote && type == "xul") { frame.setAttribute("style", "-moz-binding: none;"); } if (type == "html") { frame.setAttribute("mozbrowser", "true"); frame.setAttribute("noisolation", "true"); frame.setAttribute("allowfullscreen", "true"); } else if (type == "xul") { frame.setAttribute("type", "content"); } let src = `data:text/html,<!doctype html>` + `<body style="height:${height}px"/>`; frame.setAttribute("src", src); document.documentElement.appendChild(frame); let mm = frame.frameLoader.messageManager; yield once(mm, "test:load"); return frame; } add_task(function*() { yield new Promise(resolve => { SpecialPowers.pushPrefEnv( { "set": [["dom.mozBrowserFramesEnabled", true], ["network.disable.ipc.security", true]] }, resolve); }); }); add_task(function*() { for (let scenario of SCENARIOS) { let [ typeA, typeB, remote ] = scenario; remote = !!remote; let heightA = HEIGHTS[0]; info(`Adding frame A, type ${typeA}, remote ${remote}, height ${heightA}`); let frameA = yield addFrame(typeA, remote, heightA); let heightB = HEIGHTS[1]; info(`Adding frame B, type ${typeB}, remote ${remote}, height ${heightB}`); let frameB = yield addFrame(typeB, remote, heightB); let frameScriptFactory = function(name) { return `function() { addMessageListener("ping", function() { sendAsyncMessage("pong", "${name}"); }); addMessageListener("check-browser-api", function() { let exists = "api" in this; sendAsyncMessage("check-browser-api", { exists, running: exists && !this.api._shuttingDown, }); }); }`; } // Load frame script into each frame { let mmA = frameA.frameLoader.messageManager; let mmB = frameB.frameLoader.messageManager; mmA.loadFrameScript("data:,(" + frameScriptFactory("A") + ")()", false); mmB.loadFrameScript("data:,(" + frameScriptFactory("B") + ")()", false); } // Ping before swap { let mmA = frameA.frameLoader.messageManager; let mmB = frameB.frameLoader.messageManager; let inflightA = once(mmA, "pong"); let inflightB = once(mmB, "pong"); info("Ping message manager for frame A"); mmA.sendAsyncMessage("ping"); let [ { data: pongA } ] = yield inflightA; is(pongA, "A", "Frame A message manager gets reply A before swap"); info("Ping message manager for frame B"); mmB.sendAsyncMessage("ping"); let [ { data: pongB } ] = yield inflightB; is(pongB, "B", "Frame B message manager gets reply B before swap"); } // Check height before swap { if (frameA.getContentDimensions) { let { height } = yield frameA.getContentDimensions(); is(height, heightA, "Frame A's content height is 200px before swap"); } if (frameB.getContentDimensions) { let { height } = yield frameB.getContentDimensions(); is(height, heightB, "Frame B's content height is 400px before swap"); } } // Ping after swap using message managers acquired before { let mmA = frameA.frameLoader.messageManager; let mmB = frameB.frameLoader.messageManager; info("swapFrameLoaders"); frameA.swapFrameLoaders(frameB); let inflightA = once(mmA, "pong"); let inflightB = once(mmB, "pong"); info("Ping message manager for frame A"); mmA.sendAsyncMessage("ping"); let [ { data: pongA } ] = yield inflightA; is(pongA, "B", "Frame A message manager acquired before swap gets reply B after swap"); info("Ping message manager for frame B"); mmB.sendAsyncMessage("ping"); let [ { data: pongB } ] = yield inflightB; is(pongB, "A", "Frame B message manager acquired before swap gets reply A after swap"); } // Check height after swap { if (frameA.getContentDimensions) { let { height } = yield frameA.getContentDimensions(); is(height, heightB, "Frame A's content height is 400px after swap"); } if (frameB.getContentDimensions) { let { height } = yield frameB.getContentDimensions(); is(height, heightA, "Frame B's content height is 200px after swap"); } } // Ping after swap using message managers acquired after { let mmA = frameA.frameLoader.messageManager; let mmB = frameB.frameLoader.messageManager; let inflightA = once(mmA, "pong"); let inflightB = once(mmB, "pong"); info("Ping message manager for frame A"); mmA.sendAsyncMessage("ping"); let [ { data: pongA } ] = yield inflightA; is(pongA, "B", "Frame A message manager acquired after swap gets reply B after swap"); info("Ping message manager for frame B"); mmB.sendAsyncMessage("ping"); let [ { data: pongB } ] = yield inflightB; is(pongB, "A", "Frame B message manager acquired after swap gets reply A after swap"); } // Verify browser API frame scripts destroyed if swapped out of browser frame if (frameA.hasAttribute("mozbrowser") != frameB.hasAttribute("mozbrowser")) { let mmA = frameA.frameLoader.messageManager; let mmB = frameB.frameLoader.messageManager; let inflightA = once(mmA, "check-browser-api"); let inflightB = once(mmB, "check-browser-api"); info("Check browser API for frame A"); mmA.sendAsyncMessage("check-browser-api"); let [ { data: apiA } ] = yield inflightA; if (frameA.hasAttribute("mozbrowser")) { ok(apiA.exists && apiA.running, "Frame A browser API exists and is running"); } else { ok(apiA.exists && !apiA.running, "Frame A browser API did exist but is now destroyed"); } info("Check browser API for frame B"); mmB.sendAsyncMessage("check-browser-api"); let [ { data: apiB } ] = yield inflightB; if (frameB.hasAttribute("mozbrowser")) { ok(apiB.exists && apiB.running, "Frame B browser API exists and is running"); } else { ok(apiB.exists && !apiB.running, "Frame B browser API did exist but is now destroyed"); } } else { info("Frames have matching mozbrowser state, skipping browser API destruction check"); } frameA.remove(); frameB.remove(); } }); ]]></script> </window>