browser_dragdrop_impl.js (6963B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // Test that drag and drop events are sent at the right time. 6 // Supports dragging between domains, windows and iframes. 7 // To avoid symmetric tests, the drag source must be in the first window. 8 // iframe domains must match outer frame domains. 9 10 // This file should be included in DND tests that define its parameters. 11 // That file needs to do the following: 12 // 1. Call await setup(configParams), where configParams is an object that 13 // configures the specific test. See `setup()` below. 14 // 2. Use Services.scriptloader.loadSubScript to load this script. 15 // 3. Define runTest(testName, srcBrowsingCxt, tgtBrowsingCxt, dndOptions), 16 // which defaults to `runDnd` below. Other definitions will typically 17 // also need to delegate some functionality to runDnd. 18 19 "use strict"; 20 21 const { MockRegistrar } = ChromeUtils.importESModule( 22 "resource://testing-common/MockRegistrar.sys.mjs" 23 ); 24 25 // The tabs, each in their own widget. 26 let tab1Cxt; 27 let tab2Cxt; 28 29 let dragServiceCid; 30 31 // JS controller for mock drag service 32 let dragController; 33 34 async function runDnd( 35 testName, 36 sourceBrowsingCxt, 37 targetBrowsingCxt, 38 dndOptions = {} 39 ) { 40 return EventUtils.synthesizeMockDragAndDrop({ 41 dragController, 42 srcElement: "dropSource", 43 targetElement: "dropTarget", 44 sourceBrowsingCxt, 45 targetBrowsingCxt, 46 id: SpecialPowers.Ci.nsIDOMWindowUtils.DEFAULT_MOUSE_POINTER_ID, 47 contextLabel: testName, 48 info, 49 record, 50 dragAction: Ci.nsIDragService.DRAGDROP_ACTION_MOVE, 51 ...dndOptions, 52 }); 53 } 54 55 async function openWindow(tabIdx, configParams) { 56 let win = 57 tabIdx == 0 ? window : await BrowserTestUtils.openNewBrowserWindow(); 58 const OUTER_URL_ARRAY = [configParams.outerURL1, configParams.outerURL2]; 59 let url = OUTER_URL_ARRAY[tabIdx]; 60 let tab = await BrowserTestUtils.openNewForegroundTab({ 61 gBrowser: win.gBrowser, 62 url, 63 }); 64 registerCleanupFunction(async function () { 65 await BrowserTestUtils.removeTab(tab); 66 if (tabIdx != 0) { 67 await BrowserTestUtils.closeWindow(win); 68 } 69 }); 70 71 // Set the URL for the iframe. Also set 72 // neverAllowSessionIsSynthesizedForTests for both frames 73 // (the second is redundant if they are in the same process). 74 const INNER_URL_ARRAY = [configParams.innerURL1, configParams.innerURL2]; 75 await SpecialPowers.spawn( 76 tab.linkedBrowser.browsingContext, 77 [tabIdx, INNER_URL_ARRAY[tabIdx]], 78 async (tabIdx, iframeUrl) => { 79 let iframe = content.document.getElementById("iframe"); 80 if (!iframe && content.document.body.shadowRoot) { 81 iframe = content.document.body.shadowRoot.getElementById("iframe"); 82 } 83 ok(iframe, `Found iframe in window ${tabIdx}`); 84 let loadedPromise = new Promise(res => { 85 iframe.addEventListener("load", res, { once: true }); 86 }); 87 iframe.src = iframeUrl; 88 await loadedPromise; 89 const ds = SpecialPowers.Cc[ 90 "@mozilla.org/widget/dragservice;1" 91 ].getService(SpecialPowers.Ci.nsIDragService); 92 ds.neverAllowSessionIsSynthesizedForTests = true; 93 } 94 ); 95 96 await SpecialPowers.spawn( 97 tab.linkedBrowser.browsingContext.children[0], 98 [], 99 () => { 100 const ds = SpecialPowers.Cc[ 101 "@mozilla.org/widget/dragservice;1" 102 ].getService(SpecialPowers.Ci.nsIDragService); 103 ds.neverAllowSessionIsSynthesizedForTests = true; 104 } 105 ); 106 107 return tab.linkedBrowser.browsingContext; 108 } 109 110 /** 111 * Configure the test. All fields are required. 112 * 113 * @param {object} configParams 114 * @param {string} configParams.outerURL1 115 * URL of window #1's main frame's content (not iframes). 116 * The document must contain an iframe with ID "iframe" 117 * that will be used to load the iframe HTML. It also 118 * must contain an element with ID "dropSource" that will be 119 * used as the source of a drag, as well as one with ID 120 * "dropTarget" that will be used as the drop target. 121 * @param {string} configParams.outerURL2 122 * Like outerURL1 but for the second window. outerURL1 and 123 * outerURL2 may be identical. Must include "dropSource" and 124 * "dropTarget" elements. 125 * @param {string} configParams.innerURL1 126 * URL of the inner frame's content in window #1. Must 127 * include "dropSource" and "dropTarget" elements. 128 * @param {string} configParams.innerURL1 129 * URL of the inner frame's content in window #2. Must 130 * include "dropSource" and "dropTarget" elements. 131 */ 132 async function setup(configParams) { 133 const oldDragService = SpecialPowers.Cc[ 134 "@mozilla.org/widget/dragservice;1" 135 ].getService(SpecialPowers.Ci.nsIDragService); 136 dragController = oldDragService.getMockDragController(); 137 dragServiceCid = MockRegistrar.register( 138 "@mozilla.org/widget/dragservice;1", 139 dragController.mockDragService 140 ); 141 ok(dragServiceCid, "MockDragService was registered"); 142 // If the mock failed then don't continue or else we could trigger native 143 // DND behavior. 144 if (!dragServiceCid) { 145 SimpleTest.finish(); 146 } 147 registerCleanupFunction(async function () { 148 MockRegistrar.unregister(dragServiceCid); 149 }); 150 dragController.mockDragService.neverAllowSessionIsSynthesizedForTests = true; 151 152 tab1Cxt = await openWindow(0, configParams); 153 tab2Cxt = await openWindow(1, configParams); 154 } 155 156 // ---------------------------------------------------------------------------- 157 // Test dragging between different frames and different domains 158 // ---------------------------------------------------------------------------- 159 // Define runTest to establish a test between two (possibly identical) contexts. 160 // runTest has the same signature as runDnd. 161 var runTest = runDnd; 162 163 add_task(async function test_dnd_tab1_to_tab1() { 164 await runTest("tab1->tab1", tab1Cxt, tab1Cxt); 165 }); 166 167 add_task(async function test_dnd_tab1_to_iframe1() { 168 await runTest("tab1->iframe1", tab1Cxt, tab1Cxt.children[0]); 169 }); 170 171 add_task(async function test_dnd_tab1_to_tab2() { 172 await runTest("tab1->tab2", tab1Cxt, tab2Cxt); 173 }); 174 175 add_task(async function test_dnd_tab1_to_iframe2() { 176 await runTest("tab1->iframe2", tab1Cxt, tab2Cxt.children[0]); 177 }); 178 179 add_task(async function test_dnd_iframe1_to_tab1() { 180 await runTest("iframe1->tab1", tab1Cxt.children[0], tab1Cxt); 181 }); 182 183 add_task(async function test_dnd_iframe1_to_iframe1() { 184 await runTest("iframe1->iframe1", tab1Cxt.children[0], tab1Cxt.children[0]); 185 }); 186 187 add_task(async function test_dnd_iframe1_to_tab2() { 188 await runTest("iframe1->tab2", tab1Cxt.children[0], tab2Cxt); 189 }); 190 191 add_task(async function test_dnd_iframe1_to_iframe2() { 192 await runTest("iframe1->iframe2", tab1Cxt.children[0], tab2Cxt.children[0]); 193 });