home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 February / PCWorld_2008-02_cd.bin / temacd / songbird / Songbird_0.4_windows-i686.exe / xulrunner / components / nsPostUpdateWin.js < prev    next >
Text File  |  2007-07-30  |  21KB  |  704 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 Google Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *  Darin Fisher <darin@meer.net> (original author)
  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. /**
  39.  * This file contains an implementation of nsIRunnable, which may be invoked
  40.  * to perform post-update modifications to the windows registry and uninstall
  41.  * logs required to complete an update of the application.  This code is very
  42.  * specific to the xpinstall wizard for windows.
  43.  */
  44.  
  45. const URI_BRAND_PROPERTIES     = "chrome://branding/locale/brand.properties";
  46.  
  47. const KEY_APPDIR          = "XCurProcD";
  48. const KEY_TMPDIR          = "TmpD";
  49. const KEY_UPDROOT         = "UpdRootD";
  50. const KEY_UAPPDATA        = "UAppData";
  51.  
  52. // see prio.h
  53. const PR_RDONLY      = 0x01;
  54. const PR_WRONLY      = 0x02;
  55. const PR_APPEND      = 0x10;
  56.  
  57. const PERMS_FILE     = 0644;
  58. const PERMS_DIR      = 0700;
  59.  
  60. const nsIWindowsRegKey = Components.interfaces.nsIWindowsRegKey;
  61.  
  62. var gConsole = null;
  63. var gAppUpdateLogPostUpdate = false;
  64.  
  65. //-----------------------------------------------------------------------------
  66.  
  67. /**
  68.  * Console logging support
  69.  */
  70. function LOG(s) {
  71.   if (gAppUpdateLogPostUpdate) {
  72.     dump("*** PostUpdateWin: " + s + "\n");
  73.     gConsole.logStringMessage(s);
  74.   }
  75. }
  76.  
  77. /**
  78.  * This function queries the XPCOM directory service.
  79.  */
  80. function getFile(key) {
  81.   var dirSvc =
  82.       Components.classes["@mozilla.org/file/directory_service;1"].
  83.       getService(Components.interfaces.nsIProperties);
  84.   return dirSvc.get(key, Components.interfaces.nsIFile);
  85. }
  86.  
  87. /**
  88.  * Creates a new file object given a native file path.
  89.  * @param   path
  90.  *          The native file path.
  91.  * @return  nsILocalFile object for the given native file path.
  92.  */
  93. function newFile(path) {
  94.   var file = Components.classes["@mozilla.org/file/local;1"]
  95.                        .createInstance(Components.interfaces.nsILocalFile);
  96.   file.initWithPath(path);
  97.   return file;
  98. }
  99.  
  100. /**
  101.  * This function returns a file input stream.
  102.  */
  103. function openFileInputStream(file) {
  104.   var stream =
  105.       Components.classes["@mozilla.org/network/file-input-stream;1"].
  106.       createInstance(Components.interfaces.nsIFileInputStream);
  107.   stream.init(file, PR_RDONLY, 0, 0);
  108.   return stream;
  109. }
  110.  
  111. /**
  112.  * This function returns a file output stream.
  113.  */
  114. function openFileOutputStream(file, flags) {
  115.   var stream =
  116.       Components.classes["@mozilla.org/network/file-output-stream;1"].
  117.       createInstance(Components.interfaces.nsIFileOutputStream);
  118.   stream.init(file, flags, 0644, 0);
  119.   return stream;
  120. }
  121.  
  122. //-----------------------------------------------------------------------------
  123.  
  124. const PREFIX_FILE = "File: ";
  125.  
  126. function InstallLogWriter() {
  127. }
  128. InstallLogWriter.prototype = {
  129.   _outputStream: null,  // nsIOutputStream to the install wizard log file
  130.  
  131.   /**
  132.    * Write a single line to the output stream.
  133.    */
  134.   _writeLine: function(s) {
  135.     s = s + "\r\n";
  136.     this._outputStream.write(s, s.length);
  137.   },
  138.  
  139.   /**
  140.    * This function creates an empty uninstall update log file if it doesn't
  141.    * exist and returns a reference to the resulting nsIFile.
  142.    */
  143.   _getUninstallLogFile: function() {
  144.     var file = getFile(KEY_APPDIR); 
  145.     file.append("uninstall");
  146.     if (!file.exists())
  147.       return null;
  148.  
  149.     file.append("uninstall.log");
  150.     if (!file.exists())
  151.       file.create(Components.interfaces.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  152.  
  153.     return file;
  154.   },
  155.  
  156.   /**
  157.    * Return the update.log file.  Use last-update.log file in case the
  158.    * updates/0 directory has already been cleaned out (see bug 311302).
  159.    */
  160.   _getUpdateLogFile: function() {
  161.     function appendUpdateLogPath(root) {
  162.       var file = root.clone();
  163.       file.append("updates");
  164.       file.append("0");
  165.       file.append("update.log");
  166.       if (file.exists())
  167.         return file;
  168.  
  169.       file = root; 
  170.       file.append("updates");
  171.       file.append("last-update.log");
  172.       if (file.exists())
  173.         return file;
  174.  
  175.       return null;
  176.     }
  177.  
  178.     // See the local appdata first if app dir is under Program Files.
  179.     var file = null;
  180.     var updRoot;
  181.     try {
  182.       updRoot = getFile(KEY_UPDROOT);
  183.     } catch (e) {
  184.     }
  185.     if (updRoot) {
  186.       file = appendUpdateLogPath(updRoot);
  187.  
  188.       // When updating from Fx 2.0.0.1 to 2.0.0.3 (or later) on Vista,
  189.       // we will have to see also user app data (see bug 351949).
  190.       if (!file)
  191.         file = appendUpdateLogPath(getFile(KEY_UAPPDATA));
  192.     }
  193.  
  194.     // See the app dir if not found or app dir is out of Program Files.
  195.     if (!file)
  196.       file = appendUpdateLogPath(getFile(KEY_APPDIR));
  197.  
  198.     return file;
  199.   },
  200.  
  201.   /**
  202.    * Read update.log to extract information about files that were
  203.    * newly added for this update.
  204.    */
  205.   _readUpdateLog: function(logFile, entries) {
  206.     var stream;
  207.     try {
  208.       stream = openFileInputStream(logFile).
  209.           QueryInterface(Components.interfaces.nsILineInputStream);
  210.  
  211.       var line = {};
  212.       while (stream.readLine(line)) {
  213.         var data = line.value.split(" ");
  214.         if (data[0] == "EXECUTE" && data[1] == "ADD") {
  215.           // The uninstaller requires the path separator to be "\" and
  216.           // relative paths to start with a "\".
  217.           var relPath = "\\" + data[2].replace(/\//g, "\\");
  218.           entries[relPath] = null;
  219.         }
  220.       }
  221.     } finally {
  222.       if (stream)
  223.         stream.close();
  224.     }
  225.   },
  226.  
  227.   /**
  228.    * Read install_wizard log files to extract information about files that were
  229.    * previously added by the xpinstall installer and software update.
  230.    */
  231.   _readXPInstallLog: function(logFile, entries) {
  232.     var stream;
  233.     try {
  234.       stream = openFileInputStream(logFile).
  235.           QueryInterface(Components.interfaces.nsILineInputStream);
  236.  
  237.       function fixPath(path, offset) {
  238.         return path.substr(offset).replace(appDirPath, "");
  239.       }
  240.  
  241.       var appDir = getFile(KEY_APPDIR);
  242.       var appDirPath = appDir.path;
  243.       var line = {};
  244.       while (stream.readLine(line)) {
  245.         var entry = line.value;
  246.         // This works with both the entries from xpinstall (e.g. Installing: )
  247.         // and from update (e.g. installing: )
  248.         var searchStr = "nstalling: ";
  249.         var index = entry.indexOf(searchStr);
  250.         if (index != -1) {
  251.           entries[fixPath(entry, index + searchStr.length)] = null;
  252.           continue;
  253.         }
  254.  
  255.         searchStr = "Replacing: ";
  256.         index = entry.indexOf(searchStr);
  257.         if (index != -1) {
  258.           entries[fixPath(entry, index + searchStr.length)] = null;
  259.           continue;
  260.         }
  261.  
  262.         searchStr = "Windows Shortcut: ";
  263.         index = entry.indexOf(searchStr);
  264.         if (index != -1) {
  265.           entries[fixPath(entry + ".lnk", index + searchStr.length)] = null;
  266.           continue;
  267.         }
  268.       }
  269.     } finally {
  270.       if (stream)
  271.         stream.close();
  272.     }
  273.   },
  274.  
  275.   _readUninstallLog: function(logFile, entries) {
  276.     var stream;
  277.     try {
  278.       stream = openFileInputStream(logFile).
  279.           QueryInterface(Components.interfaces.nsILineInputStream);
  280.  
  281.       var line = {};
  282.       var searchStr = "File: ";
  283.       while (stream.readLine(line)) {
  284.         var index = line.value.indexOf(searchStr);
  285.         if (index != -1) {
  286.           var str = line.value.substr(index + searchStr.length);
  287.           entries.push(str);
  288.         }
  289.       }
  290.     } finally {
  291.       if (stream)
  292.         stream.close();
  293.     }
  294.   },
  295.  
  296.   /**
  297.    * This function initializes the log writer and is responsible for
  298.    * translating 'update.log' and the 'install_wizard' logs to the NSIS format.
  299.    */
  300.   begin: function() {
  301.     var updateLog = this._getUpdateLogFile();
  302.     if (!updateLog)
  303.       return;
  304.  
  305.     var newEntries = { };
  306.     this._readUpdateLog(updateLog, newEntries);
  307.  
  308.     try {
  309.       const nsIDirectoryEnumerator = Components.interfaces.nsIDirectoryEnumerator;
  310.       const nsILocalFile = Components.interfaces.nsILocalFile;
  311.       var prefixWizLog = "install_wizard";
  312.       var uninstallDir = getFile(KEY_APPDIR); 
  313.       uninstallDir.append("uninstall");
  314.       var entries = uninstallDir.directoryEntries.QueryInterface(nsIDirectoryEnumerator);
  315.       while (true) {
  316.         var wizLog = entries.nextFile;
  317.         if (!wizLog)
  318.           break;
  319.         if (wizLog instanceof nsILocalFile && !wizLog.isDirectory() &&
  320.             wizLog.leafName.indexOf(prefixWizLog) == 0) {
  321.           this._readXPInstallLog(wizLog, newEntries);
  322.           wizLog.remove(false);
  323.         }
  324.       }
  325.     }
  326.     catch (e) {}
  327.     if (entries)
  328.       entries.close();
  329.  
  330.     var uninstallLog = this._getUninstallLogFile();
  331.     var oldEntries = [];
  332.     this._readUninstallLog(uninstallLog, oldEntries);
  333.  
  334.     // Prevent writing duplicate entries in the log file
  335.     for (var relPath in newEntries) {
  336.       if (oldEntries.indexOf(relPath) != -1)
  337.         delete newEntries[relPath];
  338.     }
  339.  
  340.     if (newEntries.length == 0)
  341.       return;
  342.  
  343.     // since we are not running with elevated privs, we can't write out
  344.     // the log file (at least, not on Vista).  So, write the output to
  345.     // temp, and then later, we'll pass the file (gCopiedLog) to
  346.     // the post update clean up process, which can copy it to
  347.     // the desired location (because it will have elevated privs)
  348.     gCopiedLog = getFile(KEY_TMPDIR);
  349.     gCopiedLog.append("uninstall");
  350.     gCopiedLog.createUnique(gCopiedLog.DIRECTORY_TYPE, PERMS_DIR);
  351.     if (uninstallLog)
  352.       uninstallLog.copyTo(gCopiedLog, "uninstall.log");
  353.     gCopiedLog.append("uninstall.log");
  354.     
  355.     LOG("uninstallLog = " + uninstallLog.path);
  356.     LOG("copiedLog = " + gCopiedLog.path);
  357.     
  358.     if (!gCopiedLog.exists())
  359.       gCopiedLog.create(Components.interfaces.nsILocalFile.NORMAL_FILE_TYPE, 
  360.                         PERMS_FILE);
  361.       
  362.     this._outputStream =
  363.         openFileOutputStream(gCopiedLog, PR_WRONLY | PR_APPEND);
  364.  
  365.     // The NSIS uninstaller deletes all directories where the installer has
  366.     // added a file if the directory is empty after the files have been removed
  367.     // so there is no need to log directories.
  368.     for (var relPath in newEntries)
  369.       this._writeLine(PREFIX_FILE + relPath);
  370.   },
  371.  
  372.   end: function() {
  373.     if (!this._outputStream)
  374.       return;
  375.     this._outputStream.close();
  376.     this._outputStream = null;
  377.   }
  378. };
  379.  
  380. var installLogWriter;
  381. var gCopiedLog;
  382.  
  383. //-----------------------------------------------------------------------------
  384.  
  385. /**
  386.  * A thin wrapper around nsIWindowsRegKey
  387.  * note, only the "read" methods are exposed.  If you want to write
  388.  * to the registry on Vista, you need to be a priveleged app.
  389.  * We've moved that code into the uninstaller.
  390.  */
  391. function RegKey() {
  392.   // Internally, we may pass parameters to this constructor.
  393.   if (arguments.length == 3) {
  394.     this._key = arguments[0];
  395.     this._root = arguments[1];
  396.     this._path = arguments[2];
  397.   } else {
  398.     this._key =
  399.         Components.classes["@mozilla.org/windows-registry-key;1"].
  400.         createInstance(nsIWindowsRegKey);
  401.   }
  402. }
  403. RegKey.prototype = {
  404.   _key: null,
  405.   _root: null,
  406.   _path: null,
  407.  
  408.   ACCESS_READ:  nsIWindowsRegKey.ACCESS_READ,
  409.  
  410.   ROOT_KEY_CURRENT_USER: nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
  411.   ROOT_KEY_LOCAL_MACHINE: nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
  412.   ROOT_KEY_CLASSES_ROOT: nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
  413.   
  414.   close: function() {
  415.     this._key.close();
  416.     this._root = null;
  417.     this._path = null;
  418.   },
  419.  
  420.   open: function(rootKey, path, mode) {
  421.     this._key.open(rootKey, path, mode);
  422.     this._root = rootKey;
  423.     this._path = path;
  424.   },
  425.  
  426.   openChild: function(path, mode) {
  427.     var child = this._key.openChild(path, mode);
  428.     return new RegKey(child, this._root, this._path + "\\" + path);
  429.   },
  430.  
  431.   readStringValue: function(name) {
  432.     return this._key.readStringValue(name);
  433.   },
  434.  
  435.   hasValue: function(name) {
  436.     return this._key.hasValue(name);
  437.   },
  438.  
  439.   hasChild: function(name) {
  440.     return this._key.hasChild(name);
  441.   },
  442.  
  443.   get childCount() {
  444.     return this._key.childCount;
  445.   },
  446.  
  447.   getChildName: function(index) {
  448.     return this._key.getChildName(index);
  449.   },
  450.  
  451.   toString: function() {
  452.     var root;
  453.     switch (this._root) {
  454.     case this.ROOT_KEY_CLASSES_ROOT:
  455.       root = "HKEY_KEY_CLASSES_ROOT";
  456.       break;
  457.     case this.ROOT_KEY_LOCAL_MACHINE:
  458.       root = "HKEY_LOCAL_MACHINE";
  459.       break;
  460.     case this.ROOT_KEY_CURRENT_USER:
  461.       root = "HKEY_CURRENT_USER";
  462.       break;
  463.     default:
  464.       LOG("unknown root key");
  465.       return "";
  466.     }
  467.     return root + "\\" + this._path;
  468.   }
  469. };
  470.  
  471. /**
  472.  * This method walks the registry looking for the registry keys of
  473.  * the previous version of the application.
  474.  */
  475. function haveOldInstall(key, brandFullName, version) {
  476.   var ourInstallDir = getFile(KEY_APPDIR);
  477.   var result = false;
  478.   var childKey, productKey, mainKey;
  479.   try {
  480.     for (var i = 0; i < key.childCount; ++i) {
  481.       var childName = key.getChildName(i);
  482.       childKey = key.openChild(childName, key.ACCESS_READ);
  483.       if (childKey.hasValue("CurrentVersion")) {
  484.         for (var j = 0; j < childKey.childCount; ++j) {
  485.           var productVer = childKey.getChildName(j); 
  486.           productKey = childKey.openChild(productVer, key.ACCESS_READ);
  487.           if (productKey.hasChild("Main")) {
  488.             mainKey = productKey.openChild("Main", key.ACCESS_READ);
  489.             var installDir = mainKey.readStringValue("Install Directory");
  490.             mainKey.close();
  491.             LOG("old install? " + installDir + " vs " + ourInstallDir.path);
  492.             LOG("old install? " + childName + " vs " + brandFullName);
  493.             LOG("old install? " + productVer.split(" ")[0] + " vs " + version);
  494.             if (newFile(installDir).equals(ourInstallDir) &&
  495.                 (childName != brandFullName ||
  496.                 productVer.split(" ")[0] != version)) {
  497.               result = true;
  498.             }
  499.           }
  500.           productKey.close();
  501.           if (result)
  502.             break;
  503.         }
  504.       }
  505.       childKey.close();
  506.       if (result)
  507.         break;
  508.     }
  509.   } catch (e) {
  510.     result = false;
  511.     if (childKey)
  512.       childKey.close();
  513.     if (productKey)
  514.       productKey.close();
  515.     if (mainKey)
  516.       mainKey.close();
  517.   }
  518.   return result;
  519. }
  520.  
  521. function checkRegistry()
  522. {
  523.   LOG("checkRegistry");
  524.  
  525.   var result = false;
  526.   
  527.   // Firefox is the only toolkit app that needs to do this. 
  528.   // return false for other applications.
  529.   var app = Components.classes["@mozilla.org/xre/app-info;1"].
  530.             getService(Components.interfaces.nsIXULAppInfo);
  531.   if (app.name == "Firefox") {          
  532.     try {
  533.       var key = new RegKey();
  534.       key.open(RegKey.prototype.ROOT_KEY_CLASSES_ROOT, "FirefoxHTML\\shell\\open\\command", key.ACCESS_READ);
  535.       var commandKey = key.readStringValue("");
  536.       LOG("commandKey = " + commandKey);
  537.       // if "-requestPending" is not found, we need to do the cleanup
  538.       result = (commandKey.indexOf("-requestPending") == -1);
  539.     } catch (e) {
  540.       LOG("failed to open command key for FirefoxHTML: " + e);
  541.     }
  542.     key.close();
  543.   }
  544.   return result;
  545. }
  546.  
  547. function checkOldInstall(rootKey, vendorShortName, brandFullName, version)
  548. {
  549.   var key = new RegKey();
  550.   var result = false;
  551.  
  552.   try {
  553.     key.open(rootKey, "SOFTWARE\\" + vendorShortName, key.ACCESS_READ);
  554.     LOG("checkOldInstall: " + key + " " + brandFullName + " " + version);
  555.     result = haveOldInstall(key, brandFullName, version);
  556.   } catch (e) {
  557.     LOG("failed trying to find old install: " + e);
  558.   }
  559.   key.close();
  560.   return result;
  561. }
  562.  
  563. //-----------------------------------------------------------------------------
  564.  
  565. function nsPostUpdateWin() {
  566.   gConsole = Components.classes["@mozilla.org/consoleservice;1"]
  567.                        .getService(Components.interfaces.nsIConsoleService);
  568.   var prefs = Components.classes["@mozilla.org/preferences-service;1"].
  569.               getService(Components.interfaces.nsIPrefBranch);
  570.   try {
  571.     gAppUpdateLogPostUpdate = prefs.getBoolPref("app.update.log.all");
  572.   }
  573.   catch (ex) {
  574.   }
  575.   try {
  576.     if (!gAppUpdateLogPostUpdate) 
  577.       gAppUpdateLogPostUpdate = prefs.getBoolPref("app.update.log.PostUpdate");
  578.   }
  579.   catch (ex) {
  580.   }
  581. }
  582.  
  583. nsPostUpdateWin.prototype = {
  584.   QueryInterface: function(iid) {
  585.     if (iid.equals(Components.interfaces.nsIRunnable) ||
  586.         iid.equals(Components.interfaces.nsISupports))
  587.       return this;
  588.     throw Components.results.NS_ERROR_NO_INTERFACE;
  589.   },
  590.  
  591.   run: function() {
  592.     // When uninstall/uninstall.update exists the uninstaller has already
  593.     // updated the uninstall.log with the files added by software update.
  594.     var updateUninstallFile = getFile(KEY_APPDIR); 
  595.     updateUninstallFile.append("uninstall");
  596.     updateUninstallFile.append("uninstall.update");
  597.     if (updateUninstallFile.exists()) {
  598.       LOG("nothing to do, uninstall.log has already been updated"); 
  599.       return;
  600.     }
  601.  
  602.     try {
  603.       installLogWriter = new InstallLogWriter();
  604.       try {
  605.         installLogWriter.begin();
  606.       } finally {
  607.         installLogWriter.end();
  608.         installLogWriter = null;
  609.       }
  610.     } catch (e) {
  611.       LOG(e);
  612.     } 
  613.     
  614.     var app =
  615.       Components.classes["@mozilla.org/xre/app-info;1"].
  616.         getService(Components.interfaces.nsIXULAppInfo).
  617.         QueryInterface(Components.interfaces.nsIXULRuntime);
  618.  
  619.     var sbs =
  620.       Components.classes["@mozilla.org/intl/stringbundle;1"].
  621.       getService(Components.interfaces.nsIStringBundleService);
  622.     var brandBundle = sbs.createBundle(URI_BRAND_PROPERTIES);
  623.  
  624.     var vendorShortName = brandBundle.GetStringFromName("vendorShortName");
  625.     var brandFullName = brandBundle.GetStringFromName("brandFullName");
  626.  
  627.     if (!gCopiedLog && 
  628.         !checkRegistry() &&
  629.         !checkOldInstall(RegKey.prototype.ROOT_KEY_LOCAL_MACHINE, 
  630.                          vendorShortName, brandFullName, app.version) &&
  631.         !checkOldInstall(RegKey.prototype.ROOT_KEY_CURRENT_USER, 
  632.                          vendorShortName, brandFullName, app.version)) {
  633.       LOG("nothing to do, so don't launch the helper");
  634.       return;
  635.     }
  636.  
  637.     try {
  638.       var winAppHelper = 
  639.         app.QueryInterface(Components.interfaces.nsIWinAppHelper);
  640.  
  641.       // note, gCopiedLog could be null
  642.       if (gCopiedLog)
  643.         LOG("calling postUpdate with: " + gCopiedLog.path);
  644.       else
  645.         LOG("calling postUpdate without a log");
  646.  
  647.       winAppHelper.postUpdate(gCopiedLog);
  648.     } catch (e) {
  649.       LOG("failed to launch the helper to do the post update cleanup: " + e); 
  650.     }
  651.   }
  652. };
  653.  
  654. //-----------------------------------------------------------------------------
  655.  
  656. var gModule = {
  657.   registerSelf: function(compMgr, fileSpec, location, type) {
  658.     compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  659.     
  660.     for (var key in this._objects) {
  661.       var obj = this._objects[key];
  662.       compMgr.registerFactoryLocation(obj.CID, obj.className, obj.contractID,
  663.                                       fileSpec, location, type);
  664.     }
  665.   },
  666.   
  667.   getClassObject: function(compMgr, cid, iid) {
  668.     if (!iid.equals(Components.interfaces.nsIFactory))
  669.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  670.  
  671.     for (var key in this._objects) {
  672.       if (cid.equals(this._objects[key].CID))
  673.         return this._objects[key].factory;
  674.     }
  675.     
  676.     throw Components.results.NS_ERROR_NO_INTERFACE;
  677.   },
  678.   
  679.   _makeFactory: #1= function(ctor) {
  680.     function ci(outer, iid) {
  681.       if (outer != null)
  682.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  683.       return (new ctor()).QueryInterface(iid);
  684.     } 
  685.     return { createInstance: ci };
  686.   },
  687.   
  688.   _objects: {
  689.     manager: { CID        : Components.ID("{d15b970b-5472-40df-97e8-eb03a04baa82}"),
  690.                contractID : "@mozilla.org/updates/post-update;1",
  691.                className  : "nsPostUpdateWin",
  692.                factory    : #1#(nsPostUpdateWin)
  693.              },
  694.   },
  695.   
  696.   canUnload: function(compMgr) {
  697.     return true;
  698.   }
  699. };
  700.  
  701. function NSGetModule(compMgr, fileSpec) {
  702.   return gModule;
  703. }
  704.