Bug 1353194 Streamline the startup extension compatibility check r=kmag

Also extend activeAddons records with a started flag to avoid
double-starting extensions that are upgraded during the startup check.

MozReview-Commit-ID: FPX71Q3lSrw

--HG--
extra : rebase_source : 06b9be6748d09ddee310882c342e6b12cfedf91b
extra : source : 3977730d0f477e54631db184bcb24b13f83e328b
This commit is contained in:
Andrew Swan 2017-09-10 12:23:45 -07:00
Родитель c8710f9df8
Коммит 6e254181e6
13 изменённых файлов: 226 добавлений и 1001 удалений

Просмотреть файл

@ -1,65 +0,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/. -->
<!ENTITY updateWizard.title "&brandShortName; Update">
<!ENTITY offline.title "&brandShortName; is working offline">
<!ENTITY offline.description "&brandShortName; needs to go online in order to see if updates
are available for your add-ons to make them compatible with this
version.">
<!ENTITY offline.toggleOffline.label "Go online now.">
<!ENTITY offline.toggleOffline.accesskey "G">
<!ENTITY mismatch.win.title "Incompatible Add-ons">
<!ENTITY mismatch.top.label "The following add-ons are not compatible with this version of
&brandShortName; and have been disabled:">
<!ENTITY mismatch.bottom.label "&brandShortName; can check if there are compatible versions
of these add-ons available.">
<!ENTITY checking.wizard.title "Checking for Compatible Add-ons">
<!ENTITY checking.top.label "Checking your incompatible add-ons for updates…">
<!ENTITY checking.status "This may take a few minutes…">
<!ENTITY found.wizard.title "Found Compatible Add-ons">
<!ENTITY found.top.label "Select the add-ons you would like to install:">
<!ENTITY found.disabledXPinstall.label "These updates cant be installed because software installation is currently
disabled. You can change this setting below.">
<!ENTITY found.enableXPInstall.label "Allow websites to install software">
<!ENTITY found.enableXPInstall.accesskey "A">
<!ENTITY installing.wizard.title "Installing Compatible Add-ons">
<!ENTITY installing.top.label "Downloading and installing updates to your add-ons…">
<!ENTITY noupdates.wizard.title "No Compatible Add-ons Found">
<!ENTITY noupdates.intro.desc "&brandShortName; was unable to find updates to your
incompatible add-ons.">
<!ENTITY noupdates.error.desc "Some problems were encountered when trying to find updates.">
<!ENTITY noupdates.checkEnabled.desc "&brandShortName; will check periodically and inform you
when compatible updates for these add-ons are found.">
<!ENTITY finished.wizard.title "Compatible Add-ons Installed">
<!ENTITY finished.top.label "&brandShortName; has installed the updates to your add-ons.">
<!ENTITY finished.checkDisabled.desc "&brandShortName; can check periodically and inform you
when updates for add-ons are found.">
<!ENTITY finished.checkEnabled.desc "&brandShortName; will check periodically and inform you
when updates for add-ons are found.">
<!ENTITY adminDisabled.wizard.title "Unable to Check for Updates">
<!ENTITY adminDisabled.warning.label "It is not possible to check for updates to incompatible add-ons
because software installation for &brandShortName; has been disabled.
Please contact your System Administrator for assistance.">
<!ENTITY versioninfo.wizard.title "Checking Compatibility of Add-ons">
<!ENTITY versioninfo.top.label "Checking your add-ons for compatibility with this
version of &brandShortName;.">
<!ENTITY versioninfo.waiting "This may take a few minutes…">
<!ENTITY installerrors.wizard.title "Problems Installing Updates">
<!ENTITY installerrors.intro.label "&brandShortName; encountered problems when updating
some of your add-ons.">
<!-- general strings used by several of the finish pages -->
<!ENTITY clickFinish.label "Click Finish to continue starting &brandShortName;.">
<!ENTITY clickFinish.labelMac "Click Done to continue starting &brandShortName;.">
<!ENTITY enableChecking.label "Allow &brandShortName; to check for updates.">

Просмотреть файл

@ -2,20 +2,16 @@
# 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/.
mismatchCheckNow=Check Now
mismatchCheckNowAccesskey=C
mismatchDontCheck=Dont Check
mismatchDontCheckAccesskey=D
installButtonText=Install Now
installButtonTextAccesskey=I
nextButtonText=Next >
nextButtonTextAccesskey=N
cancelButtonText=Cancel
cancelButtonTextAccesskey=C
statusPrefix=Finished checking %S
downloadingPrefix=Downloading: %S
installingPrefix=Installing: %S
closeButton=Close
installErrors=%S was unable to install updates for the following add-ons:
checkingErrors=%S was unable to check for updates for the following add-ons:
installErrorItemFormat=%S (%S)
# LOCALIZATION NOTE (addonUpdateHeader)
# %S will be replace with the localized name of the application
addonUpdateTitle=%S Update
# LOCALIZATION NOTE (addonUpdateMessage)
# %S will be replace with the localized name of the application
addonUpdateMessage=%S is updating your extensions…
addonUpdateCancelMessage=Still updating. Want to wait?
# LOCALIZATION NOTE (addonUpdateCancelButton)
# %S will be replace with the localized name of the application
addonUpdateCancelButton=Stop update and launch %S

Просмотреть файл

@ -110,7 +110,6 @@
locale/@AB_CD@/mozapps/extensions/extensions.properties (%chrome/mozapps/extensions/extensions.properties)
locale/@AB_CD@/mozapps/extensions/blocklist.dtd (%chrome/mozapps/extensions/blocklist.dtd)
locale/@AB_CD@/mozapps/extensions/about.dtd (%chrome/mozapps/extensions/about.dtd)
locale/@AB_CD@/mozapps/extensions/update.dtd (%chrome/mozapps/extensions/update.dtd)
locale/@AB_CD@/mozapps/extensions/update.properties (%chrome/mozapps/extensions/update.properties)
locale/@AB_CD@/mozapps/extensions/newaddon.dtd (%chrome/mozapps/extensions/newaddon.dtd)
locale/@AB_CD@/mozapps/extensions/newaddon.properties (%chrome/mozapps/extensions/newaddon.properties)

Просмотреть файл

@ -0,0 +1,26 @@
body {
font: message-box;
min-width: 480px;
}
#message {
font-size: 14px;
}
#message, #cancel-section {
margin: 10px 5px;
}
#progress {
width: calc(100% - 10px);
margin: 0 5px;
}
#cancel-section {
display: flex;
justify-content: space-between;
}
#cancel-message {
vertical-align: middle;
}

Просмотреть файл

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="update.js"></script>
<link rel="stylesheet" href="chrome://mozapps/content/extensions/update.css">
</head>
<body>
<div>
<div id="message"></div>
<progress id="progress" val="0" max="1"></progress>
<div id="cancel-section">
<span id="cancel-message"></span>
<button id="cancel-btn"></button>
</div>
</div>
</body>
</html>

Просмотреть файл

@ -1,633 +1,24 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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/. */
// This UI is only opened from the Extension Manager when the app is upgraded.
"use strict";
/* exported gAdminDisabledPage, gFinishedPage, gFoundPage, gInstallErrorsPage,
* gNoUpdatesPage, gOfflinePage, gUpdatePage */
Components.utils.import("resource://gre/modules/Services.jsm");
const PREF_UPDATE_EXTENSIONS_ENABLED = "extensions.update.enabled";
const PREF_XPINSTALL_ENABLED = "xpinstall.enabled";
let BRAND_PROPS = "chrome://branding/locale/brand.properties";
let UPDATE_PROPS = "chrome://mozapps/locale/extensions/update.properties";
// timeout (in milliseconds) to wait for response to the metadata ping
const METADATA_TIMEOUT = 30000;
let appName = Services.strings.createBundle(BRAND_PROPS)
.GetStringFromName("brandShortName");
let bundle = Services.strings.createBundle(UPDATE_PROPS);
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", "resource://gre/modules/addons/AddonRepository.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/Log.jsm");
var logger = null;
let titleText = bundle.formatStringFromName("addonUpdateTitle", [appName], 1);
let messageText = bundle.formatStringFromName("addonUpdateMessage", [appName], 1);
let cancelText = bundle.GetStringFromName("addonUpdateCancelMessage");
let cancelButtonText = bundle.formatStringFromName("addonUpdateCancelButton", [appName], 1);
var gUpdateWizard = {
// When synchronizing app compatibility info this contains all installed
// add-ons. When checking for compatible versions this contains only
// incompatible add-ons.
addons: [],
// Contains a Set of IDs for add-on that were disabled by the application update.
affectedAddonIDs: null,
// The add-ons that we found updates available for
addonsToUpdate: [],
shouldSuggestAutoChecking: false,
shouldAutoCheck: false,
xpinstallEnabled: true,
xpinstallLocked: false,
// cached AddonInstall entries for add-ons we might want to update,
// keyed by add-on ID
addonInstalls: new Map(),
shuttingDown: false,
// Count the add-ons disabled by this update, enabled/disabled by
// metadata checks, and upgraded.
disabled: 0,
metadataEnabled: 0,
metadataDisabled: 0,
upgraded: 0,
upgradeFailed: 0,
upgradeDeclined: 0,
document.title = titleText;
init() {
logger = Log.repository.getLogger("addons.update-dialog");
// XXX could we pass the addons themselves rather than the IDs?
this.affectedAddonIDs = new Set(window.arguments[0]);
try {
this.shouldSuggestAutoChecking =
!Services.prefs.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
} catch (e) {
}
try {
this.xpinstallEnabled = Services.prefs.getBoolPref(PREF_XPINSTALL_ENABLED);
this.xpinstallLocked = Services.prefs.prefIsLocked(PREF_XPINSTALL_ENABLED);
} catch (e) {
}
if (Services.io.offline)
document.documentElement.currentPage = document.getElementById("offline");
else
document.documentElement.currentPage = document.getElementById("versioninfo");
},
onWizardFinish: function gUpdateWizard_onWizardFinish() {
if (this.shouldSuggestAutoChecking)
Services.prefs.setBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED, this.shouldAutoCheck);
},
_setUpButton(aButtonID, aButtonKey, aDisabled) {
var strings = document.getElementById("updateStrings");
var button = document.documentElement.getButton(aButtonID);
if (aButtonKey) {
button.label = strings.getString(aButtonKey);
try {
button.setAttribute("accesskey", strings.getString(aButtonKey + "Accesskey"));
} catch (e) {
}
}
button.disabled = aDisabled;
},
setButtonLabels(aBackButton, aBackButtonIsDisabled,
aNextButton, aNextButtonIsDisabled,
aCancelButton, aCancelButtonIsDisabled) {
this._setUpButton("back", aBackButton, aBackButtonIsDisabled);
this._setUpButton("next", aNextButton, aNextButtonIsDisabled);
this._setUpButton("cancel", aCancelButton, aCancelButtonIsDisabled);
},
// Update Errors
errorItems: [],
checkForErrors(aElementIDToShow) {
if (this.errorItems.length > 0)
document.getElementById(aElementIDToShow).hidden = false;
},
onWizardClose(aEvent) {
return this.onWizardCancel();
},
onWizardCancel() {
gUpdateWizard.shuttingDown = true;
// Allow add-ons to continue downloading and installing
// in the background, though some may require a later restart
// Pages that are waiting for user input go into the background
// on cancel
if (gMismatchPage.waiting) {
logger.info("Dialog closed in mismatch page");
if (gUpdateWizard.addonInstalls.size > 0) {
gInstallingPage.startInstalls(
Array.from(gUpdateWizard.addonInstalls.values()));
}
return true;
}
// Pages that do asynchronous things will just keep running and check
// gUpdateWizard.shuttingDown to trigger background behaviour
if (!gInstallingPage.installing) {
logger.info("Dialog closed while waiting for updated compatibility information");
} else {
logger.info("Dialog closed while downloading and installing updates");
}
return true;
}
};
var gOfflinePage = {
onPageAdvanced() {
Services.io.offline = false;
return true;
},
toggleOffline() {
var nextbtn = document.documentElement.getButton("next");
nextbtn.disabled = !nextbtn.disabled;
}
}
// Addon listener to count addons enabled/disabled by metadata checks
var listener = {
onDisabled(aAddon) {
gUpdateWizard.affectedAddonIDs.add(aAddon.id);
gUpdateWizard.metadataDisabled++;
},
onEnabled(aAddon) {
gUpdateWizard.affectedAddonIDs.delete(aAddon.id);
gUpdateWizard.metadataEnabled++;
}
};
var gVersionInfoPage = {
_completeCount: 0,
_totalCount: 0,
_versionInfoDone: false,
async onPageShow() {
gUpdateWizard.setButtonLabels(null, true,
"nextButtonText", true,
"cancelButtonText", false);
gUpdateWizard.disabled = gUpdateWizard.affectedAddonIDs.size;
// Ensure compatibility overrides are up to date before checking for
// individual addon updates.
AddonManager.addAddonListener(listener);
if (AddonRepository.isMetadataStale()) {
// Do the metadata ping, listening for any newly enabled/disabled add-ons.
await AddonRepository.repopulateCache(METADATA_TIMEOUT);
if (gUpdateWizard.shuttingDown) {
logger.debug("repopulateCache completed after dialog closed");
}
}
// Fetch the add-ons that are still affected by this update,
// excluding the hotfix add-on.
let idlist = Array.from(gUpdateWizard.affectedAddonIDs).filter(
a => a.id != AddonManager.hotfixID);
if (idlist.length < 1) {
gVersionInfoPage.onAllUpdatesFinished();
return;
}
logger.debug("Fetching affected addons " + idlist.toSource());
let fetchedAddons = await AddonManager.getAddonsByIDs(idlist);
// We shouldn't get nulls here, but let's be paranoid...
gUpdateWizard.addons = fetchedAddons.filter(a => a);
if (gUpdateWizard.addons.length < 1) {
gVersionInfoPage.onAllUpdatesFinished();
return;
}
gVersionInfoPage._totalCount = gUpdateWizard.addons.length;
for (let addon of gUpdateWizard.addons) {
logger.debug("VersionInfo Finding updates for ${id}", addon);
addon.findUpdates(gVersionInfoPage, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
}
},
onAllUpdatesFinished() {
AddonManager.removeAddonListener(listener);
AddonManagerPrivate.recordSimpleMeasure("appUpdate_disabled",
gUpdateWizard.disabled);
AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_enabled",
gUpdateWizard.metadataEnabled);
AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_disabled",
gUpdateWizard.metadataDisabled);
// Record 0 for these here in case we exit early; values will be replaced
// later if we actually upgrade any.
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded", 0);
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed", 0);
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined", 0);
// Filter out any add-ons that are now enabled.
let addonList = gUpdateWizard.addons.map(a => a.id + ":" + a.appDisabled);
logger.debug("VersionInfo updates finished: found " + addonList.toSource());
let filteredAddons = [];
for (let a of gUpdateWizard.addons) {
if (a.appDisabled) {
logger.debug("Continuing with add-on " + a.id);
filteredAddons.push(a);
} else if (gUpdateWizard.addonInstalls.has(a.id)) {
gUpdateWizard.addonInstalls.get(a.id).cancel();
gUpdateWizard.addonInstalls.delete(a.id);
}
}
gUpdateWizard.addons = filteredAddons;
if (gUpdateWizard.shuttingDown) {
// jump directly to updating auto-update add-ons in the background
if (gUpdateWizard.addonInstalls.size > 0) {
let installs = Array.from(gUpdateWizard.addonInstalls.values());
gInstallingPage.startInstalls(installs);
}
return;
}
if (filteredAddons.length > 0) {
if (!gUpdateWizard.xpinstallEnabled && gUpdateWizard.xpinstallLocked) {
document.documentElement.currentPage = document.getElementById("adminDisabled");
return;
}
document.documentElement.currentPage = document.getElementById("mismatch");
} else {
logger.info("VersionInfo: No updates require further action");
// VersionInfo compatibility updates resolved all compatibility problems,
// close this window and continue starting the application...
// XXX Bug 314754 - We need to use setTimeout to close the window due to
// the EM using xmlHttpRequest when checking for updates.
setTimeout(close, 0);
}
},
// UpdateListener
onUpdateFinished(aAddon, status) {
++this._completeCount;
if (status != AddonManager.UPDATE_STATUS_NO_ERROR) {
logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
" failed for " + aAddon.id + ": " + status);
gUpdateWizard.errorItems.push(aAddon);
} else {
logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
" finished for " + aAddon.id);
}
// If we're not in the background, just make a list of add-ons that have
// updates available
if (!gUpdateWizard.shuttingDown) {
// If we're still in the update check window and the add-on is now active
// then it won't have been disabled by startup
if (aAddon.active) {
AddonManagerPrivate.removeStartupChange(AddonManager.STARTUP_CHANGE_DISABLED, aAddon.id);
gUpdateWizard.metadataEnabled++;
}
// Update the status text and progress bar
var updateStrings = document.getElementById("updateStrings");
var statusElt = document.getElementById("versioninfo.status");
var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
statusElt.setAttribute("value", statusString);
// Update the status text and progress bar
var progress = document.getElementById("versioninfo.progress");
progress.mode = "normal";
progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
}
if (this._completeCount == this._totalCount)
this.onAllUpdatesFinished();
},
onUpdateAvailable(aAddon, aInstall) {
logger.debug("VersionInfo got an install for " + aAddon.id + ": " + aAddon.version);
gUpdateWizard.addonInstalls.set(aAddon.id, aInstall);
},
};
var gMismatchPage = {
waiting: false,
onPageShow() {
gMismatchPage.waiting = true;
gUpdateWizard.setButtonLabels(null, true,
"mismatchCheckNow", false,
"mismatchDontCheck", false);
document.documentElement.getButton("next").focus();
var incompatible = document.getElementById("mismatch.incompatible");
for (let addon of gUpdateWizard.addons) {
var listitem = document.createElement("listitem");
listitem.setAttribute("label", addon.name + " " + addon.version);
incompatible.appendChild(listitem);
}
}
};
var gUpdatePage = {
_totalCount: 0,
_completeCount: 0,
onPageShow() {
gMismatchPage.waiting = false;
gUpdateWizard.setButtonLabels(null, true,
"nextButtonText", true,
"cancelButtonText", false);
document.documentElement.getButton("next").focus();
gUpdateWizard.errorItems = [];
this._totalCount = gUpdateWizard.addons.length;
for (let addon of gUpdateWizard.addons) {
logger.debug("UpdatePage requesting update for " + addon.id);
// Redundant call to find updates again here when we already got them
// in the VersionInfo page: https://bugzilla.mozilla.org/show_bug.cgi?id=960597
addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
}
},
onAllUpdatesFinished() {
if (gUpdateWizard.shuttingDown)
return;
var nextPage = document.getElementById("noupdates");
if (gUpdateWizard.addonsToUpdate.length > 0)
nextPage = document.getElementById("found");
document.documentElement.currentPage = nextPage;
},
// UpdateListener
onUpdateAvailable(aAddon, aInstall) {
logger.debug("UpdatePage got an update for " + aAddon.id + ": " + aAddon.version);
gUpdateWizard.addonsToUpdate.push(aInstall);
},
onUpdateFinished(aAddon, status) {
if (status != AddonManager.UPDATE_STATUS_NO_ERROR)
gUpdateWizard.errorItems.push(aAddon);
++this._completeCount;
if (!gUpdateWizard.shuttingDown) {
// Update the status text and progress bar
var updateStrings = document.getElementById("updateStrings");
var statusElt = document.getElementById("checking.status");
var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
statusElt.setAttribute("value", statusString);
var progress = document.getElementById("checking.progress");
progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
}
if (this._completeCount == this._totalCount)
this.onAllUpdatesFinished()
},
};
var gFoundPage = {
onPageShow() {
gUpdateWizard.setButtonLabels(null, true,
"installButtonText", false,
null, false);
var foundUpdates = document.getElementById("found.updates");
for (let install of gUpdateWizard.addonsToUpdate) {
let listItem = foundUpdates.appendItem(install.name + " " + install.version);
listItem.setAttribute("type", "checkbox");
listItem.setAttribute("checked", "true");
listItem.install = install;
}
if (!gUpdateWizard.xpinstallEnabled) {
document.getElementById("xpinstallDisabledAlert").hidden = false;
document.getElementById("enableXPInstall").focus();
document.documentElement.getButton("next").disabled = true;
} else {
document.documentElement.getButton("next").focus();
document.documentElement.getButton("next").disabled = false;
}
},
toggleXPInstallEnable(aEvent) {
var enabled = aEvent.target.checked;
gUpdateWizard.xpinstallEnabled = enabled;
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
pref.setBoolPref(PREF_XPINSTALL_ENABLED, enabled);
this.updateNextButton();
},
updateNextButton() {
if (!gUpdateWizard.xpinstallEnabled) {
document.documentElement.getButton("next").disabled = true;
return;
}
var oneChecked = false;
var foundUpdates = document.getElementById("found.updates");
var updates = foundUpdates.getElementsByTagName("listitem");
for (let update of updates) {
if (!update.checked)
continue;
oneChecked = true;
break;
}
gUpdateWizard.setButtonLabels(null, true,
"installButtonText", true,
null, false);
document.getElementById("found").setAttribute("next", "installing");
document.documentElement.getButton("next").disabled = !oneChecked;
}
};
var gInstallingPage = {
_installs: [],
_errors: [],
_strings: null,
_currentInstall: -1,
_installing: false,
// Initialize fields we need for installing and tracking progress,
// and start iterating through the installations
startInstalls(aInstallList) {
if (!gUpdateWizard.xpinstallEnabled) {
return;
}
let installs = Array.from(aInstallList).map(a => a.existingAddon.id);
logger.debug("Start installs for " + installs.toSource());
this._errors = [];
this._installs = aInstallList;
this._installing = true;
this.startNextInstall();
},
onPageShow() {
gUpdateWizard.setButtonLabels(null, true,
"nextButtonText", true,
null, true);
var foundUpdates = document.getElementById("found.updates");
var updates = foundUpdates.getElementsByTagName("listitem");
let toInstall = [];
for (let update of updates) {
if (!update.checked) {
logger.info("User chose to cancel update of " + update.label);
gUpdateWizard.upgradeDeclined++;
update.install.cancel();
continue;
}
toInstall.push(update.install);
}
this._strings = document.getElementById("updateStrings");
this.startInstalls(toInstall);
},
startNextInstall() {
if (this._currentInstall >= 0) {
this._installs[this._currentInstall].removeListener(this);
}
this._currentInstall++;
if (this._installs.length == this._currentInstall) {
Services.obs.notifyObservers(null, "TEST:all-updates-done");
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded",
gUpdateWizard.upgraded);
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed",
gUpdateWizard.upgradeFailed);
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined",
gUpdateWizard.upgradeDeclined);
this._installing = false;
if (gUpdateWizard.shuttingDown) {
return;
}
var nextPage = this._errors.length > 0 ? "installerrors" : "finished";
document.getElementById("installing").setAttribute("next", nextPage);
document.documentElement.advance();
return;
}
let install = this._installs[this._currentInstall];
if (gUpdateWizard.shuttingDown && !AddonManager.shouldAutoUpdate(install.existingAddon)) {
logger.debug("Don't update " + install.existingAddon.id + " in background");
gUpdateWizard.upgradeDeclined++;
install.cancel();
this.startNextInstall();
return;
}
install.addListener(this);
install.install();
},
// InstallListener
onDownloadStarted(aInstall) {
if (gUpdateWizard.shuttingDown) {
return;
}
var strings = document.getElementById("updateStrings");
var label = strings.getFormattedString("downloadingPrefix", [aInstall.name]);
var actionItem = document.getElementById("actionItem");
actionItem.value = label;
},
onDownloadProgress(aInstall) {
if (gUpdateWizard.shuttingDown) {
return;
}
var downloadProgress = document.getElementById("downloadProgress");
downloadProgress.value = Math.ceil(100 * aInstall.progress / aInstall.maxProgress);
},
onDownloadEnded(aInstall) {
},
onDownloadFailed(aInstall) {
this._errors.push(aInstall);
gUpdateWizard.upgradeFailed++;
this.startNextInstall();
},
onInstallStarted(aInstall) {
if (gUpdateWizard.shuttingDown) {
return;
}
var strings = document.getElementById("updateStrings");
var label = strings.getFormattedString("installingPrefix", [aInstall.name]);
var actionItem = document.getElementById("actionItem");
actionItem.value = label;
},
onInstallEnded(aInstall, aAddon) {
if (!gUpdateWizard.shuttingDown) {
// Remember that this add-on was updated during startup
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
aAddon.id);
}
gUpdateWizard.upgraded++;
this.startNextInstall();
},
onInstallFailed(aInstall) {
this._errors.push(aInstall);
gUpdateWizard.upgradeFailed++;
this.startNextInstall();
}
};
var gInstallErrorsPage = {
onPageShow() {
gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
document.documentElement.getButton("finish").focus();
},
};
// Displayed when there are incompatible add-ons and the xpinstall.enabled
// pref is false and locked.
var gAdminDisabledPage = {
onPageShow() {
gUpdateWizard.setButtonLabels(null, true, null, true,
"cancelButtonText", true);
document.documentElement.getButton("finish").focus();
}
};
// Displayed when selected add-on updates have been installed without error.
// There can still be add-ons that are not compatible and don't have an update.
var gFinishedPage = {
onPageShow() {
gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
document.documentElement.getButton("finish").focus();
if (gUpdateWizard.shouldSuggestAutoChecking) {
document.getElementById("finishedCheckDisabled").hidden = false;
gUpdateWizard.shouldAutoCheck = true;
} else
document.getElementById("finishedCheckEnabled").hidden = false;
document.documentElement.getButton("finish").focus();
}
};
// Displayed when there are incompatible add-ons and there are no available
// updates.
var gNoUpdatesPage = {
onPageShow(aEvent) {
gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
if (gUpdateWizard.shouldSuggestAutoChecking) {
document.getElementById("noupdatesCheckDisabled").hidden = false;
gUpdateWizard.shouldAutoCheck = true;
} else
document.getElementById("noupdatesCheckEnabled").hidden = false;
gUpdateWizard.checkForErrors("updateCheckErrorNotFound");
document.documentElement.getButton("finish").focus();
}
};
window.addEventListener("load", e => {
document.getElementById("message").textContent = messageText;
document.getElementById("cancel-message").textContent = cancelText;
document.getElementById("cancel-btn").textContent = cancelButtonText;
window.sizeToContent();
});

Просмотреть файл

@ -1,194 +0,0 @@
<?xml version="1.0"?>
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# 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/.
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://mozapps/skin/extensions/update.css" type="text/css"?>
<!DOCTYPE wizard [
<!ENTITY % updateDTD SYSTEM "chrome://mozapps/locale/extensions/update.dtd">
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%updateDTD;
%brandDTD;
]>
<wizard id="updateWizard"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&updateWizard.title;"
windowtype="Addons:Compatibility"
branded="true"
onload="gUpdateWizard.init();"
onwizardfinish="gUpdateWizard.onWizardFinish();"
onwizardcancel="return gUpdateWizard.onWizardCancel();"
onclose="return gUpdateWizard.onWizardClose(event);"
buttons="accept,cancel">
<script type="application/javascript" src="chrome://mozapps/content/extensions/update.js"/>
<stringbundleset id="updateSet">
<stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
<stringbundle id="updateStrings" src="chrome://mozapps/locale/extensions/update.properties"/>
</stringbundleset>
<wizardpage id="dummy" pageid="dummy"/>
<wizardpage id="offline" pageid="offline" next="versioninfo"
label="&offline.title;"
onpageadvanced="return gOfflinePage.onPageAdvanced();">
<description>&offline.description;</description>
<checkbox id="toggleOffline"
checked="true"
label="&offline.toggleOffline.label;"
accesskey="&offline.toggleOffline.accesskey;"
oncommand="gOfflinePage.toggleOffline();"/>
</wizardpage>
<wizardpage id="versioninfo" pageid="versioninfo" next="mismatch"
label="&versioninfo.wizard.title;"
onpageshow="gVersionInfoPage.onPageShow();">
<label>&versioninfo.top.label;</label>
<separator class="thin"/>
<progressmeter id="versioninfo.progress" mode="undetermined"/>
<hbox align="center">
<image id="versioninfo.throbber" class="throbber"/>
<label flex="1" id="versioninfo.status" crop="right">&versioninfo.waiting;</label>
</hbox>
<separator/>
</wizardpage>
<wizardpage id="mismatch" pageid="mismatch" next="checking"
label="&mismatch.win.title;"
onpageshow="gMismatchPage.onPageShow();">
<label>&mismatch.top.label;</label>
<separator class="thin"/>
<listbox id="mismatch.incompatible" flex="1"/>
<separator class="thin"/>
<label>&mismatch.bottom.label;</label>
</wizardpage>
<wizardpage id="checking" pageid="checking" next="noupdates"
label="&checking.wizard.title;"
onpageshow="gUpdatePage.onPageShow();">
<label>&checking.top.label;</label>
<separator class="thin"/>
<progressmeter id="checking.progress"/>
<hbox align="center">
<image id="checking.throbber" class="throbber"/>
<label id="checking.status" flex="1" crop="right">&checking.status;</label>
</hbox>
</wizardpage>
<wizardpage id="noupdates" pageid="noupdates"
label="&noupdates.wizard.title;"
onpageshow="gNoUpdatesPage.onPageShow();">
<description>&noupdates.intro.desc;</description>
<separator class="thin"/>
<hbox id="updateCheckErrorNotFound" class="alertBox" hidden="true" align="top">
<description flex="1">&noupdates.error.desc;</description>
</hbox>
<separator class="thin"/>
<description id="noupdatesCheckEnabled" hidden="true">
&noupdates.checkEnabled.desc;
</description>
<vbox id="noupdatesCheckDisabled" hidden="true">
<description>&finished.checkDisabled.desc;</description>
<checkbox label="&enableChecking.label;" checked="true"
oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
</vbox>
<separator flex="1"/>
#ifndef XP_MACOSX
<label>&clickFinish.label;</label>
#else
<label>&clickFinish.labelMac;</label>
#endif
<separator class="thin"/>
</wizardpage>
<wizardpage id="found" pageid="found" next="installing"
label="&found.wizard.title;"
onpageshow="gFoundPage.onPageShow();">
<label>&found.top.label;</label>
<separator class="thin"/>
<listbox id="found.updates" flex="1" seltype="multiple"
onclick="gFoundPage.updateNextButton();"/>
<separator class="thin"/>
<vbox align="left" id="xpinstallDisabledAlert" hidden="true">
<description>&found.disabledXPinstall.label;</description>
<checkbox label="&found.enableXPInstall.label;"
id="enableXPInstall"
accesskey="&found.enableXPInstall.accesskey;"
oncommand="gFoundPage.toggleXPInstallEnable(event);"/>
</vbox>
</wizardpage>
<wizardpage id="installing" pageid="installing" next="finished"
label="&installing.wizard.title;"
onpageshow="gInstallingPage.onPageShow();">
<label>&installing.top.label;</label>
<progressmeter id="downloadProgress"/>
<hbox align="center">
<image id="installing.throbber" class="throbber"/>
<label id="actionItem" flex="1" crop="right"/>
</hbox>
<separator/>
</wizardpage>
<wizardpage id="installerrors" pageid="installerrors"
label="&installerrors.wizard.title;"
onpageshow="gInstallErrorsPage.onPageShow();">
<hbox align="top" class="alertBox">
<description flex="1">&installerrors.intro.label;</description>
</hbox>
<separator flex="1"/>
#ifndef XP_MACOSX
<label>&clickFinish.label;</label>
#else
<label>&clickFinish.labelMac;</label>
#endif
<separator class="thin"/>
</wizardpage>
<wizardpage id="adminDisabled" pageid="adminDisabled"
label="&adminDisabled.wizard.title;"
onpageshow="gAdminDisabledPage.onPageShow();">
<separator/>
<hbox class="alertBox" align="top">
<description flex="1">&adminDisabled.warning.label;</description>
</hbox>
<separator flex="1"/>
#ifndef XP_MACOSX
<label>&clickFinish.label;</label>
#else
<label>&clickFinish.labelMac;</label>
#endif
<separator class="thin"/>
</wizardpage>
<wizardpage id="finished" pageid="finished"
label="&finished.wizard.title;"
onpageshow="gFinishedPage.onPageShow();">
<label>&finished.top.label;</label>
<separator/>
<description id="finishedCheckEnabled" hidden="true">
&finished.checkEnabled.desc;
</description>
<vbox id="finishedCheckDisabled" hidden="true">
<description>&finished.checkDisabled.desc;</description>
<checkbox label="&enableChecking.label;" checked="true"
oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
</vbox>
<separator flex="1"/>
#ifndef XP_MACOSX
<label>&clickFinish.label;</label>
#else
<label>&clickFinish.labelMac;</label>
#endif
<separator class="thin"/>
</wizardpage>
</wizard>

Просмотреть файл

@ -36,6 +36,8 @@ XPCOMUtils.defineLazyModuleGetters(this, {
isAddonPartOfE10SRollout: "resource://gre/modules/addons/E10SAddonsRollout.jsm",
JSONFile: "resource://gre/modules/JSONFile.jsm",
LegacyExtensionsUtils: "resource://gre/modules/LegacyExtensionsUtils.jsm",
setTimeout: "resource://gre/modules/Timer.jsm",
clearTimeout: "resource://gre/modules/Timer.jsm",
DownloadAddonInstall: "resource://gre/modules/addons/XPIInstall.jsm",
LocalAddonInstall: "resource://gre/modules/addons/XPIInstall.jsm",
@ -107,7 +109,6 @@ const OBSOLETE_PREFERENCES = [
"extensions.installCache",
];
const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
const DIR_EXTENSIONS = "extensions";
@ -772,6 +773,20 @@ function canRunInSafeMode(aAddon) {
return aAddon._installLocation.isSystem;
}
/**
* Determine if this addon should be disabled due to being legacy
*
* @param {Addon} addon The addon to check
*
* @returns {boolean} Whether the addon should be disabled for being legacy
*/
function isDisabledLegacy(addon) {
return (!AddonSettings.ALLOW_LEGACY_EXTENSIONS &&
LEGACY_TYPES.has(addon.type) &&
!addon._installLocation.isSystem &&
addon.signedState !== AddonManager.SIGNEDSTATE_PRIVILEGED);
}
/**
* Calculates whether an add-on should be appDisabled or not.
*
@ -828,9 +843,7 @@ function isUsableAddon(aAddon) {
return false;
}
if (!AddonSettings.ALLOW_LEGACY_EXTENSIONS && LEGACY_TYPES.has(aAddon.type) &&
!aAddon._installLocation.isSystem &&
aAddon.signedState !== AddonManager.SIGNEDSTATE_PRIVILEGED) {
if (isDisabledLegacy(aAddon)) {
logger.warn(`disabling legacy extension ${aAddon.id}`);
return false;
}
@ -2160,10 +2173,11 @@ this.XPIProvider = {
AddonManagerPrivate.markProviderSafe(this);
if (aAppChanged && !this.allAppGlobal &&
Services.prefs.getBoolPref(PREF_EM_SHOW_MISMATCH_UI, true)) {
Services.prefs.getBoolPref(PREF_EM_SHOW_MISMATCH_UI, true) &&
AddonManager.updateEnabled) {
let addonsToUpdate = this.shouldForceUpdateCheck(aAppChanged);
if (addonsToUpdate) {
this.showUpgradeUI(addonsToUpdate);
this.noLegacyStartupCheck(addonsToUpdate);
flushCaches = true;
}
}
@ -2195,6 +2209,12 @@ this.XPIProvider = {
AddonManagerPrivate.recordTimestamp("XPI_bootstrap_addons_begin");
for (let addon of this.sortBootstrappedAddons()) {
// The startup update check above may have already started some
// extensions, make sure not to try to start them twice.
let activeAddon = this.activeAddons.get(addon.id);
if (activeAddon && activeAddon.started) {
continue;
}
try {
let reason = BOOTSTRAP_REASONS.APP_STARTUP;
// Eventually set INSTALLED reason when a bootstrap addon
@ -2415,9 +2435,8 @@ this.XPIProvider = {
* application directory then we may need to synchronize compatibility
* information but only if the mismatch UI isn't disabled.
*
* @returns False if no update check is needed, otherwise an array of add-on
* IDs to check for updates. Array may be empty if no add-ons can be/need
* to be updated, but the metadata check needs to be performed.
* @returns null if no update check is needed, otherwise an array of add-on
* IDs to check for updates.
*/
shouldForceUpdateCheck(aAppChanged) {
AddonManagerPrivate.recordSimpleMeasure("XPIDB_metadata_age", AddonRepository.metadataAge());
@ -2432,49 +2451,132 @@ this.XPIProvider = {
for (let addon of addons) {
if ((startupChanges.indexOf(addon.id) != -1) &&
(addon.permissions() & AddonManager.PERM_CAN_UPGRADE) &&
!addon.isCompatible) {
(!addon.isCompatible || isDisabledLegacy(addon))) {
logger.debug("shouldForceUpdateCheck: can upgrade disabled add-on " + addon.id);
forceUpdate.push(addon.id);
}
}
}
if (AddonRepository.isMetadataStale()) {
logger.debug("shouldForceUpdateCheck: metadata is stale");
return forceUpdate;
}
if (forceUpdate.length > 0) {
return forceUpdate;
}
return false;
return null;
},
/**
* Shows the "Compatibility Updates" UI.
* Perform startup check for updates of legacy extensions.
* This runs during startup when an app update has made some add-ons
* incompatible and legacy add-on support is diasabled.
* In this case, we just do a quiet update check.
*
* @param aAddonIDs
* Array opf addon IDs that were disabled by the application update, and
* should therefore be checked for updates.
* @param {Array<string>} ids The ids of the addons to check for updates.
*
* @returns {Set<string>} The ids of any addons that were updated. These
* addons will have been started by the update
* process so they should not be started by the
* regular bootstrap startup code.
*/
showUpgradeUI(aAddonIDs) {
logger.debug("XPI_showUpgradeUI: " + aAddonIDs.toSource());
Services.telemetry.getHistogramById("ADDON_MANAGER_UPGRADE_UI_SHOWN").add(1);
noLegacyStartupCheck(ids) {
let started = new Set();
const DIALOG = "chrome://mozapps/content/extensions/update.html";
const SHOW_DIALOG_DELAY = 1000;
const SHOW_CANCEL_DELAY = 30000;
// Flip a flag to indicate that we interrupted startup with an interactive prompt
Services.startup.interrupted = true;
// Keep track of a value between 0 and 1 indicating the progress
// for each addon. Just combine these linearly into a single
// value for the progress bar in the update dialog.
let updateProgress = val => {};
let progressByID = new Map();
function setProgress(id, val) {
progressByID.set(id, val);
updateProgress(Array.from(progressByID.values()).reduce((a, b) => a + b) / progressByID.size);
}
var variant = Cc["@mozilla.org/variant;1"].
createInstance(Ci.nsIWritableVariant);
variant.setFromVariant(aAddonIDs);
// Do an update check for one addon and try to apply the update if
// there is one. Progress for the check is arbitrarily defined as
// 10% done when the update check is done, between 10-90% during the
// download, then 100% when the update has been installed.
let checkOne = async (id) => {
logger.debug(`Checking for updates to disabled addon ${id}\n`);
// This *must* be modal as it has to block startup.
var features = "chrome,centerscreen,dialog,titlebar,modal";
var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
setProgress(id, 0);
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
let addon = await AddonManager.getAddonByID(id);
let install = await new Promise(resolve => addon.findUpdates({
onUpdateFinished() { resolve(null); },
onUpdateAvailable(addon, install) { resolve(install); },
}, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED));
if (!install) {
setProgress(id, 1);
return;
}
setProgress(id, 0.1);
let installPromise = new Promise(resolve => {
let finish = () => {
setProgress(id, 1);
resolve();
};
install.addListener({
onDownloadProgress() {
if (install.maxProgress != 0) {
setProgress(id, 0.1 + 0.8 * install.progress / install.maxProgress);
}
},
onDownloadEnded() {
setProgress(id, 0.9);
},
onDownloadFailed: finish,
onInstallFailed: finish,
onInstallEnded() {
started.add(id);
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED, id);
finish();
},
});
});
install.install();
await installPromise;
};
let finished = false;
Promise.all(ids.map(checkOne)).then(() => { finished = true; });
let window;
let timer = setTimeout(() => {
const FEATURES = "chrome,dialog,centerscreen,scrollbars=no";
window = Services.ww.openWindow(null, DIALOG, "", FEATURES, null);
let cancelDiv;
window.addEventListener("DOMContentLoaded", e => {
let progress = window.document.getElementById("progress");
updateProgress = val => { progress.value = val; };
cancelDiv = window.document.getElementById("cancel-section");
cancelDiv.setAttribute("style", "display: none;");
let cancelBtn = window.document.getElementById("cancel-btn");
cancelBtn.addEventListener("click", e => { finished = true; });
});
timer = setTimeout(() => {
cancelDiv.removeAttribute("style");
window.sizeToContent();
}, SHOW_CANCEL_DELAY - SHOW_DIALOG_DELAY);
}, SHOW_DIALOG_DELAY);
Services.tm.spinEventLoopUntil(() => finished);
clearTimeout(timer);
if (window) {
window.close();
}
return started;
},
async updateSystemAddons() {
@ -4181,6 +4283,7 @@ this.XPIProvider = {
bootstrapScope: null,
// a Symbol passed to this add-on, which it can use to identify itself
instanceID: Symbol(aId),
started: false,
});
// Mark the add-on as active for the crash reporter before loading
@ -4337,12 +4440,18 @@ this.XPIProvider = {
// That will be logged below.
}
// Extensions are automatically deinitialized in the correct order at shutdown.
if (aMethod == "shutdown" && aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
activeAddon.disable = true;
for (let addon of this.getDependentAddons(aAddon)) {
if (addon.active)
this.updateAddonDisabledState(addon);
if (aMethod == "startup") {
activeAddon.started = true;
} else if (aMethod == "shutdown") {
activeAddon.started = false;
// Extensions are automatically deinitialized in the correct order at shutdown.
if (aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
activeAddon.disable = true;
for (let addon of this.getDependentAddons(aAddon)) {
if (addon.active)
this.updateAddonDisabledState(addon);
}
}
}

Просмотреть файл

@ -16,8 +16,9 @@ toolkit.jar:
content/mozapps/extensions/blocklist.js (content/blocklist.js)
content/mozapps/extensions/blocklist.css (content/blocklist.css)
content/mozapps/extensions/blocklist.xml (content/blocklist.xml)
* content/mozapps/extensions/update.xul (content/update.xul)
content/mozapps/extensions/update.html (content/update.html)
content/mozapps/extensions/update.js (content/update.js)
content/mozapps/extensions/update.css (content/update.css)
content/mozapps/extensions/eula.xul (content/eula.xul)
content/mozapps/extensions/eula.js (content/eula.js)
content/mozapps/extensions/newaddon.xul (content/newaddon.xul)

Просмотреть файл

@ -1,28 +0,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/. */
.throbber {
list-style-image: url("chrome://global/skin/icons/loading.png");
width: 16px;
height: 16px;
margin-top: 5px;
margin-bottom: 5px;
margin-inline-start: 5px;
margin-inline-end: 2px;
}
@media (min-resolution: 2dppx) {
.throbber {
list-style-image: url("chrome://global/skin/icons/loading@2x.png");
}
}
.alertBox {
background-color: InfoBackground;
color: InfoText;
border: 1px outset InfoBackground;
margin-left: 3px;
margin-right: 3px;
padding: 5px;
}

Просмотреть файл

@ -15,7 +15,6 @@ toolkit.jar:
skin/classic/mozapps/extensions/heart.png (extensions/heart.png)
skin/classic/mozapps/extensions/about.css (extensions/about.css)
* skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
skin/classic/mozapps/extensions/update.css (extensions/update.css)
skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/mozapps/extensions/blocklist.css (extensions/blocklist.css)
* skin/classic/mozapps/extensions/newaddon.css (extensions/newaddon.css)

Просмотреть файл

@ -61,7 +61,6 @@
skin/classic/mozapps/downloads/unknownContentType.css (../../windows/mozapps/downloads/unknownContentType.css)
skin/classic/mozapps/extensions/about.css (../../windows/mozapps/extensions/about.css)
skin/classic/mozapps/extensions/blocklist.css (../../windows/mozapps/extensions/blocklist.css)
skin/classic/mozapps/extensions/update.css (../../windows/mozapps/extensions/update.css)
skin/classic/mozapps/extensions/discover-logo.png (../../windows/mozapps/extensions/discover-logo.png)
skin/classic/mozapps/extensions/rating-won.png (../../windows/mozapps/extensions/rating-won.png)
skin/classic/mozapps/extensions/rating-not-won.png (../../windows/mozapps/extensions/rating-not-won.png)

Просмотреть файл

@ -1,28 +0,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/. */
.throbber {
list-style-image: url("chrome://global/skin/icons/loading.png");
width: 16px;
height: 16px;
margin-top: 5px;
margin-bottom: 5px;
margin-inline-start: 5px;
margin-inline-end: 2px;
}
@media (min-resolution: 1.1dppx) {
.throbber {
list-style-image: url("chrome://global/skin/icons/loading@2x.png");
}
}
.alertBox {
background-color: InfoBackground;
color: InfoText;
border: 1px outset InfoBackground;
margin-left: 3px;
margin-right: 3px;
padding: 5px;
}