Add code to build UI from the update manager, track state and update name on nsIUpdate

This commit is contained in:
ben%bengoodger.com 2005-06-15 00:05:31 +00:00
Родитель effb177f02
Коммит 49d84b63a3
6 изменённых файлов: 221 добавлений и 70 удалений

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

@ -8,6 +8,8 @@ errorsPageHeader=Update Failed
IAgreeLabel=I Agree
license404Error=The license file could not be found. Please contact the distributor.
downloadingLicense=Downloading license text...
statusSucceededFormat=Installed on: %S
statusFailed=Install Failed
# The prefix /update2/0/ uniquely identifies the format of this URL. If you
# change the format of this URL, then you MUST change the prefix (i.e.,
# increment 0 to 1).

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

@ -150,10 +150,6 @@ update {
padding: 5px;
}
update[state="downloading"] {
-moz-binding: url("chrome://mozapps/content/update/updates.xml#update-downloading");
}
#updatesView[showcompletedupdates=true] > update {
display: none
}

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

@ -35,10 +35,18 @@
*
* ***** END LICENSE BLOCK ***** */
const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
const nsIIncrementalDownload = Components.interfaces.nsIIncrementalDownload;
const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const PREF_UPDATE_MANUAL_URL = "app.update.manual.url";
const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
const nsIIncrementalDownload = Components.interfaces.nsIIncrementalDownload;
const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const PREF_UPDATE_MANUAL_URL = "app.update.manual.url";
const STATE_DOWNLOADING = "downloading";
const STATE_PENDING = "pending";
const STATE_APPLYING = "applying";
const STATE_SUCCEEDED = "succeeded";
const STATE_FAILED = "failed";
/**
* Logs a string to the error console.
@ -258,32 +266,41 @@ var gLicensePage = {
};
var gDownloadingPage = {
_createAndInsertItem: function(update, state) {
var element = document.createElementNS(XMLNS_XUL, "update");
updatesView.appendChild(element);
element.setUpdate(update);
return element;
},
onPageShow: function() {
// Build the UI for the active download
var update = document.createElementNS(XMLNS_XUL, "update");
update.setAttribute("state", "downloading");
update.setAttribute("name", "Firefox 1.0.4");
update.setAttribute("status", "Blah");
update.setAttribute("url", "http://www.bengoodger.com/");
update.setAttribute("mode", "normal");
update.id = "activeDownloadItem";
var updatesView = document.getElementById("updatesView");
updatesView.appendChild(update);
var um = Components.classes["@mozilla.org/updates/update-manager;1"]
.getService(Components.interfaces.nsIUpdateManager);
var activeUpdate = um.activeUpdate;
if (um.activeUpdate) {
var element = this._createAndInsertItem(activeUpdate);
element.id = "activeDownloadItem";
}
updatesView.addEventListener("update-pause", this.onPause, false);
// Add this UI as a listener for active downloads
var updates =
Components.classes["@mozilla.org/updates/update-service;1"].
getService(Components.interfaces.nsIApplicationUpdateService);
var state = updates.downloadUpdate(gUpdates.update, false);
if (state == "failed")
this.showVerificationError();
else
updates.addDownloadListener(this);
if (gUpdates.update) {
// Add this UI as a listener for active downloads
var updates =
Components.classes["@mozilla.org/updates/update-service;1"].
getService(Components.interfaces.nsIApplicationUpdateService);
var state = updates.downloadUpdate(gUpdates.update, false);
if (state == "failed")
this.showVerificationError();
else
updates.addDownloadListener(this);
}
// Build the UI for previously installed updates
// ...
for (var i = 0; i < um.updateCount; ++i) {
var update = um.getUpdateAt(i);
this._createAndInsertItem(update);
}
gUpdates.headerVisible = false;
},

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

@ -57,31 +57,77 @@
<content>
<xul:hbox pack="start">
<xul:label class="update-item-name" xbl:inherits="value=name" crop="right" flex="1"/>
<xul:link class="update-item-details" xbl:inherits="href=url">&details.link;</xul:link>
<xul:link class="update-item-details" xbl:inherits="href=detailsURL">&details.link;</xul:link>
</xul:hbox>
<xul:label class="update-item-status" xbl:inherits="xbl:text=status" flex="1"/>
<xul:deck anonid="modeDeck">
<xul:label class="update-item-status" xbl:inherits="xbl:text=status" flex="1"/>
<xul:vbox>
<xul:progressmeter class="update-item-progress" xbl:inherits="value=progress" mode="undetermined"/>
<xul:hbox align="center">
<xul:label class="update-item-status" xbl:inherits="value=status" flex="1" crop="right"/>
<xul:button class="update-item-pause"
label="&pause.label;" accesskey="&pause.accesskey;"
oncommand="this.parentNode.parentNode.fireEvent('pause');"/>
</xul:hbox>
</xul:vbox>
</xul:deck>
<xul:stringbundle anonid="strings"
src="chrome://mozapps/locale/update/updates.properties"/>
</content>
</binding>
<binding id="update-downloading" extends="chrome://mozapps/content/shared/richview.xml#richview-item">
<resources>
<stylesheet src="chrome://mozapps/content/update/updates.css"/>
</resources>
<content>
<xul:hbox pack="start">
<xul:label class="update-item-name" xbl:inherits="value=name" crop="right" flex="1"/>
<xul:link class="update-item-details" xbl:inherits="href=url" label="&details.link;"/>
</xul:hbox>
<xul:progressmeter class="update-item-progress" xbl:inherits="value=progress" mode="undetermined"/>
<xul:hbox align="center">
<xul:label class="update-item-status" xbl:inherits="value=status" flex="1" crop="right"/>
<xul:button class="update-item-pause"
label="&pause.label;" accesskey="&pause.accesskey;"
oncommand="this.parentNode.parentNode.fireEvent('pause');"/>
</xul:hbox>
</content>
<implementation>
<field name="eventPrefix">"update-"</field>
<field name="_update">null</field>
<field name="_strings">
document.getAnonymousElementByAttribute(this, "anonid", "strings");
</field>
<field name="_modeDeck">
document.getAnonymousElementByAttribute(this, "anonid", "modeDeck");
</field>
<method name="setUpdate">
<parameter name="update"/>
<body><![CDATA[
this._update = update;
this.setAttribute("name", update.name);
this.setAttribute("detailsURL", update.detailsURL);
this.state = update.state;
]]></body>
</method>
<property name="state">
<getter><![CDATA[
return this.getAttribute("state");
]]></getter>
<setter><![CDATA[
this.setAttribute("state", val);
this._modeDeck.setAttribute("selectedIndex", val == "downloading" ? 1 : 0);
if (val == "failed") {
var failed = this._strings.getString("statusFailed");
this.setAttribute("status", failed);
}
else if (val != "downloading") {
var sdf =
Components.classes["@mozilla.org/intl/scriptabledateformat;1"].
getService(Components.interfaces.nsIScriptableDateFormat);
var installDate = new Date(this._update.installDate);
var dateString = sdf.FormatDateTime("", sdf.dateFormatLong,
sdf.timeFormatSeconds,
date.getFullYear(),
date.getMonth() + 1,
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds());
dateString = this._strings.getFormattedString("statusSucceededFormat",
[dateString]);
this.setAttribute("status", dateString);
}
return val;
]]></setter>
</property>
<property name="state"
onset="this.setAttribute('state', val); return val;"
onget="return this.getAttribute('state');"/>
</implementation>
</binding>

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

@ -90,6 +90,11 @@ interface nsIUpdate : nsISupports
*/
attribute AString type;
/**
* The name of the update, or "<Application Name> <Update Version>"
*/
attribute AString name;
/**
* The Application version of this update.
*/
@ -129,6 +134,13 @@ interface nsIUpdate : nsISupports
*/
attribute long long installDate;
/**
* The state of this update
* XXXben: in a multi-patch world, this needs to move down into the
* nsIUpdatePatch interface, but for simplicity it shall stay here.
*/
attribute AString state;
/**
* The number of patches supplied by this update.
*/
@ -271,7 +283,7 @@ interface nsIUpdateManager : nsISupports
/**
*
*/
void removeUpdateAtInde(in long index);
void removeUpdateAtIndex(in long index);
/**
*

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

@ -20,6 +20,7 @@
*
* Contributor(s):
* Ben Goodger <ben@mozilla.org> (Original Author)
* Darin Fisher <darin@meer.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -56,6 +57,8 @@ const PREF_UPDATE_LASTUPDATETIME_FMT = "app.update.lastUpdateTime.%ID%";
const PREF_APP_EXTENSIONS_VERSION = "app.extensions.version";
const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
const URI_BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties";
const URI_UPDATE_NS = "http://www.mozilla.org/2005/app-update";
const KEY_APPDIR = "XCurProcD";
@ -700,7 +703,7 @@ UpdateManager.prototype = {
if (!this._activeUpdate) {
var updates = this._loadXMLFileIntoArray(getFile(KEY_APPDIR,
[FILE_UPDATE_ACTIVE]));
if (updates.length > 0)
if (updates.length > 0)
this._activeUpdate = updates[0];
}
},
@ -891,7 +894,25 @@ function Update(update) {
this.detailsURL = update.getAttribute("detailsURL");
this.licenseURL = update.getAttribute("licenseURL");
this.installDate = update.hasAttribute("installDate") ? parseInt(update.getAttribute("installDate")) : 0;
this.state = update.getAttribute("state");
this.isCompleteUpdate = false;
// The Update Name is either the string provided by the <update> element, or
// the string: "<App Name> <Update App Version>"
var name = "";
if (update.hasAttribute("name"))
name = update.getAttribute("name");
else {
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var brandBundle = sbs.createBundle(URI_BRAND_PROPERTIES);
var updateBundle = sbs.createBundle(URI_UPDATES_PROPERTIES);
var appName = brandBundle.GetStringFromName("brandShortName");
name = updateBundle.formatStringFromName("updateName",
[appName, this.version], 2);
}
this.name = name;
}
Update.prototype = {
/**
@ -914,11 +935,13 @@ Update.prototype = {
serialize: function(updates) {
var update = updates.createElementNS(URI_UPDATE_NS, "update");
update.setAttribute("type", this.type);
update.setAttribute("name", this.name);
update.setAttribute("version", this.version);
update.setAttribute("extensionVersion", this.extensionVersion);
update.setAttribute("detailsURL", this.detailsURL);
update.setAttribute("licenseURL", this.licenseURL);
update.setAttribute("installDate", this.installDate);
update.setAttribute("state", this.state);
document.documentElement.appendChild(update);
for (var i = 0; i < this.patchCount; ++i)
@ -1117,6 +1140,11 @@ Downloader.prototype = {
*/
_patch: null,
/**
* The nsIUpdate that we are downloading
*/
_update: null,
/**
* The nsIIncrementalDownload object handling the download
*/
@ -1330,6 +1358,8 @@ Downloader.prototype = {
var updateDir = this._getUpdatesDir();
this._update = update;
// This function may return null, which indicates that there are no patches
// to download.
this._patch = this._selectPatch(update, updateDir);
@ -1360,9 +1390,16 @@ Downloader.prototype = {
this._request.start(this, null);
this._writeStatusFile(updateDir, STATE_DOWNLOADING);
update.state = STATE_DOWNLOADING;
var um = Components.classes["@mozilla.org/updates/update-manager;1"]
.getService(Components.interfaces.nsIUpdateManager);
um.saveUpdates();
return STATE_DOWNLOADING;
},
/**
*
*/
_listeners: [],
/**
@ -1433,6 +1470,10 @@ Downloader.prototype = {
status = Components.results.NS_ERROR_UNEXPECTED;
}
this._writeStatusFile(this._getUpdatesDir(), state);
this._update.state = state;
var um = Components.classes["@mozilla.org/updates/update-manager;1"]
.getService(Components.interfaces.nsIUpdateManager);
um.saveUpdates();
}
var listenerCount = this._listeners.length;
@ -1454,10 +1495,22 @@ Downloader.prototype = {
}
};
/**
* A manager for update check timers. Manages timers that fire over long
* periods of time (e.g. days, weeks).
* @constructor
*/
function TimerManager() {
}
TimerManager.prototype = {
/**
* The set of registered timers.
*/
_timers: { },
/**
*
*/
registerTimer: function(id, callback, interval, type) {
const nsITimer = Components.interfaces.nsITimer;
var timer = Components.classes["@mozilla.org/timer;1"]
@ -1465,17 +1518,40 @@ TimerManager.prototype = {
var timerInterval = getPref("getIntPref", PREF_APP_UPDATE_TIMER, 5000);
var self = this;
/**
* A callback object implementing nsITimerCallback that determines if the
* user-registered callback should be invoked yet.
* @param id
* The id of the timer that fired
* @param callback
* The nsITimerCallback object supplied by the user that should be
* notified if the user's interval has expired.
* @param interval
* The user's interval
* @constructor
*/
function TimerCallback(id, callback, interval) {
this.id = id;
this.callback = callback;
this.interval = interval;
}
TimerCallback.prototype = {
/**
*
*/
notify: function(timer) {
LOG("self._timers = " + self._timers.toSource());
var lastUpdateTime = self._timers[this.id].lastUpdateTime;
var now = Math.round(Date.now() / 1000);
LOG("notify = " + (now - lastUpdateTime) + " > " + this.interval);
// Fudge the lastUpdateTime by some random increment of the update
// check interval (e.g. some random slice of 10 minutes) so that when
// the time comes to check, we offset each client request by a random
// amount so they don't all hit at once.
lastUpdateTime += Math.round(Math.random() * this.interval);
if ((now - lastUpdateTime) > this.interval &&
this.callback instanceof Components.interfaces.nsITimerCallback) {
this.callback.notify(timer);
@ -1504,6 +1580,9 @@ TimerManager.prototype = {
this._timers[id] = { timer: timer, lastUpdateTime: lastUpdateTime };
},
/**
* See nsISupports.idl
*/
QueryInterface: function(iid) {
if (!iid.equals(Components.interfaces.nsIUpdateTimerManager) &&
!iid.equals(Components.interfaces.nsISupports))
@ -1512,23 +1591,23 @@ TimerManager.prototype = {
}
};
function Version(aMajor, aMinor, aRelease, aBuild, aPlus)
{
/**
* Represents the version of an entity, in Firefox Version Format.
* @constructor
*/
function Version(aMajor, aMinor, aRelease, aBuild, aPlus) {
this.major = aMajor || 0;
this.minor = aMinor || 0;
this.release = aRelease || 0;
this.build = aBuild || 0;
this.plus = aPlus || 0;
}
Version.prototype = {
toString: function Version_toString()
{
toString: function Version_toString() {
return this.major + "." + this.minor + "." + this.subminor + "." + this.release + (this.plus ? "+" : "");
},
compare: function (aVersion)
{
compare: function (aVersion) {
var fields = ["major", "minor", "release", "build", "plus"];
for (var i = 0; i < fields.length; ++i) {
@ -1542,6 +1621,10 @@ Version.prototype = {
}
}
/**
* A service that provides a means to compare two FVF strings.
* @constructor
*/
function VersionChecker() {
}
@ -1552,16 +1635,14 @@ VersionChecker.prototype = {
// -ve if B is newer
// equal if A == B
// +ve if A is newer
compare: function(aVersionA, aVersionB)
{
compare: function(aVersionA, aVersionB) {
var a = this._decomposeVersion(aVersionA);
var b = this._decomposeVersion(aVersionB);
return a.compare(b);
},
_decomposeVersion: function(aVersion)
{
_decomposeVersion: function(aVersion) {
var plus = 0;
if (aVersion.charAt(aVersion.length-1) == "+") {
aVersion = aVersion.substr(0, aVersion.length-1);
@ -1577,23 +1658,20 @@ VersionChecker.prototype = {
plus);
},
_getValidInt: function(aPartString)
{
_getValidInt: function(aPartString) {
var integer = parseInt(aPartString);
if (isNaN(integer))
return 0;
return integer;
},
isValidVersion: function(aVersion)
{
isValidVersion: function(aVersion) {
return /^([0-9]+\.){0,3}[0-9]+\+?$/.test(aVersion);
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function(aIID)
{
QueryInterface: function(aIID) {
if (!aIID.equals(Components.interfaces.nsIVersionChecker) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;