home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2006 December
/
PCWorld_2006-12_cd.bin
/
komunikace
/
netscape
/
nsb-install-8-1-2.exe
/
components
/
nsUpdateService.js
< prev
next >
Wrap
Text File
|
2006-01-06
|
75KB
|
1,994 lines
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Update Service.
*
* The Initial Developer of the Original Code is Ben Goodger.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@bengoodger.com>
*
* 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
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const PREF_APP_ID = "app.id";
const PREF_APP_VERSION = "app.version";
const PREF_SAFETYNET_VERSION = "safetynet.version";
const PREF_UPDATE_APP_ENABLED = "app.update.enabled";
const PREF_UPDATE_APP_AUTOUPDATEENABLED = "app.update.autoUpdateEnabled";
const PREF_UPDATE_APP_URI = "app.update.url";
const PREF_UPDATE_APP_UPDATESAVAILABLE = "app.update.updatesAvailable";
const PREF_UPDATE_APP_INTERVAL = "app.update.interval";
const PREF_UPDATE_APP_LASTUPDATEDATE = "app.update.lastUpdateDate";
const PREF_UPDATE_APP_PERFORMED = "app.update.performed";
// MERC BILL - NEW OPTIONS
const PREF_UPDATE_APP_AUTOINSTALL = "update.autoInstallUpdates";
const PREF_UPDATE_EXTENSIONS_AUTOINSTALL = "update.autoDownloadInstallUpdatesToExtension";
//const PREF_UPDATE_NOTIFYDOWNLOADED_UPDATES = "update.notifyOfDownloadedUpdates";
//const PREF_UPDATE_NOTIFYAVAILABLE_UPDATES = "update.notifyOfAvailableUpdates";
// END MERC
const PREF_UPDATE_EXTENSIONS_ENABLED = "extensions.update.enabled";
const PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED = "extensions.update.autoUpdateEnabled";
const PREF_UPDATE_EXTENSIONS_AUTOUPDATE = "extensions.update.autoUpdate";
const PREF_UPDATE_EXTENSIONS_COUNT = "extensions.update.count";
const PREF_UPDATE_EXTENSIONS_INTERVAL = "extensions.update.interval";
const PREF_UPDATE_EXTENSIONS_LASTUPDATEDATE = "extensions.update.lastUpdateDate";
const PREF_UPDATE_EXTENSIONS_SEVERITY_THRESHOLD = "extensions.update.severity.threshold";
const PREF_UPDATE_INTERVAL = "update.interval";
const PREF_UPDATE_SEVERITY = "update.severity";
const PREF_UPDATE_SHOW_SLIDING_NOTIFICATION = "update.showSlidingNotification";
const PREF_SILENT_UPDATE_ENABLED = "silent.update.enabled";
const PREF_SILENT_UPDATE_INTERVAL = "silent.update.interval";
const PREF_SILENT_UPDATE_LASTUPDATEDATE = "silent.update.lastUpdateDate";
const nsIExtensionManager = Components.interfaces.nsIExtensionManager;
const nsIUpdateService = Components.interfaces.nsIUpdateService;
const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
const UPDATED_EXTENSIONS = 0x01;
const UPDATED_APP = 0x02;
const UPDATED_SILENT = 0x04;
function APP_NS(aProperty)
{
return "http://www.mozilla.org/2004/app-rdf#" + aProperty;
}
function SILENT_NS(aProperty)
{
return "http://www.mozilla.org/2004/silent-rdf#" + aProperty;
}
function getOSKey()
{
return "windows";
}
function stackTraceFunctionFormat(aFunctionName)
{
var classDelimiter = aFunctionName.indexOf("_");
var className = aFunctionName.substr(0, classDelimiter);
if (!className)
className == "<global>";
var functionName = aFunctionName.substr(classDelimiter + 1, aFunctionName.length);
if (!functionName)
functionName == "<anonymous>";
return className + "::" + functionName;
}
function stackTrace(aArguments, aMaxCount)
{
dump("=[STACKTRACE]=====================================================\n");
dump("*** at: " + stackTraceFunctionFormat(aArguments.callee.name) + "()\n");
var temp = aArguments.callee.caller;
var count = 0;
while (temp) {
dump("*** " + stackTraceFunctionFormat(temp.name) + ")\n");
temp = temp.arguments.callee.caller;
if (aMaxCount > 0 && ++count == aMaxCount)
break;
}
dump("==================================================================\n");
}
var gPref = null;
var gOS = null;
var gRDF = null;
var gSiteControls = null;
var gParentalControls = null;
function nsUpdateService()
{
this.debug("Starting update service...");
gPref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
gOS = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
gRDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
gSiteControls = Components.classes["@mozilla.org/browser/sitecontrols-service;1"]
.getService(Components.interfaces.nsISiteControlsService);
gParentalControls = Components.classes["@mozilla.org/browser/parentalcontrols-service;1"]
.getService(Components.interfaces.nsIParentalControlsService);
this.watchForUpdates();
var pbi = gPref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
pbi.addObserver(PREF_UPDATE_APP_AUTOUPDATEENABLED, this, false);
pbi.addObserver(PREF_UPDATE_APP_ENABLED, this, false);
pbi.addObserver(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED, this, false);
pbi.addObserver(PREF_UPDATE_EXTENSIONS_ENABLED, this, false);
pbi.addObserver(PREF_UPDATE_INTERVAL, this, false);
pbi.addObserver(PREF_UPDATE_APP_INTERVAL, this, false);
pbi.addObserver(PREF_UPDATE_EXTENSIONS_INTERVAL, this, false);
pbi.addObserver(PREF_SILENT_UPDATE_INTERVAL, this, false);
// MERC BILL
pbi.addObserver(PREF_UPDATE_APP_AUTOINSTALL, this, false);
pbi.addObserver(PREF_UPDATE_EXTENSIONS_AUTOINSTALL, this, false);
// Observe xpcom-shutdown to unhook pref branch observers above to avoid
// shutdown leaks.
gOS.addObserver(this, "xpcom-shutdown", false);
// Reset update state from previous session if an app update was installed.
if (gPref.prefHasUserValue(PREF_UPDATE_APP_PERFORMED))
gPref.clearUserPref(PREF_UPDATE_APP_PERFORMED);
}
nsUpdateService.prototype = {
_updateObserver: null,
_appAutoUpdateEnabled: true,
_extAutoUpdateEnabled: true,
_silentAutoUpdateEnabled: true,
_debug: false,
/////////////////////////////////////////////////////////////////////////////
// nsIUpdateService
debug: function(msg){
if (this._debug){
dump('**** -- nsUpdateService: '+msg+'\n');
}
},
watchForUpdates: function nsUpdateService_watchForUpdates ()
{
this.debug("calling Watch for Updates...");
// 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.
this._appAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_APP_AUTOUPDATEENABLED);
this._extAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED);
this._silentAutoUpdateEnabled = gPref.getBoolPref(PREF_SILENT_UPDATE_ENABLED);
if (!this._appAutoUpdateEnabled && !this._extAutoUpdateEnabled
&& !this._silentAutoUpdateEnabled)
return;
this._makeTimer(gPref.getIntPref(PREF_UPDATE_INTERVAL));
this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_SILENT,
nsIUpdateService.SOURCE_EVENT_BACKGROUND);
},
_getAllowedTypes: function nsUpdateService__getAllowedTypes(aRequestedTypes)
{
// Figure out what types we're allowed to update. These options
// differ from PREF_UPDATE_*_AUTOUPDATEENABLED since they effectively
// shut down the update UI if the administrator/distributor has configured
// a build to have disallowed these types of update.
var extUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
var appUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_APP_ENABLED);
var silentUpdateEnabled = gPref.getBoolPref(PREF_SILENT_UPDATE_ENABLED);
var types = 0;
if (extUpdateEnabled) {
if (aRequestedTypes & nsIUpdateItem.TYPE_EXTENSION)
types |= nsIUpdateItem.TYPE_EXTENSION;
if (aRequestedTypes & nsIUpdateItem.TYPE_THEME)
types |= nsIUpdateItem.TYPE_THEME;
}
if (appUpdateEnabled &&
(aRequestedTypes & nsIUpdateItem.TYPE_APP))
types |= nsIUpdateItem.TYPE_APP;
if (silentUpdateEnabled &&
(aRequestedTypes & nsIUpdateItem.TYPE_SILENT))
types |= nsIUpdateItem.TYPE_SILENT;
return types;
},
checkForUpdates: function nsUpdateService_checkForUpdates (aItems, aItemCount, aUpdateTypes, aSourceEvent, aParentWindow)
{
this.debug("Checking for updates service...");
var types = this._getAllowedTypes(aUpdateTypes);
// Nothing to update
if (!types) {
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var bundle = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
ps.alert(aParentWindow,
bundle.GetStringFromName("updatesdisabledtitle"),
bundle.GetStringFromName("updatesdisabledmessage"));
return;
}
switch (aSourceEvent) {
case nsIUpdateService.SOURCE_EVENT_MISMATCH:
case nsIUpdateService.SOURCE_EVENT_USER:
if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_USER &&
gPref.getBoolPref(PREF_UPDATE_APP_PERFORMED)) {
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var bundle = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
var brandBundle = sbs.createBundle("chrome://global/locale/brand.properties");
var brandShortName = brandBundle.GetStringFromName("brandShortName");
var message = bundle.formatStringFromName("appupdateperformedmessage",
[brandShortName, brandShortName], 2);
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
ps.alert(aParentWindow,
bundle.GetStringFromName("appupdateperformedtitle"),
message);
return;
}
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var wizard = wm.getMostRecentWindow("Update:Wizard");
if (wizard)
wizard.focus();
else {
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
var ary = Components.classes["@mozilla.org/supports-array;1"]
.createInstance(Components.interfaces.nsISupportsArray);
var updateTypes = Components.classes["@mozilla.org/supports-PRUint8;1"]
.createInstance(Components.interfaces.nsISupportsPRUint8);
updateTypes.data = types;
ary.AppendElement(updateTypes);
var sourceEvent = Components.classes["@mozilla.org/supports-PRUint8;1"]
.createInstance(Components.interfaces.nsISupportsPRUint8);
sourceEvent.data = aSourceEvent;
ary.AppendElement(sourceEvent);
for (var i = 0; i < aItems.length; ++i)
ary.AppendElement(aItems[i]);
var features = "chrome,centerscreen";
if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_MISMATCH) {
features += ",modal"; // Must block in mismatch mode since there's
// no main evt loop yet.
}
// 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","", features, ary);
}
break;
case nsIUpdateService.SOURCE_EVENT_BACKGROUND:
// Rather than show a UI, call the checkForUpdates function directly here.
// The Browser's inline front end update notification system listens for the
// updates that this function broadcasts.
this.checkForUpdatesInternal([], 0, types, aSourceEvent);
break;
}
},
_canUpdate: function (aPreference, aSourceEvent, aUpdateTypes)
{
// Always can update if the autoupdate preference is set, otherwise,
// allow updates only when not in backround update mode, i.e. when the user
// explicitly asked.
return aPreference ? true
: aSourceEvent != nsIUpdateService.SOURCE_EVENT_BACKGROUND;
},
checkForUpdatesInternal: function nsUpdateService_checkForUpdatesInternal (aItems, aItemCount, aUpdateTypes, aSourceEvent)
{
var types = this._getAllowedTypes(aUpdateTypes);
// Listen for notifications sent out by the app updater (implemented here) and the
// extension updater (implemented in nsExtensionItemUpdater)
var canUpdate;
this._updateObserver = new nsUpdateObserver(types, aSourceEvent, this);
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
if (types & nsIUpdateItem.TYPE_APP) {
if (this._canUpdate(this._appAutoUpdateEnabled, aSourceEvent, types)) {
gOS.addObserver(this._updateObserver, "Update:App:Ended", false);
this._currentVersion = new nsAppUpdateInfo();
this._newestVersion = new nsAppUpdateInfo();
if (!this._updateObserver.appUpdater) {
this._updateObserver.appUpdater = new nsAppUpdater(this);
this._updateObserver.appUpdater.checkForUpdates();
}
}
}
// JMC: Check for silent updates
if (types & nsIUpdateItem.TYPE_SILENT) {
if (this._canUpdate(this._silentAutoUpdateEnabled, aSourceEvent, types)) {
gOS.addObserver(this._updateObserver, "Update:Silent:Ended", false);
this._currentVersion = new nsSilentUpdateInfo();
this._newestVersion = new nsSilentUpdateInfo();
if (!this._updateObserver.silentUpdater) {
this._updateObserver.silentUpdater = new nsSilentUpdater(this);
this._updateObserver.silentUpdater.checkForUpdates();
}
}
}
if (types & nsIUpdateItem.TYPE_ADDON) { // TYPE_EXTENSION, TYPE_ANY, etc.
if (this._canUpdate(this._extAutoUpdateEnabled, aSourceEvent, types)) {
gOS.addObserver(this._updateObserver, "Update:Extension:Started", false);
gOS.addObserver(this._updateObserver, "Update:Extension:Item-Ended", false);
gOS.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, false);
}
}
if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_BACKGROUND &&
(this._appAutoUpdateEnabled || this._extAutoUpdateEnabled)) {
if (types & nsIUpdateItem.TYPE_ADDON)
gPref.setIntPref(PREF_UPDATE_EXTENSIONS_LASTUPDATEDATE, this._nowInMilliseconds / 1000);
if (types & nsIUpdateItem.TYPE_APP)
gPref.setIntPref(PREF_UPDATE_APP_LASTUPDATEDATE, this._nowInMilliseconds / 1000);
if (types & nsIUpdateItem.TYPE_SILENT)
gPref.setIntPref(PREF_SILENT_UPDATE_LASTUPDATEDATE, this._nowInMilliseconds / 1000);
}
},
get updateCount()
{
// 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.extensionUpdatesAvailable;
if (this.appUpdatesAvailable)
++updateCount;
return updateCount;
},
get updateSeverity()
{
return gPref.getIntPref(PREF_UPDATE_SEVERITY);
},
_appUpdatesAvailable: undefined,
get appUpdatesAvailable()
{
if (this._appUpdatesAvailable === undefined) {
return (gPref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE) &&
gPref.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 gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT);
return this._extensionUpdatesAvailable;
},
set extensionUpdatesAvailable(aValue)
{
this._extensionUpdatesAvailable = aValue;
return aValue;
},
_newestVersion: null,
get newestVersion()
{
return this._newestVersion;
},
_currentVersion: null,
get currentVersion()
{
return this._currentVersion;
},
/////////////////////////////////////////////////////////////////////////////
// nsITimerCallback
_shouldUpdate: function nsUpdateService__shouldUpdate (aIntervalPref, aLastCheckPref)
{
var interval = gPref.getIntPref(aIntervalPref);
var lastUpdateTime = gPref.getIntPref(aLastCheckPref);
return ((Math.round(this._nowInMilliseconds/1000) - lastUpdateTime) > Math.round(interval/1000));
},
notify: function nsUpdateService_notify (aTimer)
{
this.debug("Update service - might be time to update check...\n");
this.debug("Timer is " + aTimer.delay + "\n");
this.debug("FACT: Ought to let your friends know when you're going to party\n");
var types = 0;
if (this._shouldUpdate(PREF_UPDATE_EXTENSIONS_INTERVAL,
PREF_UPDATE_EXTENSIONS_LASTUPDATEDATE)) {
types |= nsIUpdateItem.TYPE_ADDON;
}
if (this._shouldUpdate(PREF_UPDATE_APP_INTERVAL,
PREF_UPDATE_APP_LASTUPDATEDATE)) {
types |= nsIUpdateItem.TYPE_APP;
}
if (this._shouldUpdate(PREF_SILENT_UPDATE_INTERVAL,
PREF_SILENT_UPDATE_LASTUPDATEDATE)) {
types |= nsIUpdateItem.TYPE_SILENT;
}
if (types)
this.checkForUpdatesInternal([], 0, types,
nsIUpdateService.SOURCE_EVENT_BACKGROUND);
},
/////////////////////////////////////////////////////////////////////////////
// nsIObserver
observe: function nsUpdateService_observe (aSubject, aTopic, aData)
{
switch (aTopic)
{
case "nsPref:changed":
var needsNotification = false;
switch (aData)
{
// MERC BILL, UPDATE AUTOINSTALL STATUS
case PREF_UPDATE_APP_AUTOINSTALL:
case PREF_UPDATE_EXTENSIONS_AUTOINSTALL:
case PREF_UPDATE_APP_AUTOUPDATEENABLED:
case PREF_UPDATE_APP_ENABLED:
this._appAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_APP_AUTOUPDATEENABLED);
if (!this._appAutoUpdateEnabled)
{
this._clearAppUpdatePrefs();
needsNotification = true;
}
else
{
// Do an initial check NOW to update any FE components and kick off the timer.
this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_APP,
nsIUpdateService.SOURCE_EVENT_BACKGROUND);
}
break;
case PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED:
case PREF_UPDATE_EXTENSIONS_ENABLED:
this._extAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED);
if (!this._extAutoUpdateEnabled) {
// Unset prefs used by the update service to signify extension updates
if (gPref.prefHasUserValue(PREF_UPDATE_EXTENSIONS_COUNT))
gPref.clearUserPref(PREF_UPDATE_EXTENSIONS_COUNT);
needsNotification = true;
}
else {
// Do an initial check NOW to update any FE components and kick off the
// timer.
this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_ADDON,
nsIUpdateService.SOURCE_EVENT_BACKGROUND);
}
break;
case PREF_UPDATE_INTERVAL:
case PREF_UPDATE_APP_INTERVAL:
case PREF_UPDATE_EXTENSIONS_INTERVAL:
case PREF_SILENT_UPDATE_INTERVAL:
this._makeTimer(gPref.getIntPref(PREF_UPDATE_INTERVAL));
break;
}
if (needsNotification) {
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
var backgroundEvt = Components.interfaces.nsIUpdateService.SOURCE_EVENT_BACKGROUND;
gOS.notifyObservers(null, "Update:Ended", backgroundEvt.toString());
}
break;
case "xpcom-shutdown":
gOS.removeObserver(this, "xpcom-shutdown");
// Clean up held observers etc to avoid leaks.
var pbi = gPref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
pbi.removeObserver(PREF_UPDATE_APP_AUTOUPDATEENABLED, this);
pbi.removeObserver(PREF_UPDATE_APP_ENABLED, this);
pbi.removeObserver(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED, this);
pbi.removeObserver(PREF_UPDATE_EXTENSIONS_ENABLED, this);
pbi.removeObserver(PREF_UPDATE_INTERVAL, this);
pbi.removeObserver(PREF_UPDATE_EXTENSIONS_INTERVAL, this);
// MERC BILL
pbi.removeObserver(PREF_UPDATE_APP_AUTOINSTALL, this);
pbi.removeObserver(PREF_UPDATE_EXTENSIONS_AUTOINSTALL, this);
// Release strongly held services.
gPref = null;
gRDF = null;
gOS = null;
if (this._timer) {
this._timer.cancel();
this._timer = null;
}
break;
}
},
/////////////////////////////////////////////////////////////////////////////
// nsUpdateService
_timer: null,
_makeTimer: function nsUpdateService__makeTimer (aDelay)
{
if (!this._timer)
this._timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
this._timer.cancel();
this._timer.initWithCallback(this, aDelay,
Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
},
get _nowInMilliseconds ()
{
var d = new Date();
return Date.UTC(d.getUTCFullYear(),
d.getUTCMonth(),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds(),
d.getUTCMilliseconds());
},
_clearAppUpdatePrefs: function nsUpdateService__clearAppUpdatePrefs ()
{
// Unset prefs used by the update service to signify application updates
if (gPref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE))
gPref.clearUserPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsUpdateService_QueryInterface (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;
}
};
function nsUpdateObserver(aUpdateTypes, aSourceEvent, aService)
{
this._updateTypes = aUpdateTypes;
this._sourceEvent = aSourceEvent;
this._service = aService;
// MERC - JCH: Get prefs for browser updates.
this._browserUpdatePref = gPref.getCharPref(PREF_UPDATE_APP_AUTOINSTALL);
this._themeExtensionPref = gPref.getCharPref(PREF_UPDATE_EXTENSIONS_AUTOINSTALL);
this.debug("Update prefs: browserUpdatePref "+this._browserUpdatePref+" ~~~~~~~~~\n");
this.debug("Update prefs: themeExtensionPref "+this._themeExtensionPref+" ~~~~~~~~~");
}
nsUpdateObserver.prototype = {
_updateTypes: 0,
_sourceEvent: 0,
_updateState: 0,
_endedTimer : null,
_browserUpdatePref : null,
_themeExtensionPref : null,
_debug: false,
appUpdater: null,
silentUpdater: null,
items : null,
itemCount : 0,
debug: function(msg){
if (this._debug){
dump('**** -- nsUpdateObserver: '+msg+'\n');
}
},
get _doneUpdating()
{
var notBackground = this._sourceEvent != nsIUpdateService.SOURCE_EVENT_BACKGROUND;
var canUpdateApp = this._service._appAutoUpdateEnabled ||
(notBackground ? gPref.getBoolPref(PREF_UPDATE_APP_ENABLED)
: false);
var canUpdateExt = this._service._extAutoUpdateEnabled ||
(notBackground ? gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED)
: false);
var test = 0;
var updatingApp = (this._updateTypes & nsIUpdateItem.TYPE_APP) &&
canUpdateApp;
var updatingExt = (this._updateTypes & nsIUpdateItem.TYPE_ADDON) &&
canUpdateExt;
var updatingSilent = (this._updateTypes & nsIUpdateItem.TYPE_SILENT);
if (updatingApp)
test |= UPDATED_APP;
if (updatingExt)
test |= UPDATED_EXTENSIONS;
if (updatingSilent)
test |= UPDATED_SILENT;
return (this._updateState & test) == test;
},
/////////////////////////////////////////////////////////////////////////////
// nsIObserver
observe: function nsUpdateObserver_observe (aSubject, aTopic, aData)
{
switch (aTopic) {
case "Update:Extension:Started":
// Reset the count
gPref.setIntPref(PREF_UPDATE_EXTENSIONS_COUNT, 0);
break;
case "Update:Extension:Item-Ended":
var newCount = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT) + 1;
gPref.setIntPref(PREF_UPDATE_EXTENSIONS_COUNT, newCount);
var threshold = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_SEVERITY_THRESHOLD);
if (this._service.updateSeverity < nsIUpdateService.SEVERITY_HIGH) {
if (newCount > threshold)
gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_MEDIUM);
else
gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_LOW);
}
break;
case "Update:Extension:Ended":
this._updateState |= UPDATED_EXTENSIONS;
break;
case "Update:App:Ended":
this._updateState |= UPDATED_APP;
this.appUpdater.destroy();
this.appUpdater = null;
break;
case "Update:Silent:Ended":
this._updateState |= UPDATED_SILENT;
if (this.silentUpdater)
{
this.silentUpdater.destroy();
this.silentUpdater = null;
}
break;
}
if (this._doneUpdating) {
// 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 nsUpdateObserver_notify (aTimer)
{
this.debug("UpdateObserver_notify: Ought to let your friends know when you're going to party");
// The Inline Browser Update UI uses this notification to refresh its update
// UI if necessary.
gOS.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)) &&
gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT) != 0);
if (!updatesAvailable)
{
updatesAvailable = ((this._updateTypes & nsIUpdateItem.TYPE_APP) ||
(this._updateTypes & nsIUpdateItem.TYPE_ANY)) &&
gPref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
}
var showNotification = gPref.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);
// MERC - JCH: Support for browser updates as determined by settings
// in SmartUpdate panel in Options dialog.
switch (this._browserUpdatePref)
{
case "ask":
alerts.showAlertNotification("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
alertTitle, alertText, true, "", this);
break;
case "downloadAskInstall":
break;
case "downloadInstall":
this.autoUpdateInstall();
break;
}
}
this.destroy();
},
autoUpdateInstall : function nsUpdateObserver_autoUpdateInstall ()
{
// Don't show the app update option or critical updates if the user has
// already installed an app update but has not yet restarted.
var updatePerformed = gPref.getBoolPref(PREF_UPDATE_APP_PERFORMED);
if (!updatePerformed && this._updateTypes & nsIUpdateItem.TYPE_APP) {
var updatesvc = Components.classes["@mozilla.org/updates/update-service;1"]
.getService(Components.interfaces.nsIUpdateService);
/* JMC: Why do we care about the currentVersion?
var currentInfo = updatesvc.currentVersion;
if (currentInfo) {
var patches = currentInfo.getCollection("patches", { });
var languages = this._currentInfo.getCollection("languages", { });
}
*/
var newestInfo = updatesvc.newestVersion;
if (newestInfo) {
var languages = newestInfo.getCollection("languages", { });
var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIXULChromeRegistry);
var selectedLocale = cr.getSelectedLocale("global");
var haveLanguage = false;
for (var i = 0; i < languages.length; ++i) {
if (languages[i].internalName == selectedLocale)
haveLanguage = true;
}
var files = newestInfo.getCollection("files", { });
// When the user upgrades the application, any optional components that
// they have installed are automatically installed. If there are remaining
// optional components that are not currently installed, then these
// are offered as an option.
/* JMC: Fixme, add optional components later
var components = newestInfo.getCollection("optional", { });
for (var i = 0; i < components.length; ++i) {
if (InstallTrigger.getVersion(components[i].internalName))
this.items.push(components[i].URL);
}
*/
this.items = [];
var selectedLocaleAvailable = false;
var languages = newestInfo.getCollection("languages", { });
for (i = 0; i < languages.length; ++i) {
if (languages[i].internalName == selectedLocale) {
selectedLocaleAvailable = true;
this.items.push(languages[i].URL);
}
}
for (i = 0; i < files.length; ++i) {
this.items.push(files[i].URL);
}
var xpimgr = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
.createInstance(Components.interfaces.nsIXPInstallManager);
xpimgr.initManagerFromChrome(this.items, this.items.length, this);
this.itemCount = this.items.length - 1;
gPref.setBoolPref(PREF_UPDATE_APP_PERFORMED, true);
}
}
},
onStateChange: function (aIndex, aState, aValue)
{
// var strings = document.getElementById("updateStrings");
var alerts = Components.classes["@mozilla.org/alerts-service;1"]
.getService(Components.interfaces.nsIAlertsService);
const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog;
switch (aState) {
case nsIXPIProgressDialog.DOWNLOAD_START:
break;
case nsIXPIProgressDialog.DOWNLOAD_DONE:
case nsIXPIProgressDialog.INSTALL_START:
break;
case nsIXPIProgressDialog.INSTALL_DONE:
/*
switch (aValue) {
case 999:
this._restartRequired = true;
alerts.showAlertNotification("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
"Smart Update Notification", "Installation of patches is complete. Reboot is required.", false, "", null);
break;
case 0:
alerts.showAlertNotification("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
"Smart Update Notification", "Installation of patches is complete.", false, "", null);
break;
default:
// XXXben ignore chrome registration errors hack!
if (!(aValue == -239 && gUpdateWizard.updatingApp)) {
this._objs[aIndex].error = aValue;
this._errors = true;
}
break;
}
*/
this.itemCount--;
if (this.itemCount == 0) {
alerts.showAlertNotification("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
"Smart Update Notification", "Installation of patches is complete.", false, "", null);
}
break;
case nsIXPIProgressDialog.DIALOG_CLOSE:
break;
}
},
_objs: [],
_errors: false,
onProgress: function (aIndex, aValue, aMaxValue)
{
// var downloadProgress = document.getElementById("downloadProgress");
// downloadProgress.value = Math.ceil((aValue/aMaxValue) * 100);
},
destroy: function nsUpdateObserver_destroy ()
{
try { gOS.removeObserver(this, "Update:Extension:Started"); } catch (e) { }
try { gOS.removeObserver(this, "Update:Extension:Item-Ended"); } catch (e) { }
try { gOS.removeObserver(this, "Update:Extension:Ended"); } catch (e) { }
try { gOS.removeObserver(this, "Update:App:Ended"); } catch (e) { }
try { gOS.removeObserver(this, "Update:Silent:Ended"); } catch (e) { }
if (this._endedTimer) {
this._endedTimer.cancel();
this._endedTimer = null;
}
},
////////////////////////////////////////////////////////////////////////////
// nsIAlertListener
onAlertFinished: function nsUpdateObserver_onAlertFinished ()
{
},
onAlertClickCallback: function nsUpdateObserver_onAlertClickCallback (aCookie)
{
var updates = Components.classes["@mozilla.org/updates/update-service;1"]
.getService(Components.interfaces.nsIUpdateService);
updates.checkForUpdates([], 0, Components.interfaces.nsIUpdateItem.TYPE_ANY,
Components.interfaces.nsIUpdateService.SOURCE_EVENT_USER,
null);
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsUpdateObserver_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsIObserver) &&
!aIID.equals(Components.interfaces.nsIAlertListener) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
///////////////////////////////////////////////////////////////////////////////
// App Updater
function nsAppUpdater(aUpdateService)
{
this._service = aUpdateService;
}
nsAppUpdater.prototype = {
_service : null,
_debug: false,
debug: function(msg){
if (this._debug){
dump('**** -- nsAppUpdater: '+msg+'\n');
}
},
checkForUpdates: function ()
{
// TEMP TEST
this.debug("start update CHECK!\n");
var dsURI = gPref.getComplexValue(PREF_UPDATE_APP_URI,
Components.interfaces.nsIPrefLocalizedString).data;
//dsURI += "?" + Math.round(Math.random() * 1000);
this._ds = gRDF.GetDataSource(dsURI);
var rds = this._ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
if (rds.loaded)
this.onDatasourceLoaded(this._ds);
else {
var sink = this._ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
// Creates a strong ref that holds this object past when it falls out of
// scope in the calling function
sink.addXMLSinkObserver(this);
}
},
destroy: function ()
{
var sink = this._ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
sink.removeXMLSinkObserver(this);
this._service = null;
},
/////////////////////////////////////////////////////////////////////////////
//
_ncR: function nsUpdateService__ncR (aProperty)
{
return gRDF.GetResource("http://home.netscape.com/NC-rdf#" + aProperty);
},
_getPropertyFromResource: function nsAppUpdater__getPropertyFromResource (aDataSource,
aSourceResource,
aProperty)
{
var rv;
try {
var property = gRDF.GetResource(APP_NS(aProperty));
rv = this._stringData(aDataSource.GetTarget(aSourceResource, property, true));
if (rv == "--")
throw Components.results.NS_ERROR_FAILURE;
}
catch (e) {
return null;
}
return rv;
},
_stringData: function nsAppUpdater__stringData (aLiteralOrResource)
{
try {
var obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFLiteral);
return obj.Value;
}
catch (e) {
try {
obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFResource);
return obj.Value;
}
catch (e) {}
}
return "--";
},
/////////////////////////////////////////////////////////////////////////////
//
onDatasourceLoaded: function nsAppUpdater_onDatasourceLoaded (aDataSource)
{
this.debug("get ID\n");
var appID = gPref.getCharPref(PREF_APP_ID);
this.debug("get app version\n");
var appVersion = gPref.getCharPref(PREF_APP_VERSION);
this.debug("after getting both\n");
var appResource = gRDF.GetResource("urn:mozilla:app:" + appID);
var updatesArc = gRDF.GetResource(APP_NS("updates"));
var updatesResource = aDataSource.GetTarget(appResource, updatesArc, true);
try {
updatesResource = updatesResource.QueryInterface(Components.interfaces.nsIRDFResource);
}
catch (e) {
this.debug("++++ ACK! Bailed out from updatesResource QueryInterface call.");
gOS.notifyObservers(null, "Update:App:Error", "");
gOS.notifyObservers(null, "Update:App:Ended", "");
return;
}
var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
.getService(Components.interfaces.nsIRDFContainerUtils);
if (cu.IsContainer(aDataSource, updatesResource)) {
var c = Components.classes["@mozilla.org/rdf/container;1"]
.getService(Components.interfaces.nsIRDFContainer);
c.Init(aDataSource, updatesResource);
var versionChecker = Components.classes["@mozilla.org/updates/version-checker;1"]
.getService(Components.interfaces.nsIVersionChecker);
var newestVersionObj = { version: appVersion, resource: null };
var updates = c.GetElements();
while (updates.hasMoreElements()) {
var update = updates.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
var version = this._getPropertyFromResource(aDataSource, update, "version");
if (!version)
continue;
if (versionChecker.compare(appVersion, version) == 0)
this._parseVersionData(aDataSource, update, this._service._currentVersion);
else if (versionChecker.compare(newestVersionObj.version, version) < 0) {
newestVersionObj.version = version;
newestVersionObj.resource = update;
}
}
if (newestVersionObj.resource) {
this.debug("+++++ We think we've got a new version available... ");
this._parseVersionData(aDataSource, newestVersionObj.resource, this._service._newestVersion);
}
// There is a newer version of the app available or there are any critical
// patches available update the severity and available updates preferences.
// XXXben also note if there are langpacks available that match the user's
// preference if they previously installed an app update when a
// langpack for their language was not available and they used another
// in the meantime.
var haveLanguage = false;
var patches = this._service._currentVersion.patches;
var languages = this._service._newestVersion.languages;
if (languages) {
var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIXULChromeRegistry);
var selectedLocale = cr.getSelectedLocale("global");
for (var i = 0; i < languages.length; ++i) {
if (languages[i].internalName == selectedLocale)
haveLanguage = true;
}
}
if ((haveLanguage && (newestVersionObj.version != appVersion)) ||
(patches && patches.length > 0)) {
gPref.setIntPref(PREF_UPDATE_SEVERITY, 2);
gPref.setBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE, true);
// JMC - Save pref of best available version here.
// Save date of version availability here too.
}
else
gPref.setBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE, false);
if (!gPref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE)) {
this._service._clearAppUpdatePrefs();
// Lower the severity to reflect the fact that there are now only Extension/
// Theme updates available
var newCount = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT);
var threshold = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_SEVERITY_THRESHOLD);
if (newCount >= threshold)
gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_MEDIUM);
else
gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_LOW);
}
}
// The Update Wizard uses this notification to determine that the application
// update process is now complete.
gOS.notifyObservers(null, "Update:App:Ended", "");
},
_parseVersionData: function nsAppUpdater__parseVersionData (aDataSource,
aUpdateResource,
aTargetObj)
{
aTargetObj.updateVersion = this._getPropertyFromResource(aDataSource, aUpdateResource, "version");
aTargetObj.updateDisplayVersion = this._getPropertyFromResource(aDataSource, aUpdateResource, "displayVersion");
if (!aTargetObj.updateDisplayVersion)
aTargetObj.updateDisplayVersion = this._getPropertyFromResource(aDataSource, aUpdateResource, "version");
aTargetObj.updateInfoURL = this._getPropertyFromResource(aDataSource, aUpdateResource, "infoURL");
aTargetObj.features = this._parseUpdateCollection(aDataSource, aUpdateResource, "features");
aTargetObj.files = this._parseUpdateCollection(aDataSource, aUpdateResource, "files");
aTargetObj.optional = this._parseUpdateCollection(aDataSource, aUpdateResource, "optional");
aTargetObj.languages = this._parseUpdateCollection(aDataSource, aUpdateResource, "languages");
aTargetObj.patches = this._parseUpdateCollection(aDataSource, aUpdateResource, "patches");
// this._dumpProps(aTargetObj);
},
_parseUpdateCollection: function nsAppUpdater__parseUpdateCollection (aDataSource,
aUpdateResource,
aCollectionName)
{
if (!aUpdateResource)
return null;
var result = [];
var collectionArc = gRDF.GetResource(APP_NS(aCollectionName));
var collectionResource = aDataSource.GetTarget(aUpdateResource, collectionArc, true);
try {
collectionResource = collectionResource.QueryInterface(Components.interfaces.nsIRDFResource);
}
catch (e) { return null; }
var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
.getService(Components.interfaces.nsIRDFContainerUtils);
if (cu.IsContainer(aDataSource, collectionResource)) {
var c = Components.classes["@mozilla.org/rdf/container;1"]
.getService(Components.interfaces.nsIRDFContainer);
c.Init(aDataSource, collectionResource);
var elements = c.GetElements();
var fileArc = gRDF.GetResource(APP_NS("file"));
var platform = getOSKey();
while (elements.hasMoreElements()) {
var element = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
var info = new nsAppUpdateInfoItem();
info.name = this._getPropertyFromResource(aDataSource, element, "name");
info.internalName = this._getPropertyFromResource(aDataSource, element, "internalName");
// Each Component has a set of app:file arcs out, which reference resources with two
// properties: app:platform and app:URL. If we find a resource whose app:platform value
// matches the platform we're running on, we use the app:URL property on that resource
// as the XPI URL, otherwise we use the default app:URL property on the Component
// resource. (It must be a cross-platform piece, e.g. a language pack)
// XXXben - what to do when platform not supported? We need some way to abort
// and tell the app that this update is not available.
var files = aDataSource.GetTargets(element, fileArc, true);
while (files.hasMoreElements()) {
var file = files.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
if (platform == this._getPropertyFromResource(aDataSource, file, "platform")) {
info.URL = this._getPropertyFromResource(aDataSource, file, "URL");
break;
}
}
if (!info.URL)
info.URL = this._getPropertyFromResource(aDataSource, element, "URL");
info.infoURL = this._getPropertyFromResource(aDataSource, element, "infoURL");
info.description = this._getPropertyFromResource(aDataSource, element, "description");
result.push(info);
}
}
return result;
},
onDatasourceError: function (aStatus, aErrorMsg)
{
gOS.notifyObservers(null, "Update:App:Error", "");
gOS.notifyObservers(null, "Update:App:Ended", "");
},
/////////////////////////////////////////////////////////////////////////////
// nsIRDFXMLSinkObserver
onBeginLoad: function(aSink)
{
},
onInterrupt: function(aSink)
{
},
onResume: function(aSink)
{
},
onEndLoad: function(aSink)
{
try {
this._ds = aSink.QueryInterface(Components.interfaces.nsIRDFDataSource);
this.onDatasourceLoaded(this._ds);
}
catch (e) { }
},
onError: function(aSink, aStatus, aErrorMsg)
{
try {
this._ds = aSink.QueryInterface(Components.interfaces.nsIRDFDataSource);
this.onDatasourceError(aStatus, aErrorMsg);
}
catch (e) { }
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsAppUpdater_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsIRDFXMLSinkObserver) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
}
// Silent Updater
///////////////////////////////////////////////////////////////////////////////
function nsSilentUpdater(aUpdateService)
{
// this.debug("SILENT UPDATE: Constructing the Silent Updater\n");
this._service = aUpdateService;
}
nsSilentUpdater.prototype = {
_service : null,
_debug : false,
debug: function(msg){
if (this._debug){
dump('**** -- nsSilentUpdater: '+msg+'\n');
}
},
_dumpProps : function dumpProps(obj, parent) {
this.debug("Dumping props for aTargetObjs");
// Go through all the properties of the passed-in object
for (var i in obj) {
// if a parent (2nd parameter) was passed in, then use that to
// build the message. Message includes i (the object's property name)
// then the object's property value on a new line
if (parent) { var msg = parent + "." + i + "\n" + obj[i]; } else { var msg = i + "\n" + obj[i]; }
// Display the message. If the user clicks "OK", then continue. If they
// click "CANCEL" then quit this level of recursion
this.debug("+++++ " + msg);
// if (!confirm(msg)) { return; }
// If this property (i) is an object, then recursively process the object
if (typeof obj[i] == "object") {
if (parent) { dumpProps(obj[i], parent + "." + i); } else { dumpProps(obj[i], i); }
}
}
},
checkForUpdates: function ()
{
var dsURI = gPref.getComplexValue(PREF_UPDATE_APP_URI,
Components.interfaces.nsIPrefLocalizedString).data;
// MERC - rfransen - Removed random number from the uri for silentupdate. Causes grief for the caching mechanism at NS.
//dsURI += "?" + Math.round(Math.random() * 1000);
// END MERC
//var entry = Components.classes["@mozilla.org/network/cache-service;1"]
// .getService(Components.interfaces.nsICacheService)
// .createSession("javascript", 1, false)
// .openCacheEntry(dsURI, 1, true);
//entry.QueryInterface(Components.interfaces.nsICacheEntryDescriptor);
//entry.doom();
this._ds = gRDF.GetDataSourceNoCache(dsURI);
var rds = this._ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
if (rds.loaded)
this.onDatasourceLoaded(this._ds);
else {
var sink = this._ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
// Creates a strong ref that holds this object past when it falls out of
// scope in the calling function
sink.addXMLSinkObserver(this);
}
},
destroy: function ()
{
// this.debug("SILENT UPDATE: destroy function\n");
//stackTrace(this);
/*
var sink = this._ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
sink.removeXMLSinkObserver(this);
this._service = null;
*/
},
/////////////////////////////////////////////////////////////////////////////
//
_ncR: function nsUpdateService__ncR (aProperty)
{
return gRDF.GetResource("http://home.netscape.com/NC-rdf#" + aProperty);
},
_getPropertyFromResource: function nsSilentUpdater__getPropertyFromResource (aDataSource,
aSourceResource,
aProperty)
{
var rv;
try {
var property = gRDF.GetResource(SILENT_NS(aProperty));
rv = this._stringData(aDataSource.GetTarget(aSourceResource, property, true));
if (rv == "--")
throw Components.results.NS_ERROR_FAILURE;
}
catch (e) {
return null;
}
return rv;
},
_stringData: function nsSilentUpdater__stringData (aLiteralOrResource)
{
try {
var obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFLiteral);
return obj.Value;
}
catch (e) {
try {
obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFResource);
return obj.Value;
}
catch (e) {}
}
return "--";
},
/////////////////////////////////////////////////////////////////////////////
//
onDatasourceLoaded: function nsSilentUpdater_onDatasourceLoaded (aDataSource)
{
// this.debug("SILENT UPDATE: Got the update.rdf, parsing...\n");
var silentVersion = gPref.getCharPref(PREF_SAFETYNET_VERSION);
var silentResource = gRDF.GetResource("urn:netscape:silent");
var updatesArc = gRDF.GetResource(SILENT_NS("updates"));
var updatesResource = aDataSource.GetTarget(silentResource, updatesArc, true);
try {
updatesResource = updatesResource.QueryInterface(Components.interfaces.nsIRDFResource);
}
catch (e) {
this.debug("++++ ACK! Bailed out from updatesResource QueryInterface call.");
gOS.notifyObservers(null, "Update:Silent:Error", "");
gOS.notifyObservers(null, "Update:Silent:Ended", "");
return;
}
var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
.getService(Components.interfaces.nsIRDFContainerUtils);
if (cu.IsContainer(aDataSource, updatesResource)) {
var c = Components.classes["@mozilla.org/rdf/container;1"]
.getService(Components.interfaces.nsIRDFContainer);
c.Init(aDataSource, updatesResource);
var versionChecker = Components.classes["@mozilla.org/updates/version-checker;1"]
.getService(Components.interfaces.nsIVersionChecker);
var newestVersionObj = { version: silentVersion, resource: null };
var biggestMinorObj = { version: silentVersion, resource: null };
var updates = c.GetElements();
while (updates.hasMoreElements()) {
var update = updates.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
var version = this._getPropertyFromResource(aDataSource, update, "version");
if (!version)
continue;
if (versionChecker.compare(silentVersion, version) == 0)
this._parseVersionData(aDataSource, update, this._service._currentVersion);
else if (versionChecker.compare(newestVersionObj.version, version) < 0) {
newestVersionObj.version = version;
newestVersionObj.resource = update;
}
if (versionChecker.biggestMinor(silentVersion, version)) {
biggestMinorObj.version = version;
biggestMinorObj.resource = update;
}
}
var files;
if (biggestMinorObj.resource) {
// this.debug("SILENT UPDATE: Got a patch, gonna install it...\n");
this._parseVersionData(aDataSource, biggestMinorObj.resource, this._service._newestVersion);
// files = this._service._newestVersion.patches;
// Because we're only patching, we're not guaranteed to be the newest
// So update again right away
if (biggestMinorObj.version != newestVersionObj.version)
{
gPref.setIntPref(PREF_SILENT_UPDATE_LASTUPDATEDATE, 0); // JMC : doublecheck this?
}
files = this._service._newestVersion.getCollection("patches", { });
if (files)
{
this.items = [];
for (i = 0; i < files.length; ++i) {
this.items.push(files[i].URL);
// this.debug("SILENT UPDATE: Pushing " + files[i].URL + " into the URL list...\n");
}
}
} else if (newestVersionObj.resource) {
// this.debug("SILENT UPDATE: Got a new version, gonna install it...\n");
this._parseVersionData(aDataSource, newestVersionObj.resource, this._service._newestVersion);
// this._service._newestVersion.files;
// this.debug("Dumping properties of _newestVersion\n");
// this._dumpProps(this._service._newestVersion);
files = this._service._newestVersion.getCollection("files", { });
if (files)
{
this.items = [];
for (i = 0; i < files.length; ++i) {
this.items.push(files[i].URL);
// this.debug("SILENT UPDATE: Pushing " + files[i].URL + " into the URL list...\n");
}
}
}
if (this.items)
{
var xpimgr = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
.createInstance(Components.interfaces.nsIXPInstallManager);
xpimgr.initManagerFromChromeWithFlags(this.items, this.items.length, this, true);
}
}
// The Update Wizard uses this notification to determine that the application
// update process is now complete.
// MERC (rpaul) sent a notification that the silent update was successful
gOS.notifyObservers(null, "Update:Silent:Success", "");
gOS.notifyObservers(null, "Update:Silent:Ended", "");
},
_parseVersionData: function nsSilentUpdater__parseVersionData (aDataSource,
aUpdateResource,
aTargetObj)
{
// this.debug ("SILENT UPDATE: in the parseVersionData function, with the " + aTargetObj + " target object\n");
aTargetObj.updateVersion = this._getPropertyFromResource(aDataSource, aUpdateResource, "version");
aTargetObj.files = this._parseUpdateCollection(aDataSource, aUpdateResource, "files");
aTargetObj.patches = this._parseUpdateCollection(aDataSource, aUpdateResource, "patches");
},
_parseUpdateCollection: function nsSilentUpdater__parseUpdateCollection (aDataSource,
aUpdateResource,
aCollectionName)
{
// this.debug("SILENT UPDATE: in parseUpdateCollection with a resource of \n");
if (!aUpdateResource)
return null;
var result = [];
var collectionArc = gRDF.GetResource(SILENT_NS(aCollectionName));
var collectionResource = aDataSource.GetTarget(aUpdateResource, collectionArc, true);
try {
collectionResource = collectionResource.QueryInterface(Components.interfaces.nsIRDFResource);
}
catch (e) { return null; }
var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
.getService(Components.interfaces.nsIRDFContainerUtils);
if (cu.IsContainer(aDataSource, collectionResource)) {
var c = Components.classes["@mozilla.org/rdf/container;1"]
.getService(Components.interfaces.nsIRDFContainer);
c.Init(aDataSource, collectionResource);
var elements = c.GetElements();
var fileArc = gRDF.GetResource(SILENT_NS("file"));
var platform = getOSKey();
while (elements.hasMoreElements()) {
var element = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
var info = new nsSilentUpdateInfoItem();
info.name = this._getPropertyFromResource(aDataSource, element, "name");
info.internalName = this._getPropertyFromResource(aDataSource, element, "internalName");
// Each Component has a set of app:file arcs out, which reference resources with two
// properties: app:platform and app:URL. If we find a resource whose app:platform value
// matches the platform we're running on, we use the app:URL property on that resource
// as the XPI URL, otherwise we use the default app:URL property on the Component
// resource. (It must be a cross-platform piece, e.g. a language pack)
// XXXben - what to do when platform not supported? We need some way to abort
// and tell the app that this update is not available.
var files = aDataSource.GetTargets(element, fileArc, true);
while (files.hasMoreElements()) {
var file = files.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
if (platform == this._getPropertyFromResource(aDataSource, file, "platform")) {
info.URL = this._getPropertyFromResource(aDataSource, file, "URL");
break;
}
}
if (!info.URL)
info.URL = this._getPropertyFromResource(aDataSource, element, "URL");
info.infoURL = this._getPropertyFromResource(aDataSource, element, "infoURL");
info.description = this._getPropertyFromResource(aDataSource, element, "description");
result.push(info);
}
}
return result;
},
onDatasourceError: function (aStatus, aErrorMsg)
{
gOS.notifyObservers(null, "Update:Silent:Error", "");
gOS.notifyObservers(null, "Update:Silent:Ended", "");
},
/////////////////////////////////////////////////////////////////////////////
// nsIRDFXMLSinkObserver
onBeginLoad: function(aSink) { },
onInterrupt: function(aSink) { },
onResume: function(aSink) { },
onEndLoad: function(aSink)
{
try {
this._ds = aSink.QueryInterface(Components.interfaces.nsIRDFDataSource);
this.onDatasourceLoaded(this._ds);
}
catch (e) { }
},
onError: function(aSink, aStatus, aErrorMsg)
{
try {
this._ds = aSink.QueryInterface(Components.interfaces.nsIRDFDataSource);
this.onDatasourceError(aStatus, aErrorMsg);
}
catch (e) { }
},
onStateChange: function (aIndex, aState, aValue)
{
// this.debug("SILENT UPDATE: onStateChange observer of the XPInstall process\n");
const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog;
switch (aState) {
case nsIXPIProgressDialog.DOWNLOAD_START:
break;
case nsIXPIProgressDialog.DOWNLOAD_DONE:
case nsIXPIProgressDialog.INSTALL_START:
break;
case nsIXPIProgressDialog.INSTALL_DONE:
// Set pref to installed version number
// this.debug("SILENT UPDATE: setting version pref to " + this._service._newestVersion.updateVersion + "\n");
gPref.setCharPref(PREF_SAFETYNET_VERSION, this._service._newestVersion.updateVersion);
gSiteControls.pollForNewTrustData();
gParentalControls.pollForNewTrustData();
break;
}
},
_objs: [],
_errors: false,
onProgress: function (aIndex, aValue, aMaxValue)
{
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsSilentUpdater_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsIObserver) &&
!aIID.equals(Components.interfaces.nsIRDFXMLSinkObserver) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
}
// End Silent Updater
function UpdateItem ()
{
}
UpdateItem.prototype = {
init: function UpdateItem_init (aID,
aVersion,
aMinAppVersion,
aMaxAppVersion,
aName,
aRow,
aXPIURL,
aIconURL,
aUpdateRDF,
aType)
{
this._id = aID;
this._version = aVersion;
this._minAppVersion = aMinAppVersion;
this._maxAppVersion = aMaxAppVersion;
this._name = aName;
this._row = aRow;
this._xpiURL = aXPIURL;
this._iconURL = aIconURL;
this._updateRDF = aUpdateRDF;
this._type = aType;
},
get id() { return this._id; },
get version() { return this._version; },
get minAppVersion() { return this._minAppVersion; },
get maxAppVersion() { return this._maxAppVersion; },
get name() { return this._name; },
get row() { return this._row; },
get xpiURL() { return this._xpiURL; },
get iconURL() { return this._iconURL },
get updateRDF() { return this._updateRDF; },
get type() { return this._type; },
get objectSource()
{
return { id : this._id,
version : this._version,
minAppVersion : this._minAppVersion,
maxAppVersion : this._maxAppVersion,
name : this._name,
row : this._row,
xpiURL : this._xpiURL,
iconURL : this._iconURL,
updateRDF : this._updateRDF,
type : this._type
}.toSource();
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function UpdateItem_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsIUpdateItem) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
function nsAppUpdateInfoItem ()
{
}
nsAppUpdateInfoItem.prototype = {
internalName: "",
name : "",
URL : "",
infoURL : "",
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsAppUpdater_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsIAppUpdateInfo) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
function nsSilentUpdateInfoItem ()
{
}
nsSilentUpdateInfoItem.prototype = {
internalName: "",
name : "",
URL : "",
infoURL : "",
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsSilentUpdater_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsISilentUpdateInfo) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
function nsAppUpdateInfo ()
{
}
nsAppUpdateInfo.prototype = {
updateVersion : "",
updateDisplayVersion: "",
updateInfoURL : "",
features : [],
files : [],
optional : [],
languages : [],
patches : [],
getCollection: function (aCollectionName, aItemCount)
{
var collection = aCollectionName in this ? this[aCollectionName] : null;
aItemCount.value = collection ? collection.length : 0;
return collection;
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsAppUpdater_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsIAppUpdateInfoCollection) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
function nsSilentUpdateInfo ()
{
}
nsSilentUpdateInfo.prototype = {
updateVersion : "",
updateDisplayVersion: "",
updateInfoURL : "",
features : [],
files : [],
patches : [],
getCollection: function (aCollectionName, aItemCount)
{
var collection = aCollectionName in this ? this[aCollectionName] : null;
aItemCount.value = collection ? collection.length : 0;
return collection;
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsSilentUpdater_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsISilentUpdateInfoCollection) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
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()
{
return this.major + "." + this.minor + "." + this.subminor + "." + this.release + (this.plus ? "+" : "");
},
compare: function (aVersion)
{
var fields = ["major", "minor", "release", "build", "plus"];
for (var i = 0; i < fields.length; ++i) {
var field = fields[i];
if (aVersion[field] > this[field])
return -1;
else if (aVersion[field] < this[field])
return 1;
}
return 0;
}
}
function nsVersionChecker()
{
}
nsVersionChecker.prototype = {
/////////////////////////////////////////////////////////////////////////////
// nsIVersionChecker
// -ve if B is newer
// equal if A == B
// +ve if A is newer
compare: function nsVersionChecker_compare (aVersionA, aVersionB)
{
var a = this._decomposeVersion(aVersionA);
var b = this._decomposeVersion(aVersionB);
return a.compare(b);
},
biggestMinor: function nsVersionChecker_biggestMinor (aVersionA, aVersionB)
{
var a = this._decomposeVersion(aVersionA);
var b = this._decomposeVersion(aVersionB);
if ((a.major == b.major) && (a.minor == b.minor))
{
// This handles the case when the browser is at a build earlier in the same day
// as the patch
// ie a=current safetynet version = 2005.8.23.11
// b=latest patch version = 2005.8.23.12
if ((a.release == b.release) && (a.build < b.build))
{
// Patch!!
return true;
}
// This handles the case when the browser is at the previous days latest build
// ie a=current safetynet version = 2005.8.23.23
// b=latest patch version = 2005.8.24.12
else if (a.build == 23) // last build of the day, look for tomorrow's latest
{
if (a.release == (b.release - 1))
{
// Patch!!
return true;
}
}
}
// This isn't a version we need to patch with.
return false;
},
_decomposeVersion: function nsVersionChecker__decomposeVersion (aVersion)
{
var plus = 0;
if (aVersion.charAt(aVersion.length-1) == "+") {
aVersion = aVersion.substr(0, aVersion.length-1);
plus = 1;
}
var parts = aVersion.split(".");
return new Version(this._getValidInt(parts[0]),
this._getValidInt(parts[1]),
this._getValidInt(parts[2]),
this._getValidInt(parts[3]),
plus);
},
_getValidInt: function nsVersionChecker__getValidInt (aPartString)
{
var integer = parseInt(aPartString);
if (isNaN(integer))
return 0;
return integer;
},
isValidVersion: function nsVersionChecker_isValidVersion (aVersion)
{
var parts = aVersion.split(".");
if (parts.length == 0)
return false;
for (var i = 0; i < parts.length; ++i) {
var part = parts[i];
if (i == parts.length - 1) {
if (part.lastIndexOf("+") != -1)
parts[i] = part.substr(0, part.length - 1);
}
var integer = parseInt(part);
if (isNaN(integer))
return false;
}
return true;
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function nsVersionChecker_QueryInterface (aIID)
{
if (!aIID.equals(Components.interfaces.nsIVersionChecker) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
var gModule = {
_firstTime: true,
registerSelf: function (aComponentManager, aFileSpec, aLocation, aType)
{
if (this._firstTime) {
this._firstTime = false;
throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
}
aComponentManager = aComponentManager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
for (var key in this._objects) {
var obj = this._objects[key];
aComponentManager.registerFactoryLocation(obj.CID, obj.className, obj.contractID,
aFileSpec, aLocation, aType);
}
},
getClassObject: function (aComponentManager, aCID, aIID)
{
if (!aIID.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
for (var key in this._objects) {
if (aCID.equals(this._objects[key].CID))
return this._objects[key].factory;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
},
_objects: {
manager: { CID: Components.ID("{B3C290A6-3943-4B89-8BBE-C01EB7B3B311}"),
contractID: "@mozilla.org/updates/update-service;1",
className: "Update Service",
factory: {
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return (new nsUpdateService()).QueryInterface(aIID);
}
}
},
version: { CID: Components.ID("{9408E0A5-509E-45E7-80C1-0F35B99FF7A9}"),
contractID: "@mozilla.org/updates/version-checker;1",
className: "Version Checker",
factory: {
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return (new nsVersionChecker()).QueryInterface(aIID);
}
}
},
item: { CID: Components.ID("{F3294B1C-89F4-46F8-98A0-44E1EAE92518}"),
contractID: "@mozilla.org/updates/item;1",
className: "Extension Item",
factory: {
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return new UpdateItem().QueryInterface(aIID);
}
}
}
},
canUnload: function (aComponentManager)
{
return true;
}
};
function NSGetModule(compMgr, fileSpec)
{
return gModule;
}