diff options
Diffstat (limited to 'toolkit/mozapps/extensions/test/browser/browser_dragdrop.js')
-rw-r--r-- | toolkit/mozapps/extensions/test/browser/browser_dragdrop.js | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js new file mode 100644 index 0000000000..ae8625a18a --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js @@ -0,0 +1,270 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const ABOUT_ADDONS_URL = "chrome://mozapps/content/extensions/aboutaddons.html"; + +const dragService = Cc["@mozilla.org/widget/dragservice;1"].getService( + Ci.nsIDragService +); + +// Test that the drag-drop-addon-installer component installs add-ons and is +// included in about:addons. There is an issue with EventUtils.synthesizeDrop +// where it throws an exception when you give it an subbrowser so we test +// the component directly. + +async function checkInstallConfirmation(...names) { + let notificationCount = 0; + let observer = { + observe(aSubject, aTopic, aData) { + let installInfo = aSubject.wrappedJSObject; + isnot( + installInfo.browser, + null, + "Notification should have non-null browser" + ); + + is( + installInfo.installs.length, + 1, + "Got one AddonInstall instance as expected" + ); + + Assert.deepEqual( + installInfo.installs[0].installTelemetryInfo, + { source: "about:addons", method: "drag-and-drop" }, + "Got the expected installTelemetryInfo" + ); + + notificationCount++; + }, + }; + Services.obs.addObserver(observer, "addon-install-started"); + + let results = []; + + let promise = promisePopupNotificationShown("addon-webext-permissions"); + for (let i = 0; i < names.length; i++) { + let panel = await promise; + let name = panel.getAttribute("name"); + results.push(name); + + info(`Saw install for ${name}`); + if (results.length < names.length) { + info( + `Waiting for installs for ${names.filter(n => !results.includes(n))}` + ); + + promise = promisePopupNotificationShown("addon-webext-permissions"); + } + panel.secondaryButton.click(); + } + + Assert.deepEqual(results.sort(), names.sort(), "Got expected installs"); + + is( + notificationCount, + names.length, + `Saw ${names.length} addon-install-started notification` + ); + Services.obs.removeObserver(observer, "addon-install-started"); +} + +function getDragOverTarget(win) { + return win.document.querySelector("categories-box"); +} + +function getDropTarget(win) { + return win.document.querySelector("drag-drop-addon-installer"); +} + +function withTestPage(fn) { + return BrowserTestUtils.withNewTab( + { url: ABOUT_ADDONS_URL, gBrowser }, + async browser => { + let win = browser.contentWindow; + await win.customElements.whenDefined("drag-drop-addon-installer"); + await fn(browser); + } + ); +} + +function initDragSession({ dragData, dropEffect }) { + let dropAction; + switch (dropEffect) { + case null: + case undefined: + case "move": + dropAction = _EU_Ci.nsIDragService.DRAGDROP_ACTION_MOVE; + break; + case "copy": + dropAction = _EU_Ci.nsIDragService.DRAGDROP_ACTION_COPY; + break; + case "link": + dropAction = _EU_Ci.nsIDragService.DRAGDROP_ACTION_LINK; + break; + default: + throw new Error(`${dropEffect} is an invalid drop effect value`); + } + + const dataTransfer = new DataTransfer(); + dataTransfer.dropEffect = dropEffect; + + for (let i = 0; i < dragData.length; i++) { + const item = dragData[i]; + for (let j = 0; j < item.length; j++) { + dataTransfer.mozSetDataAt(item[j].type, item[j].data, i); + } + } + + dragService.startDragSessionForTests(dropAction); + const session = dragService.getCurrentSession(); + session.dataTransfer = dataTransfer; + + return session; +} + +async function simulateDragAndDrop(win, dragData) { + const dropTarget = getDropTarget(win); + const dragOverTarget = getDragOverTarget(win); + const dropEffect = "move"; + + const session = initDragSession({ dragData, dropEffect }); + + info("Simulate drag over and wait for the drop target to be visible"); + + EventUtils.synthesizeDragOver( + dragOverTarget, + dragOverTarget, + dragData, + dropEffect, + win + ); + + // This make sure that the fake dataTransfer has still + // the expected drop effect after the synthesizeDragOver call. + session.dataTransfer.dropEffect = "move"; + + await BrowserTestUtils.waitForCondition( + () => !dropTarget.hidden, + "Wait for the drop target element to be visible" + ); + + info("Simulate drop dragData on drop target"); + + EventUtils.synthesizeDropAfterDragOver( + null, + session.dataTransfer, + dropTarget, + win, + { _domDispatchOnly: true } + ); + + dragService.endDragSession(true); +} + +// Simulates dropping a URL onto the manager +add_task(async function test_drop_url() { + for (let fileType of ["xpi", "zip"]) { + await withTestPage(async browser => { + const url = TESTROOT + `addons/browser_dragdrop1.${fileType}`; + const promise = checkInstallConfirmation("Drag Drop test 1"); + + await simulateDragAndDrop(browser.contentWindow, [ + [{ type: "text/x-moz-url", data: url }], + ]); + + await promise; + }); + } +}); + +// Simulates dropping a file onto the manager +add_task(async function test_drop_file() { + for (let fileType of ["xpi", "zip"]) { + await withTestPage(async browser => { + let fileurl = get_addon_file_url(`browser_dragdrop1.${fileType}`); + let promise = checkInstallConfirmation("Drag Drop test 1"); + + await simulateDragAndDrop(browser.contentWindow, [ + [{ type: "application/x-moz-file", data: fileurl.file }], + ]); + + await promise; + }); + } +}); + +// Simulates dropping two urls onto the manager +add_task(async function test_drop_multiple_urls() { + await withTestPage(async browser => { + let url1 = TESTROOT + "addons/browser_dragdrop1.xpi"; + let url2 = TESTROOT2 + "addons/browser_dragdrop2.zip"; + let promise = checkInstallConfirmation( + "Drag Drop test 1", + "Drag Drop test 2" + ); + + await simulateDragAndDrop(browser.contentWindow, [ + [{ type: "text/x-moz-url", data: url1 }], + [{ type: "text/x-moz-url", data: url2 }], + ]); + + await promise; + }); +}).skip(); // TODO(rpl): this fails because mozSetDataAt throws IndexSizeError. + +// Simulates dropping two files onto the manager +add_task(async function test_drop_multiple_files() { + await withTestPage(async browser => { + let fileurl1 = get_addon_file_url("browser_dragdrop1.zip"); + let fileurl2 = get_addon_file_url("browser_dragdrop2.xpi"); + let promise = checkInstallConfirmation( + "Drag Drop test 1", + "Drag Drop test 2" + ); + + await simulateDragAndDrop(browser.contentWindow, [ + [{ type: "application/x-moz-file", data: fileurl1.file }], + [{ type: "application/x-moz-file", data: fileurl2.file }], + ]); + + await promise; + }); +}).skip(); // TODO(rpl): this fails because mozSetDataAt throws IndexSizeError. + +// Simulates dropping a file and a url onto the manager (weird, but should still work) +add_task(async function test_drop_file_and_url() { + await withTestPage(async browser => { + let url = TESTROOT + "addons/browser_dragdrop1.xpi"; + let fileurl = get_addon_file_url("browser_dragdrop2.zip"); + let promise = checkInstallConfirmation( + "Drag Drop test 1", + "Drag Drop test 2" + ); + + await simulateDragAndDrop(browser.contentWindow, [ + [{ type: "text/x-moz-url", data: url }], + [{ type: "application/x-moz-file", data: fileurl.file }], + ]); + + await promise; + }); +}).skip(); // TODO(rpl): this fails because mozSetDataAt throws IndexSizeError. + +// Test that drag-and-drop of an incompatible addon generates +// an error. +add_task(async function test_drop_incompat_file() { + await withTestPage(async browser => { + let url = `${TESTROOT}/addons/browser_dragdrop_incompat.xpi`; + + let panelPromise = promisePopupNotificationShown("addon-install-failed"); + await simulateDragAndDrop(browser.contentWindow, [ + [{ type: "text/x-moz-url", data: url }], + ]); + + let panel = await panelPromise; + ok(panel, "Got addon-install-failed popup"); + panel.button.click(); + }); +}); |