зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1559392 - Support drag+drop of add-ons into HTML about:addons r=rpl
Depends on D65834 Differential Revision: https://phabricator.services.mozilla.com/D65835 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
04fd0b27d7
Коммит
71bcb7f89c
|
@ -27,9 +27,11 @@
|
|||
<script defer src="chrome://mozapps/content/extensions/message-bar.js"></script>
|
||||
<script defer src="chrome://mozapps/content/extensions/abuse-reports.js"></script>
|
||||
<script defer src="chrome://mozapps/content/extensions/shortcuts.js"></script>
|
||||
<script defer src="chrome://mozapps/content/extensions/drag-drop-addon-installer.js"></script>
|
||||
<script defer src="chrome://mozapps/content/extensions/aboutaddons.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<drag-drop-addon-installer></drag-drop-addon-installer>
|
||||
<div id="full">
|
||||
<div id="sidebar">
|
||||
<categories-box id="categories" orientation="vertical">
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* import-globals-from aboutaddonsCommon.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
class DragDropAddonInstaller extends HTMLElement {
|
||||
connectedCallback() {
|
||||
window.addEventListener("drop", this);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener("drop", this);
|
||||
}
|
||||
|
||||
canInstallFromEvent(e) {
|
||||
let types = e.dataTransfer.types;
|
||||
return (
|
||||
types.includes("text/uri-list") ||
|
||||
types.includes("text/x-moz-url") ||
|
||||
types.includes("application/x-moz-file")
|
||||
);
|
||||
}
|
||||
|
||||
handleEvent(e) {
|
||||
if (!XPINSTALL_ENABLED) {
|
||||
// Nothing to do if we can't install add-ons.
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.type == "drop" && this.canInstallFromEvent(e)) {
|
||||
this.onDrop(e);
|
||||
}
|
||||
}
|
||||
|
||||
async onDrop(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let dataTransfer = e.dataTransfer;
|
||||
let browser = getBrowserElement();
|
||||
let urls = [];
|
||||
|
||||
// Convert every dropped item into a url, without this should be sync.
|
||||
for (let i = 0; i < dataTransfer.mozItemCount; i++) {
|
||||
let url = dataTransfer.mozGetDataAt("text/uri-list", i);
|
||||
if (!url) {
|
||||
url = dataTransfer.mozGetDataAt("text/x-moz-url", i);
|
||||
}
|
||||
if (url) {
|
||||
url = url.split("\n")[0];
|
||||
} else {
|
||||
let file = dataTransfer.mozGetDataAt("application/x-moz-file", i);
|
||||
if (file) {
|
||||
url = Services.io.newFileURI(file).spec;
|
||||
}
|
||||
}
|
||||
|
||||
if (url) {
|
||||
urls.push(url);
|
||||
}
|
||||
}
|
||||
|
||||
// Install the add-ons, the await clears the event data so we do this last.
|
||||
for (let url of urls) {
|
||||
let install = await AddonManager.getInstallForURL(url, {
|
||||
telemetryInfo: {
|
||||
source: "about:addons",
|
||||
method: "drag-and-drop",
|
||||
},
|
||||
});
|
||||
|
||||
AddonManager.installAddonFromAOM(
|
||||
browser,
|
||||
document.documentURIObject,
|
||||
install
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define("drag-drop-addon-installer", DragDropAddonInstaller);
|
|
@ -22,6 +22,7 @@ toolkit.jar:
|
|||
content/mozapps/extensions/abuse-report-panel.css (content/abuse-report-panel.css)
|
||||
content/mozapps/extensions/abuse-report-panel.js (content/abuse-report-panel.js)
|
||||
content/mozapps/extensions/default-theme.svg (content/default-theme.svg)
|
||||
content/mozapps/extensions/drag-drop-addon-installer.js (content/drag-drop-addon-installer.js)
|
||||
content/mozapps/extensions/firefox-compact-dark.svg (content/firefox-compact-dark.svg)
|
||||
content/mozapps/extensions/firefox-compact-light.svg (content/firefox-compact-light.svg)
|
||||
content/mozapps/extensions/message-bar.css (content/message-bar.css)
|
||||
|
|
|
@ -4,7 +4,9 @@ prefs =
|
|||
tags = addons
|
||||
support-files =
|
||||
addons/browser_dragdrop1.xpi
|
||||
addons/browser_dragdrop1.zip
|
||||
addons/browser_dragdrop2.xpi
|
||||
addons/browser_dragdrop2.zip
|
||||
addons/browser_dragdrop_incompat.xpi
|
||||
addons/browser_installssl.xpi
|
||||
addons/browser_theme.xpi
|
||||
|
@ -32,7 +34,9 @@ support-files =
|
|||
|
||||
generated-files =
|
||||
addons/browser_dragdrop1.xpi
|
||||
addons/browser_dragdrop1.zip
|
||||
addons/browser_dragdrop2.xpi
|
||||
addons/browser_dragdrop2.zip
|
||||
addons/browser_dragdrop_incompat.xpi
|
||||
addons/browser_installssl.xpi
|
||||
addons/browser_theme.xpi
|
||||
|
@ -48,7 +52,7 @@ skip-if = (!debug && os == 'win') #Bug 1489496
|
|||
[browser_bug572561.js]
|
||||
[browser_checkAddonCompatibility.js]
|
||||
[browser_dragdrop.js]
|
||||
skip-if = true # Bug 1559392
|
||||
skip-if = true # Bug 1626824
|
||||
[browser_extension_sideloading_permission.js]
|
||||
[browser_file_xpi_no_process_switch.js]
|
||||
[browser_globalwarnings.js]
|
||||
|
|
|
@ -2,21 +2,17 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// This tests simulated drag and drop of files into the add-ons manager.
|
||||
// We test with the add-ons manager in its own tab if in Firefox otherwise
|
||||
// in its own window.
|
||||
// Tests are only simulations of the drag and drop events, we cannot really do
|
||||
// this automatically.
|
||||
const ABOUT_ADDONS_URL = "chrome://mozapps/content/extensions/aboutaddons.html";
|
||||
|
||||
// Instead of loading EventUtils.js into the test scope in browser-test.js for all tests,
|
||||
// we only need EventUtils.js for a few files which is why we are using loadSubScript.
|
||||
var gManagerWindow;
|
||||
var EventUtils = {};
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
|
||||
EventUtils
|
||||
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 = {
|
||||
|
@ -74,145 +70,201 @@ async function checkInstallConfirmation(...names) {
|
|||
Services.obs.removeObserver(observer, "addon-install-started");
|
||||
}
|
||||
|
||||
function getViewContainer(gManagerWindow) {
|
||||
return gManagerWindow.document.getElementById("category-box");
|
||||
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() {
|
||||
let url = TESTROOT + "addons/browser_dragdrop1.xpi";
|
||||
gManagerWindow = await open_manager("addons://list/extension");
|
||||
let promise = checkInstallConfirmation("Drag Drop test 1");
|
||||
let viewContainer = getViewContainer(gManagerWindow);
|
||||
let effect = EventUtils.synthesizeDrop(
|
||||
viewContainer,
|
||||
viewContainer,
|
||||
[[{ type: "text/x-moz-url", data: url }]],
|
||||
"copy",
|
||||
gManagerWindow
|
||||
);
|
||||
is(effect, "copy", "Drag should be accepted");
|
||||
await promise;
|
||||
await close_manager(gManagerWindow);
|
||||
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() {
|
||||
let fileurl = get_addon_file_url("browser_dragdrop1.xpi");
|
||||
gManagerWindow = await open_manager("addons://list/extension");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
let promise = checkInstallConfirmation("Drag Drop test 1");
|
||||
let viewContainer = getViewContainer(gManagerWindow);
|
||||
let effect = EventUtils.synthesizeDrop(
|
||||
viewContainer,
|
||||
viewContainer,
|
||||
[[{ type: "application/x-moz-file", data: fileurl.file }]],
|
||||
"copy",
|
||||
gManagerWindow
|
||||
);
|
||||
is(effect, "copy", "Drag should be accepted");
|
||||
await promise;
|
||||
await close_manager(gManagerWindow);
|
||||
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() {
|
||||
let url1 = TESTROOT + "addons/browser_dragdrop1.xpi";
|
||||
let url2 = TESTROOT2 + "addons/browser_dragdrop2.xpi";
|
||||
gManagerWindow = await open_manager("addons://list/extension");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
let promise = checkInstallConfirmation(
|
||||
"Drag Drop test 1",
|
||||
"Drag Drop test 2"
|
||||
);
|
||||
let viewContainer = getViewContainer(gManagerWindow);
|
||||
let effect = EventUtils.synthesizeDrop(
|
||||
viewContainer,
|
||||
viewContainer,
|
||||
[
|
||||
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 }],
|
||||
],
|
||||
"copy",
|
||||
gManagerWindow
|
||||
);
|
||||
is(effect, "copy", "Drag should be accepted");
|
||||
await promise;
|
||||
await close_manager(gManagerWindow);
|
||||
});
|
||||
]);
|
||||
|
||||
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() {
|
||||
let fileurl1 = get_addon_file_url("browser_dragdrop1.xpi");
|
||||
let fileurl2 = get_addon_file_url("browser_dragdrop2.xpi");
|
||||
gManagerWindow = await open_manager("addons://list/extension");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
let promise = checkInstallConfirmation(
|
||||
"Drag Drop test 1",
|
||||
"Drag Drop test 2"
|
||||
);
|
||||
let viewContainer = getViewContainer(gManagerWindow);
|
||||
let effect = EventUtils.synthesizeDrop(
|
||||
viewContainer,
|
||||
viewContainer,
|
||||
[
|
||||
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 }],
|
||||
],
|
||||
"copy",
|
||||
gManagerWindow
|
||||
);
|
||||
is(effect, "copy", "Drag should be accepted");
|
||||
await promise;
|
||||
await close_manager(gManagerWindow);
|
||||
});
|
||||
]);
|
||||
|
||||
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() {
|
||||
let url = TESTROOT + "addons/browser_dragdrop1.xpi";
|
||||
let fileurl = get_addon_file_url("browser_dragdrop2.xpi");
|
||||
gManagerWindow = await open_manager("addons://list/extension");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
let promise = checkInstallConfirmation(
|
||||
"Drag Drop test 1",
|
||||
"Drag Drop test 2"
|
||||
);
|
||||
let viewContainer = getViewContainer(gManagerWindow);
|
||||
let effect = EventUtils.synthesizeDrop(
|
||||
viewContainer,
|
||||
viewContainer,
|
||||
[
|
||||
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 }],
|
||||
],
|
||||
"copy",
|
||||
gManagerWindow
|
||||
);
|
||||
is(effect, "copy", "Drag should be accepted");
|
||||
await promise;
|
||||
await close_manager(gManagerWindow);
|
||||
});
|
||||
]);
|
||||
|
||||
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() {
|
||||
let gManagerWindow = await open_manager("addons://list/extension");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
await withTestPage(async browser => {
|
||||
let url = `${TESTROOT}/addons/browser_dragdrop_incompat.xpi`;
|
||||
|
||||
let errorPromise = TestUtils.topicObserved("addon-install-failed");
|
||||
let panelPromise = promisePopupNotificationShown("addon-install-failed");
|
||||
await simulateDragAndDrop(browser.contentWindow, [
|
||||
[{ type: "text/x-moz-url", data: url }],
|
||||
]);
|
||||
|
||||
let url = `${TESTROOT}/addons/browser_dragdrop_incompat.xpi`;
|
||||
let viewContainer = getViewContainer(gManagerWindow);
|
||||
EventUtils.synthesizeDrop(
|
||||
viewContainer,
|
||||
viewContainer,
|
||||
[[{ type: "text/x-moz-url", data: url }]],
|
||||
"copy",
|
||||
gManagerWindow
|
||||
);
|
||||
|
||||
await errorPromise;
|
||||
ok(true, "Got addon-install-failed event");
|
||||
|
||||
await close_manager(gManagerWindow);
|
||||
let panel = await panelPromise;
|
||||
ok(panel, "Got addon-install-failed popup");
|
||||
panel.button.click();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,12 +20,13 @@ addons = [
|
|||
output_dir = OBJDIR_FILES._tests.testing.mochitest.browser.toolkit.mozapps.extensions.test.browser.addons
|
||||
|
||||
for addon in addons:
|
||||
indir = 'addons/%s' % addon
|
||||
xpi = '%s.xpi' % indir
|
||||
for file_type in ['xpi', 'zip']:
|
||||
indir = 'addons/%s' % addon
|
||||
path = '%s.%s' % (indir, file_type)
|
||||
|
||||
GENERATED_FILES += [xpi]
|
||||
GENERATED_FILES[xpi].script = '../create_xpi.py'
|
||||
GENERATED_FILES[xpi].inputs = [indir]
|
||||
GENERATED_FILES += [path]
|
||||
GENERATED_FILES[path].script = '../create_xpi.py'
|
||||
GENERATED_FILES[path].inputs = [indir]
|
||||
|
||||
output_dir += ['!%s' % xpi]
|
||||
output_dir += ['!%s' % path]
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче