245953 - software update works poorly, including:

- various problems with update wizard
- hides app update notification icon for that session after the user has
visited the products page
- makes sure the app update info is cleared when no newer versions are present
- make app update work when extension update is disabled, general code
correctness etc.
- make sure the browser window that contains the product page is focused.


git-svn-id: svn://10.0.0.236/trunk@159051 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
ben%bengoodger.com 2004-07-12 08:57:18 +00:00
parent 88ff2b2f5a
commit 81e31b2cd0
11 changed files with 412 additions and 162 deletions

View File

@ -73,6 +73,7 @@ pref("update.extensions.autoUpdate", false);
pref("update.interval", 604800000); // every 7 days
pref("update.lastUpdateDate", 0); // UTC offset when last update was performed.
pref("update.showSlidingNotification", true); // windows-only slide-up taskbar notification
// These prefs relate to the number and severity of updates available. This is a
// cache that the browser notification mechanism uses to determine if it should show

View File

@ -51,8 +51,8 @@ BEGIN
ID_DDE_APPLICATION_NAME, "Firefox Debug"
IDS_STARTMENU_APPNAME, "Firefox Debug"
#else
ID_DDE_APPLICATION_NAME, "Firefox"
IDS_STARTMENU_APPNAME, "Firefox"
ID_DDE_APPLICATION_NAME, "Firefox Debug"
IDS_STARTMENU_APPNAME, "Firefox Debug"
#endif
END

View File

@ -539,6 +539,9 @@ function delayedStartup()
shell.shouldCheckDefaultBrowser = checkEveryTime.value;
}
#endif
var updatePanel = document.getElementById("statusbar-updates");
updatePanel.init();
}
function Shutdown()

View File

@ -1 +1 @@
0.9.0+
0.9.1+

View File

@ -40,7 +40,7 @@ const nsIUpdateService = Components.interfaces.nsIUpdateService;
const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
const PREF_EM_APP_ID = "app.id";
const PREF_EM_APP_VERSION = "app.version";
const PREF_EM_APP_EXTENSIONS_VERSION = "app.extensions.version";
const PREF_EM_APP_BUILDID = "app.build_id";
const PREF_EM_LAST_APP_VERSION = "extensions.lastAppVersion";
const PREF_UPDATE_COUNT = "update.extensions.count";
@ -161,35 +161,27 @@ const ROOT_THEME = "urn:mozilla:theme:root";
function getItemPrefix(aItemType)
{
var prefix = "";
switch (aItemType) {
case nsIUpdateItem.TYPE_EXTENSION:
if (aItemType & nsIUpdateItem.TYPE_EXTENSION)
prefix = PREFIX_EXTENSION;
break;
case nsIUpdateItem.TYPE_THEME:
else if (aItemType & nsIUpdateItem.TYPE_THEME)
prefix = PREFIX_THEME;
break;
}
return prefix;
}
function getItemRoot(aItemType)
{
var root = "";
switch (aItemType) {
case nsIUpdateItem.TYPE_EXTENSION:
if (aItemType & nsIUpdateItem.TYPE_EXTENSION)
root = ROOT_EXTENSION;
break;
case nsIUpdateItem.TYPE_THEME:
else if (aItemType & nsIUpdateItem.TYPE_THEME)
root = ROOT_THEME;
break;
}
return root;
}
function getItemRoots(aItemType)
{
var roots = [];
if (aItemType == nsIUpdateItem.TYPE_ADDON)
if (aItemType & nsIUpdateItem.TYPE_ADDON)
roots = roots.concat([getItemRoot(nsIUpdateItem.TYPE_EXTENSION),
getItemRoot(nsIUpdateItem.TYPE_THEME)]);
else
@ -1680,9 +1672,9 @@ nsExtensionManager.prototype = {
}
if (file.exists()) {
if (aItemType == nsIUpdateItem.TYPE_EXTENSION)
if (aItemType & nsIUpdateItem.TYPE_EXTENSION)
this.installExtension(file, nsIExtensionManager.FLAG_INSTALL_GLOBAL);
else if (aItemType == nsIUpdateItem.TYPE_THEME)
else if (aItemType & nsIUpdateItem.TYPE_THEME)
this.installTheme(file, nsIExtensionManager.FLAG_INSTALL_GLOBAL);
}
else
@ -1826,7 +1818,7 @@ nsExtensionManager.prototype = {
// now is the same one that was started last time.
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var currAppVersion = pref.getCharPref(PREF_EM_APP_VERSION);
var currAppVersion = pref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
try {
var lastAppVersion = pref.getCharPref(PREF_EM_LAST_APP_VERSION);
}
@ -1843,9 +1835,9 @@ nsExtensionManager.prototype = {
for (var i = 0; i < items.length; ++i) {
// Now disable the extension so it won't hurt anything.
var itemType = getItemType(this._ds._getResourceForItem(items[i].id).Value);
if (itemType == nsIUpdateItem.TYPE_EXTENSION)
if (itemType & nsIUpdateItem.TYPE_EXTENSION)
this.disableExtension(items[i].id);
else if (itemType == nsIUpdateItem.TYPE_THEME) {
else if (itemType & nsIUpdateItem.TYPE_THEME) {
var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIXULChromeRegistry);
var pref = Components.classes["@mozilla.org/preferences-service;1"]
@ -2263,7 +2255,7 @@ nsExtensionManager.prototype = {
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var appID = pref.getCharPref(PREF_EM_APP_ID);
var appVersion = pref.getCharPref(PREF_EM_APP_VERSION);
var appVersion = pref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
if (aItems.length == 0) {
var addonType = nsIUpdateItem.TYPE_ADDON;
@ -2680,12 +2672,14 @@ nsExtensionItemUpdater.prototype = {
var version = aDatasource.GetTarget(extensionRes, versionArc, true);
if (!version) { // Report an error if the update manifest is incomplete
this.onDatasourceError(aItem, "malformed-rdf");
// XXXben show console message
return;
}
version = version.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
var updateLink = aDatasource.GetTarget(extensionRes, updateLinkArc, true);
if (!updateLink) { // Report an error if the update manifest is incomplete
this.onDatasourceError(aItem, "malformed-rdf");
// XXXben show console message
return;
}
updateLink = updateLink.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
@ -2832,7 +2826,7 @@ nsExtensionsDataSource.prototype = {
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var appVersion = pref.getCharPref(PREF_EM_APP_VERSION);
var appVersion = pref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
var appID = pref.getCharPref(PREF_EM_APP_ID);
var targets = aDS.GetTargets(aSource, this._emR("targetApplication"), true);
@ -2871,7 +2865,7 @@ nsExtensionsDataSource.prototype = {
var itemType = getItemType(e.Value);
var id = stripPrefix(e.Value, itemType);
var item = Components.classes["@mozilla.org/updates/item;1"]
.createInstance(Components.interfaces.nsIUpdateItem);
.createInstance(Components.interfaces.nsIUpdateItem);
item.init(id, this._getItemProperty(e, "version"),
this._getItemProperty(e, "name"),
-1, "", "", this._getItemProperty(e, "updateURL"),
@ -2912,7 +2906,7 @@ nsExtensionsDataSource.prototype = {
item.init(id, this.getItemProperty(id, "version"),
this.getItemProperty(id, "name"),
-1, "", "",
this.getItemProperty(id, "updateURL"), aType);
this.getItemProperty(id, "updateURL"), getItemType(roots[i]));
items.push(item);
}
}
@ -3455,7 +3449,7 @@ nsExtensionsDataSource.prototype = {
if (aProperty.EqualsNode(this._emR("iconURL"))) {
var itemType = getItemType(aSource.Value);
if (itemType == nsIUpdateItem.TYPE_EXTENSION) {
if (itemType & nsIUpdateItem.TYPE_EXTENSION) {
var hasIconURL = this._composite.hasArcOut(aSource, aProperty);
// If the download entry doesn't have a IconURL property, use a
// generic icon URL instead.
@ -3478,7 +3472,7 @@ nsExtensionsDataSource.prototype = {
}
}
}
else if (itemType == nsIUpdateItem.TYPE_THEME) {
else if (itemType & nsIUpdateItem.TYPE_THEME) {
var res = this._getThemeJARURL(aSource, "icon.png", "chrome://mozapps/skin/extensions/themeGeneric.png");
if (res)
return res;
@ -3486,7 +3480,7 @@ nsExtensionsDataSource.prototype = {
}
else if (aProperty.EqualsNode(this._emR("previewImage"))) {
var itemType = getItemType(aSource.Value);
if (itemType == nsIUpdateItem.TYPE_THEME) {
if (itemType & nsIUpdateItem.TYPE_THEME) {
var res = this._getThemeJARURL(aSource, "preview.png", null);
if (res)
return res;

View File

@ -43,9 +43,14 @@ const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
const nsIUpdateService = Components.interfaces.nsIUpdateService;
const nsIExtensionManager = Components.interfaces.nsIExtensionManager;
const PREF_APP_ID = "app.id";
const PREF_UPDATE_APP_UPDATESAVAILABLE = "update.app.updatesAvailable";
const PREF_UPDATE_EXTENSIONS_ENABLED = "update.extensions.enabled";
const PREF_APP_ID = "app.id";
const PREF_UPDATE_EXTENSIONS_ENABLED = "update.extensions.enabled";
const PREF_UPDATE_APP_UPDATESAVAILABLE = "update.app.updatesAvailable";
const PREF_UPDATE_APP_UPDATEVERSION = "update.app.updateVersion";
const PREF_UPDATE_APP_UPDATEDESCRIPTION = "update.app.updateDescription";
const PREF_UPDATE_APP_UPDATEURL = "update.app.updateURL";
const PREF_UPDATE_EXTENSIONS_COUNT = "update.extensions.count";
var gSourceEvent = null;
var gUpdateTypes = null;
@ -64,6 +69,7 @@ var gUpdateWizard = {
shouldAutoCheck: false,
updatingApp: false,
remainingExtensionUpdateCount: 0,
init: function ()
{
@ -85,11 +91,6 @@ var gUpdateWizard = {
gMismatchPage.init();
},
uninit: function ()
{
gUpdatePage.uninit();
},
onWizardFinish: function ()
{
if (this.shouldSuggestAutoChecking) {
@ -121,10 +122,82 @@ var gUpdateWizard = {
.createInstance(Components.interfaces.nsISupportsString);
url.data = updates.appUpdateURL;
ary.AppendElement(url);
ww.openWindow(null, "chrome://browser/content/browser.xul",
"_blank", "chrome,all,dialog=no", ary);
function obs(aWindow)
{
this._win = aWindow;
}
obs.prototype = {
_win: null,
notify: function (aTimer)
{
this._win.focus();
}
};
var win = ww.openWindow(null, "chrome://browser/content/browser.xul",
"_blank", "chrome,all,dialog=no", ary);
var timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
timer.initWithCallback(new obs(win), 100,
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
#endif
// Clear the "app update available" pref as an interim amnesty assuming
// the user actually does install the new version. If they don't, a subsequent
// update check will poke them again.
this.clearAppUpdatePrefs();
}
else {
// Downloading and Installed Extension
this.clearExtensionUpdatePrefs();
}
// Send an event to refresh any FE notification components.
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
var userEvt = Components.interfaces.nsIUpdateService.SOURCE_EVENT_USER;
os.notifyObservers(null, "Update:Ended", userEvt.toString());
},
clearAppUpdatePrefs: function ()
{
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
// Set the "applied app updates this session" pref so that the update service
// does not display the update notifier for application updates anymore this
// session, to give the user a one-cycle amnesty to install the newer version.
var updates = Components.classes["@mozilla.org/updates/update-service;1"]
.getService(Components.interfaces.nsIUpdateService);
updates.appUpdatesAvailable = false;
// Unset prefs used by the update service to signify application updates
if (pref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE))
pref.clearUserPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
if (pref.prefHasUserValue(PREF_UPDATE_APP_UPDATEVERSION))
pref.clearUserPref(PREF_UPDATE_APP_UPDATEVERSION);
if (pref.prefHasUserValue(PREF_UPDATE_APP_UPDATEURL))
pref.clearUserPref(PREF_UPDATE_APP_UPDATEDESCRIPTION);
if (pref.prefHasUserValue(PREF_UPDATE_APP_UPDATEURL))
pref.clearUserPref(PREF_UPDATE_APP_UPDATEURL);
},
clearExtensionUpdatePrefs: function ()
{
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
// Set the "applied extension updates this session" pref so that the
// update service does not display the update notifier for extension
// updates anymore this session (the updates will be applied at the next
// start).
var updates = Components.classes["@mozilla.org/updates/update-service;1"]
.getService(Components.interfaces.nsIUpdateService);
updates.extensionUpdatesAvailable = this.remainingExtensionUpdateCount;
if (pref.prefHasUserValue(PREF_UPDATE_EXTENSIONS_COUNT))
pref.clearUserPref(PREF_UPDATE_EXTENSIONS_COUNT);
},
_setUpButton: function (aButtonID, aButtonKey, aDisabled)
@ -208,7 +281,6 @@ var gMismatchPage = {
var gUpdatePage = {
_completeCount: 0,
_updateState: 0,
_messages: ["Update:Extension:Started",
"Update:Extension:Ended",
"Update:Extension:Item-Started",
@ -235,8 +307,6 @@ var gUpdatePage = {
.getService(Components.interfaces.nsIUpdateService);
updates.checkForUpdatesInternal(gUpdateWizard.items, gUpdateWizard.items.length,
gUpdateTypes, gSourceEvent);
this._updateState = nsIUpdateService.UPDATED_NONE;
},
uninit: function ()
@ -319,6 +389,7 @@ var gUpdatePage = {
}
if (canFinish) {
gUpdatePage.uninit();
if (gUpdateWizard.itemsToUpdate.length > 0 || gUpdateWizard.appUpdatesAvailable)
document.getElementById("checking").setAttribute("next", "found");
document.documentElement.advance();
@ -350,7 +421,7 @@ var gFoundPage = {
// If we have an App entry in the list, check it and uncheck
// the others since the two are mutually exclusive installs.
updateitem.type = item.type;
if (item.type == nsIUpdateItem.TYPE_APP) {
if (item.type & nsIUpdateItem.TYPE_APP) {
updateitem.checked = true;
this._appUpdateExists = true;
this._appSelected = true;
@ -373,7 +444,7 @@ var gFoundPage = {
{
var i;
if (this._appUpdateExists) {
if (aEvent.target.type == nsIUpdateItem.TYPE_APP) {
if (aEvent.target.type & nsIUpdateItem.TYPE_APP) {
for (i = 0; i < this._nonAppItems.length; ++i) {
var nonAppItem = this._nonAppItems[i];
nonAppItem.checked = !aEvent.target.checked;
@ -421,11 +492,13 @@ var gInstallingPage = {
// Get XPInstallManager and kick off download/install
// process, registering us as an observer.
var items = [];
gUpdateWizard.remainingExtensionUpdateCount = gUpdateWizard.itemsToUpdate.length;
var foundList = document.getElementById("foundList");
for (var i = 0; i < foundList.childNodes.length; ++i) {
var item = foundList.childNodes[i];
if (item.type != nsIUpdateItem.TYPE_APP) {
if (!(item.type & nsIUpdateItem.TYPE_APP) && item.checked) {
items.push(item.url);
this._objs.push({ name: item.name });
}
@ -460,6 +533,8 @@ var gInstallingPage = {
this._objs[aIndex].error = aValue;
this._errors = true;
}
else
--gUpdateWizard.remainingExtensionUpdateCount;
break;
case nsIXPIProgressDialog.DIALOG_CLOSE:
document.getElementById("installing").setAttribute("next", this._errors ? "errors" : "finished");

View File

@ -51,7 +51,6 @@
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&updateWizard.title;"
onload="gUpdateWizard.init()"
onunload="gUpdateWizard.uninit()"
onwizardfinish="gUpdateWizard.onWizardFinish();"
style="width: 40em;"
buttons="accept,cancel">

View File

@ -18,15 +18,40 @@
<xul:label xbl:inherits="value=updateCount" flex="1" crop="right"/>
</xul:hbox>
</content>
<implementation implements="nsIAlertListener">
<constructor>
<implementation implements="nsIObserver">
<destructor>
<![CDATA[
this.refreshData(null);
]]>
</constructor>
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.removeObserver(this, "Update:Ended");
]]>
</destructor>
<method name="init">
<body>
<![CDATA[
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(this, "Update:Ended", false);
this.refreshData();
]]>
</body>
</method>
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
if (aTopic == "Update:Ended")
this.refreshData();
]]>
</body>
</method>
<method name="refreshData">
<parameter name="aSourceEvent"/>
<body>
<![CDATA[
var updates = Components.classes["@mozilla.org/updates/update-service;1"]
@ -37,7 +62,11 @@
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var updateStrings = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
var key = "updatesAvailableTooltip-" + this.severity;
var key;
if (this.updateCount > 0)
key = "updatesAvailableTooltip-" + this.severity;
else
key = "updatesCheckForUpdatesTooltip";
var tooltip = updateStrings.GetStringFromName(key);
this.setAttribute("tooltiptext", tooltip);
]]>

View File

@ -22,3 +22,4 @@ updatesAvailableTooltip-0=Update(s) Available
updatesAvailableTooltip-1=Update(s) Available
updatesAvailableTooltip-2=Critical Update(s) Available
updatesCheckForUpdatesTooltip=Double-click here to check for Updates

View File

@ -35,6 +35,7 @@
*
* ***** END LICENSE BLOCK ***** */
// XXXben - document these files
#include "nsISupports.idl"
@ -93,6 +94,10 @@ interface nsIUpdateService : nsISupports
readonly attribute string appUpdateVersion;
readonly attribute wstring appUpdateDescription;
readonly attribute wstring appUpdateURL;
// these are per-session settings
attribute boolean appUpdatesAvailable;
attribute long extensionUpdatesAvailable;
};
[scriptable, uuid(22d35700-5765-42e1-914b-a0da7c911a8c)]

View File

@ -35,22 +35,23 @@
*
* ***** END LICENSE BLOCK ***** */
const PREF_APP_ID = "app.id";
const PREF_APP_VERSION = "app.version";
const PREF_UPDATE_APP_ENABLED = "update.app.enabled";
const PREF_UPDATE_APP_URI = "update.app.url";
const PREF_UPDATE_APP_UPDATESAVAILABLE = "update.app.updatesAvailable";
const PREF_UPDATE_APP_UPDATEVERSION = "update.app.updateVersion";
const PREF_UPDATE_APP_UPDATEDESCRIPTION = "update.app.updateDescription";
const PREF_UPDATE_APP_UPDATEURL = "update.app.updateURL";
const PREF_APP_ID = "app.id";
const PREF_APP_VERSION = "app.version";
const PREF_UPDATE_APP_ENABLED = "update.app.enabled";
const PREF_UPDATE_APP_URI = "update.app.url";
const PREF_UPDATE_APP_UPDATESAVAILABLE = "update.app.updatesAvailable";
const PREF_UPDATE_APP_UPDATEVERSION = "update.app.updateVersion";
const PREF_UPDATE_APP_UPDATEDESCRIPTION = "update.app.updateDescription";
const PREF_UPDATE_APP_UPDATEURL = "update.app.updateURL";
const PREF_UPDATE_SHOW_SLIDING_NOTIFICATION = "update.showSlidingNotification";
const PREF_UPDATE_EXTENSIONS_ENABLED = "update.extensions.enabled";
const PREF_UPDATE_EXTENSIONS_AUTOUPDATE = "update.extensions.autoUpdate";
const PREF_UPDATE_EXTENSIONS_COUNT = "update.extensions.count";
const PREF_UPDATE_EXTENSIONS_ENABLED = "update.extensions.enabled";
const PREF_UPDATE_EXTENSIONS_AUTOUPDATE = "update.extensions.autoUpdate";
const PREF_UPDATE_EXTENSIONS_COUNT = "update.extensions.count";
const PREF_UPDATE_INTERVAL = "update.interval";
const PREF_UPDATE_LASTUPDATEDATE = "update.lastUpdateDate";
const PREF_UPDATE_SEVERITY = "update.severity";
const PREF_UPDATE_INTERVAL = "update.interval";
const PREF_UPDATE_LASTUPDATEDATE = "update.lastUpdateDate";
const PREF_UPDATE_SEVERITY = "update.severity";
const nsIUpdateService = Components.interfaces.nsIUpdateService;
const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
@ -63,23 +64,26 @@ function nsUpdateService()
this._pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
this.watchForUpdates();
var pbi = this._pref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
pbi.addObserver(PREF_UPDATE_APP_ENABLED, this, false);
pbi.addObserver(PREF_UPDATE_EXTENSIONS_ENABLED, this, false);
pbi.addObserver(PREF_UPDATE_INTERVAL, this, false);
// Observe xpcom-shutdown to unhook pref branch observers above to avoid
// shutdown leaks.
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(this, "xpcom-shutdown", false);
}
nsUpdateService.prototype = {
_timer: null,
_pref: null,
_updateObserver: null,
// whether or not we're currently updating. prevents multiple simultaneous
// update operations.
updating: false,
updateEnded: function ()
{
// this.updating = false;
delete this._updateObserver;
},
_appUpdatesEnabled: true,
_extUpdatesEnabled: true,
/////////////////////////////////////////////////////////////////////////////
// nsIUpdateService
watchForUpdates: function ()
@ -87,18 +91,17 @@ nsUpdateService.prototype = {
// This is called when the app starts, so check to see if the time interval
// expired between now and the last time an automated update was performed.
// now is the same one that was started last time.
var appUpdatesEnabled = this._pref.getBoolPref(PREF_UPDATE_APP_ENABLED);
var extUpdatesEnabled = this._pref.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
if (!appUpdatesEnabled && !extUpdatesEnabled)
this._appUpdatesEnabled = this._pref.getBoolPref(PREF_UPDATE_APP_ENABLED);
this._extUpdatesEnabled = this._pref.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
if (!this._appUpdatesEnabled && !this._extUpdatesEnabled)
return;
var interval = this._pref.getIntPref(PREF_UPDATE_INTERVAL);
var lastUpdateTime = this._pref.getIntPref(PREF_UPDATE_LASTUPDATEDATE);
var timeSinceLastCheck = lastUpdateTime ? Math.abs(Date.UTC() - lastUpdateTime) : 0;
var timeSinceLastCheck = lastUpdateTime ? this._nowInMilliseconds - lastUpdateTime : 0;
if (timeSinceLastCheck > interval) {
// if (!this.updating)
this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_ANY,
nsIUpdateService.SOURCE_EVENT_BACKGROUND);
this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_ANY,
nsIUpdateService.SOURCE_EVENT_BACKGROUND);
}
else
this._makeTimer(interval - timeSinceLastCheck);
@ -106,8 +109,6 @@ nsUpdateService.prototype = {
checkForUpdates: function (aItems, aItemCount, aUpdateTypes, aSourceEvent, aParentWindow)
{
// if (this.updating) return;
switch (aSourceEvent) {
case nsIUpdateService.SOURCE_EVENT_MISMATCH:
case nsIUpdateService.SOURCE_EVENT_USER:
@ -125,6 +126,9 @@ nsUpdateService.prototype = {
ary.AppendElement(sourceEvent);
for (var i = 0; i < aItems.length; ++i)
ary.AppendElement(aItems[i]);
// This *must* be modal so as not to break startup! This code is invoked before
// the main event loop is initiated (via checkForMismatches).
ww.openWindow(aParentWindow, "chrome://mozapps/content/update/update.xul",
"", "chrome,modal,centerscreen", ary);
break;
@ -134,48 +138,57 @@ nsUpdateService.prototype = {
// updates that this function broadcasts.
this.checkForUpdatesInternal([], 0, aUpdateTypes, aSourceEvent);
// If this was a background update, reset timer.
this._makeTimer(this._pref.getIntPref(PREF_UPDATE_INTERVAL));
break;
}
},
checkForUpdatesInternal: function (aItems, aItemCount, aUpdateTypes, aSourceEvent)
{
// this.updating = true;
// Listen for notifications sent out by the app updater (implemented here) and the
// extension updater (implemented in nsExtensionItemUpdater)
if (this._updateObserver) {
this._updateObserver.destroy();
this._updateObserver = null;
}
this._updateObserver = new nsUpdateObserver(aUpdateTypes, aSourceEvent, this);
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(this._updateObserver, "Update:Extension:Item-Ended", false);
os.addObserver(this._updateObserver, "Update:Extension:Ended", false);
os.addObserver(this._updateObserver, "Update:App:Ended", false);
if ((aUpdateTypes == nsIUpdateItem.TYPE_ANY) || (aUpdateTypes == nsIUpdateItem.TYPE_APP)) {
var dsURI = this._pref.getComplexValue(PREF_UPDATE_APP_URI,
Components.interfaces.nsIPrefLocalizedString).data;
var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
var ds = rdf.GetDataSource(dsURI);
var rds = ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
if (rds.loaded)
this.datasourceLoaded(ds);
else {
var sink = ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
sink.addXMLSinkObserver(new nsAppUpdateXMLRDFDSObserver(this));
if ((aUpdateTypes & nsIUpdateItem.TYPE_ANY) || (aUpdateTypes & nsIUpdateItem.TYPE_APP)) {
if (this._appUpdatesEnabled) {
os.addObserver(this._updateObserver, "Update:App:Ended", false);
var dsURI = this._pref.getComplexValue(PREF_UPDATE_APP_URI,
Components.interfaces.nsIPrefLocalizedString).data;
var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
var ds = rdf.GetDataSource(dsURI);
var rds = ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
if (rds.loaded)
this.datasourceLoaded(ds);
else {
var sink = ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
sink.addXMLSinkObserver(new nsAppUpdateXMLRDFDSObserver(this));
}
}
}
if (aUpdateTypes != nsIUpdateItem.TYPE_APP) {
var em = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager);
em.update(aItems, aItems.length);
if (!(aUpdateTypes & nsIUpdateItem.TYPE_APP)) { // TYPE_EXTENSION, TYPE_ANY, etc.
if (this._extUpdatesEnabled) {
os.addObserver(this._updateObserver, "Update:Extension:Item-Ended", false);
os.addObserver(this._updateObserver, "Update:Extension:Ended", false);
var em = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager);
em.update(aItems, aItems.length);
}
}
if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_BACKGROUND)
this._pref.setIntPref(PREF_UPDATE_LASTUPDATEDATE, Math.abs(Date.UTC()));
if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_BACKGROUND &&
(this._appUpdatesEnabled || this._extUpdatesEnabled)) {
this._pref.setIntPref(PREF_UPDATE_LASTUPDATEDATE, this._nowInMilliseconds);
// If this was a background update, reset timer.
this._makeTimer(this._pref.getIntPref(PREF_UPDATE_INTERVAL));
}
},
_rdf: null,
@ -239,6 +252,8 @@ nsUpdateService.prototype = {
Components.interfaces.nsISupportsString,
descStr);
}
else
this._clearAppUpdatePrefs();
// The Update Wizard uses this notification to determine that the application
// update process is now complete.
@ -259,9 +274,8 @@ nsUpdateService.prototype = {
{
// The number of available updates is the number of extension/theme/other
// updates + 1 for an application update, if one is available.
var updateCount = this._pref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT);
if (this._pref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE) &&
this._pref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE))
var updateCount = this.extensionUpdatesAvailable;
if (this.appUpdatesAvailable)
++updateCount;
return updateCount;
},
@ -289,14 +303,92 @@ nsUpdateService.prototype = {
Components.interfaces.nsIPrefLocalizedString).data;
},
_appUpdatesAvailable: undefined,
get appUpdatesAvailable()
{
if (this._appUpdatesAvailable === undefined) {
return (this._pref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE) &&
this._pref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE));
}
return this._appUpdatesAvailable;
},
set appUpdatesAvailable(aValue)
{
this._appUpdatesAvailable = aValue;
return aValue;
},
_extensionUpdatesAvailable: undefined,
get extensionUpdatesAvailable()
{
if (this._extensionUpdatesAvailable === undefined)
return this._pref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT);
return this._extensionUpdatesAvailable;
},
set extensionUpdatesAvailable(aValue)
{
this._extensionUpdatesAvailable = aValue;
return aValue;
},
/////////////////////////////////////////////////////////////////////////////
// nsITimerCallback
notify: function (aTimer)
{
// if (this.updating) return;
this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_ANY,
nsIUpdateService.SOURCE_EVENT_BACKGROUND);
this._makeTimer(this._pref.getIntPref(PREF_UPDATE_INTERVAL));
},
/////////////////////////////////////////////////////////////////////////////
// nsIObserver
observe: function (aSubject, aTopic, aData)
{
switch (aTopic) {
case "nsPref:changed":
// User changed update prefs in Tools->Options
var noUpdatingGoingOn = !this._appUpdatesEnabled && !this._extUpdatesEnabled;
this._appUpdatesEnabled = this._pref.getBoolPref(PREF_UPDATE_APP_ENABLED);
this._extUpdatesEnabled = this._pref.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
var needsNotification = false;
if (!this._appUpdatesEnabled) {
this._clearAppUpdatePrefs();
needsNotification = true;
}
if (!this._extUpdatesEnabled) {
// Unset prefs used by the update service to signify extension updates
if (this._pref.prefHasUserValue(PREF_UPDATE_EXTENSIONS_COUNT))
this._pref.clearUserPref(PREF_UPDATE_EXTENSIONS_COUNT);
needsNotification = true;
}
if (needsNotification) {
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
var backgroundEvt = Components.interfaces.nsIUpdateService.SOURCE_EVENT_BACKGROUND;
os.notifyObservers(null, "Update:Ended", backgroundEvt.toString());
}
var needsToStartTimer = (noUpdatingGoingOn && this._appUpdatesEnabled) ||
(noUpdatingGoingOn && this._extUpdatesEnabled);
// If the update interval changed or we went from a state in which there was
// no updating going on into one in which there is, we need to start the
// update timer
if (aData == PREF_UPDATE_INTERVAL || needsToStartTimer)
this._makeTimer(this._pref.getIntPref(PREF_UPDATE_INTERVAL));
break;
case "xpcom-shutdown":
// Clean up held observers etc to avoid leaks.
var pbi = this._pref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
pbi.removeObserver(PREF_UPDATE_APP_ENABLED, this);
pbi.removeObserver(PREF_UPDATE_EXTENSIONS_ENABLED, this);
pbi.removeObserver(PREF_UPDATE_INTERVAL, this);
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.removeObserver(this, "xpcom-shutdown");
break;
}
},
/////////////////////////////////////////////////////////////////////////////
@ -311,12 +403,38 @@ nsUpdateService.prototype = {
this._timer.initWithCallback(this, aDelay,
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
},
get _nowInMilliseconds ()
{
var d = new Date();
return Date.UTC(d.getUTCFullYear(),
d.getUTCMonth(),
d.getUTCDay(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds(),
d.getUTCMilliseconds());
},
_clearAppUpdatePrefs: function ()
{
// Unset prefs used by the update service to signify application updates
if (this._pref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE))
this._pref.clearUserPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
if (this._pref.prefHasUserValue(PREF_UPDATE_APP_UPDATEVERSION))
this._pref.clearUserPref(PREF_UPDATE_APP_UPDATEVERSION);
if (this._pref.prefHasUserValue(PREF_UPDATE_APP_UPDATEURL))
this._pref.clearUserPref(PREF_UPDATE_APP_UPDATEDESCRIPTION);
if (this._pref.prefHasUserValue(PREF_UPDATE_APP_UPDATEURL))
this._pref.clearUserPref(PREF_UPDATE_APP_UPDATEURL);
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function (aIID)
{
if (!aIID.equals(Components.interfaces.nsIUpdateService) &&
!aIID.equals(Components.interfaces.nsIObserver) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
@ -336,13 +454,19 @@ nsUpdateObserver.prototype = {
_updateTypes: 0,
_sourceEvent: 0,
_updateState: 0,
_endedTimer : null,
get _doneUpdating()
{
var appUpdatesEnabled = this._service._appUpdatesEnabled;
var extUpdatesEnabled = this._service._extUpdatesEnabled;
var test = 0;
var updatingApp = this._updateTypes == nsIUpdateItem.TYPE_ANY ||
this._updateTypes == nsIUpdateItem.TYPE_APP;
var updatingExt = this._updateTypes != nsIUpdateItem.TYPE_APP;
var updatingApp = (this._updateTypes & nsIUpdateItem.TYPE_ANY ||
this._updateTypes & nsIUpdateItem.TYPE_APP) &&
appUpdatesEnabled;
var updatingExt = !(this._updateTypes & nsIUpdateItem.TYPE_APP) &&
extUpdatesEnabled;
if (updatingApp)
test |= UPDATED_APP;
@ -374,50 +498,69 @@ nsUpdateObserver.prototype = {
}
if (this._doneUpdating) {
// The Inline Browser Update UI uses this notification to refresh its update
// UI if necessary.
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, "Update:Ended", this._sourceEvent.toString());
// Show update notification UI if:
// We were updating any types and any item was found
// We were updating extensions and an extension update was found.
// We were updating app and an app update was found.
var updatesAvailable = (((this._updateTypes == nsIUpdateItem.TYPE_EXTENSION) ||
(this._updateTypes == nsIUpdateItem.TYPE_ANY)) &&
this._pref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT) != 0);
if (!updatesAvailable) {
updatesAvailable = ((this._updateTypes == nsIUpdateItem.TYPE_APP) ||
(this._updateTypes == nsIUpdateItem.TYPE_ANY)) &&
this._pref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
}
if (updatesAvailable && this._sourceEvent == nsIUpdateService.SOURCE_EVENT_BACKGROUND) {
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var bundle = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
var alertTitle = bundle.GetStringFromName("updatesAvailableTitle");
var alertText = bundle.GetStringFromName("updatesAvailableText");
var alerts = Components.classes["@mozilla.org/alerts-service;1"]
.getService(Components.interfaces.nsIAlertsService);
alerts.showAlertNotification("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
alertTitle, alertText, true, "", this);
}
os.removeObserver(this, "Update:Extension:Item-Ended");
os.removeObserver(this, "Update:Extension:Ended");
os.removeObserver(this, "Update:App:Ended");
this._service.updating = false;
// Do the finalize stuff on a timer to let other observers have a chance to
// handle
if (this._endedTimer)
this._endedTimer.cancel();
this._endedTimer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
this._endedTimer.initWithCallback(this, 0,
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}
},
notify: function (aTimer)
{
// The Inline Browser Update UI uses this notification to refresh its update
// UI if necessary.
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, "Update:Ended", this._sourceEvent.toString());
// Show update notification UI if:
// We were updating any types and any item was found
// We were updating extensions and an extension update was found.
// We were updating app and an app update was found.
var updatesAvailable = (((this._updateTypes & nsIUpdateItem.TYPE_EXTENSION) ||
(this._updateTypes & nsIUpdateItem.TYPE_ANY)) &&
this._pref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT) != 0);
if (!updatesAvailable) {
updatesAvailable = ((this._updateTypes & nsIUpdateItem.TYPE_APP) ||
(this._updateTypes & nsIUpdateItem.TYPE_ANY)) &&
this._pref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
}
var showNotification = this._pref.getBoolPref(PREF_UPDATE_SHOW_SLIDING_NOTIFICATION);
if (showNotification && updatesAvailable &&
this._sourceEvent == nsIUpdateService.SOURCE_EVENT_BACKGROUND) {
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var bundle = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
var alertTitle = bundle.GetStringFromName("updatesAvailableTitle");
var alertText = bundle.GetStringFromName("updatesAvailableText");
var alerts = Components.classes["@mozilla.org/alerts-service;1"]
.getService(Components.interfaces.nsIAlertsService);
alerts.showAlertNotification("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
alertTitle, alertText, true, "", this);
}
this.destroy();
},
destroy: function ()
{
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
try { os.removeObserver(this, "Update:Extension:Item-Ended"); } catch (e) { }
try { os.removeObserver(this, "Update:Extension:Ended"); } catch (e) { }
try { os.removeObserver(this, "Update:App:Ended"); } catch (e) { }
},
////////////////////////////////////////////////////////////////////////////
// nsIObserver
// nsIAlertListener
onAlertFinished: function ()
{
},