Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
*/
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) {
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(win, { 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(win, dropAction);
const session = dragService.getCurrentSession(win);
session.dataTransfer = dataTransfer;
return session;
}
async function simulateDragAndDrop(win, dragData) {
const dropTarget = getDropTarget(win);
const dragOverTarget = getDragOverTarget(win);
const dropEffect = "move";
const session = initDragSession(win, { 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.getDragSession(win).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();
});
});