/* Any copyright is dedicated to the public domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Test that "cut, copy, paste, selectall" and caretstatechanged event works from inside an <iframe mozbrowser>. "use strict"; SimpleTest.waitForExplicitFinish(); SimpleTest.requestFlakyTimeout("untriaged"); browserElementTestHelpers.setEnabledPref(true); browserElementTestHelpers.setupAccessibleCaretPref(); browserElementTestHelpers.addPermission(); const { Services } = SpecialPowers.Cu.import('resource://gre/modules/Services.jsm'); var gTextarea = null; var mm; var iframeOuter; var iframeInner; var state = 0; var stateMeaning; var defaultData; var pasteData; var focusScript; var createEmbededFrame = false; var testSelectionChange = false; function copyToClipboard(str) { gTextarea.value = str; SpecialPowers.wrap(gTextarea).editor.selectAll(); SpecialPowers.wrap(gTextarea).editor.copy(); } function getScriptForGetContent() { var script = 'data:,\ var elt = content.document.getElementById("text"); \ var txt = ""; \ if (elt) { \ if (elt.tagName === "DIV" || elt.tagName === "BODY") { \ txt = elt.textContent; \ } else { \ txt = elt.value; \ } \ } \ sendAsyncMessage("content-text", txt);'; return script; } function getScriptForSetFocus() { var script = 'data:,' + focusScript + 'sendAsyncMessage("content-focus")'; return script; } function runTest() { iframeOuter = document.createElement('iframe'); iframeOuter.setAttribute('mozbrowser', 'true'); if (createEmbededFrame) { iframeOuter.src = "file_empty.html"; } document.body.appendChild(iframeOuter); gTextarea = document.createElement('textarea'); document.body.appendChild(gTextarea); iframeOuter.addEventListener("mozbrowserloadend", function onloadend(e) { iframeOuter.removeEventListener("mozbrowserloadend", onloadend); if (createEmbededFrame) { var contentWin = SpecialPowers.wrap(iframeOuter) .QueryInterface(SpecialPowers.Ci.nsIFrameLoaderOwner) .frameLoader.docShell.contentViewer.DOMDocument.defaultView; var contentDoc = contentWin.document; iframeInner = contentDoc.createElement('iframe'); iframeInner.setAttribute('mozbrowser', true); iframeInner.setAttribute('remote', 'false'); contentDoc.body.appendChild(iframeInner); iframeInner.addEventListener("mozbrowserloadend", function onloadendinner(e) { iframeInner.removeEventListener("mozbrowserloadend", onloadendinner); mm = SpecialPowers.getBrowserFrameMessageManager(iframeInner); dispatchTest(e); }); } else { iframeInner = iframeOuter; mm = SpecialPowers.getBrowserFrameMessageManager(iframeInner); dispatchTest(e); } }); } function doCommand(cmd) { var COMMAND_MAP = { 'cut': 'cmd_cut', 'copy': 'cmd_copyAndCollapseToEnd', 'paste': 'cmd_paste', 'selectall': 'cmd_selectAll' }; var script = 'data:,docShell.doCommand("' + COMMAND_MAP[cmd] + '");'; mm.loadFrameScript(script, false); } function dispatchTest(e) { iframeInner.addEventListener("mozbrowserloadend", function onloadend2(e) { iframeInner.removeEventListener("mozbrowserloadend", onloadend2); iframeInner.focus(); SimpleTest.executeSoon(function() { testSelectAll(e); }); }); switch (state) { case 0: // test for textarea defaultData = "Test for selection change event"; pasteData = "from parent "; iframeInner.src = "data:text/html,<html><body>" + "<textarea id='text'>" + defaultData + "</textarea>" + "</body>" + "</html>"; stateMeaning = " (test: textarea)"; focusScript = "var elt=content.document.getElementById('text');elt.focus();elt.select();"; break; case 1: // test for input text defaultData = "Test for selection change event"; pasteData = "from parent "; iframeInner.src = "data:text/html,<html><body>" + "<input type='text' id='text' value='" + defaultData + "'>" + "</body>" + "</html>"; stateMeaning = " (test: <input type=text>)"; focusScript = "var elt=content.document.getElementById('text');elt.focus();elt.select();"; break; case 2: // test for input number defaultData = "12345"; pasteData = "67890"; iframeInner.src = "data:text/html,<html><body>" + "<input type='number' id='text' value='" + defaultData + "'>" + "</body>" + "</html>"; stateMeaning = " (test: <input type=number>)"; focusScript = "var elt=content.document.getElementById('text');elt.focus();elt.select();"; break; case 3: // test for div contenteditable defaultData = "Test for selection change event"; pasteData = "from parent "; iframeInner.src = "data:text/html,<html><body>" + "<div contenteditable='true' id='text'>" + defaultData + "</div>" + "</body>" + "</html>"; stateMeaning = " (test: content editable div)"; focusScript = "var elt=content.document.getElementById('text');elt.focus();"; break; case 4: // test for normal div defaultData = "Test for selection change event"; pasteData = "from parent "; iframeInner.src = "data:text/html,<html><body>" + "<div id='text'>" + defaultData + "</div>" + "</body>" + "</html>"; stateMeaning = " (test: normal div)"; focusScript = "var elt=content.document.getElementById('text');elt.focus();"; break; case 5: // test for normal div with designMode:on defaultData = "Test for selection change event"; pasteData = "from parent "; iframeInner.src = "data:text/html,<html><body id='text'>" + defaultData + "</body>" + "<script>document.designMode='on';</script>" + "</html>"; stateMeaning = " (test: normal div with designMode:on)"; focusScript = "var elt=content.document.getElementById('text');elt.focus();"; break; default: if (createEmbededFrame || browserElementTestHelpers.getOOPByDefaultPref()) { SimpleTest.finish(); } else { createEmbededFrame = true; // clean up and run test again. document.body.removeChild(iframeOuter); document.body.removeChild(gTextarea); state = 0; runTest(); } break; } } function isChildProcess() { return SpecialPowers.Cc["@mozilla.org/xre/app-info;1"] .getService(SpecialPowers.Ci.nsIXULRuntime) .processType != SpecialPowers.Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; } function testSelectAll(e) { // Skip mozbrowser test if we're at child process. if (!isChildProcess()) { let eventName = "mozbrowsercaretstatechanged"; iframeOuter.addEventListener(eventName, function caretchangeforselectall(e) { iframeOuter.removeEventListener(eventName, caretchangeforselectall, true); ok(true, "got mozbrowsercaretstatechanged event." + stateMeaning); ok(e.detail, "event.detail is not null." + stateMeaning); ok(e.detail.width != 0, "event.detail.width is not zero" + stateMeaning); ok(e.detail.height != 0, "event.detail.height is not zero" + stateMeaning); SimpleTest.executeSoon(function() { testCopy1(e); }); }, true); } mm.addMessageListener('content-focus', function messageforfocus(msg) { mm.removeMessageListener('content-focus', messageforfocus); // test selectall command, after calling this the caretstatechanged event should be fired. doCommand('selectall'); if (isChildProcess()) { SimpleTest.executeSoon(function() { testCopy1(e); }); } }); mm.loadFrameScript(getScriptForSetFocus(), false); } function testCopy1(e) { // Right now we're at "selectall" state, so we can test copy commnad by // calling doCommand copyToClipboard(""); let setup = function() { doCommand("copy"); }; let nextTest = function(success) { ok(success, "copy command works" + stateMeaning); SimpleTest.executeSoon(function() { testPaste1(e); }); }; let success = function() { nextTest(true); } let fail = function() { nextTest(false); } let compareData = defaultData; SimpleTest.waitForClipboard(compareData, setup, success, fail); } function testPaste1(e) { // Next test paste command, first we copy to global clipboard in parent side. // Then paste it to child side. copyToClipboard(pasteData); doCommand('selectall'); doCommand("paste"); SimpleTest.executeSoon(function() { testPaste2(e); }); } function testPaste2(e) { mm.addMessageListener('content-text', function messageforpaste(msg) { mm.removeMessageListener('content-text', messageforpaste); if (state == 4) { // normal div cannot paste, so the content remain unchange ok(SpecialPowers.wrap(msg).json === defaultData, "paste command works" + stateMeaning); } else if (state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) { // Something weird when we doCommand with content editable element in OOP. Mark this case as todo todo(false, "paste command works" + stateMeaning); } else { ok(SpecialPowers.wrap(msg).json === pasteData, "paste command works" + stateMeaning); } SimpleTest.executeSoon(function() { testCut1(e); }); }); mm.loadFrameScript(getScriptForGetContent(), false); } function testCut1(e) { // Clean clipboard first copyToClipboard(""); let setup = function() { doCommand("selectall"); doCommand("cut"); }; let nextTest = function(success) { if (state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) { // Something weird when we doCommand with content editable element in OOP. todo(false, "cut function works" + stateMeaning); } else { ok(success, "cut function works" + stateMeaning); } SimpleTest.executeSoon(function() { testCut2(e); }); }; let success = function() { nextTest(true); } let fail = function() { nextTest(false); } let compareData = pasteData; // Something weird when we doCommand with content editable element in OOP. // Always true in this case // Normal div case cannot cut, always true as well. if ((state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) || state == 4) { compareData = function() { return true; } } SimpleTest.waitForClipboard(compareData, setup, success, fail); } function testCut2(e) { mm.addMessageListener('content-text', function messageforcut(msg) { mm.removeMessageListener('content-text', messageforcut); // normal div cannot cut if (state == 4) { ok(SpecialPowers.wrap(msg).json !== "", "cut command works" + stateMeaning); } else if (state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) { // Something weird when we doCommand with content editable element in OOP. Mark this case as todo todo(false, "cut command works" + stateMeaning); } else { ok(SpecialPowers.wrap(msg).json === "", "cut command works" + stateMeaning); } state++; dispatchTest(e); }); mm.loadFrameScript(getScriptForGetContent(), false); } // Give our origin permission to open browsers, and remove it when the test is complete. var principal = SpecialPowers.wrap(document).nodePrincipal; var context = { url: SpecialPowers.wrap(principal.URI).spec, originAttributes: { appId: principal.appId, inIsolatedMozBrowser: true }}; addEventListener('testready', function() { SpecialPowers.pushPermissions([ {type: 'browser', allow: 1, context: context} ], runTest); });