Bug 562622 - Implementation of the automatic vs. manual update design mockups. r=dtownsend, a=blocking2.0-beta3

This commit is contained in:
Blair McBride 2010-06-02 21:13:03 +12:00
Родитель 96bb9a2ce3
Коммит 148aadce27
21 изменённых файлов: 1270 добавлений и 35 удалений

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

@ -2,6 +2,9 @@
<!ENTITY search.placeholder "Search all add-ons">
<!ENTITY loading.label "Loading…">
<!ENTITY listEmpty.installed.label "You don't have any add-ons of this type installed">
<!ENTITY listEmpty.availableUpdates.label "No updates found">
<!ENTITY listEmpty.recentUpdates.label "You haven't recently updated any add-ons">
<!ENTITY listEmpty.findUpdates.label "Check For Updates">
<!ENTITY listEmpty.search.label "Could not find any matching add-ons">
<!ENTITY listEmpty.button.label "Learn more about add-ons">
@ -15,14 +18,22 @@
<!ENTITY view.features.label "Extensions">
<!ENTITY view.appearance.label "Themes">
<!ENTITY view.plugins.label "Plugins">
<!ENTITY view.recentUpdates.label "Recent Updates">
<!ENTITY view.availableUpdates.label "Available Updates">
<!-- addon updates -->
<!ENTITY updates.updateNow.label "Update add-ons">
<!ENTITY updates.updateAddonsNow.label "Update Add-ons Now">
<!ENTITY updates.updateAddonsNow.accesskey "U">
<!ENTITY updates.viewUpdates.label "View Recent Updates">
<!ENTITY updates.viewUpdates.accesskey "V">
<!ENTITY updates.backgroudUpdateCheck.label "Check for Updates Automatically">
<!ENTITY updates.backgroudUpdateCheck.accesskey "C">
<!ENTITY updates.updating.label "Updating add-ons">
<!ENTITY updates.installed.label "Your add-ons have been updated.">
<!ENTITY updates.downloaded.label "Your add-on updates have been downloaded.">
<!ENTITY updates.restart.label "Restart now to complete installation">
<!ENTITY updates.noneFound.label "No updates found">
<!ENTITY updates.manualUpdatesFound.label "View Available Updates">
<!-- addon actions -->
<!ENTITY cmd.showDetails.label "Show more information">
@ -46,6 +57,10 @@
<!ENTITY cmd.contribute.accesskey "C">
<!ENTITY cmd.contribute.tooltip "Contribute to the development of this add-on">
<!ENTITY cmd.showReleaseNotes.label "Show Release Notes">
<!ENTITY cmd.showReleaseNotes.tooltip "Show the release notes for this update">
<!ENTITY cmd.hideReleaseNotes.label "Hide Release Notes">
<!ENTITY cmd.hideReleaseNotes.tooltip "Hide the release notes for this update">
<!-- detail view -->
<!ENTITY detail.version.label "Version">
@ -96,6 +111,7 @@
<!ENTITY addon.homepage "Homepage">
<!ENTITY addon.unknownDate "Unknown">
<!ENTITY addon.disabled.postfix "(disabled)">
<!ENTITY addon.update.postfix "Update">
<!ENTITY addon.undo.label "Undo?">
<!ENTITY addon.undo.tooltip "Undo this action">
<!ENTITY addon.undoUninstall.label "Undo?">
@ -104,6 +120,11 @@
<!ENTITY addon.restartNow.tooltip "Restart now to complete installation">
<!ENTITY addon.install.label "Install">
<!ENTITY addon.install.tooltip "Install this add-on">
<!ENTITY addon.updateNow.label "Update Now">
<!ENTITY addon.updateNow.tooltip "Install the update for this add-on">
<!ENTITY addon.checkingForUpdates.label "Checking for updates…">
<!ENTITY addon.releaseNotes.label "Release Notes:">
<!ENTITY addon.loadingReleaseNotes.label "Loading…">
<!ENTITY addon.errorLoadingReleaseNotes.label "Sorry, but there was an error loading the release notes.">
<!ENTITY addon.createdBy.label "By ">

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

@ -14,6 +14,8 @@ header-searchengine=Search Engines
header-extension=Extensions
header-theme=Themes
header-plugin=Plugins
header-recentUpdates=Recent Updates
header-availableUpdates=Available Updates
#LOCALIZATION NOTE (header-goBack) %S is the name of the pane to go back to
header-goBack=Back to %S

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

@ -92,6 +92,14 @@
visibility: collapse;
}
.addon:not([upgrade="true"]) .update-postfix {
display: none;
}
#detail-view[loading] > .detail-view-container {
display: none;
}
.view-pane:not(#updates-view) .addon .relnotes-toggle {
display: none;
}

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

@ -50,6 +50,7 @@ Cu.import("resource://gre/modules/AddonRepository.jsm");
const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
const PREF_MAXRESULTS = "extensions.getAddons.maxResults";
const PREF_BACKGROUND_UPDATE = "extensions.update.enabled";
const LOADING_MSG_DELAY = 100;
@ -61,6 +62,11 @@ const SEARCH_SCORE_MATCH_WHOLEWORD = 10;
const SEARCH_SCORE_MATCH_WORDBOUNDRY = 6;
const SEARCH_SCORE_MATCH_SUBSTRING = 3;
const UPDATES_RECENT_TIMESPAN = 2 * 24 * 3600000; // 2 days (in milliseconds)
const UPDATES_RELEASENOTES_TRANSFORMFILE = "chrome://mozapps/content/extensions/updateinfo.xsl";
const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
const VIEW_DEFAULT = "addons://list/extension";
const INTEGER_FIELDS = ["dateUpdated", "size", "relevancescore"];
@ -145,8 +151,8 @@ var gEventManager = {
var self = this;
["onEnabling", "onEnabled", "onDisabling", "onDisabled", "onUninstalling",
"onUninstalled", "onInstalled", "onOperationCancelled",
"onUpdateAvailable", "onUpdateFinished",
"onCompatibilityUpdateAvailable"].forEach(function(aEvent) {
"onUpdateAvailable", "onUpdateFinished", "onCompatibilityUpdateAvailable",
"onPropertyChanged"].forEach(function(aEvent) {
self[aEvent] = function() {
self.delegateAddonEvent(aEvent, Array.splice(arguments, 0));
};
@ -262,6 +268,7 @@ var gViewController = {
this.viewObjects["discover"] = gDiscoverView;
this.viewObjects["list"] = gListView;
this.viewObjects["detail"] = gDetailView;
this.viewObjects["updates"] = gUpdatesView;
for each (let view in this.viewObjects)
view.initialize();
@ -273,6 +280,17 @@ var gViewController = {
if (this.currentViewObj)
this.currentViewObj.hide();
this.currentViewRequest = 0;
for each(let view in this.viewObjects) {
if ("shutdown" in view) {
try {
view.shutdown();
} catch(e) {
// this shouldn't be fatal
Cu.reportError(e);
}
}
}
},
parseViewId: function(aViewId) {
@ -352,6 +370,28 @@ var gViewController = {
}
},
cmd_goToRecentUpdates: {
isEnabled: function() true,
doCommand: function() {
gViewController.loadView("addons://updates/recent");
}
},
cmd_goToAvailableUpdates: {
isEnabled: function() true,
doCommand: function() {
gViewController.loadView("addons://updates/available");
}
},
cmd_toggleBackgroundUpdateCheck: {
isEnabled: function() true,
doCommand: function() {
var enabled = !Services.prefs.getBoolPref(PREF_BACKGROUND_UPDATE);
Services.prefs.setBoolPref(PREF_BACKGROUND_UPDATE, enabled);
}
},
cmd_showItemDetails: {
isEnabled: function(aAddon) {
return !!aAddon;
@ -363,23 +403,36 @@ var gViewController = {
},
cmd_findAllUpdates: {
isEnabled: function() true,
inProgress: false,
isEnabled: function() !this.inProgress,
doCommand: function() {
document.getElementById("updates-checkNow").hidden = true;
this.inProgress = true;
gViewController.updateCommand("cmd_findAllUpdates");
document.getElementById("updates-noneFound").hidden = true;
document.getElementById("updates-progress").hidden = false;
document.getElementById("updates-manualUpdatesFound").hidden = true;
var pendingChecks = 0;
var numUpdated = 0;
var numManualUpdates = 0;
var restartNeeded = false;
var self = this;
function updateStatus() {
if (pendingChecks > 0)
return;
self.inProgress = false;
gViewController.updateCommand("cmd_findAllUpdates");
document.getElementById("updates-progress").hidden = true;
gUpdatesView.maybeRefresh();
if (numManualUpdates > 0 && numUpdated == 0) {
document.getElementById("updates-manualUpdatesFound").hidden = false;
return;
}
if (numUpdated == 0) {
document.getElementById("updates-checkNow").hidden = false;
document.getElementById("updates-noneFound").hidden = false;
return;
}
@ -414,8 +467,14 @@ var gViewController = {
onUpdateAvailable: function(aAddon, aInstall) {
gEventManager.delegateAddonEvent("onUpdateAvailable",
[aAddon, aInstall]);
aInstall.addListener(updateInstallListener);
aInstall.install();
if (aAddon.applyBackgroundUpdates !== false) {
aInstall.addListener(updateInstallListener);
aInstall.install();
} else {
pendingChecks--;
numManualUpdates++;
updateStatus();
}
},
onNoUpdateAvailable: function(aAddon) {
pendingChecks--;
@ -453,7 +512,8 @@ var gViewController = {
onUpdateAvailable: function(aAddon, aInstall) {
gEventManager.delegateAddonEvent("onUpdateAvailable",
[aAddon, aInstall]);
aInstall.install();
if (aAddon.applyBackgroundUpdates !== false)
aInstall.install();
},
onNoUpdateAvailable: function(aAddon) {
gEventManager.delegateAddonEvent("onNoUpdateAvailable",
@ -596,17 +656,22 @@ var gViewController = {
if (!this.currentViewObj)
return;
var addon = this.currentViewObj.getSelectedAddon();
for (let commandId in this.commands) {
let cmd = this.commands[commandId];
let cmdElt = document.getElementById(commandId);
cmdElt.setAttribute("disabled", !cmd.isEnabled(addon));
if ("getTooltip" in cmd) {
let tooltip = cmd.getTooltip(addon);
if (tooltip)
cmdElt.setAttribute("tooltiptext", tooltip);
else
cmdElt.removeAttribute("tooltiptext");
}
for (let commandId in this.commands)
this.updateCommand(commandId, addon);
},
updateCommand: function(aCommandId, aAddon) {
if (typeof aAddon == "undefined")
aAddon = this.currentViewObj.getSelectedAddon();
var cmd = this.commands[aCommandId];
var cmdElt = document.getElementById(aCommandId);
cmdElt.setAttribute("disabled", !cmd.isEnabled(aAddon));
if ("getTooltip" in cmd) {
let tooltip = cmd.getTooltip(aAddon);
if (tooltip)
cmdElt.setAttribute("tooltiptext", tooltip);
else
cmdElt.removeAttribute("tooltiptext");
}
},
@ -648,6 +713,11 @@ function isPending(aAddon, aAction) {
return !!(aAddon.pendingOperations & action);
}
function isInState(aInstall, aState) {
var state = AddonManager["STATE_" + aState.toUpperCase()];
return aInstall.state == state;
}
function createItem(aObj, aIsInstall, aRequiresRestart, aIsRemote) {
let item = document.createElement("richlistitem");
@ -807,6 +877,7 @@ var gCategories = {
if (item) {
item.hidden = false;
item.disabled = false;
this.node.suppressOnSelect = true;
this.node.selectedItem = item;
this.node.suppressOnSelect = false;
@ -1355,12 +1426,14 @@ var gDetailView = {
_notificationContainer: null,
_notificationText: null,
_autoUpdate: null,
initialize: function() {
this.node = document.getElementById("detail-view");
this._notificationContainer = document.getElementById("detail-notification");
this._notificationText = document.getElementById("detail-notification-text");
this._autoUpdate = document.getElementById("detail-autoUpdate");
var self = this;
var autoUpdate = document.getElementById("detail-autoUpdate");
@ -1414,9 +1487,9 @@ var gDetailView = {
desc.textContent = aAddon.fullDescription ? aAddon.fullDescription
: aAddon.description;
document.getElementById("detail-autoUpdate").checked = aAddon.applyBackgroundUpdates;
self._autoUpdate.checked = aAddon.applyBackgroundUpdates;
var canUpdate = hasPermission(aAddon, "upgrade");
document.getElementById("detail-autoUpdate").hidden = !canUpdate;
self._autoUpdate.hidden = !canUpdate;
document.getElementById("detail-findUpdates").hidden = !canUpdate;
document.getElementById("detail-prefs").hidden = !aAddon.optionsURL;
@ -1504,6 +1577,277 @@ var gDetailView = {
onOperationCancelled: function() {
this.updateState();
},
onPropertyChanged: function(aProperties) {
if (aProperties.indexOf("applyBackgroundUpdates") != -1) {
this._autoUpdate.checked = this._addon.applyBackgroundUpdates;
}
}
};
var gUpdatesView = {
node: null,
_listBox: null,
_emptyNotice: null,
_sorters: null,
_updatePrefs: null,
_backgroundUpdateCheck: null,
_categoryItem: null,
_numManualUpdaters: 0,
initialize: function() {
this.node = document.getElementById("updates-view");
this._listBox = document.getElementById("updates-list");
this._emptyNotice = document.getElementById("updates-list-empty");
this._sorters = document.getElementById("updates-sorters");
this._sorters.handler = this;
this._backgroundUpdateCheck = document.getElementById("utils-backgroudUpdateCheck");
this._categoryItem = gCategories.get("addons://updates/available");
this._updatePrefs = Services.prefs.getBranch("extensions.update.");
this._updatePrefs.QueryInterface(Ci.nsIPrefBranch2);
this._updatePrefs.addObserver("", this, false);
this.updateBackgroundCheck();
this.updateManualUpdatersCount(true);
this.updateAvailableCount(true);
AddonManager.addAddonListener(this);
AddonManager.addInstallListener(this);
},
shutdown: function() {
AddonManager.removeAddonListener(this);
AddonManager.removeInstallListener(this);
this._updatePrefs.removeObserver("", this);
delete this._updatePrefs;
},
show: function(aType, aRequest) {
gHeader.setName(gStrings.ext.GetStringFromName("header-" + aType + "Updates"));
document.getElementById("empty-availableUpdates-msg").hidden = aType != "available";
document.getElementById("empty-recentUpdates-msg").hidden = aType != "recent";
this.showEmptyNotice(false);
while (this._listBox.itemCount > 0)
this._listBox.removeItemAt(0);
if (aType == "recent")
this._showRecentUpdates(aRequest);
else
this._showAvailableUpdates(false, aRequest);
},
hide: function() {
// do nothing
},
_showRecentUpdates: function(aRequest) {
var self = this;
AddonManager.getAllAddons(function(aAddonsList) {
if (gViewController && aRequest != gViewController.currentViewRequest)
return;
let threshold = Date.now() - UPDATES_RECENT_TIMESPAN;
aAddonsList.forEach(function(aAddon) {
if (!aAddon.updateDate || aAddon.updateDate.getTime() < threshold)
return;
let item = createItem(aAddon);
self._listBox.appendChild(item);
});
if (self._listBox.itemCount > 0)
self.onSortChanged(self._sorters.sortBy, self._sorters.ascending);
else
self.showEmptyNotice(true);
gViewController.notifyViewChanged();
});
},
_showAvailableUpdates: function(aIsRefresh, aRequest) {
var self = this;
AddonManager.getAllInstalls(function(aInstallsList) {
if (!aIsRefresh && gViewController && aRequest != gViewController.currentViewRequest)
return;
if (aIsRefresh) {
while (self._listBox.itemCount > 0)
self._listBox.removeItemAt(0);
}
aInstallsList.forEach(function(aInstall) {
if (!self.isManualUpdate(aInstall))
return;
let item = createItem(aInstall.existingAddon);
item.setAttribute("upgrade", true);
self._listBox.appendChild(item);
});
if (self._listBox.itemCount > 0)
self.onSortChanged(self._sorters.sortBy, self._sorters.ascending);
else
self.showEmptyNotice(true);
// ensure badge count is in sync
self._categoryItem.badgeCount = self._listBox.itemCount;
if (!aIsRefresh)
gViewController.notifyViewChanged();
});
},
getSelectedAddon: function() {
return null;
},
showEmptyNotice: function(aShow) {
this._emptyNotice.hidden = !aShow;
},
isManualUpdate: function(aInstall, aOnlyAvailable) {
var isManual = aInstall.existingAddon &&
aInstall.existingAddon.applyBackgroundUpdates === false;
if (isManual && aOnlyAvailable)
return isInState(aInstall, "available");
return isManual;
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "nsPref:changed" && aData == "enabled")
this.updateBackgroundCheck();
},
updateBackgroundCheck: function() {
let isEnabled = this._updatePrefs.getBoolPref("enabled");
this._backgroundUpdateCheck.setAttribute("checked", isEnabled);
},
maybeRefresh: function() {
if (gViewController.currentViewId == "addons://updates/available") {
this._showAvailableUpdates(true);
} else {
this.updateManualUpdatersCount();
this.updateAvailableCount();
}
},
maybeShowCategory: function() {
var hide = this._numManualUpdaters == 0;
if (this._categoryItem.disabled != hide) {
this._categoryItem.disabled = hide;
var event = document.createEvent("Events");
event.initEvent("CategoryVisible", true, true);
this._categoryItem.dispatchEvent(event);
}
},
updateManualUpdatersCount: function(aInitializing) {
if (aInitializing)
gPendingInitializations++;
var self = this;
AddonManager.getAllAddons(function(aAddonList) {
var manualUpdaters = aAddonList.filter(function(aAddon) {
return aAddon.applyBackgroundUpdates === false;
});
self._numManualUpdaters = manualUpdaters.length;
self.maybeShowCategory();
if (aInitializing)
notifyInitialized();
});
},
updateAvailableCount: function(aInitializing) {
if (aInitializing)
gPendingInitializations++;
var self = this;
AddonManager.getAllInstalls(function(aInstallsList) {
var count = aInstallsList.filter(function(aInstall) {
return self.isManualUpdate(aInstall, true);
}).length;
self._categoryItem.badgeCount = count;
if (aInitializing)
notifyInitialized();
});
},
getSelectedAddon: function() {
var item = this._listBox.selectedItem;
if (item)
return item.mAddon;
return null;
},
getListItemForID: function(aId) {
var listitem = this._listBox.firstChild;
while (listitem) {
if (listitem.mAddon.id == aId)
return listitem;
listitem = listitem.nextSibling;
}
return null;
},
onSortChanged: function(aSortBy, aAscending) {
var hints = aAscending ? "ascending" : "descending";
if (INTEGER_FIELDS.indexOf(aSortBy) >= 0)
hints += " integer";
var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
getService(Ci.nsIXULSortService);
sortService.sort(this._listBox, aSortBy, hints);
},
onNewInstall: function(aInstall) {
if (!this.isManualUpdate(aInstall))
return;
this.maybeRefresh();
},
onExternalInstall: function(aAddon) {
if (aAddon.applyBackgroundUpdates === false) {
this._numManualUpdaters++;
this.maybeShowCategory();
}
},
onDownloadStarted: function(aInstall) {
if (!this.isManualUpdate(aInstall))
return;
this.maybeRefresh();
},
onInstallStarted: function(aInstall) {
if (!this.isManualUpdate(aInstall))
return;
this.maybeRefresh();
},
onInstallEnded: function(aAddon) {
if (aAddon.applyBackgroundUpdates === false) {
this._numManualUpdaters++;
this.maybeShowCategory();
}
},
onApplyBackgroundUpdatesChanged: function(aAddon) {
if (!("applyBackgroundUpdates" in aAddon))
return;
if (aAddon.applyBackgroundUpdates)
this._numManualUpdaters--;
else
this._numManualUpdaters++;
this.maybeShowCategory();
},
onPropertyChanged: function(aAddon, aProperties) {
if (aProperties.indexOf("applyBackgroundUpdates") != -1)
this.onApplyBackgroundUpdatesChanged(aAddon);
}
};

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

@ -399,7 +399,13 @@
return this.getAttribute("count");
]]></getter>
<setter><![CDATA[
this.setAttribute("count", aCount);
if (this.getAttribute("count") == val)
return;
this.setAttribute("count", val);
var event = document.createEvent("Events");
event.initEvent("CategoryBadgeUpdated", true, true);
this.dispatchEvent(event);
]]></setter>
</property>
</implementation>
@ -736,7 +742,7 @@
</binding>
<!-- Addon - generic - A normal addon item -->
<!-- Addon - generic - A normal addon item, or an update to one -->
<binding id="addon-generic"
extends="chrome://mozapps/content/extensions/extensions.xml#addon-base">
<content>
@ -744,14 +750,30 @@
<xul:image anonid="icon" class="icon"/>
<xul:spacer flex="1"/>
</xul:vbox>
<xul:vbox flex="1" class="fade">
<xul:vbox flex="1" class="basicinfo-container fade">
<xul:hbox class="name-container">
<xul:label anonid="name" class="name" xbl:inherits="value=name"/>
<xul:label anonid="version" class="version"/>
<xul:label class="disabled-postfix" value="&addon.disabled.postfix;"/>
<xul:label class="update-postfix" value="&addon.update.postfix;"/>
</xul:hbox>
<xul:label anonid="creator" class="creator"/>
<xul:description anonid="description" class="description" flex="1"/>
<xul:vbox anonid="relnotes-container" class="relnotes-container">
<xul:label class="relnotes-header" value="&addon.releaseNotes.label;"/>
<xul:label anonid="relnotes-loading" value="&addon.loadingReleaseNotes.label;"/>
<xul:label anonid="relnotes-error" hidden="true"
value="&addon.errorLoadingReleaseNotes.label;"/>
<xul:vbox anonid="relnotes"/>
</xul:vbox>
<xul:button anonid="relnotes-toggle" class="relnotes-toggle"
hidden="true" label="&cmd.showReleaseNotes.label;"
tooltiptext="&cmd.showReleaseNotes.tooltip;"
showlabel="&cmd.showReleaseNotes.label;"
showtooltip="&cmd.showReleaseNotes.tooltip;"
hidelabel="&cmd.hideReleaseNotes.label;"
hidetooltip="&cmd.hideReleaseNotes.tooltip;"
oncommand="document.getBindingParent(this).toggleReleaseNotes();"/>
</xul:vbox>
<xul:vbox align="end">
<xul:vbox class="details-container fade">
@ -803,6 +825,13 @@
<xul:image class="spinner"/>
<xul:label value="&addon.checkingForUpdates.label;"/>
</xul:hbox>
<xul:hbox anonid="update-available" hidden="true">
<xul:label value="An update is available"/>
<xul:button anonid="update-btn" class="addon-control"
label="&addon.updateNow.label;"
tooltiptext="&addon.updateNow.tooltip;"
oncommand="document.getBindingParent(this).upgrade();"/>
</xul:hbox>
<xul:hbox anonid="install-status" class="install-status"
hidden="true"/>
</xul:hbox>
@ -853,6 +882,20 @@
if (numExtraDetails == 0)
this._toggleMore.hidden = true;
if (!this.mAddon.applyBackgroundUpdates) {
var self = this;
AddonManager.getAllInstalls(function(aInstallsList) {
for (let i = 0; i < aInstallsList.length; i++) {
let install = aInstallsList[i];
if (install.existingAddon &&
install.existingAddon.id == self.mAddon.id &&
install.state == AddonManager.STATE_AVAILABLE) {
self.onNewInstall(install);
}
}
});
}
gEventManager.registerAddonListener(this, this.mAddon.id);
]]></constructor>
@ -918,6 +961,10 @@
document.getAnonymousElementByAttribute(this, "anonid",
"remove-btn");
</field>
<field name="_updateBtn">
document.getAnonymousElementByAttribute(this, "anonid",
"update-btn");
</field>
<field name="_controlContainer">
document.getAnonymousElementByAttribute(this, "anonid",
"control-container");
@ -930,6 +977,31 @@
document.getAnonymousElementByAttribute(this, "anonid",
"checking-update");
</field>
<field name="_updateAvailable">
document.getAnonymousElementByAttribute(this, "anonid",
"update-available");
</field>
<field name="_relNotesLoaded">false</field>
<field name="_relNotesToggle">
document.getAnonymousElementByAttribute(this, "anonid",
"relnotes-toggle");
</field>
<field name="_relNotesLoading">
document.getAnonymousElementByAttribute(this, "anonid",
"relnotes-loading");
</field>
<field name="_relNotesError">
document.getAnonymousElementByAttribute(this, "anonid",
"relnotes-error");
</field>
<field name="_relNotesContainer">
document.getAnonymousElementByAttribute(this, "anonid",
"relnotes-container");
</field>
<field name="_relNotes">
document.getAnonymousElementByAttribute(this, "anonid",
"relnotes");
</field>
<property name="userDisabled">
<getter><![CDATA[
@ -972,11 +1044,17 @@
<method name="_showStatus">
<parameter name="aType"/>
<body><![CDATA[
this._controlContainer.hidden = aType != "none";
this._controlContainer.hidden = aType != "none" &&
!(aType == "update-available" && !this.hasAttribute("upgrade"));
this._installStatus.hidden = aType != "progress";
if (aType == "progress")
this._installStatus.refreshState();
this._checkingUpdate.hidden = aType != "checking-update";
this._updateAvailable.hidden = aType != "update-available";
this._relNotesToggle.hidden = !(this.mManualUpdate ?
this.mManualUpdate.releaseNotesURI :
this.mAddon.releaseNotesURI);
]]></body>
</method>
@ -1064,6 +1142,128 @@
]]></body>
</method>
<method name="_updateUpgradeInfo">
<body><![CDATA[
this._version.value = this.mManualUpdate.version;
]]></body>
</method>
<method name="_fetchReleaseNotes">
<parameter name="aURI"/>
<body><![CDATA[
var self = this;
if (!aURI || this._relNotesLoaded) {
sendToggleEvent();
return;
}
var relNotesData = null, transformData = null;
this._relNotesLoaded = true;
this._relNotesLoading.hidden = false;
this._relNotesError.hidden = true;
function sendToggleEvent() {
var event = document.createEvent("Events");
event.initEvent("RelNotesToggle", true, true);
self.dispatchEvent(event);
}
function showRelNotes() {
if (!relNotesData || !transformData)
return;
self._relNotesLoading.hidden = true;
var processor = Components.classes["@mozilla.org/document-transformer;1?type=xslt"]
.createInstance(Components.interfaces.nsIXSLTProcessor);
processor.flags |= Components.interfaces.nsIXSLTProcessorPrivate.DISABLE_ALL_LOADS;
processor.importStylesheet(transformData);
var fragment = processor.transformToFragment(relNotesData, document);
self._relNotes.appendChild(fragment);
if (self.hasAttribute("show-relnotes")) {
var container = self._relNotesContainer;
container.style.height = container.scrollHeight + "px";
}
sendToggleEvent();
}
function handleError() {
dataReq.abort();
styleReq.abort();
self._relNotesLoading.hidden = true;
self._relNotesError.hidden = false;
self._relNotesLoaded = false; // allow loading to be re-tried
sendToggleEvent();
}
function handleResponse(aEvent) {
var req = aEvent.target;
if (req.responseXML &&
req.responseXML.documentElement.namespaceURI != XMLURI_PARSE_ERROR) {
if (req == dataReq)
relNotesData = req.responseXML;
else
transformData = req.responseXML;
showRelNotes();
} else {
handleError();
}
}
var dataReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
dataReq.open("GET", aURI.spec, true);
dataReq.addEventListener("load", handleResponse, false);
dataReq.addEventListener("error", handleError, false);
dataReq.send(null);
var styleReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
styleReq.open("GET", UPDATES_RELEASENOTES_TRANSFORMFILE, true);
styleReq.addEventListener("load", handleResponse, false);
styleReq.addEventListener("error", handleError, false);
styleReq.send(null);
]]></body>
</method>
<method name="toggleReleaseNotes">
<body><![CDATA[
if (this.hasAttribute("show-relnotes")) {
this._relNotesContainer.style.height = "0px";
this.removeAttribute("show-relnotes");
this._relNotesToggle.setAttribute(
"label",
this._relNotesToggle.getAttribute("showlabel")
);
this._relNotesToggle.setAttribute(
"tooltiptext",
this._relNotesToggle.getAttribute("showtooltip")
);
var event = document.createEvent("Events");
event.initEvent("RelNotesToggle", true, true);
this.dispatchEvent(event);
} else {
this._relNotesContainer.style.height = this._relNotesContainer.scrollHeight +
"px";
this.setAttribute("show-relnotes", true);
this._relNotesToggle.setAttribute(
"label",
this._relNotesToggle.getAttribute("hidelabel")
);
this._relNotesToggle.setAttribute(
"tooltiptext",
this._relNotesToggle.getAttribute("hidetooltip")
);
var uri = this.mManualUpdate ?
this.mManualUpdate.releaseNotesURI :
this.mAddon.releaseNotesURI;
this._fetchReleaseNotes(uri);
}
]]></body>
</method>
<method name="uninstall">
<body><![CDATA[
// If uninstalling does not require a restart then just disable it
@ -1086,6 +1286,14 @@
]]></body>
</method>
<method name="upgrade">
<body><![CDATA[
var install = this.mManualUpdate;
delete this.mManualUpdate;
install.install();
]]></body>
</method>
<method name="showInDetailView">
<body><![CDATA[
gViewController.loadView("addons://detail/" +
@ -1150,6 +1358,25 @@
</method>
<method name="onNewInstall">
<parameter name="aInstall"/>
<body><![CDATA[
if (!this.mAddon.applyBackgroundUpdates) {
this.mManualUpdate = aInstall;
this._showStatus("update-available");
this._updateUpgradeInfo();
}
]]></body>
</method>
<method name="onDownloadStarted">
<parameter name="aInstall"/>
<body><![CDATA[
this._showStatus("progress");
this._installStatus.initWithInstall(aInstall);
]]></body>
</method>
<method name="onInstallStarted">
<parameter name="aInstall"/>
<body><![CDATA[
this._showStatus("progress");
@ -1291,7 +1518,8 @@
<body><![CDATA[
this.mAddon = this.mAddon || this.mInstall.addon;
if (this.mAddon) {
this._icon.src = this.mAddon.iconURL || this.mInstall.iconURL;
this._icon.src = this.mAddon.iconURL ||
(this.mInstall ? this.mInstall.iconURL : "");
this._name.value = this.mAddon.name;
} else {
this._icon.src = this.mInstall.iconURL;

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

@ -80,6 +80,9 @@
<command id="cmd_findAllUpdates"/>
<command id="cmd_restartApp"/>
<command id="cmd_goToDiscoverPane"/>
<command id="cmd_goToRecentUpdates"/>
<command id="cmd_goToAvailableUpdates"/>
<command id="cmd_toggleBackgroundUpdateCheck"/>
</commandset>
<!-- view commands - these act on the selected addon -->
@ -125,6 +128,12 @@
<richlistitem id="category-plugins" value="addons://list/plugin"
class="category"
name="&view.plugins.label;"/>
<richlistitem id="category-availableUpdates" value="addons://updates/available"
class="category"
name="&view.availableUpdates.label;" disabled="true"/>
<richlistitem id="category-recentUpdates" value="addons://updates/recent"
class="category"
name="&view.recentUpdates.label;" disabled="true"/>
</richlistbox>
</vbox>
@ -143,9 +152,9 @@
<image class="spinner"/>
<label id="updates-noneFound" hidden="true"
value="&updates.noneFound.label;"/>
<button id="updates-checkNow" class="button-link"
label="&updates.updateNow.label;"
command="cmd_findAllUpdates"/>
<button id="updates-manualUpdatesFound" class="button-link"
hidden="true" label="&updates.manualUpdatesFound.label;"
command="cmd_goToAvailableUpdates"/>
<label id="updates-progress" hidden="true"
value="&updates.updating.label;"/>
<label id="updates-installed" hidden="true"
@ -156,6 +165,24 @@
label="&updates.restart.label;"
command="cmd_restartApp"/>
</hbox>
<button id="header-utils-btn" type="menu">
<menupopup id="utils-menu">
<menuitem id="utils-updateNow"
label="&updates.updateAddonsNow.label;"
accesskey="&updates.updateAddonsNow.accesskey;"
command="cmd_findAllUpdates"/>
<menuitem id="utils-viewUpdates"
label="&updates.viewUpdates.label;"
accesskey="&updates.viewUpdates.accesskey;"
command="cmd_goToRecentUpdates"/>
<menuseparator/>
<menuitem id="utils-backgroudUpdateCheck"
label="&updates.backgroudUpdateCheck.label;"
accesskey="&updates.backgroudUpdateCheck.accesskey;"
type="checkbox" autocheck="false"
command="cmd_toggleBackgroundUpdateCheck"/>
</menupopup>
</button>
<textbox id="header-search" type="search" searchbutton="true"
placeholder="&search.placeholder;"/>
<image id="header-searching"/>
@ -216,6 +243,27 @@
<richlistbox id="addon-list" class="list" flex="1"/>
</vbox>
<!-- updates view -->
<vbox id="updates-view" flex="1" class="view-pane">
<hbox class="view-header" pack="end">
<hbox id="updates-sorters" class="sort-controls" sortby="dateUpdated"
ascending="false"/>
</hbox>
<vbox id="updates-list-empty" class="empty-list-notice"
flex="1" hidden="true">
<spacer flex="1"/>
<label id="empty-availableUpdates-msg" value="&listEmpty.availableUpdates.label;"/>
<label id="empty-recentUpdates-msg" value="&listEmpty.recentUpdates.label;"/>
<button label="&listEmpty.findUpdates.label;" class="addon-control"
command="cmd_findAllUpdates"/>
<spacer flex="3"/>
</vbox>
<hbox id="update-all-container" hidden="true">
<button label="Update these add-ons" class="addon-control"/>
</hbox>
<richlistbox id="updates-list" class="list" flex="1"/>
</vbox>
<!-- detail view -->
<hbox id="detail-view" flex="1" class="view-pane">
<spacer flex="1"/>

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

@ -61,7 +61,11 @@ _TEST_FILES = \
browser_updatessl.js \
browser_updatessl.rdf \
browser_installssl.js \
browser_backgroundupdate_menuitem.js \
browser_recentupdates.js \
browser_manualupdates.js \
redirect.sjs \
releaseNotes.xhtml \
$(NULL)
include $(topsrcdir)/config/rules.mk

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

@ -0,0 +1,55 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests the menuitem for the background update check
var gManagerWindow;
function test() {
waitForExplicitFinish();
open_manager(null, function(aWindow) {
gManagerWindow = aWindow;
run_next_test();
});
}
function end_test() {
close_manager(gManagerWindow, function() {
finish();
});
}
add_test(function() {
var menuitem = gManagerWindow.document.getElementById("utils-backgroudUpdateCheck");
function is_backgroundcheck_insync(aExpected) {
var enabled = Services.prefs.getBoolPref("extensions.update.enabled");
var checked = menuitem.getAttribute("checked") == "true";
is(enabled, aExpected, "Background check should be " + (aExpected ? "enabled" : "disabled"));
is(checked, enabled, "Background check menuitem should be in sync with preference");
}
is_backgroundcheck_insync(true);
info("Setting background check pref to true");
Services.prefs.setBoolPref("extensions.update.enabled", false);
is_backgroundcheck_insync(false);
info("Setting background check pref to false");
Services.prefs.setBoolPref("extensions.update.enabled", true);
is_backgroundcheck_insync(true);
info("Clicking on background check menuitem - setting to unchecked");
var utilsBtn = gManagerWindow.document.getElementById("header-utils-btn");
utilsBtn.addEventListener("popupshown", function() {
EventUtils.synthesizeMouse(menuitem, 2, 2, { }, gManagerWindow);
executeSoon(function() {
is_backgroundcheck_insync(false);
info("Manually invoking command to toggle background update check on");
gManagerWindow.gViewController.doCommand("cmd_toggleBackgroundUpdateCheck");
is_backgroundcheck_insync(true);
run_next_test();
});
}, false);
EventUtils.synthesizeMouse(utilsBtn, 2, 2, { }, gManagerWindow);
});

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

@ -0,0 +1,165 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests manual updates, including the Available Updates pane
var gProvider;
var gManagerWindow;
var gCategoryUtilities;
var gAvailableCategory;
function test() {
waitForExplicitFinish();
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "auto updating addon",
version: "1.0",
applyBackgroundUpdates: true
}]);
open_manager(null, function(aWindow) {
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
run_next_test();
});
}
function end_test() {
close_manager(gManagerWindow, function() {
finish();
});
}
add_test(function() {
gAvailableCategory = gManagerWindow.gCategories.get("addons://updates/available");
is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should initially be hidden");
gProvider.createAddons([{
id: "addon2@tests.mozilla.org",
name: "manually updating addon",
version: "1.0",
applyBackgroundUpdates: false
}]);
is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should now be visible");
gAvailableCategory.addEventListener("CategoryVisible", function() {
gAvailableCategory.removeEventListener("CategoryVisible", arguments.callee, false);
is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should not be visible");
gAvailableCategory.addEventListener("CategoryVisible", function() {
gAvailableCategory.removeEventListener("CategoryVisible", arguments.callee, false);
is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should be visible");
run_next_test();
}, false);
gProvider.addons[1].applyBackgroundUpdates = false;
}, false);
gProvider.addons[1].applyBackgroundUpdates = true;
});
add_test(function() {
gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
gAvailableCategory.removeEventListener("CategoryBadgeUpdated", arguments.callee, false);
is(gAvailableCategory.badgeCount, 1, "Badge for Available Updates should now be 1");
run_next_test();
}, false);
gProvider.createInstalls([{
name: "manually updating addon (new and improved!)",
existingAddon: gProvider.addons[1],
version: "1.1",
releaseNotesURI: Services.io.newURI(TESTROOT + "thereIsNoFileHere.xhtml", null, null)
}]);
});
add_test(function() {
wait_for_view_load(gManagerWindow, function() {
is(gManagerWindow.document.getElementById("categories").selectedItem.value, "addons://updates/available", "Available Updates category should now be selected");
is(gManagerWindow.gViewController.currentViewId, "addons://updates/available", "Available Updates view should be the current view");
run_next_test();
}, true);
EventUtils.synthesizeMouse(gAvailableCategory, 0, 0, { }, gManagerWindow);
});
add_test(function() {
var list = gManagerWindow.document.getElementById("updates-list");
is(list.itemCount, 1, "Should be 1 available update listed");
var item = list.firstChild;
is(item.mAddon.id, "addon2@tests.mozilla.org", "Update item should be for the manually updating addon");
setTimeout(function() {
// this updates asynchronously
is(item._version.value, "1.1", "Update item should have version number of the update");
var postfix = gManagerWindow.document.getAnonymousElementByAttribute(item, "class", "update-postfix");
is_element_visible(gManagerWindow, postfix, true, "'Update' postfix should be visible");
is_element_visible(gManagerWindow, item._updateAvailable, true, "");
is_element_visible(gManagerWindow, item._relNotesToggle, true, "Release notes toggle should be visible");
info("Opening release notes");
item.addEventListener("RelNotesToggle", function() {
item.removeEventListener("RelNotesToggle", arguments.callee, false);
info("Release notes now open");
is_element_visible(gManagerWindow, item._relNotesLoading, false, "Release notes loading message should be hidden");
is_element_visible(gManagerWindow, item._relNotesError, true, "Release notes error message should be visible");
is(item._relNotes.childElementCount, 0, "Release notes should be empty");
info("Closing release notes");
item.addEventListener("RelNotesToggle", function() {
item.removeEventListener("RelNotesToggle", arguments.callee, false);
info("Release notes now closed");
info("Setting Release notes URI to something that should load");
gProvider.installs[0].releaseNotesURI = Services.io.newURI(TESTROOT + "releaseNotes.xhtml", null, null)
info("Re-opening release notes");
item.addEventListener("RelNotesToggle", function() {
item.removeEventListener("RelNotesToggle", arguments.callee, false);
info("Release notes now open");
is_element_visible(gManagerWindow, item._relNotesLoading, false, "Release notes loading message should be hidden");
is_element_visible(gManagerWindow, item._relNotesError, false, "Release notes error message should be hidden");
isnot(item._relNotes.childElementCount, 0, "Release notes should have been inserted into container");
run_next_test();
}, false);
EventUtils.synthesizeMouse(item._relNotesToggle, 2, 2, { }, gManagerWindow);
is_element_visible(gManagerWindow, item._relNotesLoading, true, "Release notes loading message should be visible");
}, false);
EventUtils.synthesizeMouse(item._relNotesToggle, 2, 2, { }, gManagerWindow);
}, false);
EventUtils.synthesizeMouse(item._relNotesToggle, 2, 2, { }, gManagerWindow);
is_element_visible(gManagerWindow, item._relNotesLoading, true, "Release notes loading message should be visible");
}, 100);
});
add_test(function() {
var list = gManagerWindow.document.getElementById("updates-list");
var item = list.firstChild;
var updateBtn = item._updateBtn;
is_element_visible(gManagerWindow, updateBtn, true, "Update button should be visible");
var install = gProvider.installs[0];
var listener = {
onInstallStarted: function() {
info("Install started");
is_element_visible(gManagerWindow, item._installStatus, true, "Install progress widget should be visible");
},
onInstallEnded: function() {
install.removeTestListener(this);
info("install ended");
is_element_visible(gManagerWindow, item._installStatus, false, "Install progress widget should be hidden");
run_next_test();
}
};
install.addTestListener(listener);
EventUtils.synthesizeMouse(updateBtn, 2, 2, { }, gManagerWindow);
});

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

@ -0,0 +1,86 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests the recent updates pane
var gProvider;
var gManagerWindow;
var gCategoryUtilities;
function test() {
waitForExplicitFinish();
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "updated 6 hours ago",
version: "1.0",
updateDate: new Date(Date.now() - (1000 * 60 * 60 * 6)),
releaseNotesURI: Services.io.newURI(TESTROOT + "releaseNotes.xhtml", null, null)
}, {
id: "addon2@tests.mozilla.org",
name: "updated 5 seconds ago",
version: "1.0",
updateDate: new Date(Date.now() - (1000 * 5))
}, {
id: "addon3@tests.mozilla.org",
name: "updated 1 month ago",
version: "1.0",
updateDate: new Date(Date.now() - (1000 * 60 * 60 * 25 * 30))
}]);
open_manager(null, function(aWindow) {
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
run_next_test();
});
}
function end_test() {
close_manager(gManagerWindow, function() {
finish();
});
}
add_test(function() {
info("Checking menuitem for Recent Updates opens that pane");
var recentCat = gManagerWindow.gCategories.get("addons://updates/recent");
is(gCategoryUtilities.isVisible(recentCat), false, "Recent Updates category should initially be hidden");
var utilsBtn = gManagerWindow.document.getElementById("header-utils-btn");
utilsBtn.addEventListener("popupshown", function() {
wait_for_view_load(gManagerWindow, function() {
is(gCategoryUtilities.isVisible(recentCat), true, "Recent Updates category should now be visible");
is(gManagerWindow.document.getElementById("categories").selectedItem.value, "addons://updates/recent", "Recent Updates category should now be selected");
is(gManagerWindow.gViewController.currentViewId, "addons://updates/recent", "Recent Updates view should be the current view");
run_next_test();
}, true);
var menuitem = gManagerWindow.document.getElementById("utils-viewUpdates");
EventUtils.synthesizeMouse(menuitem, 2, 2, { }, gManagerWindow);
}, false);
EventUtils.synthesizeMouse(utilsBtn, 2, 2, { }, gManagerWindow);
});
add_test(function() {
var updatesList = gManagerWindow.document.getElementById("updates-list");
var items = updatesList.getElementsByTagName("richlistitem");
var possible = ["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon2@tests.mozilla.org"];
var expected = ["addon2@tests.mozilla.org", "addon1@tests.mozilla.org"];
for (let i = 0; i < items.length; i++) {
let item = items[i];
let itemId = item.mAddon.id;
if (possible.indexOf(itemId) == -1)
continue; // skip over any other addons, such as shipped addons that would update on every build
isnot(expected.length, 0, "Should be expecting more items");
is(itemId, expected.shift(), "Should get expected item based on recenty of update");
if (itemId == "addon1@tests.mozilla.org")
is_element_visible(gManagerWindow, item._relNotesToggle, true, "Release notes toggle should be visible for addon with release notes");
else
is_element_visible(gManagerWindow, item._relNotesToggle, false, "Release notes toggle should be hidden for addon with no release notes");
}
run_next_test();
});

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

@ -48,8 +48,8 @@ function get_addon_file_url(aFilename) {
return fileurl.QueryInterface(Ci.nsIFileURL);
}
function wait_for_view_load(aManagerWindow, aCallback) {
if (!aManagerWindow.gViewController.isLoading) {
function wait_for_view_load(aManagerWindow, aCallback, aForceWait) {
if (!aForceWait && !aManagerWindow.gViewController.isLoading) {
aCallback(aManagerWindow);
return;
}
@ -114,6 +114,13 @@ function restart_manager(aManagerWindow, aView, aCallback) {
close_manager(aManagerWindow, function() { open_manager(aView, aCallback); });
}
function is_element_visible(aWindow, aElement, aExpected, aMsg) {
isnot(aElement, null, "Element should not be null, when checking visibility");
var style = aWindow.getComputedStyle(aElement, "");
var visible = style.display != "none" && style.visibility == "visible";
is(visible, aExpected, aMsg);
}
function CategoryUtilities(aManagerWindow) {
this.window = aManagerWindow;
@ -346,6 +353,10 @@ MockProvider.prototype = {
for (var prop in aAddonProp) {
if (prop == "id")
continue;
if (prop == "applyBackgroundUpdates") {
addon._applyBackgroundUpdates = aAddonProp[prop];
continue;
}
addon[prop] = aAddonProp[prop];
}
this.addAddon(addon);
@ -612,6 +623,7 @@ function MockAddon(aId, aName, aType, aOperationsRequiringRestart) {
this.blocklistState = 0;
this.appDisabled = false;
this._userDisabled = false;
this._applyBackgroundUpdates = true;
this.scope = AddonManager.SCOPE_PROFILE;
this.isActive = true;
this.creator = "";
@ -647,6 +659,15 @@ MockAddon.prototype = {
return val;
},
get applyBackgroundUpdates() {
return this._applyBackgroundUpdates;
},
set applyBackgroundUpdates(val) {
this._applyBackgroundUpdates = val;
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["applyBackgroundUpdates"]);
},
isCompatibleWith: function(aAppVersion, aPlatformVersion) {
return true;

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

@ -0,0 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en-US" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<h1>OMG, an update!!!!</h1>
<ul>
<li>Made everything more awesome</li>
<li>Added hot sauce</li>
</ul>
</body>
</html>

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

@ -130,6 +130,12 @@
#category-plugins > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
}
#category-availableUpdates > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
}
#category-recentUpdates > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
}
/*** header ***/
@ -168,6 +174,35 @@
list-style-image: url("chrome://global/skin/icons/loading_16.png");
}
#header-utils-btn {
-moz-appearance: none;
min-width: 4.5em;
border-width: 1px;
-moz-border-top-colors: #999;
-moz-border-bottom-colors: #999;
-moz-border-left-colors: #999;
-moz-border-right-colors: #999;
-moz-border-radius: 8px;
background-image: -moz-linear-gradient(#FFF, #BBB);
list-style-image: url("chrome://mozapps/skin/extensions/utilities.png");
}
#header-utils-btn:hover,
#header-utils-btn[open="true"] {
-moz-border-top-colors: #777;
-moz-border-bottom-colors: #777;
-moz-border-left-colors: #777;
-moz-border-right-colors: #777;
}
#header-utils-btn[open="true"] {
-moz-box-shadow: inset 3px 3px 8px #555;
}
#header-utils-btn:-moz-focusring > .button-box {
border: none;
}
.view-header {
background: -moz-linear-gradient(top, #FFF, #E8E8E8 50%, #FFF);
padding: 4px;
@ -337,7 +372,8 @@
}
.addon .name,
.addon .version {
.addon .version,
.addon .update-postfix {
font-size: 150%;
margin-bottom: 0px;
}
@ -365,6 +401,10 @@
opacity: 1;
}
.addon .basicinfo-container {
-moz-box-align: start;
}
.addon .details-container {
-moz-box-align: end;
-moz-margin-start: 20px;
@ -380,6 +420,44 @@
border-color: #AAA;
}
.addon .relnotes-container {
-moz-box-align: start;
height: 0px;
overflow: hidden;
opacity: 0;
-moz-transition-property: height, opacity;
-moz-transition-duration: 0.5s, 0.5s;
}
.addon[show-relnotes] .relnotes-container {
opacity: 1;
-moz-transition-property: height, opacity;
-moz-transition-duration: 0.5s, 0.5s;
}
.addon .relnotes-header {
font-weight: bold;
margin: 10px 0px;
}
.addon .relnotes-toggle {
-moz-appearance: none;
border: none;
background: transparent;
font-weight: bold;
-moz-box-direction: reverse;
cursor: pointer;
list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
}
.addon .relnotes-toggle > .button-box > .button-icon {
-moz-padding-start: 4px;
}
.addon[show-relnotes] .relnotes-toggle {
list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
}
/*** item - uninstalled ***/

Двоичные данные
toolkit/themes/gnomestripe/mozapps/extensions/utilities.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

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

@ -23,6 +23,7 @@ toolkit.jar:
+ skin/classic/mozapps/extensions/rating-unrated.png (extensions/rating-unrated.png)
+ skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
+ skin/classic/mozapps/extensions/pause.png (extensions/pause.png)
+ skin/classic/mozapps/extensions/utilities.png (extensions/utilities.png)
+ skin/classic/mozapps/passwordmgr/key.png (passwordmgr/key.png)
+ skin/classic/mozapps/plugins/notifyPluginBlocked.png (plugins/pluginBlocked-16.png)
+ skin/classic/mozapps/plugins/notifyPluginCrashed.png (plugins/pluginGeneric-16.png)

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

@ -130,6 +130,12 @@
#category-plugins > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
}
#category-availableUpdates > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
}
#category-recentUpdates > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
}
/*** header ***/
@ -168,6 +174,35 @@
list-style-image: url("chrome://global/skin/icons/loading_16.png");
}
#header-utils-btn {
-moz-appearance: none;
min-width: 4.5em;
border-width: 1px;
-moz-border-top-colors: #999;
-moz-border-bottom-colors: #999;
-moz-border-left-colors: #999;
-moz-border-right-colors: #999;
-moz-border-radius: 8px;
background-image: -moz-linear-gradient(#FFF, #BBB);
list-style-image: url("chrome://mozapps/skin/extensions/utilities.png");
}
#header-utils-btn:hover,
#header-utils-btn[open="true"] {
-moz-border-top-colors: #777;
-moz-border-bottom-colors: #777;
-moz-border-left-colors: #777;
-moz-border-right-colors: #777;
}
#header-utils-btn[open="true"] {
-moz-box-shadow: inset 3px 3px 8px #555;
}
#header-utils-btn:-moz-focusring > .button-box {
border: none;
}
.view-header {
background: -moz-linear-gradient(top, #FFF, #E8E8E8 50%, #FFF);
padding: 4px;
@ -337,7 +372,8 @@
}
.addon .name,
.addon .version {
.addon .version,
.addon .update-postfix {
font-size: 150%;
margin-bottom: 0px;
}
@ -365,6 +401,10 @@
opacity: 1;
}
.addon .basicinfo-container {
-moz-box-align: start;
}
.addon .details-container {
-moz-box-align: end;
-moz-margin-start: 20px;
@ -380,6 +420,44 @@
border-color: #AAA;
}
.addon .relnotes-container {
-moz-box-align: start;
height: 0px;
overflow: hidden;
opacity: 0;
-moz-transition-property: height, opacity;
-moz-transition-duration: 0.5s, 0.5s;
}
.addon[show-relnotes] .relnotes-container {
opacity: 1;
-moz-transition-property: height, opacity;
-moz-transition-duration: 0.5s, 0.5s;
}
.addon .relnotes-header {
font-weight: bold;
margin: 10px 0px;
}
.addon .relnotes-toggle {
-moz-appearance: none;
border: none;
background: transparent;
font-weight: bold;
-moz-box-direction: reverse;
cursor: pointer;
list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
}
.addon .relnotes-toggle > .button-box > .button-icon {
-moz-padding-start: 4px;
}
.addon[show-relnotes] .relnotes-toggle {
list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
}
/*** item - uninstalled ***/

Двоичные данные
toolkit/themes/pinstripe/mozapps/extensions/utilities.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

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

@ -25,6 +25,7 @@ toolkit.jar:
skin/classic/mozapps/extensions/rating-unrated.png (extensions/rating-unrated.png)
skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
skin/classic/mozapps/extensions/pause.png (extensions/pause.png)
skin/classic/mozapps/extensions/utilities.png (extensions/utilities.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)

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

@ -130,6 +130,12 @@
#category-plugins > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
}
#category-availableUpdates > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
}
#category-recentUpdates > .category-icon {
list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
}
/*** header ***/
@ -168,6 +174,35 @@
list-style-image: url("chrome://global/skin/icons/loading_16.png");
}
#header-utils-btn {
-moz-appearance: none;
min-width: 4.5em;
border-width: 1px;
-moz-border-top-colors: #999;
-moz-border-bottom-colors: #999;
-moz-border-left-colors: #999;
-moz-border-right-colors: #999;
-moz-border-radius: 8px;
background-image: -moz-linear-gradient(#FFF, #BBB);
list-style-image: url("chrome://mozapps/skin/extensions/utilities.png");
}
#header-utils-btn:hover,
#header-utils-btn[open="true"] {
-moz-border-top-colors: #777;
-moz-border-bottom-colors: #777;
-moz-border-left-colors: #777;
-moz-border-right-colors: #777;
}
#header-utils-btn[open="true"] {
-moz-box-shadow: inset 3px 3px 8px #555;
}
#header-utils-btn:-moz-focusring > .button-box {
border: none;
}
.view-header {
background: -moz-linear-gradient(top, #FFF, #E8E8E8 50%, #FFF);
padding: 4px;
@ -337,7 +372,8 @@
}
.addon .name,
.addon .version {
.addon .version,
.addon .update-postfix {
font-size: 150%;
margin-bottom: 0px;
}
@ -365,6 +401,10 @@
opacity: 1;
}
.addon .basicinfo-container {
-moz-box-align: start;
}
.addon .details-container {
-moz-box-align: end;
-moz-margin-start: 20px;
@ -380,6 +420,44 @@
border-color: #AAA;
}
.addon .relnotes-container {
-moz-box-align: start;
height: 0px;
overflow: hidden;
opacity: 0;
-moz-transition-property: height, opacity;
-moz-transition-duration: 0.5s, 0.5s;
}
.addon[show-relnotes] .relnotes-container {
opacity: 1;
-moz-transition-property: height, opacity;
-moz-transition-duration: 0.5s, 0.5s;
}
.addon .relnotes-header {
font-weight: bold;
margin: 10px 0px;
}
.addon .relnotes-toggle {
-moz-appearance: none;
border: none;
background: transparent;
font-weight: bold;
-moz-box-direction: reverse;
cursor: pointer;
list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
}
.addon .relnotes-toggle > .button-box > .button-icon {
-moz-padding-start: 4px;
}
.addon[show-relnotes] .relnotes-toggle {
list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
}
/*** item - uninstalled ***/

Двоичные данные
toolkit/themes/winstripe/mozapps/extensions/utilities.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

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

@ -31,6 +31,7 @@ toolkit.jar:
skin/classic/mozapps/extensions/rating-unrated.png (extensions/rating-unrated.png)
skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
skin/classic/mozapps/extensions/pause.png (extensions/pause.png)
skin/classic/mozapps/extensions/utilities.png (extensions/utilities.png)
skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/mozapps/handling/handling.css (handling/handling.css)
skin/classic/mozapps/passwordmgr/key.png (passwordmgr/key.png)
@ -93,6 +94,7 @@ toolkit.jar:
skin/classic/aero/mozapps/extensions/rating-unrated.png (extensions/rating-unrated.png)
skin/classic/aero/mozapps/extensions/cancel.png (extensions/cancel.png)
skin/classic/aero/mozapps/extensions/pause.png (extensions/pause.png)
skin/classic/aero/mozapps/extensions/utilities.png (extensions/utilities.png)
skin/classic/aero/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/aero/mozapps/handling/handling.css (handling/handling.css)
skin/classic/aero/mozapps/passwordmgr/key.png (passwordmgr/key-aero.png)