const PREF_APP_ID                           = "app.id";
const PREF_APP_VERSION                      = "app.version";
const PREF_SAFETYNET_VERSION								= "safetynet.version";
const PREF_UPDATE_APP_DISABLED               = "app.update.disabled";
const PREF_UPDATE_APP_AUTOUPDATEDISABLED     = "app.update.autoUpdateDisabled";
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_DISABLED            = "extensions.update.disabled";
const PREF_UPDATE_EXTENSIONS_AUTOUPDATEDISABLED  = "extensions.update.autoUpdateDisabled";
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_DISABLED						= "silent.update.disabled";
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_AUTOUPDATEDISABLED, this, false);
  pbi.addObserver(PREF_UPDATE_APP_DISABLED, this, false);
  pbi.addObserver(PREF_UPDATE_EXTENSIONS_AUTOUPDATEDISABLED, this, false);
  pbi.addObserver(PREF_UPDATE_EXTENSIONS_DISABLED, 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,
  _appAutoUpdateDisabled: true,
  _extAutoUpdateDisabled: true,
  _silentAutoUpdateDisabled: 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._appAutoUpdateDisabled = gPref.getBoolPref(PREF_UPDATE_APP_AUTOUPDATEDISABLED);
    this._extAutoUpdateDisabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_AUTOUPDATEDISABLED);
    this._silentAutoUpdateDisabled = gPref.getBoolPref(PREF_SILENT_UPDATE_DISABLED);

    if (!this._appAutoUpdateDisabled && !this._extAutoUpdateDisabled
     && !this._silentAutoUpdateDisabled)
      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_*_AUTOUPDATEDISABLED since they effectively
    // shut down the update UI if the administrator/distributor has configured
    // a build to have disallowed these types of update.
    var extUpdateDisabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_DISABLED);
    var appUpdateDisabled = gPref.getBoolPref(PREF_UPDATE_APP_DISABLED);
    var silentUpdateDisabled = gPref.getBoolPref(PREF_SILENT_UPDATE_DISABLED);

    var types = 0;
    if (extUpdateDisabled) {
      if (aRequestedTypes & nsIUpdateItem.TYPE_EXTENSION)
        types |= nsIUpdateItem.TYPE_EXTENSION;
      if (aRequestedTypes & nsIUpdateItem.TYPE_THEME)
        types |= nsIUpdateItem.TYPE_THEME;
    }
    if (appUpdateDisabled &&
        (aRequestedTypes & nsIUpdateItem.TYPE_APP))
      types |= nsIUpdateItem.TYPE_APP;

		if (silentUpdateDisabled &&
				 (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._appAutoUpdateDisabled, 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._silentAutoUpdateDisabled, 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._extAutoUpdateDisabled, 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._appAutoUpdateDisabled || this._extAutoUpdateDisabled)) {
      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_AUTOUPDATEDISABLED:
				case PREF_UPDATE_APP_DISABLED:
					this._appAutoUpdateDisabled = gPref.getBoolPref(PREF_UPDATE_APP_AUTOUPDATEDISABLED);
					if (!this._appAutoUpdateDisabled)
					{
						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_AUTOUPDATEDISABLED:
				case PREF_UPDATE_EXTENSIONS_DISABLED:
					this._extAutoUpdateDisabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_AUTOUPDATEDISABLED);
					if (!this._extAutoUpdateDisabled) {
						// 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_AUTOUPDATEDISABLED, this);
      pbi.removeObserver(PREF_UPDATE_APP_DISABLED, this);
      pbi.removeObserver(PREF_UPDATE_EXTENSIONS_AUTOUPDATEDISABLED, this);
      pbi.removeObserver(PREF_UPDATE_EXTENSIONS_DISABLED, 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._appAutoUpdateDisabled ||
                        (notBackground ? gPref.getBoolPref(PREF_UPDATE_APP_DISABLED)
                                       : false);
    var canUpdateExt = this._service._extAutoUpdateDisabled ||
                        (notBackground ? gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_DISABLED)
                                       : 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;
}


