Bug 1221141 - Support installing addon from local directory in about:debugging. r=janx

--HG--
rename : devtools/client/locales/en-US/aboutdebugging.properties => devtools/client/aboutdebugging/moz.build
This commit is contained in:
Alexandre Poirot 2015-11-25 07:54:26 -08:00
parent 5be55f54e9
commit 5cf2eb0e19
12 changed files with 248 additions and 4 deletions

View File

@ -12,7 +12,8 @@ h2, h3, h4 {
}
button {
width: 100px;
padding-left: 20px;
padding-right: 20px;
}
#body {
@ -80,3 +81,12 @@ label {
.target-details {
flex: 1;
}
.addon-controls {
display: flex;
flex-direction: row;
}
.addon-options {
flex: 1;
}

View File

@ -8,6 +8,7 @@
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
const { loader } = Components.utils.import(
"resource://devtools/shared/Loader.jsm", {});
@ -23,6 +24,12 @@ loader.lazyRequireGetter(this, "WorkersComponent",
"devtools/client/aboutdebugging/components/workers", true);
loader.lazyRequireGetter(this, "Services");
loader.lazyImporter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
const Strings = Services.strings.createBundle(
"chrome://devtools/locale/aboutdebugging.properties");
var AboutDebugging = {
_prefListeners: [],
@ -83,6 +90,10 @@ var AboutDebugging = {
updateCheckbox();
});
// Link buttons to their associated actions.
let loadAddonButton = document.getElementById("load-addon-from-file");
loadAddonButton.addEventListener("click", this.loadAddonFromFile);
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
@ -98,6 +109,29 @@ var AboutDebugging = {
});
},
loadAddonFromFile() {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(window,
Strings.GetStringFromName("selectAddonFromFile"),
Ci.nsIFilePicker.modeOpen);
let res = fp.show();
if (res == Ci.nsIFilePicker.returnCancel || !fp.file) {
return;
}
let file = fp.file;
// AddonManager.installTemporaryAddon accepts either
// addon directory or final xpi file.
if (!file.isDirectory() && !file.leafName.endsWith(".xpi")) {
file = file.parent;
}
try {
AddonManager.installTemporaryAddon(file);
} catch(e) {
alert("Error while installing the addon:\n" + e.message + "\n");
throw e;
}
},
destroy() {
let telemetry = this._telemetry;
telemetry.toolClosed("aboutdebugging");

View File

@ -34,8 +34,13 @@
<div class="header">
<h1 class="header-name">&aboutDebugging.addons;</h1>
</div>
<input id="enable-addon-debugging" type="checkbox" data-pref="devtools.chrome.enabled"/>
<label for="enable-addon-debugging" title="&aboutDebugging.addonDebugging.tooltip;">&aboutDebugging.addonDebugging.label;</label>
<div class="addon-controls">
<div class="addon-options">
<input id="enable-addon-debugging" type="checkbox" data-pref="devtools.chrome.enabled"/>
<label for="enable-addon-debugging" title="&aboutDebugging.addonDebugging.tooltip;">&aboutDebugging.addonDebugging.label;</label>
</div>
<button id="load-addon-from-file">&aboutDebugging.loadTemporaryAddon;</button>
</div>
<div id="addons"></div>
</div>
<div id="tab-workers" class="tab">

View File

@ -0,0 +1,13 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DIRS += [
'components',
]
BROWSER_CHROME_MANIFESTS += [
'test/browser.ini'
]

View File

@ -0,0 +1,7 @@
Components.utils.import("resource://gre/modules/Services.jsm");
function startup() {
Services.obs.notifyObservers(null, "test-devtools", null);
}
function shutdown() {}
function install() {}
function uninstall() {}

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<!--
# 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/.
-->
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest"
em:id="test-devtools@mozilla.org"
em:name="test-devtools"
em:version="1.0"
em:type="2"
em:creator="Mozilla">
<em:bootstrap>true</em:bootstrap>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>44.0a1</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -0,0 +1,8 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
addons/
[browser_addons_install.js]

View File

@ -0,0 +1,61 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
const ADDON_ID = "test-devtools@mozilla.org";
const ADDON_NAME = "test-devtools";
add_task(function *() {
let { tab, document } = yield openAboutDebugging("addons");
// Mock the file picker to select a test addon
let MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(null);
let file = get_supports_file("addons/unpacked/install.rdf");
MockFilePicker.returnFiles = [file.file];
// Wait for a message sent by the addon's bootstrap.js file
let promise = new Promise(done => {
Services.obs.addObserver(function listener() {
Services.obs.removeObserver(listener, "test-devtools", false);
ok(true, "Addon installed and running its bootstrap.js file");
done();
}, "test-devtools", false);
});
// Trigger the file picker by clicking on the button
document.getElementById("load-addon-from-file").click();
// Wait for the addon execution
yield promise;
// Check that the addon appears in the UI
let names = [...document.querySelectorAll("#addons .target-name")];
names = names.map(element => element.textContent);
ok(names.includes(ADDON_NAME), "The addon name appears in the list of addons: " + names);
// Now uninstall this addon
yield new Promise(done => {
AddonManager.getAddonByID(ADDON_ID, addon => {
let listener = {
onUninstalled: function(aUninstalledAddon) {
if (aUninstalledAddon != addon) {
return;
}
AddonManager.removeAddonListener(listener);
done();
}
};
AddonManager.addAddonListener(listener);
addon.uninstall();
});
});
// Ensure that the UI removes the addon from the list
names = [...document.querySelectorAll("#addons .target-name")];
names = names.map(element => element.textContent);
ok(!names.includes(ADDON_NAME), "After uninstall, the addon name disappears from the list of addons: " + names);
yield closeAboutDebugging(tab);
});

View File

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
DevToolsUtils.testing = true;
const CHROME_ROOT = gTestPath.substr(0, gTestPath.lastIndexOf("/") + 1);
registerCleanupFunction(() => {
DevToolsUtils.testing = false;
});
function openAboutDebugging() {
info("opening about:debugging");
return addTab("about:debugging").then(tab => {
let browser = tab.linkedBrowser;
return {
tab,
document: browser.contentDocument,
window: browser.contentWindow
};
});
}
function closeAboutDebugging(tab) {
info("Closing about:debugging");
return removeTab(tab);
}
function addTab(aUrl, aWindow) {
info("Adding tab: " + aUrl);
return new Promise(done => {
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
targetWindow.focus();
let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onLoad() {
linkedBrowser.removeEventListener("load", onLoad, true);
info("Tab added and finished loading: " + aUrl);
done(tab);
}, true);
});
}
function removeTab(aTab, aWindow) {
info("Removing tab.");
return new Promise(done => {
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
let tabContainer = targetBrowser.tabContainer;
tabContainer.addEventListener("TabClose", function onClose(aEvent) {
tabContainer.removeEventListener("TabClose", onClose, false);
info("Tab removed and finished closing.");
done();
}, false);
targetBrowser.removeTab(aTab);
});
}
function get_supports_file(path) {
let cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIChromeRegistry);
let fileurl = cr.convertChromeURL(Services.io.newURI(CHROME_ROOT + path, null, null));
return fileurl.QueryInterface(Ci.nsIFileURL);
}

View File

@ -6,4 +6,5 @@
<!ENTITY aboutDebugging.addons "Add-ons">
<!ENTITY aboutDebugging.addonDebugging.label "Enable add-on debugging">
<!ENTITY aboutDebugging.addonDebugging.tooltip "Turning this on will allow you to debug add-ons and various other parts of the browser chrome">
<!ENTITY aboutDebugging.loadTemporaryAddon "Load Temporary Add-on">
<!ENTITY aboutDebugging.workers "Workers">

View File

@ -5,6 +5,7 @@
debug = Debug
extensions = Extensions
selectAddonFromFile = Select Add-on Directory or XPI File
serviceWorkers = Service Workers
sharedWorkers = Shared Workers
otherWorkers = Other Workers

View File

@ -7,7 +7,7 @@
include('../templates.mozbuild')
DIRS += [
'aboutdebugging/components',
'aboutdebugging',
'animationinspector',
'canvasdebugger',
'commandline',