home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 March / PCWorld_2007-03_cd.bin / komunikace / nvu / nvu-1.0-cs-CZ.win32.installer.exe / components / nsUpdateService.js < prev    next >
Text File  |  2005-06-17  |  49KB  |  1,266 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is the Update Service.
  16.  *
  17.  * The Initial Developer of the Original Code is Ben Goodger.
  18.  * Portions created by the Initial Developer are Copyright (C) 2004
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *  Ben Goodger <ben@bengoodger.com>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const PREF_APP_ID                           = "app.id";
  39. const PREF_APP_VERSION                      = "app.version";
  40. const PREF_UPDATE_APP_ENABLED               = "app.update.enabled";
  41. const PREF_UPDATE_APP_AUTOUPDATEENABLED     = "app.update.autoUpdateEnabled";
  42. const PREF_UPDATE_APP_URI                   = "app.update.url";
  43. const PREF_UPDATE_APP_UPDATESAVAILABLE      = "app.update.updatesAvailable";
  44. const PREF_UPDATE_APP_INTERVAL              = "app.update.interval";
  45. const PREF_UPDATE_APP_LASTUPDATEDATE        = "app.update.lastUpdateDate";
  46. const PREF_UPDATE_APP_PERFORMED             = "app.update.performed";
  47.  
  48. const PREF_UPDATE_EXTENSIONS_ENABLED            = "extensions.update.enabled";
  49. const PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED  = "extensions.update.autoUpdateEnabled";
  50. const PREF_UPDATE_EXTENSIONS_AUTOUPDATE         = "extensions.update.autoUpdate";
  51. const PREF_UPDATE_EXTENSIONS_COUNT              = "extensions.update.count";
  52. const PREF_UPDATE_EXTENSIONS_INTERVAL           = "extensions.update.interval";
  53. const PREF_UPDATE_EXTENSIONS_LASTUPDATEDATE     = "extensions.update.lastUpdateDate";
  54. const PREF_UPDATE_EXTENSIONS_SEVERITY_THRESHOLD = "extensions.update.severity.threshold";
  55.  
  56. const PREF_UPDATE_INTERVAL                  = "update.interval";
  57. const PREF_UPDATE_SEVERITY                  = "update.severity";
  58. const PREF_UPDATE_SHOW_SLIDING_NOTIFICATION = "update.showSlidingNotification";
  59.  
  60. const nsIExtensionManager = Components.interfaces.nsIExtensionManager;
  61. const nsIUpdateService    = Components.interfaces.nsIUpdateService;
  62. const nsIUpdateItem       = Components.interfaces.nsIUpdateItem;
  63.  
  64. const UPDATED_EXTENSIONS  = 0x01;
  65. const UPDATED_APP         = 0x02;
  66.  
  67. function APP_NS(aProperty)
  68. {
  69.   return "http://www.mozilla.org/2004/app-rdf#" + aProperty;
  70. }
  71.  
  72. function getOSKey()
  73. {
  74.   return "windows";
  75. }
  76.  
  77. function stackTraceFunctionFormat(aFunctionName)
  78. {
  79.   var classDelimiter = aFunctionName.indexOf("_");
  80.   var className = aFunctionName.substr(0, classDelimiter);
  81.   if (!className)
  82.     className == "<global>";
  83.   var functionName = aFunctionName.substr(classDelimiter + 1, aFunctionName.length);
  84.   if (!functionName) 
  85.     functionName == "<anonymous>";
  86.   return className + "::" + functionName;
  87. }
  88.  
  89. function stackTrace(aArguments, aMaxCount)
  90. {
  91.   dump("=[STACKTRACE]=====================================================\n");
  92.   dump("*** at: " + stackTraceFunctionFormat(aArguments.callee.name) + "()\n");
  93.   var temp = aArguments.callee.caller;
  94.   var count = 0;
  95.   while (temp) {
  96.     dump("***     " + stackTraceFunctionFormat(temp.name) + ")\n");
  97.     temp = temp.arguments.callee.caller;
  98.     if (aMaxCount > 0 && ++count == aMaxCount)
  99.       break;
  100.   }
  101.   dump("==================================================================\n");
  102. }
  103.  
  104. var gPref   = null;
  105. var gOS     = null;
  106. var gRDF    = null;
  107.  
  108. function nsUpdateService()
  109. {
  110.   gPref = Components.classes["@mozilla.org/preferences-service;1"]
  111.                     .getService(Components.interfaces.nsIPrefBranch);
  112.   gOS   = Components.classes["@mozilla.org/observer-service;1"]
  113.                     .getService(Components.interfaces.nsIObserverService);
  114.   gRDF  = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  115.                     .getService(Components.interfaces.nsIRDFService);
  116.   
  117.   this.watchForUpdates();
  118.  
  119.   var pbi = gPref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  120.   pbi.addObserver(PREF_UPDATE_APP_AUTOUPDATEENABLED, this, false);
  121.   pbi.addObserver(PREF_UPDATE_APP_ENABLED, this, false);
  122.   pbi.addObserver(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED, this, false);
  123.   pbi.addObserver(PREF_UPDATE_EXTENSIONS_ENABLED, this, false);
  124.   pbi.addObserver(PREF_UPDATE_INTERVAL, this, false);
  125.   pbi.addObserver(PREF_UPDATE_APP_INTERVAL, this, false);
  126.   pbi.addObserver(PREF_UPDATE_EXTENSIONS_INTERVAL, this, false);
  127.  
  128.   // Observe xpcom-shutdown to unhook pref branch observers above to avoid 
  129.   // shutdown leaks.
  130.   gOS.addObserver(this, "xpcom-shutdown", false);
  131.   
  132.   // Reset update state from previous session if an app update was installed.
  133.   if (gPref.prefHasUserValue(PREF_UPDATE_APP_PERFORMED)) 
  134.     gPref.clearUserPref(PREF_UPDATE_APP_PERFORMED);
  135. }
  136.  
  137. nsUpdateService.prototype = {
  138.   _updateObserver: null,
  139.   _appAutoUpdateEnabled: true,
  140.   _extAutoUpdateEnabled: true,
  141.     
  142.   /////////////////////////////////////////////////////////////////////////////
  143.   // nsIUpdateService
  144.   watchForUpdates: function nsUpdateService_watchForUpdates ()
  145.   {
  146.     // This is called when the app starts, so check to see if the time interval
  147.     // expired between now and the last time an automated update was performed.
  148.     // now is the same one that was started last time. 
  149.     this._appAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_APP_AUTOUPDATEENABLED);
  150.     this._extAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED);
  151.     if (!this._appAutoUpdateEnabled && !this._extAutoUpdateEnabled)
  152.       return;
  153.  
  154.     this._makeTimer(gPref.getIntPref(PREF_UPDATE_INTERVAL));
  155.   },
  156.   
  157.   _getAllowedTypes: function nsUpdateService__getAllowedTypes(aRequestedTypes)
  158.   {
  159.     // Figure out what types we're allowed to update. These options
  160.     // differ from PREF_UPDATE_*_AUTOUPDATEENABLED since they effectively
  161.     // shut down the update UI if the administrator/distributor has configured
  162.     // a build to have disallowed these types of update.
  163.     var extUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
  164.     var appUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_APP_ENABLED);
  165.     
  166.     var types = 0;
  167.     if (extUpdateEnabled) {
  168.       if (aRequestedTypes & nsIUpdateItem.TYPE_EXTENSION)
  169.         types |= nsIUpdateItem.TYPE_EXTENSION;
  170.       if (aRequestedTypes & nsIUpdateItem.TYPE_THEME)
  171.         types |= nsIUpdateItem.TYPE_THEME;
  172.     }
  173.     if (appUpdateEnabled && 
  174.         (aRequestedTypes & nsIUpdateItem.TYPE_APP))
  175.       types |= nsIUpdateItem.TYPE_APP;
  176.  
  177.     return types;
  178.   },
  179.   
  180.   checkForUpdates: function nsUpdateService_checkForUpdates (aItems, aItemCount, aUpdateTypes, aSourceEvent, aParentWindow)
  181.   {
  182.     var types = this._getAllowedTypes(aUpdateTypes);
  183.     
  184.     // Nothing to update
  185.     if (!types) {
  186.       var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  187.                           .getService(Components.interfaces.nsIStringBundleService);
  188.       var bundle = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
  189.       var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  190.                          .getService(Components.interfaces.nsIPromptService);
  191.       ps.alert(aParentWindow, 
  192.                bundle.GetStringFromName("updatesdisabledtitle"), 
  193.                bundle.GetStringFromName("updatesdisabledmessage"));
  194.       return;
  195.     }
  196.   
  197.     switch (aSourceEvent) {
  198.     case nsIUpdateService.SOURCE_EVENT_MISMATCH:
  199.     case nsIUpdateService.SOURCE_EVENT_USER:
  200.       if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_USER && 
  201.           gPref.getBoolPref(PREF_UPDATE_APP_PERFORMED)) {
  202.         var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  203.                             .getService(Components.interfaces.nsIStringBundleService);
  204.         var bundle = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
  205.         var brandBundle = sbs.createBundle("chrome://global/locale/brand.properties");
  206.         var brandShortName = brandBundle.GetStringFromName("brandShortName");        
  207.         var message = bundle.formatStringFromName("appupdateperformedmessage",
  208.                                                   [brandShortName, brandShortName], 2);
  209.         var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  210.                           .getService(Components.interfaces.nsIPromptService);
  211.         ps.alert(aParentWindow, 
  212.                  bundle.GetStringFromName("appupdateperformedtitle"), 
  213.                  message);
  214.         return;
  215.       }
  216.       
  217.       var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  218.                          .getService(Components.interfaces.nsIWindowMediator);
  219.       var wizard = wm.getMostRecentWindow("Update:Wizard");
  220.       if (wizard)
  221.         wizard.focus();
  222.       else {
  223.         var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  224.                           .getService(Components.interfaces.nsIWindowWatcher);
  225.         var ary = Components.classes["@mozilla.org/supports-array;1"]
  226.                             .createInstance(Components.interfaces.nsISupportsArray);
  227.         var updateTypes = Components.classes["@mozilla.org/supports-PRUint8;1"]
  228.                                     .createInstance(Components.interfaces.nsISupportsPRUint8);
  229.         updateTypes.data = types;
  230.         ary.AppendElement(updateTypes);
  231.         var sourceEvent = Components.classes["@mozilla.org/supports-PRUint8;1"]
  232.                                     .createInstance(Components.interfaces.nsISupportsPRUint8);
  233.         sourceEvent.data = aSourceEvent;
  234.         ary.AppendElement(sourceEvent);
  235.         for (var i = 0; i < aItems.length; ++i)
  236.           ary.AppendElement(aItems[i]);
  237.  
  238.         var features = "chrome,centerscreen";
  239.         if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_MISMATCH) {
  240.           features += ",modal"; // Must block in mismatch mode since there's 
  241.                                 // no main evt loop yet. 
  242.         }
  243.         // This *must* be modal so as not to break startup! This code is invoked before
  244.         // the main event loop is initiated (via checkForMismatches).
  245.         ww.openWindow(aParentWindow, "chrome://mozapps/content/update/update.xul", 
  246.                       "", features, ary);
  247.       }
  248.       break;
  249.     case nsIUpdateService.SOURCE_EVENT_BACKGROUND:
  250.       // Rather than show a UI, call the checkForUpdates function directly here. 
  251.       // The Browser's inline front end update notification system listens for the
  252.       // updates that this function broadcasts.
  253.       this.checkForUpdatesInternal([], 0, types, aSourceEvent);
  254.  
  255.       break;
  256.     }  
  257.   },
  258.   
  259.   _canUpdate: function (aPreference, aSourceEvent, aUpdateTypes)
  260.   {
  261.     // Always can update if the autoupdate preference is set, otherwise, 
  262.     // allow updates only when not in backround update mode, i.e. when the user
  263.     // explicitly asked. 
  264.     return aPreference ? true 
  265.                        : aSourceEvent != nsIUpdateService.SOURCE_EVENT_BACKGROUND;
  266.   },
  267.   
  268.   checkForUpdatesInternal: function nsUpdateService_checkForUpdatesInternal (aItems, aItemCount, aUpdateTypes, aSourceEvent)
  269.   {
  270.     var types = this._getAllowedTypes(aUpdateTypes);
  271.  
  272.     // Listen for notifications sent out by the app updater (implemented here) and the
  273.     // extension updater (implemented in nsExtensionItemUpdater)
  274.     var canUpdate;
  275.     this._updateObserver = new nsUpdateObserver(types, aSourceEvent, this);
  276.     var os = Components.classes["@mozilla.org/observer-service;1"]
  277.                        .getService(Components.interfaces.nsIObserverService);
  278.     if (types & nsIUpdateItem.TYPE_APP) {
  279.       if (this._canUpdate(this._appAutoUpdateEnabled, aSourceEvent, types)) {
  280.         gOS.addObserver(this._updateObserver, "Update:App:Ended", false);
  281.         
  282.         this._currentVersion  = new nsAppUpdateInfo();
  283.         this._newestVersion   = new nsAppUpdateInfo();
  284.  
  285.         if (!this._updateObserver.appUpdater) {
  286.           this._updateObserver.appUpdater = new nsAppUpdater(this);
  287.           this._updateObserver.appUpdater.checkForUpdates();
  288.         }
  289.       }
  290.     }
  291.     if (types & nsIUpdateItem.TYPE_ADDON) { // TYPE_EXTENSION, TYPE_ANY, etc.
  292.       if (this._canUpdate(this._extAutoUpdateEnabled, aSourceEvent, types)) {
  293.         gOS.addObserver(this._updateObserver, "Update:Extension:Started", false);
  294.         gOS.addObserver(this._updateObserver, "Update:Extension:Item-Ended", false);
  295.         gOS.addObserver(this._updateObserver, "Update:Extension:Ended", false);
  296.  
  297.         var em = Components.classes["@mozilla.org/extensions/manager;1"]
  298.                            .getService(Components.interfaces.nsIExtensionManager);
  299.         em.update(aItems, aItems.length, false);
  300.       }
  301.     }
  302.     
  303.     if (aSourceEvent == nsIUpdateService.SOURCE_EVENT_BACKGROUND && 
  304.         (this._appAutoUpdateEnabled || this._extAutoUpdateEnabled)) {
  305.       if (types & nsIUpdateItem.TYPE_ADDON)
  306.         gPref.setIntPref(PREF_UPDATE_EXTENSIONS_LASTUPDATEDATE, this._nowInMilliseconds / 1000);
  307.       if (types & nsIUpdateItem.TYPE_APP)
  308.         gPref.setIntPref(PREF_UPDATE_APP_LASTUPDATEDATE, this._nowInMilliseconds / 1000);
  309.     }
  310.   },
  311.   
  312.   get updateCount()
  313.   {
  314.     // The number of available updates is the number of extension/theme/other
  315.     // updates + 1 for an application update, if one is available.
  316.     var updateCount = this.extensionUpdatesAvailable;
  317.     if (this.appUpdatesAvailable)
  318.       ++updateCount;
  319.     return updateCount;
  320.   },
  321.   
  322.   get updateSeverity()
  323.   {
  324.     return gPref.getIntPref(PREF_UPDATE_SEVERITY);
  325.   },
  326.   
  327.   _appUpdatesAvailable: undefined,
  328.   get appUpdatesAvailable() 
  329.   {
  330.     if (this._appUpdatesAvailable === undefined) {
  331.       return (gPref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE) && 
  332.               gPref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE));
  333.     }
  334.     return this._appUpdatesAvailable;
  335.   },
  336.   set appUpdatesAvailable(aValue)
  337.   {
  338.     this._appUpdatesAvailable = aValue;
  339.     return aValue;
  340.   },
  341.   
  342.   _extensionUpdatesAvailable: undefined,
  343.   get extensionUpdatesAvailable()
  344.   {
  345.     if (this._extensionUpdatesAvailable === undefined)
  346.       return gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT);
  347.     return this._extensionUpdatesAvailable;
  348.   },
  349.   set extensionUpdatesAvailable(aValue)
  350.   {
  351.     this._extensionUpdatesAvailable = aValue;
  352.     return aValue;
  353.   },
  354.   
  355.   _newestVersion: null,
  356.   get newestVersion()
  357.   {
  358.     return this._newestVersion;
  359.   },
  360.   _currentVersion: null,
  361.   get currentVersion()
  362.   {
  363.     return this._currentVersion;
  364.   },
  365.     
  366.   /////////////////////////////////////////////////////////////////////////////
  367.   // nsITimerCallback
  368.   _shouldUpdate: function nsUpdateService__shouldUpdate (aIntervalPref, aLastCheckPref)
  369.   {
  370.     var interval = gPref.getIntPref(aIntervalPref);
  371.     var lastUpdateTime = gPref.getIntPref(aLastCheckPref);
  372.     return ((Math.round(this._nowInMilliseconds/1000) - lastUpdateTime) > Math.round(interval/1000));
  373.   },
  374.   
  375.   notify: function nsUpdateService_notify (aTimer)
  376.   {
  377.     var types = 0;
  378.     if (this._shouldUpdate(PREF_UPDATE_EXTENSIONS_INTERVAL, 
  379.                            PREF_UPDATE_EXTENSIONS_LASTUPDATEDATE)) {
  380.       types |= nsIUpdateItem.TYPE_ADDON;         
  381.     }
  382.     if (this._shouldUpdate(PREF_UPDATE_APP_INTERVAL,
  383.                            PREF_UPDATE_APP_LASTUPDATEDATE)) {
  384.       types |= nsIUpdateItem.TYPE_APP;         
  385.     }
  386.     if (types)
  387.       this.checkForUpdatesInternal([], 0, types, 
  388.                                    nsIUpdateService.SOURCE_EVENT_BACKGROUND);
  389.   },
  390.  
  391.   /////////////////////////////////////////////////////////////////////////////
  392.   // nsIObserver
  393.   observe: function nsUpdateService_observe (aSubject, aTopic, aData)
  394.   {
  395.     switch (aTopic) {
  396.     case "nsPref:changed":
  397.       var needsNotification = false;
  398.       switch (aData) {
  399.       case PREF_UPDATE_APP_AUTOUPDATEENABLED:
  400.       case PREF_UPDATE_APP_ENABLED:
  401.         this._appAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_APP_AUTOUPDATEENABLED);
  402.         if (!this._appAutoUpdateEnabled) {
  403.           this._clearAppUpdatePrefs();
  404.           needsNotification = true;
  405.         }
  406.         else {
  407.           // Do an initial check NOW to update any FE components and kick off the
  408.           // timer. 
  409.           this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_APP, 
  410.                                        nsIUpdateService.SOURCE_EVENT_BACKGROUND);
  411.         }
  412.         break;
  413.       case PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED:
  414.       case PREF_UPDATE_EXTENSIONS_ENABLED:
  415.         this._extAutoUpdateEnabled = gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED);
  416.         if (!this._extAutoUpdateEnabled) {
  417.           // Unset prefs used by the update service to signify extension updates
  418.           if (gPref.prefHasUserValue(PREF_UPDATE_EXTENSIONS_COUNT))
  419.             gPref.clearUserPref(PREF_UPDATE_EXTENSIONS_COUNT);
  420.           needsNotification = true;
  421.         }
  422.         else {
  423.           // Do an initial check NOW to update any FE components and kick off the
  424.           // timer. 
  425.           this.checkForUpdatesInternal([], 0, nsIUpdateItem.TYPE_ADDON, 
  426.                                        nsIUpdateService.SOURCE_EVENT_BACKGROUND);
  427.         }
  428.         break;
  429.       case PREF_UPDATE_INTERVAL:
  430.       case PREF_UPDATE_APP_INTERVAL:
  431.       case PREF_UPDATE_EXTENSIONS_INTERVAL:
  432.         this._makeTimer(gPref.getIntPref(PREF_UPDATE_INTERVAL));
  433.         break;
  434.       }
  435.     
  436.       if (needsNotification) {
  437.         var os = Components.classes["@mozilla.org/observer-service;1"]
  438.                            .getService(Components.interfaces.nsIObserverService);
  439.         var backgroundEvt = Components.interfaces.nsIUpdateService.SOURCE_EVENT_BACKGROUND;
  440.         gOS.notifyObservers(null, "Update:Ended", backgroundEvt.toString());
  441.       }
  442.       break;
  443.     case "xpcom-shutdown":
  444.       gOS.removeObserver(this, "xpcom-shutdown");    
  445.       
  446.       // Clean up held observers etc to avoid leaks. 
  447.       var pbi = gPref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  448.       pbi.removeObserver(PREF_UPDATE_APP_AUTOUPDATEENABLED, this);
  449.       pbi.removeObserver(PREF_UPDATE_APP_ENABLED, this);
  450.       pbi.removeObserver(PREF_UPDATE_EXTENSIONS_AUTOUPDATEENABLED, this);
  451.       pbi.removeObserver(PREF_UPDATE_EXTENSIONS_ENABLED, this);
  452.       pbi.removeObserver(PREF_UPDATE_INTERVAL, this);
  453.       pbi.removeObserver(PREF_UPDATE_EXTENSIONS_INTERVAL, this);
  454.     
  455.       // Release strongly held services.
  456.       gPref = null;
  457.       gRDF  = null;
  458.       gOS   = null;
  459.       if (this._timer) {
  460.         this._timer.cancel();
  461.         this._timer = null;
  462.       }
  463.       break;
  464.     }  
  465.   },
  466.  
  467.   /////////////////////////////////////////////////////////////////////////////
  468.   // nsUpdateService
  469.   _timer: null,
  470.   _makeTimer: function nsUpdateService__makeTimer (aDelay)
  471.   {
  472.     if (!this._timer) 
  473.       this._timer = Components.classes["@mozilla.org/timer;1"]
  474.                               .createInstance(Components.interfaces.nsITimer);
  475.     this._timer.cancel();
  476.     this._timer.initWithCallback(this, aDelay, 
  477.                                  Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
  478.   },
  479.   
  480.   get _nowInMilliseconds ()
  481.   {
  482.     var d = new Date();
  483.     return Date.UTC(d.getUTCFullYear(), 
  484.                     d.getUTCMonth(),
  485.                     d.getUTCDay(),
  486.                     d.getUTCHours(),
  487.                     d.getUTCMinutes(),
  488.                     d.getUTCSeconds(),
  489.                     d.getUTCMilliseconds());
  490.   },
  491.   
  492.   _clearAppUpdatePrefs: function nsUpdateService__clearAppUpdatePrefs ()
  493.   {
  494.     // Unset prefs used by the update service to signify application updates
  495.     if (gPref.prefHasUserValue(PREF_UPDATE_APP_UPDATESAVAILABLE))
  496.       gPref.clearUserPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
  497.   },
  498.  
  499.   /////////////////////////////////////////////////////////////////////////////
  500.   // nsISupports
  501.   QueryInterface: function nsUpdateService_QueryInterface (aIID) 
  502.   {
  503.     if (!aIID.equals(Components.interfaces.nsIUpdateService) &&
  504.         !aIID.equals(Components.interfaces.nsIObserver) && 
  505.         !aIID.equals(Components.interfaces.nsISupports))
  506.       throw Components.results.NS_ERROR_NO_INTERFACE;
  507.     return this;
  508.   }
  509. };
  510.  
  511. function nsUpdateObserver(aUpdateTypes, aSourceEvent, aService)
  512. {
  513.   this._updateTypes = aUpdateTypes;
  514.   this._sourceEvent = aSourceEvent;
  515.   this._service = aService;
  516. }
  517.  
  518. nsUpdateObserver.prototype = {
  519.   _updateTypes: 0,
  520.   _sourceEvent: 0,
  521.   _updateState: 0,
  522.   _endedTimer : null,
  523.   
  524.   appUpdater: null,
  525.   
  526.   get _doneUpdating()
  527.   {
  528.     var notBackground = this._sourceEvent != nsIUpdateService.SOURCE_EVENT_BACKGROUND;
  529.     var canUpdateApp = this._service._appAutoUpdateEnabled || 
  530.                         (notBackground ? gPref.getBoolPref(PREF_UPDATE_APP_ENABLED) 
  531.                                        : false);
  532.     var canUpdateExt = this._service._extAutoUpdateEnabled || 
  533.                         (notBackground ? gPref.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED) 
  534.                                        : false);
  535.    
  536.     var test = 0;
  537.     var updatingApp = (this._updateTypes & nsIUpdateItem.TYPE_APP) && 
  538.                        canUpdateApp;
  539.     var updatingExt = (this._updateTypes & nsIUpdateItem.TYPE_ADDON) &&
  540.                        canUpdateExt;
  541.     
  542.     if (updatingApp)
  543.       test |= UPDATED_APP;
  544.     if (updatingExt)
  545.       test |= UPDATED_EXTENSIONS;
  546.     
  547.     return (this._updateState & test) == test;
  548.   },
  549.   
  550.   /////////////////////////////////////////////////////////////////////////////
  551.   // nsIObserver
  552.   observe: function nsUpdateObserver_observe (aSubject, aTopic, aData)
  553.   {
  554.     switch (aTopic) {
  555.     case "Update:Extension:Started":
  556.       // Reset the count
  557.       gPref.setIntPref(PREF_UPDATE_EXTENSIONS_COUNT, 0);
  558.       break;
  559.     case "Update:Extension:Item-Ended":
  560.       var newCount = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT) + 1;
  561.       gPref.setIntPref(PREF_UPDATE_EXTENSIONS_COUNT, newCount);
  562.       var threshold = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_SEVERITY_THRESHOLD);
  563.       if (this._service.updateSeverity < nsIUpdateService.SEVERITY_HIGH) {
  564.         if (newCount > threshold)
  565.           gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_MEDIUM);
  566.         else
  567.           gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_LOW);
  568.       }
  569.       break;
  570.     case "Update:Extension:Ended":
  571.       this._updateState |= UPDATED_EXTENSIONS;
  572.       break;
  573.     case "Update:App:Ended":
  574.       this._updateState |= UPDATED_APP;
  575.       
  576.       this.appUpdater.destroy();
  577.       this.appUpdater = null;
  578.       break;
  579.     }
  580.     
  581.     if (this._doneUpdating) {
  582.       // Do the finalize stuff on a timer to let other observers have a chance to
  583.       // handle
  584.       if (this._endedTimer)
  585.         this._endedTimer.cancel();
  586.       this._endedTimer = Components.classes["@mozilla.org/timer;1"]
  587.                                    .createInstance(Components.interfaces.nsITimer);
  588.       this._endedTimer.initWithCallback(this, 0, 
  589.                                         Components.interfaces.nsITimer.TYPE_ONE_SHOT);
  590.     }
  591.   },
  592.   
  593.   notify: function nsUpdateObserver_notify (aTimer)
  594.   {
  595.     // The Inline Browser Update UI uses this notification to refresh its update 
  596.     // UI if necessary.
  597.     gOS.notifyObservers(null, "Update:Ended", this._sourceEvent.toString());
  598.  
  599.     // Show update notification UI if:
  600.     // We were updating any types and any item was found
  601.     // We were updating extensions and an extension update was found.
  602.     // We were updating app and an app update was found. 
  603.     var updatesAvailable = (((this._updateTypes & nsIUpdateItem.TYPE_EXTENSION) || 
  604.                               (this._updateTypes & nsIUpdateItem.TYPE_ANY)) && 
  605.                             gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT) != 0);
  606.     if (!updatesAvailable) {
  607.       updatesAvailable = ((this._updateTypes & nsIUpdateItem.TYPE_APP) || 
  608.                           (this._updateTypes & nsIUpdateItem.TYPE_ANY)) && 
  609.                           gPref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE);
  610.     }
  611.     
  612.     var showNotification = gPref.getBoolPref(PREF_UPDATE_SHOW_SLIDING_NOTIFICATION);
  613.     if (showNotification && updatesAvailable && 
  614.         this._sourceEvent == nsIUpdateService.SOURCE_EVENT_BACKGROUND) {
  615.       var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  616.                           .getService(Components.interfaces.nsIStringBundleService);
  617.       var bundle = sbs.createBundle("chrome://mozapps/locale/update/update.properties");
  618.  
  619.       var alertTitle = bundle.GetStringFromName("updatesAvailableTitle");
  620.       var alertText = bundle.GetStringFromName("updatesAvailableText");
  621.       
  622.       var alerts = Components.classes["@mozilla.org/alerts-service;1"]
  623.                             .getService(Components.interfaces.nsIAlertsService);
  624.       alerts.showAlertNotification("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png",
  625.                                     alertTitle, alertText, true, "", this);
  626.     }
  627.  
  628.     this.destroy();
  629.   },
  630.  
  631.   destroy: function nsUpdateObserver_destroy ()
  632.   {
  633.     try { gOS.removeObserver(this, "Update:Extension:Started");    } catch (e) { }
  634.     try { gOS.removeObserver(this, "Update:Extension:Item-Ended"); } catch (e) { }
  635.     try { gOS.removeObserver(this, "Update:Extension:Ended");      } catch (e) { }
  636.     try { gOS.removeObserver(this, "Update:App:Ended");            } catch (e) { }
  637.     
  638.     if (this._endedTimer) {
  639.       this._endedTimer.cancel();
  640.       this._endedTimer = null;
  641.     }
  642.   },
  643.  
  644.   ////////////////////////////////////////////////////////////////////////////
  645.   // nsIAlertListener
  646.   onAlertFinished: function nsUpdateObserver_onAlertFinished ()
  647.   {
  648.   },
  649.   
  650.   onAlertClickCallback: function nsUpdateObserver_onAlertClickCallback (aCookie)
  651.   {
  652.     var updates = Components.classes["@mozilla.org/updates/update-service;1"]
  653.                             .getService(Components.interfaces.nsIUpdateService);
  654.     updates.checkForUpdates([], 0, Components.interfaces.nsIUpdateItem.TYPE_ANY, 
  655.                             Components.interfaces.nsIUpdateService.SOURCE_EVENT_USER,
  656.                             null);
  657.   },
  658.   
  659.   /////////////////////////////////////////////////////////////////////////////
  660.   // nsISupports
  661.   QueryInterface: function nsUpdateObserver_QueryInterface (aIID) 
  662.   {
  663.     if (!aIID.equals(Components.interfaces.nsIObserver) &&
  664.         !aIID.equals(Components.interfaces.nsIAlertListener) && 
  665.         !aIID.equals(Components.interfaces.nsISupports))
  666.       throw Components.results.NS_ERROR_NO_INTERFACE;
  667.     return this;
  668.   }
  669. };
  670.  
  671. ///////////////////////////////////////////////////////////////////////////////
  672. // App Updater
  673. function nsAppUpdater(aUpdateService)
  674. {
  675.   this._service = aUpdateService;
  676. }
  677.  
  678. nsAppUpdater.prototype = {
  679.   _service    : null,
  680.  
  681.   checkForUpdates: function ()
  682.   {
  683.     var dsURI = gPref.getComplexValue(PREF_UPDATE_APP_URI, 
  684.                                       Components.interfaces.nsIPrefLocalizedString).data;
  685.     dsURI += "?" + Math.round(Math.random() * 1000);
  686.     this._ds = gRDF.GetDataSource(dsURI);
  687.     var rds = this._ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
  688.     if (rds.loaded)
  689.       this.onDatasourceLoaded(this._ds);
  690.     else {
  691.       var sink = this._ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
  692.       
  693.       // Creates a strong ref that holds this object past when it falls out of
  694.       // scope in the calling function
  695.       sink.addXMLSinkObserver(this);
  696.     }
  697.   },
  698.   
  699.   destroy: function ()
  700.   {
  701.     var sink = this._ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
  702.     sink.removeXMLSinkObserver(this);
  703.     this._service = null;
  704.   },
  705.   
  706.   /////////////////////////////////////////////////////////////////////////////
  707.   //
  708.   _ncR: function nsUpdateService__ncR (aProperty)
  709.   {
  710.     return gRDF.GetResource("http://home.netscape.com/NC-rdf#" + aProperty);
  711.   },
  712.   
  713.   _getPropertyFromResource: function nsAppUpdater__getPropertyFromResource (aDataSource,
  714.                                                                             aSourceResource, 
  715.                                                                             aProperty)
  716.   {
  717.     var rv;
  718.     try {
  719.       var property = gRDF.GetResource(APP_NS(aProperty));
  720.       rv = this._stringData(aDataSource.GetTarget(aSourceResource, property, true));
  721.       if (rv == "--")
  722.         throw Components.results.NS_ERROR_FAILURE;
  723.     }
  724.     catch (e) { 
  725.       return null;
  726.     }
  727.     return rv;
  728.   },
  729.  
  730.   _stringData: function nsAppUpdater__stringData (aLiteralOrResource)
  731.   {
  732.     try {
  733.       var obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFLiteral);
  734.       return obj.Value;
  735.     }
  736.     catch (e) {
  737.       try {
  738.         obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFResource);
  739.         return obj.Value;
  740.       }
  741.       catch (e) {}
  742.     }
  743.     return "--";
  744.   },
  745.  
  746.   /////////////////////////////////////////////////////////////////////////////
  747.   //
  748.   onDatasourceLoaded: function nsAppUpdater_onDatasourceLoaded (aDataSource)
  749.   {
  750.     var appID = gPref.getCharPref(PREF_APP_ID);
  751.     var appVersion = gPref.getCharPref(PREF_APP_VERSION);
  752.     
  753.     var appResource = gRDF.GetResource("urn:mozilla:app:" + appID);
  754.     var updatesArc = gRDF.GetResource(APP_NS("updates"));
  755.     var updatesResource = aDataSource.GetTarget(appResource, updatesArc, true);
  756.     
  757.     try {
  758.       updatesResource = updatesResource.QueryInterface(Components.interfaces.nsIRDFResource);
  759.     }
  760.     catch (e) { 
  761.       gOS.notifyObservers(null, "Update:App:Error", "");
  762.       gOS.notifyObservers(null, "Update:App:Ended", "");
  763.       return; 
  764.     }
  765.     
  766.     var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
  767.                        .getService(Components.interfaces.nsIRDFContainerUtils);
  768.     if (cu.IsContainer(aDataSource, updatesResource)) {
  769.       var c = Components.classes["@mozilla.org/rdf/container;1"]
  770.                         .getService(Components.interfaces.nsIRDFContainer);
  771.       c.Init(aDataSource, updatesResource);
  772.       
  773.       var versionChecker = Components.classes["@mozilla.org/updates/version-checker;1"]
  774.                                      .getService(Components.interfaces.nsIVersionChecker);
  775.       
  776.       var newestVersionObj = { version: appVersion, resource: null };
  777.       var updates = c.GetElements();
  778.       while (updates.hasMoreElements()) {
  779.         var update = updates.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  780.         var version = this._getPropertyFromResource(aDataSource, update, "version");
  781.         if (!version)
  782.           continue;
  783.         if (versionChecker.compare(appVersion, version) == 0)
  784.           this._parseVersionData(aDataSource, update, this._service._currentVersion);
  785.         else if (versionChecker.compare(newestVersionObj.version, version) < 0) {
  786.           newestVersionObj.version = version;
  787.           newestVersionObj.resource = update;
  788.         }
  789.       }
  790.       if (newestVersionObj.resource)
  791.         this._parseVersionData(aDataSource, newestVersionObj.resource, this._service._newestVersion);
  792.       
  793.       // There is a newer version of the app available or there are any critical
  794.       // patches available update the severity and available updates preferences.
  795.       // XXXben also note if there are langpacks available that match the user's
  796.       //        preference if they previously installed an app update when a 
  797.       //        langpack for their language was not available and they used another
  798.       //        in the meantime. 
  799.       var haveLanguage = false;
  800.       var patches = this._service._currentVersion.patches;
  801.       var languages = this._service._newestVersion.languages;
  802.       if (languages) {
  803.         var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  804.                            .getService(Components.interfaces.nsIXULChromeRegistry);
  805.         var selectedLocale = cr.getSelectedLocale("global");
  806.         for (var i = 0; i < languages.length; ++i) {
  807.           if (languages[i].internalName == selectedLocale)
  808.             haveLanguage = true;
  809.         }
  810.       }
  811.       
  812.       if ((haveLanguage && (newestVersionObj.version != appVersion)) || 
  813.           (patches && patches.length > 0)) {
  814.         gPref.setIntPref(PREF_UPDATE_SEVERITY, 2);
  815.         gPref.setBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE, true);
  816.       }
  817.       else
  818.         gPref.setBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE, false);
  819.  
  820.       if (!gPref.getBoolPref(PREF_UPDATE_APP_UPDATESAVAILABLE)) {
  821.         this._service._clearAppUpdatePrefs();
  822.  
  823.         // Lower the severity to reflect the fact that there are now only Extension/
  824.         // Theme updates available
  825.         var newCount = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_COUNT);
  826.         var threshold = gPref.getIntPref(PREF_UPDATE_EXTENSIONS_SEVERITY_THRESHOLD);
  827.         if (newCount >= threshold)
  828.           gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_MEDIUM);
  829.         else
  830.           gPref.setIntPref(PREF_UPDATE_SEVERITY, nsIUpdateService.SEVERITY_LOW);
  831.       }
  832.     }
  833.  
  834.     // The Update Wizard uses this notification to determine that the application
  835.     // update process is now complete. 
  836.     gOS.notifyObservers(null, "Update:App:Ended", "");
  837.   },
  838.   
  839.   _parseVersionData: function nsAppUpdater__parseVersionData (aDataSource, 
  840.                                                               aUpdateResource, 
  841.                                                               aTargetObj)
  842.   {
  843.     aTargetObj.updateVersion          = this._getPropertyFromResource(aDataSource, aUpdateResource, "version");
  844.     aTargetObj.updateDisplayVersion   = this._getPropertyFromResource(aDataSource, aUpdateResource, "displayVersion");
  845.     if (!aTargetObj.updateDisplayVersion)
  846.       aTargetObj.updateDisplayVersion = this._getPropertyFromResource(aDataSource, aUpdateResource, "version");
  847.     aTargetObj.updateInfoURL          = this._getPropertyFromResource(aDataSource, aUpdateResource, "infoURL");
  848.     
  849.     aTargetObj.features   = this._parseUpdateCollection(aDataSource, aUpdateResource, "features");
  850.     aTargetObj.files      = this._parseUpdateCollection(aDataSource, aUpdateResource, "files");
  851.     aTargetObj.optional   = this._parseUpdateCollection(aDataSource, aUpdateResource, "optional");
  852.     aTargetObj.languages  = this._parseUpdateCollection(aDataSource, aUpdateResource, "languages");
  853.     aTargetObj.patches    = this._parseUpdateCollection(aDataSource, aUpdateResource, "patches");
  854.   },
  855.   
  856.   _parseUpdateCollection: function nsAppUpdater__parseUpdateCollection (aDataSource, 
  857.                                                                         aUpdateResource, 
  858.                                                                         aCollectionName)
  859.   {
  860.     if (!aUpdateResource)
  861.       return null;
  862.   
  863.     var result = [];
  864.     
  865.     var collectionArc = gRDF.GetResource(APP_NS(aCollectionName));
  866.     var collectionResource = aDataSource.GetTarget(aUpdateResource, collectionArc, true);
  867.     
  868.     try {
  869.       collectionResource = collectionResource.QueryInterface(Components.interfaces.nsIRDFResource);
  870.     }
  871.     catch (e) { return null; }
  872.     
  873.     var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
  874.                        .getService(Components.interfaces.nsIRDFContainerUtils);
  875.     if (cu.IsContainer(aDataSource, collectionResource)) {
  876.       var c = Components.classes["@mozilla.org/rdf/container;1"]
  877.                         .getService(Components.interfaces.nsIRDFContainer);
  878.       c.Init(aDataSource, collectionResource);
  879.       
  880.       var elements = c.GetElements();
  881.       var fileArc = gRDF.GetResource(APP_NS("file"));
  882.       var platform = getOSKey();
  883.       while (elements.hasMoreElements()) {
  884.         var element = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  885.         var info = new nsAppUpdateInfoItem();
  886.         info.name          = this._getPropertyFromResource(aDataSource, element, "name");
  887.         info.internalName  = this._getPropertyFromResource(aDataSource, element, "internalName");
  888.         
  889.         // Each Component has a set of app:file arcs out, which reference resources with two 
  890.         // properties: app:platform and app:URL. If we find a resource whose app:platform value
  891.         // matches the platform we're running on, we use the app:URL property on that resource
  892.         // as the XPI URL, otherwise we use the default app:URL property on the Component
  893.         // resource. (It must be a cross-platform piece, e.g. a language pack)
  894.         // XXXben - what to do when platform not supported? We need some way to abort 
  895.         //          and tell the app that this update is not available. 
  896.         var files = aDataSource.GetTargets(element, fileArc, true);
  897.         while (files.hasMoreElements()) {
  898.           var file = files.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  899.           if (platform == this._getPropertyFromResource(aDataSource, file, "platform")) {
  900.             info.URL = this._getPropertyFromResource(aDataSource, file, "URL");
  901.             break;
  902.           }
  903.         }
  904.         if (!info.URL)
  905.           info.URL         = this._getPropertyFromResource(aDataSource, element, "URL");
  906.         info.infoURL       = this._getPropertyFromResource(aDataSource, element, "infoURL");
  907.         info.description   = this._getPropertyFromResource(aDataSource, element, "description");
  908.         result.push(info);
  909.       }
  910.     }
  911.  
  912.     return result;
  913.   },
  914.   
  915.   onDatasourceError: function (aStatus, aErrorMsg)
  916.   {
  917.     gOS.notifyObservers(null, "Update:App:Error", "");
  918.     gOS.notifyObservers(null, "Update:App:Ended", "");
  919.   },
  920.   
  921.   /////////////////////////////////////////////////////////////////////////////
  922.   // nsIRDFXMLSinkObserver
  923.   onBeginLoad: function(aSink)
  924.   {
  925.   },
  926.   onInterrupt: function(aSink)
  927.   {
  928.   },
  929.   onResume: function(aSink)
  930.   {
  931.   },
  932.   
  933.   onEndLoad: function(aSink)
  934.   {
  935.     try {
  936.       this._ds = aSink.QueryInterface(Components.interfaces.nsIRDFDataSource);
  937.       this.onDatasourceLoaded(this._ds);
  938.     }
  939.     catch (e) { }
  940.   },
  941.   
  942.   onError: function(aSink, aStatus, aErrorMsg)
  943.   {
  944.     try {
  945.       this._ds = aSink.QueryInterface(Components.interfaces.nsIRDFDataSource);
  946.       this.onDatasourceError(aStatus, aErrorMsg);
  947.     }
  948.     catch (e) { }
  949.   },
  950.  
  951.   /////////////////////////////////////////////////////////////////////////////
  952.   // nsISupports
  953.   QueryInterface: function nsAppUpdater_QueryInterface (aIID) 
  954.   {
  955.     if (!aIID.equals(Components.interfaces.nsIRDFXMLSinkObserver) &&
  956.         !aIID.equals(Components.interfaces.nsISupports))
  957.       throw Components.results.NS_ERROR_NO_INTERFACE;
  958.     return this;
  959.   }
  960. }
  961.  
  962. function UpdateItem ()
  963. {
  964. }
  965.  
  966. UpdateItem.prototype = {
  967.   init: function UpdateItem_init (aID, 
  968.                                   aVersion, 
  969.                                   aMinAppVersion, 
  970.                                   aMaxAppVersion, 
  971.                                   aName, 
  972.                                   aRow, 
  973.                                   aXPIURL, 
  974.                                   aIconURL, 
  975.                                   aUpdateRDF, 
  976.                                   aType)
  977.   {
  978.     this._id            = aID;
  979.     this._version       = aVersion;
  980.     this._minAppVersion = aMinAppVersion;
  981.     this._maxAppVersion = aMaxAppVersion;
  982.     this._name          = aName;
  983.     this._row           = aRow;
  984.     this._xpiURL        = aXPIURL;
  985.     this._iconURL       = aIconURL;
  986.     this._updateRDF     = aUpdateRDF;
  987.     this._type          = aType;
  988.   },
  989.   
  990.   get id()            { return this._id;            },
  991.   get version()       { return this._version;       },
  992.   get minAppVersion() { return this._minAppVersion; },
  993.   get maxAppVersion() { return this._maxAppVersion; },
  994.   get name()          { return this._name;          },
  995.   get row()           { return this._row;           },
  996.   get xpiURL()        { return this._xpiURL;        },
  997.   get iconURL()       { return this._iconURL        },
  998.   get updateRDF()     { return this._updateRDF;     },
  999.   get type()          { return this._type;          },
  1000.  
  1001.   get objectSource()
  1002.   {
  1003.     return { id             : this._id, 
  1004.              version        : this._version, 
  1005.              minAppVersion  : this._minAppVersion,
  1006.              maxAppVersion  : this._maxAppVersion,
  1007.              name           : this._name, 
  1008.              row            : this._row, 
  1009.              xpiURL         : this._xpiURL, 
  1010.              iconURL        : this._iconURL, 
  1011.              updateRDF      : this._updateRDF,
  1012.              type           : this._type 
  1013.            }.toSource();
  1014.   },
  1015.  
  1016.   /////////////////////////////////////////////////////////////////////////////
  1017.   // nsISupports
  1018.   QueryInterface: function UpdateItem_QueryInterface (aIID) 
  1019.   {
  1020.     if (!aIID.equals(Components.interfaces.nsIUpdateItem) &&
  1021.         !aIID.equals(Components.interfaces.nsISupports))
  1022.       throw Components.results.NS_ERROR_NO_INTERFACE;
  1023.     return this;
  1024.   }
  1025. };
  1026.  
  1027. function nsAppUpdateInfoItem ()
  1028. {
  1029. }
  1030. nsAppUpdateInfoItem.prototype = {
  1031.   internalName: "",
  1032.   name        : "",
  1033.   URL         : "",
  1034.   infoURL     : "",
  1035.  
  1036.   /////////////////////////////////////////////////////////////////////////////
  1037.   // nsISupports
  1038.   QueryInterface: function nsAppUpdater_QueryInterface (aIID) 
  1039.   {
  1040.     if (!aIID.equals(Components.interfaces.nsIAppUpdateInfo) &&
  1041.         !aIID.equals(Components.interfaces.nsISupports))
  1042.       throw Components.results.NS_ERROR_NO_INTERFACE;
  1043.     return this;
  1044.   }
  1045. };
  1046.  
  1047. function nsAppUpdateInfo ()
  1048. {
  1049. }
  1050. nsAppUpdateInfo.prototype = {
  1051.   updateVersion       : "",
  1052.   updateDisplayVersion: "",
  1053.   updateInfoURL       : "",
  1054.  
  1055.   features  : [],
  1056.   files     : [],
  1057.   optional  : [],
  1058.   languages : [],
  1059.   patches   : [], 
  1060.   
  1061.   getCollection: function (aCollectionName, aItemCount)
  1062.   {
  1063.     var collection = aCollectionName in this ? this[aCollectionName] : null;
  1064.     aItemCount.value = collection ? collection.length : 0;
  1065.     return collection;
  1066.   },
  1067.  
  1068.   /////////////////////////////////////////////////////////////////////////////
  1069.   // nsISupports
  1070.   QueryInterface: function nsAppUpdater_QueryInterface (aIID) 
  1071.   {
  1072.     if (!aIID.equals(Components.interfaces.nsIAppUpdateInfoCollection) &&
  1073.         !aIID.equals(Components.interfaces.nsISupports))
  1074.       throw Components.results.NS_ERROR_NO_INTERFACE;
  1075.     return this;
  1076.   }
  1077. };
  1078.  
  1079. function Version(aMajor, aMinor, aRelease, aBuild, aPlus) 
  1080.   this.major    = aMajor    || 0;
  1081.   this.minor    = aMinor    || 0;
  1082.   this.release  = aRelease  || 0;
  1083.   this.build    = aBuild    || 0;
  1084.   this.plus     = aPlus     || 0;
  1085. }
  1086.  
  1087. Version.prototype = {
  1088.   toString: function Version_toString() 
  1089.   {
  1090.     return this.major + "." + this.minor + "." + this.subminor + "." + this.release + (this.plus ? "+" : "");
  1091.   },
  1092.   
  1093.   compare: function (aVersion)
  1094.   {
  1095.     var fields = ["major", "minor", "release", "build", "plus"];
  1096.     
  1097.     for (var i = 0; i < fields.length; ++i) {
  1098.       var field = fields[i];
  1099.       if (aVersion[field] > this[field])
  1100.         return -1;
  1101.       else if (aVersion[field] < this[field])
  1102.         return 1;
  1103.     }
  1104.     return 0;
  1105.   }
  1106. }
  1107.  
  1108. function nsVersionChecker()
  1109. {
  1110. }
  1111.  
  1112. nsVersionChecker.prototype = {
  1113.   /////////////////////////////////////////////////////////////////////////////
  1114.   // nsIVersionChecker
  1115.   
  1116.   // -ve      if B is newer
  1117.   // equal    if A == B
  1118.   // +ve      if A is newer
  1119.   compare: function nsVersionChecker_compare (aVersionA, aVersionB)
  1120.   {
  1121.     var a = this._decomposeVersion(aVersionA);
  1122.     var b = this._decomposeVersion(aVersionB);
  1123.     
  1124.     return a.compare(b);
  1125.   },
  1126.   
  1127.   _decomposeVersion: function nsVersionChecker__decomposeVersion (aVersion)
  1128.   {
  1129.     var plus = 0;
  1130.     if (aVersion.charAt(aVersion.length-1) == "+") {
  1131.       aVersion = aVersion.substr(0, aVersion.length-1);
  1132.       plus = 1;
  1133.     }
  1134.  
  1135.     var parts = aVersion.split(".");
  1136.     
  1137.     return new Version(this._getValidInt(parts[0]),
  1138.                        this._getValidInt(parts[1]),
  1139.                        this._getValidInt(parts[2]),
  1140.                        this._getValidInt(parts[3]),
  1141.                        plus);
  1142.   },
  1143.   
  1144.   _getValidInt: function nsVersionChecker__getValidInt (aPartString)
  1145.   {
  1146.     var integer = parseInt(aPartString);
  1147.     if (isNaN(integer))
  1148.       return 0;
  1149.     return integer;
  1150.   },
  1151.   
  1152.   isValidVersion: function nsVersionChecker_isValidVersion (aVersion)
  1153.   {
  1154.     var parts = aVersion.split(".");
  1155.     if (parts.length == 0)
  1156.       return false;
  1157.     for (var i = 0; i < parts.length; ++i) {
  1158.       var part = parts[i];
  1159.       if (i == parts.length - 1) {
  1160.         if (part.lastIndexOf("+") != -1) 
  1161.           parts[i] = part.substr(0, part.length - 1);
  1162.       }
  1163.       var integer = parseInt(part);
  1164.       if (isNaN(integer))
  1165.         return false;
  1166.     }
  1167.     return true;
  1168.   },
  1169.  
  1170.   /////////////////////////////////////////////////////////////////////////////
  1171.   // nsISupports
  1172.   QueryInterface: function nsVersionChecker_QueryInterface (aIID) 
  1173.   {
  1174.     if (!aIID.equals(Components.interfaces.nsIVersionChecker) &&
  1175.         !aIID.equals(Components.interfaces.nsISupports))
  1176.       throw Components.results.NS_ERROR_NO_INTERFACE;
  1177.     return this;
  1178.   }
  1179. };
  1180.  
  1181. var gModule = {
  1182.   _firstTime: true,
  1183.   
  1184.   registerSelf: function (aComponentManager, aFileSpec, aLocation, aType) 
  1185.   {
  1186.     if (this._firstTime) {
  1187.       this._firstTime = false;
  1188.       throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  1189.     }
  1190.     aComponentManager = aComponentManager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  1191.     
  1192.     for (var key in this._objects) {
  1193.       var obj = this._objects[key];
  1194.       aComponentManager.registerFactoryLocation(obj.CID, obj.className, obj.contractID,
  1195.                                                 aFileSpec, aLocation, aType);
  1196.     }
  1197.   },
  1198.   
  1199.   getClassObject: function (aComponentManager, aCID, aIID) 
  1200.   {
  1201.     if (!aIID.equals(Components.interfaces.nsIFactory))
  1202.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1203.  
  1204.     for (var key in this._objects) {
  1205.       if (aCID.equals(this._objects[key].CID))
  1206.         return this._objects[key].factory;
  1207.     }
  1208.     
  1209.     throw Components.results.NS_ERROR_NO_INTERFACE;
  1210.   },
  1211.   
  1212.   _objects: {
  1213.     manager: { CID: Components.ID("{B3C290A6-3943-4B89-8BBE-C01EB7B3B311}"),
  1214.                contractID: "@mozilla.org/updates/update-service;1",
  1215.                className: "Update Service",
  1216.                factory: {
  1217.                           createInstance: function (aOuter, aIID) 
  1218.                           {
  1219.                             if (aOuter != null)
  1220.                               throw Components.results.NS_ERROR_NO_AGGREGATION;
  1221.                             
  1222.                             return (new nsUpdateService()).QueryInterface(aIID);
  1223.                           }
  1224.                         }
  1225.              },
  1226.     version: { CID: Components.ID("{9408E0A5-509E-45E7-80C1-0F35B99FF7A9}"),
  1227.                contractID: "@mozilla.org/updates/version-checker;1",
  1228.                className: "Version Checker",
  1229.                factory: {
  1230.                           createInstance: function (aOuter, aIID) 
  1231.                           {
  1232.                             if (aOuter != null)
  1233.                               throw Components.results.NS_ERROR_NO_AGGREGATION;
  1234.                             
  1235.                             return (new nsVersionChecker()).QueryInterface(aIID);
  1236.                           }
  1237.                         }
  1238.              },
  1239.     item:    { CID: Components.ID("{F3294B1C-89F4-46F8-98A0-44E1EAE92518}"),
  1240.                contractID: "@mozilla.org/updates/item;1",
  1241.                className: "Extension Item",
  1242.                factory: {
  1243.                           createInstance: function (aOuter, aIID) 
  1244.                           {
  1245.                             if (aOuter != null)
  1246.                               throw Components.results.NS_ERROR_NO_AGGREGATION;
  1247.                             
  1248.                             return new UpdateItem().QueryInterface(aIID);
  1249.                           }
  1250.                         } 
  1251.              }  
  1252.    },
  1253.   
  1254.   canUnload: function (aComponentManager) 
  1255.   {
  1256.     return true;
  1257.   }
  1258. };
  1259.  
  1260. function NSGetModule(compMgr, fileSpec) 
  1261. {
  1262.   return gModule;
  1263. }
  1264.  
  1265.