зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1317363 Implement the new sideloading flow r=florian,rhelmer
MozReview-Commit-ID: JgloWKYAhlK --HG-- extra : rebase_source : 732c178b13c231b36dea0c8c9f0871c7486e18c3
This commit is contained in:
Родитель
992114f5fd
Коммит
2e63037fde
|
@ -469,6 +469,62 @@ const gXPInstallObserver = {
|
|||
}
|
||||
};
|
||||
|
||||
const gExtensionsNotifications = {
|
||||
initialized: false,
|
||||
init() {
|
||||
this.updateAlerts();
|
||||
this.boundUpdate = this.updateAlerts.bind(this);
|
||||
ExtensionsUI.on("change", this.boundUpdate);
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
uninit() {
|
||||
// uninit() can race ahead of init() in some cases, if that happens,
|
||||
// we have no handler to remove.
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
ExtensionsUI.off("change", this.boundUpdate);
|
||||
},
|
||||
|
||||
updateAlerts() {
|
||||
let sideloaded = ExtensionsUI.sideloaded;
|
||||
if (sideloaded.size == 0) {
|
||||
gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_ADDONS);
|
||||
} else {
|
||||
gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_ADDONS,
|
||||
"addon-alert");
|
||||
}
|
||||
|
||||
let container = document.getElementById("PanelUI-footer-addons");
|
||||
|
||||
while (container.firstChild) {
|
||||
container.firstChild.remove();
|
||||
}
|
||||
|
||||
// Strings below to be properly localized in bug 1316996
|
||||
const DEFAULT_EXTENSION_ICON =
|
||||
"chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
let items = 0;
|
||||
for (let addon of sideloaded) {
|
||||
if (++items > 4) {
|
||||
break;
|
||||
}
|
||||
let button = document.createElement("toolbarbutton");
|
||||
button.setAttribute("label", `"${addon.name}" added to Firefox`);
|
||||
|
||||
let icon = addon.iconURL || DEFAULT_EXTENSION_ICON;
|
||||
button.setAttribute("image", icon);
|
||||
|
||||
button.addEventListener("click", evt => {
|
||||
ExtensionsUI.showSideloaded(gBrowser, addon);
|
||||
});
|
||||
|
||||
container.appendChild(button);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var LightWeightThemeWebInstaller = {
|
||||
init() {
|
||||
let mm = window.messageManager;
|
||||
|
|
|
@ -47,6 +47,7 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
|
|||
["ContentSearch", "resource:///modules/ContentSearch.jsm"],
|
||||
["Deprecated", "resource://gre/modules/Deprecated.jsm"],
|
||||
["E10SUtils", "resource:///modules/E10SUtils.jsm"],
|
||||
["ExtensionsUI", "resource:///modules/ExtensionsUI.jsm"],
|
||||
["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
|
||||
["GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm"],
|
||||
["LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"],
|
||||
|
@ -1372,6 +1373,8 @@ var gBrowserInit = {
|
|||
|
||||
gMenuButtonUpdateBadge.init();
|
||||
|
||||
gExtensionsNotifications.init();
|
||||
|
||||
window.addEventListener("mousemove", MousePosTracker);
|
||||
window.addEventListener("dragover", MousePosTracker);
|
||||
|
||||
|
@ -1501,6 +1504,8 @@ var gBrowserInit = {
|
|||
|
||||
gFxAccounts.uninit();
|
||||
|
||||
gExtensionsNotifications.uninit();
|
||||
|
||||
Services.obs.removeObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");
|
||||
|
||||
try {
|
||||
|
@ -2576,10 +2581,12 @@ var gMenuButtonBadgeManager = {
|
|||
BADGEID_APPUPDATE: "update",
|
||||
BADGEID_DOWNLOAD: "download",
|
||||
BADGEID_FXA: "fxa",
|
||||
BADGEID_ADDONS: "addons",
|
||||
|
||||
fxaBadge: null,
|
||||
downloadBadge: null,
|
||||
appUpdateBadge: null,
|
||||
addonsBadge: null,
|
||||
|
||||
init() {
|
||||
PanelUI.panel.addEventListener("popupshowing", this, true);
|
||||
|
@ -2596,7 +2603,7 @@ var gMenuButtonBadgeManager = {
|
|||
},
|
||||
|
||||
_showBadge() {
|
||||
let badgeToShow = this.downloadBadge || this.appUpdateBadge || this.fxaBadge;
|
||||
let badgeToShow = this.downloadBadge || this.appUpdateBadge || this.fxaBadge || this.addonsBadge;
|
||||
|
||||
if (badgeToShow) {
|
||||
PanelUI.menuButton.setAttribute("badge-status", badgeToShow);
|
||||
|
@ -2612,6 +2619,8 @@ var gMenuButtonBadgeManager = {
|
|||
this.downloadBadge = badgeStatus;
|
||||
} else if (badgeId == this.BADGEID_FXA) {
|
||||
this.fxaBadge = badgeStatus;
|
||||
} else if (badgeId == this.BADGEID_ADDONS) {
|
||||
this.addonsBadge = badgeStatus;
|
||||
} else {
|
||||
Cu.reportError("The badge ID '" + badgeId + "' is unknown!");
|
||||
}
|
||||
|
|
|
@ -75,7 +75,8 @@
|
|||
<popupnotification id="addon-webext-permissions-notification" hidden="true">
|
||||
<popupnotificationcontent orient="vertical">
|
||||
<description id="addon-webext-perm-header" class="addon-webext-perm-header"/>
|
||||
<label id="addon-webext-perm-text" class="addon-webext-perm-text"/>
|
||||
<description id="addon-webext-perm-text" class="addon-webext-perm-text"/>
|
||||
<label id="addon-webext-perm-intro" class="addon-webext-perm-text"/>
|
||||
<html:ul id="addon-webext-perm-list" class="addon-webext-perm-list"/>
|
||||
</popupnotificationcontent>
|
||||
</popupnotification>
|
||||
|
|
|
@ -305,6 +305,7 @@ skip-if = os == "mac" # decoder doctor isn't implemented on osx
|
|||
[browser_drag.js]
|
||||
skip-if = true # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
|
||||
[browser_extension_permissions.js]
|
||||
[browser_extension_sideloading.js]
|
||||
[browser_favicon_change.js]
|
||||
[browser_favicon_change_not_in_document.js]
|
||||
[browser_findbarClose.js]
|
||||
|
|
|
@ -39,13 +39,8 @@ function promiseGetAddonByID(id) {
|
|||
function checkNotification(panel, url) {
|
||||
let icon = panel.getAttribute("icon");
|
||||
|
||||
let uls = panel.firstChild.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "ul");
|
||||
is(uls.length, 1, "Found the permissions list");
|
||||
let ul = uls[0];
|
||||
|
||||
let headers = panel.firstChild.getElementsByClassName("addon-webext-perm-text");
|
||||
is(headers.length, 1, "Found the header");
|
||||
let header = headers[0];
|
||||
let ul = document.getElementById("addon-webext-perm-list");
|
||||
let header = document.getElementById("addon-webext-perm-intro");
|
||||
|
||||
if (url == PERMS_XPI) {
|
||||
// The icon should come from the extension, don't bother with the precise
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
// MockAddon mimics the AddonInternal interface and MockProvider implements
|
||||
// just enough of the AddonManager provider interface to make it look like
|
||||
// we have sideloaded webextensions so the sideloading flow can be tested.
|
||||
|
||||
// MockAddon -> callback
|
||||
let setCallbacks = new Map();
|
||||
|
||||
class MockAddon {
|
||||
constructor(props) {
|
||||
this._userDisabled = false;
|
||||
this.pendingOperations = 0;
|
||||
this.type = "extension";
|
||||
|
||||
for (let name in props) {
|
||||
if (name == "userDisabled") {
|
||||
this._userDisabled = props[name];
|
||||
}
|
||||
this[name] = props[name];
|
||||
}
|
||||
}
|
||||
|
||||
markAsSeen() {
|
||||
this.seen = true;
|
||||
}
|
||||
|
||||
get userDisabled() {
|
||||
return this._userDisabled;
|
||||
}
|
||||
|
||||
set userDisabled(val) {
|
||||
this._userDisabled = val;
|
||||
let fn = setCallbacks.get(this);
|
||||
if (fn) {
|
||||
setCallbacks.delete(this);
|
||||
fn(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
get permissions() {
|
||||
return this._userDisabled ? AddonManager.PERM_CAN_ENABLE : AddonManager.PERM_CAN_DISABLE;
|
||||
}
|
||||
}
|
||||
|
||||
class MockProvider {
|
||||
constructor(...addons) {
|
||||
this.addons = new Set(addons);
|
||||
}
|
||||
|
||||
startup() { }
|
||||
shutdown() { }
|
||||
|
||||
getAddonByID(id, callback) {
|
||||
for (let addon of this.addons) {
|
||||
if (addon.id == id) {
|
||||
callback(addon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback(null);
|
||||
}
|
||||
|
||||
getAddonsByTypes(types, callback) {
|
||||
let addons = [];
|
||||
if (!types || types.includes("extension")) {
|
||||
addons = [...this.addons];
|
||||
}
|
||||
callback(addons);
|
||||
}
|
||||
}
|
||||
|
||||
function promiseViewLoaded(tab, viewid) {
|
||||
let win = tab.linkedBrowser.contentWindow;
|
||||
if (win.gViewController && !win.gViewController.isLoading &&
|
||||
win.gViewController.currentViewId == viewid) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
function listener() {
|
||||
if (win.gViewController.currentViewId != viewid) {
|
||||
return;
|
||||
}
|
||||
win.document.removeEventListener("ViewChanged", listener);
|
||||
resolve();
|
||||
}
|
||||
win.document.addEventListener("ViewChanged", listener);
|
||||
});
|
||||
}
|
||||
|
||||
function promisePopupNotificationShown(name) {
|
||||
return new Promise(resolve => {
|
||||
function popupshown() {
|
||||
let notification = PopupNotifications.getNotification(name);
|
||||
if (!notification) {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(notification, `${name} notification shown`);
|
||||
ok(PopupNotifications.isPanelOpen, "notification panel open");
|
||||
|
||||
PopupNotifications.panel.removeEventListener("popupshown", popupshown);
|
||||
resolve(PopupNotifications.panel.firstChild);
|
||||
}
|
||||
|
||||
PopupNotifications.panel.addEventListener("popupshown", popupshown);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseSetDisabled(addon) {
|
||||
return new Promise(resolve => {
|
||||
setCallbacks.set(addon, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
// XXX remove this when prompts are enabled by default
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
["extensions.webextPermissionPrompts", true],
|
||||
]});
|
||||
|
||||
const ID1 = "addon1@tests.mozilla.org";
|
||||
let mock1 = new MockAddon({
|
||||
id: ID1,
|
||||
name: "Test 1",
|
||||
userDisabled: true,
|
||||
seen: false,
|
||||
userPermissions: {
|
||||
permissions: ["history"],
|
||||
hosts: ["https://*/*"],
|
||||
},
|
||||
});
|
||||
|
||||
const ID2 = "addon2@tests.mozilla.org";
|
||||
let mock2 = new MockAddon({
|
||||
id: ID2,
|
||||
name: "Test 2",
|
||||
userDisabled: true,
|
||||
seen: false,
|
||||
userPermissions: {
|
||||
permissions: [],
|
||||
hosts: [],
|
||||
},
|
||||
});
|
||||
|
||||
let provider = new MockProvider(mock1, mock2);
|
||||
AddonManagerPrivate.registerProvider(provider, [{
|
||||
id: "extension",
|
||||
name: "Extensions",
|
||||
uiPriority: 4000,
|
||||
flags: AddonManager.TYPE_UI_VIEW_LIST |
|
||||
AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL,
|
||||
}]);
|
||||
registerCleanupFunction(function*() {
|
||||
AddonManagerPrivate.unregisterProvider(provider);
|
||||
|
||||
// clear out ExtensionsUI state about sideloaded extensions so
|
||||
// subsequent tests don't get confused.
|
||||
ExtensionsUI.sideloaded.clear();
|
||||
ExtensionsUI.emit("change");
|
||||
});
|
||||
|
||||
let changePromise = new Promise(resolve => {
|
||||
ExtensionsUI.on("change", function listener() {
|
||||
ExtensionsUI.off("change", listener);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
ExtensionsUI._checkForSideloaded();
|
||||
yield changePromise;
|
||||
|
||||
// Check for the addons badge on the hamburger menu
|
||||
let menuButton = document.getElementById("PanelUI-menu-button");
|
||||
is(menuButton.getAttribute("badge-status"), "addon-alert", "Should have addon alert badge");
|
||||
|
||||
// Find the menu entries for sideloaded extensions
|
||||
yield PanelUI.show();
|
||||
|
||||
let addons = document.getElementById("PanelUI-footer-addons");
|
||||
is(addons.children.length, 2, "Have 2 menu entries for sideloaded extensions");
|
||||
|
||||
// Click the first sideloaded extension
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
|
||||
let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
addons.children[0].click();
|
||||
|
||||
// about:addons should load and go to the list of extensions
|
||||
let tab = yield tabPromise;
|
||||
is(tab.linkedBrowser.currentURI.spec, "about:addons", "Newly opened tab is at about:addons");
|
||||
|
||||
const VIEW = "addons://list/extension";
|
||||
yield promiseViewLoaded(tab, VIEW);
|
||||
let win = tab.linkedBrowser.contentWindow;
|
||||
ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
|
||||
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
||||
|
||||
// Wait for the permission prompt and cancel it
|
||||
let panel = yield popupPromise;
|
||||
let disablePromise = promiseSetDisabled(mock1);
|
||||
panel.secondaryButton.click();
|
||||
|
||||
let value = yield disablePromise;
|
||||
is(value, true, "Addon should remain disabled");
|
||||
|
||||
let [addon1, addon2] = yield AddonManager.getAddonsByIDs([ID1, ID2]);
|
||||
ok(addon1.seen, "Addon should be marked as seen");
|
||||
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
||||
is(addon2.userDisabled, true, "Addon 2 should still be disabled");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// Should still have 1 entry in the hamburger menu
|
||||
yield PanelUI.show();
|
||||
|
||||
addons = document.getElementById("PanelUI-footer-addons");
|
||||
is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions");
|
||||
|
||||
// Click the second sideloaded extension
|
||||
tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
|
||||
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
addons.children[0].click();
|
||||
|
||||
tab = yield tabPromise;
|
||||
is(tab.linkedBrowser.currentURI.spec, "about:addons", "Newly opened tab is at about:addons");
|
||||
|
||||
isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
|
||||
|
||||
yield promiseViewLoaded(tab, VIEW);
|
||||
win = tab.linkedBrowser.contentWindow;
|
||||
ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
|
||||
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
||||
|
||||
// Wait for the permission prompt and accept it this time
|
||||
panel = yield popupPromise;
|
||||
disablePromise = promiseSetDisabled(mock2);
|
||||
panel.button.click();
|
||||
|
||||
value = yield disablePromise;
|
||||
is(value, false, "Addon should be set to enabled");
|
||||
|
||||
[addon1, addon2] = yield AddonManager.getAddonsByIDs([ID1, ID2]);
|
||||
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
||||
is(addon2.userDisabled, false, "Addon 2 should now be enabled");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
|
@ -16,6 +16,7 @@
|
|||
</vbox>
|
||||
|
||||
<footer id="PanelUI-footer">
|
||||
<vbox id="PanelUI-footer-addons"></vbox>
|
||||
<toolbarbutton id="PanelUI-update-status"
|
||||
oncommand="gMenuButtonUpdateBadge.onMenuPanelCommand(event);"
|
||||
wrap="true"
|
||||
|
|
|
@ -1061,27 +1061,6 @@ BrowserGlue.prototype = {
|
|||
this._showPlacesLockedNotificationBox();
|
||||
}
|
||||
|
||||
// For any add-ons that were installed disabled and can be enabled offer
|
||||
// them to the user.
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
AddonManager.getAllAddons(addons => {
|
||||
for (let addon of addons) {
|
||||
// If this add-on has already seen (or seen is undefined for non-XPI
|
||||
// add-ons) then skip it.
|
||||
if (addon.seen !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this add-on cannot be enabled (either already enabled or
|
||||
// appDisabled) then skip it.
|
||||
if (!(addon.permissions & AddonManager.PERM_CAN_ENABLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
win.openUILinkIn("about:newaddon?id=" + addon.id, "tab");
|
||||
}
|
||||
});
|
||||
|
||||
ExtensionsUI.init();
|
||||
|
||||
let signingRequired;
|
||||
|
|
|
@ -7,15 +7,86 @@ const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
|
|||
|
||||
this.EXPORTED_SYMBOLS = ["ExtensionsUI"];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://devtools/shared/event-emitter.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
|
||||
"extensions.webextPermissionPrompts", false);
|
||||
|
||||
const DEFAULT_EXENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
this.ExtensionsUI = {
|
||||
sideloaded: new Set(),
|
||||
|
||||
init() {
|
||||
Services.obs.addObserver(this, "webextension-permission-prompt", false);
|
||||
|
||||
this._checkForSideloaded();
|
||||
},
|
||||
|
||||
_checkForSideloaded() {
|
||||
AddonManager.getAllAddons(addons => {
|
||||
// Check for any side-loaded addons that the user is allowed
|
||||
// to enable.
|
||||
let sideloaded = addons.filter(
|
||||
addon => addon.seen === false && (addon.permissions & AddonManager.PERM_CAN_ENABLE));
|
||||
|
||||
if (!sideloaded.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (WEBEXT_PERMISSION_PROMPTS) {
|
||||
for (let addon of sideloaded) {
|
||||
this.sideloaded.add(addon);
|
||||
}
|
||||
this.emit("change");
|
||||
} else {
|
||||
// This and all the accompanying about:newaddon code can eventually
|
||||
// be removed. See bug 1331521.
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
for (let addon of sideloaded) {
|
||||
win.openUILinkIn(`about:newaddon?id=${addon.id}`, "tab");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
showSideloaded(browser, addon) {
|
||||
addon.markAsSeen();
|
||||
this.sideloaded.delete(addon);
|
||||
this.emit("change");
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
let listener = (subject, topic) => {
|
||||
if (subject.location.href == "about:addons") {
|
||||
Services.obs.removeObserver(listener, topic);
|
||||
resolve(subject);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(listener, "EM-loaded", false);
|
||||
});
|
||||
let tab = browser.addTab("about:addons");
|
||||
browser.selectedTab = tab;
|
||||
loadPromise.then(win => {
|
||||
win.loadView("addons://list/extension");
|
||||
let info = {
|
||||
addon,
|
||||
icon: addon.iconURL,
|
||||
type: "sideload",
|
||||
};
|
||||
this.showPermissionsPrompt(browser.selectedBrowser, info).then(answer => {
|
||||
addon.userDisabled = !answer;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
|
@ -57,8 +128,27 @@ this.ExtensionsUI = {
|
|||
// let header = bundle.getFormattedString("webextPerms.header", [name])
|
||||
// let listHeader = bundle.getString("webextPerms.listHeader");
|
||||
let header = "Add ADDON?".replace("ADDON", name);
|
||||
let text = "";
|
||||
let listHeader = "It can:";
|
||||
|
||||
// let acceptText = bundle.getString("webextPerms.accept.label");
|
||||
// let acceptKey = bundle.getString("webextPerms.accept.accessKey");
|
||||
// let cancelText = bundle.getString("webextPerms.cancel.label");
|
||||
// let cancelKey = bundle.getString("webextPerms.cancel.accessKey");
|
||||
let acceptText = "Add extension";
|
||||
let acceptKey = "A";
|
||||
let cancelText = "Cancel";
|
||||
let cancelKey = "C";
|
||||
|
||||
if (info.type == "sideload") {
|
||||
header = `${name} added`;
|
||||
text = "Another program on your computer installed an add-on that may affect your browser. Please review this add-on's permission requests and choose to Enable or Disable";
|
||||
acceptText = "Enable";
|
||||
acceptKey = "E";
|
||||
cancelText = "Disable";
|
||||
cancelKey = "D";
|
||||
}
|
||||
|
||||
let formatPermission = perm => {
|
||||
try {
|
||||
// return bundle.getString(`webextPerms.description.${perm}`);
|
||||
|
@ -94,15 +184,6 @@ this.ExtensionsUI = {
|
|||
...perms.hosts.map(formatHostPermission),
|
||||
];
|
||||
|
||||
// let acceptText = bundle.getString("webextPerms.accept.label");
|
||||
// let acceptKey = bundle.getString("webextPerms.accept.accessKey");
|
||||
// let cancelText = bundle.getString("webextPerms.cancel.label");
|
||||
// let cancelKey = bundle.getString("webextPerms.cancel.accessKey");
|
||||
let acceptText = "Add extension";
|
||||
let acceptKey = "A";
|
||||
let cancelText = "Cancel";
|
||||
let cancelKey = "C";
|
||||
|
||||
let rendered = false;
|
||||
let popupOptions = {
|
||||
hideClose: true,
|
||||
|
@ -124,7 +205,11 @@ this.ExtensionsUI = {
|
|||
list.firstChild.remove();
|
||||
}
|
||||
|
||||
let listHeaderEl = doc.getElementById("addon-webext-perm-text");
|
||||
if (text) {
|
||||
doc.getElementById("addon-webext-perm-text").textContent = text;
|
||||
}
|
||||
|
||||
let listHeaderEl = doc.getElementById("addon-webext-perm-intro");
|
||||
listHeaderEl.value = listHeader;
|
||||
listHeaderEl.hidden = (msgs.length == 0);
|
||||
|
||||
|
@ -162,3 +247,5 @@ this.ExtensionsUI = {
|
|||
});
|
||||
},
|
||||
};
|
||||
|
||||
EventEmitter.decorate(ExtensionsUI);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
|
||||
<style type="text/css">
|
||||
path {
|
||||
stroke: #bb3817;
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
<path d="M6.6,9c0.3,0,0.5-0.3,0.6-0.6C7,7.7,7,6.9,7.1,6.2c0.1-0.3,0.3-0.4,0.6-0.4c0.3,0,0.3,0.4,1,0.4
|
||||
c0.3,0,0.8-0.1,0.8-1.1S9,3.9,8.7,3.9c-0.6,0-0.7,0.5-1,0.5c-0.3,0-0.5-0.2-0.6-0.5c0-0.3,0-0.7,0-1c0-0.3-0.2-0.5-0.5-0.6
|
||||
c0,0,0,0-0.1,0c-0.5,0-1,0.1-1.6,0C4.7,2.3,4.5,2.1,4.6,1.8c0-0.4,0.4-0.3,0.4-1C5,0.5,4.9,0,3.8,0S2.7,0.5,2.7,0.8
|
||||
c0,0.6,0.5,0.7,0.5,1c0,0.3-0.2,0.5-0.5,0.5C2.1,2.4,1.6,2.4,1,2.4c-0.3,0-0.5,0.2-0.6,0.5c0,0,0,0,0,0.1v0.7c0,0-0.1,0.8,0.6,0.8
|
||||
C1.5,4.5,1.6,4,2.2,4c0.3,0,0.7,0.7,0.7,1.3S2.4,6.6,2.2,6.6C1.6,6.6,1.5,6,1.1,6C0.4,5.9,0.5,6.7,0.5,6.7v1.7C0.4,8.7,0.7,9,1,9
|
||||
c0,0,0,0,0,0h2.1C3.1,9,4,9,4,8.4c0-0.4-0.7-0.6-0.7-1.2C3.5,6.7,4,6.3,4.5,6.3c0.6,0,1.2,0.6,1.2,0.8c0,0.6-0.6,0.8-0.6,1.2
|
||||
C5.1,9,5.9,9,5.9,9L6.6,9z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.2 KiB |
|
@ -155,6 +155,11 @@
|
|||
filter: none;
|
||||
}
|
||||
|
||||
#PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
|
||||
height: 13px;
|
||||
background: transparent url(chrome://browser/skin/addons/addon-badge.svg) no-repeat center;
|
||||
}
|
||||
|
||||
.panel-subviews {
|
||||
padding: 4px;
|
||||
background-clip: padding-box;
|
||||
|
@ -561,7 +566,8 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
top: 25%;
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status]::after {
|
||||
#PanelUI-update-status[update-status]::after,
|
||||
#PanelUI-footer-addons > toolbarbutton::after {
|
||||
content: "";
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
@ -582,6 +588,24 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
background-color: #D90000;
|
||||
}
|
||||
|
||||
#PanelUI-footer-addons > toolbarbutton {
|
||||
background-color: #C7F5FF;
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
width: calc(@menuPanelWidth@ + 30px);
|
||||
padding-inline-start: 15px;
|
||||
border-inline-start-style: none;
|
||||
}
|
||||
|
||||
#PanelUI-footer-addons > toolbarbutton > .toolbarbutton-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
#PanelUI-footer-addons > toolbarbutton::after {
|
||||
background-image: url(chrome://browser/skin/addons/addon-badge.svg);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-status {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
|
@ -616,6 +640,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
#PanelUI-help,
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-fxa-icon,
|
||||
#PanelUI-footer-addons > toolbarbutton,
|
||||
#PanelUI-customize,
|
||||
#PanelUI-quit {
|
||||
margin: 0;
|
||||
|
@ -677,6 +702,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
}
|
||||
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-footer-addons > toolbarbutton,
|
||||
#PanelUI-customize {
|
||||
flex: 1;
|
||||
padding-inline-start: 15px;
|
||||
|
@ -841,6 +867,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-fxa-icon,
|
||||
#PanelUI-footer-addons > toolbarbutton,
|
||||
#PanelUI-customize,
|
||||
#PanelUI-help,
|
||||
#PanelUI-quit {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
skin/classic/browser/aboutTabCrashed.css (../shared/aboutTabCrashed.css)
|
||||
skin/classic/browser/aboutWelcomeBack.css (../shared/aboutWelcomeBack.css)
|
||||
skin/classic/browser/content-contextmenu.svg (../shared/content-contextmenu.svg)
|
||||
skin/classic/browser/addons/addon-badge.svg (../shared/addons/addon-badge.svg)
|
||||
skin/classic/browser/addons/addon-install-blocked.svg (../shared/addons/addon-install-blocked.svg)
|
||||
skin/classic/browser/addons/addon-install-confirm.svg (../shared/addons/addon-install-confirm.svg)
|
||||
skin/classic/browser/addons/addon-install-downloading.svg (../shared/addons/addon-install-downloading.svg)
|
||||
|
|
|
@ -89,7 +89,8 @@ const PROP_JSON_FIELDS = ["id", "syncGUID", "location", "version", "type",
|
|||
"softDisabled", "foreignInstall", "hasBinaryComponents",
|
||||
"strictCompatibility", "locales", "targetApplications",
|
||||
"targetPlatforms", "multiprocessCompatible", "signedState",
|
||||
"seen", "dependencies", "hasEmbeddedWebExtension", "mpcOptedOut"];
|
||||
"seen", "dependencies", "hasEmbeddedWebExtension", "mpcOptedOut",
|
||||
"userPermissions"];
|
||||
|
||||
// Properties that should be migrated where possible from an old database. These
|
||||
// shouldn't include properties that can be read directly from install.rdf files
|
||||
|
|
Загрузка…
Ссылка в новой задаче