home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2006 February / maximum-cd-2006-02.iso / Software / Apps / firefox_setup_1.5.exe / browser.xpi / bin / components / nsPostUpdateWin.js < prev    next >
Encoding:
Text File  |  2005-11-11  |  24.2 KB  |  834 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. const URI_UNINSTALL_PROPERTIES = "chrome://branding/content/uninstall.properties";
  47.  
  48. const KEY_APPDIR          = "XCurProcD";
  49. const KEY_WINDIR          = "WinD";
  50. const KEY_COMPONENTS_DIR  = "ComsD";
  51. const KEY_PLUGINS_DIR     = "APlugns";
  52. const KEY_EXECUTABLE_FILE = "XREExeF";
  53.  
  54. // see prio.h
  55. const PR_RDONLY      = 0x01;
  56. const PR_WRONLY      = 0x02;
  57. const PR_APPEND      = 0x10;
  58.  
  59. const nsIWindowsRegKey = Components.interfaces.nsIWindowsRegKey;
  60.  
  61. //-----------------------------------------------------------------------------
  62.  
  63. /**
  64.  * Console logging support
  65.  */
  66. function LOG(s) {
  67.   dump("*** PostUpdateWin: " + s + "\n");
  68. }
  69.  
  70. /**
  71.  * This function queries the XPCOM directory service.
  72.  */
  73. function getFile(key) {
  74.   var dirSvc =
  75.       Components.classes["@mozilla.org/file/directory_service;1"].
  76.       getService(Components.interfaces.nsIProperties);
  77.   return dirSvc.get(key, Components.interfaces.nsIFile);
  78. }
  79.  
  80. /**
  81.  * Return the full path given a relative path and a base directory.
  82.  */
  83. function getFileRelativeTo(dir, relPath) {
  84.   var file = dir.clone().QueryInterface(Components.interfaces.nsILocalFile);
  85.   file.setRelativeDescriptor(dir, relPath);
  86.   return file;
  87. }
  88.  
  89. /**
  90.  * Creates a new file object given a native file path.
  91.  * @param   path
  92.  *          The native file path.
  93.  * @return  nsILocalFile object for the given native file path.
  94.  */
  95. function newFile(path) {
  96.   var file = Components.classes["@mozilla.org/file/local;1"]
  97.                        .createInstance(Components.interfaces.nsILocalFile);
  98.   file.initWithPath(path);
  99.   return file;
  100. }
  101.  
  102. /**
  103.  * This function returns a file input stream.
  104.  */
  105. function openFileInputStream(file) {
  106.   var stream =
  107.       Components.classes["@mozilla.org/network/file-input-stream;1"].
  108.       createInstance(Components.interfaces.nsIFileInputStream);
  109.   stream.init(file, PR_RDONLY, 0, 0);
  110.   return stream;
  111. }
  112.  
  113. /**
  114.  * This function returns a file output stream.
  115.  */
  116. function openFileOutputStream(file, flags) {
  117.   var stream =
  118.       Components.classes["@mozilla.org/network/file-output-stream;1"].
  119.       createInstance(Components.interfaces.nsIFileOutputStream);
  120.   stream.init(file, flags, 0644, 0);
  121.   return stream;
  122. }
  123.  
  124. /**
  125.  * Gets the current value of the locale.  It's possible for this preference to
  126.  * be localized, so we have to do a little extra work here.  Similar code
  127.  * exists in nsHttpHandler.cpp when building the UA string.
  128.  */
  129. function getLocale() {
  130.   const prefName = "general.useragent.locale";
  131.   var prefs =
  132.       Components.classes["@mozilla.org/preferences-service;1"].
  133.       getService(Components.interfaces.nsIPrefBranch);
  134.   try {
  135.     return prefs.getComplexValue(prefName,
  136.                Components.interfaces.nsIPrefLocalizedString).data;
  137.   } catch (e) {}
  138.  
  139.   return prefs.getCharPref(prefName);
  140. }
  141.  
  142. //-----------------------------------------------------------------------------
  143.  
  144. const PREFIX_INSTALLING = "installing: ";
  145. const PREFIX_CREATE_FOLDER = "create folder: ";
  146. const PREFIX_CREATE_REGISTRY_KEY = "create registry key: ";
  147. const PREFIX_STORE_REGISTRY_VALUE_STRING = "store registry value string: ";
  148.  
  149. function InstallLogWriter() {
  150. }
  151. InstallLogWriter.prototype = {
  152.   _outputStream: null,  // nsIOutputStream to the install wizard log file
  153.  
  154.   /**
  155.    * Write a single line to the output stream.
  156.    */
  157.   _writeLine: function(s) {
  158.     s = s + "\n";
  159.     this._outputStream.write(s, s.length);
  160.   },
  161.  
  162.   /**
  163.    * This function creates an empty install wizard log file and returns
  164.    * a reference to the resulting nsIFile.
  165.    */
  166.   _getInstallLogFile: function() {
  167.     function makeLeafName(index) {
  168.       return "install_wizard" + index + ".log"
  169.     }
  170.  
  171.     var file = getFile(KEY_APPDIR); 
  172.     file.append("uninstall");
  173.     if (!file.exists())
  174.       return null;
  175.  
  176.     file.append("x");
  177.     var n = 0;
  178.     do {
  179.       file.leafName = makeLeafName(++n);
  180.     } while (file.exists());
  181.  
  182.     if (n == 1)
  183.       return null;  // What, no install wizard log file?
  184.  
  185.     file.leafName = makeLeafName(n - 1);
  186.     return file;
  187.   },
  188.  
  189.   /**
  190.    * Return the update.log file.  Use last-update.log file in case the
  191.    * updates/0 directory has already been cleaned out (see bug 311302).
  192.    */
  193.   _getUpdateLogFile: function() {
  194.     var file = getFile(KEY_APPDIR); 
  195.     file.append("updates");
  196.     file.append("0");
  197.     file.append("update.log");
  198.     if (file.exists())
  199.       return file;
  200.  
  201.     file = getFile(KEY_APPDIR); 
  202.     file.append("updates");
  203.     file.append("last-update.log");
  204.     if (file.exists())
  205.       return file;
  206.  
  207.     return null;
  208.   },
  209.  
  210.   /**
  211.    * Read update.log to extract information about files that were
  212.    * newly added for this update.
  213.    */
  214.   _readUpdateLog: function(logFile, entries) {
  215.     var appDir = getFile(KEY_APPDIR);
  216.     var stream;
  217.     try {
  218.       stream = openFileInputStream(logFile).
  219.           QueryInterface(Components.interfaces.nsILineInputStream);
  220.  
  221.       var dirs = {};
  222.       var line = {};
  223.       while (stream.readLine(line)) {
  224.         var data = line.value.split(" ");
  225.         if (data[0] == "EXECUTE" && data[1] == "ADD") {
  226.           var file = getFileRelativeTo(appDir, data[2]);
  227.           
  228.           // remember all parent directories
  229.           var parent = file.parent;
  230.           while (!parent.equals(appDir)) {
  231.             dirs[parent.path] = true;
  232.             parent = parent.parent;
  233.           }
  234.  
  235.           // remember the file
  236.           entries.files.push(file.path);
  237.         }
  238.       }
  239.       for (var d in dirs)
  240.         entries.dirs.push(d);
  241.       // Sort the directories so that subdirectories are deleted first.
  242.       entries.dirs.sort();
  243.     } finally {
  244.       if (stream)
  245.         stream.close();
  246.     }
  247.   },
  248.  
  249.   /**
  250.    * This function initializes the log writer and is responsible for
  251.    * translating 'update.log' to the install wizard format.
  252.    */
  253.   begin: function() {
  254.     var installLog = this._getInstallLogFile();
  255.     var updateLog = this._getUpdateLogFile();
  256.     if (!installLog || !updateLog)
  257.       return;
  258.  
  259.     var entries = { dirs: [], files: [] };
  260.     this._readUpdateLog(updateLog, entries);
  261.  
  262.     this._outputStream =
  263.         openFileOutputStream(installLog, PR_WRONLY | PR_APPEND);
  264.     this._writeLine("\nUPDATE [" + new Date().toUTCString() + "]");
  265.  
  266.     var i;
  267.     // The log file is processed in reverse order, so list directories before
  268.     // files to ensure that the directories will be deleted.
  269.     for (i = 0; i < entries.dirs.length; ++i)
  270.       this._writeLine(PREFIX_CREATE_FOLDER + entries.dirs[i]);
  271.     for (i = 0; i < entries.files.length; ++i)
  272.       this._writeLine(PREFIX_INSTALLING + entries.files[i]);
  273.   },
  274.  
  275.   /**
  276.    * This function records the creation of a registry key.
  277.    */
  278.   registryKeyCreated: function(keyPath) {
  279.     if (!this._outputStream)
  280.       return;
  281.     this._writeLine(PREFIX_CREATE_REGISTRY_KEY + keyPath + " []");
  282.   },
  283.  
  284.   /**
  285.    * This function records the creation of a registry key value.
  286.    */
  287.   registryKeyValueSet: function(keyPath, valueName) {
  288.     if (!this._outputStream)
  289.       return;
  290.     this._writeLine(
  291.         PREFIX_STORE_REGISTRY_VALUE_STRING + keyPath + " [" + valueName + "]");
  292.   },
  293.  
  294.   end: function() {
  295.     if (!this._outputStream)
  296.       return;
  297.     this._outputStream.close();
  298.     this._outputStream = null;
  299.   }
  300. };
  301.  
  302. var installLogWriter;
  303.  
  304. //-----------------------------------------------------------------------------
  305.  
  306. /**
  307.  * A thin wrapper around nsIWindowsRegKey that keeps track of its path
  308.  * and notifies the installLogWriter when modifications are made.
  309.  */
  310. function RegKey() {
  311.   // Internally, we may pass parameters to this constructor.
  312.   if (arguments.length == 3) {
  313.     this._key = arguments[0];
  314.     this._root = arguments[1];
  315.     this._path = arguments[2];
  316.   } else {
  317.     this._key =
  318.         Components.classes["@mozilla.org/windows-registry-key;1"].
  319.         createInstance(nsIWindowsRegKey);
  320.   }
  321. }
  322. RegKey.prototype = {
  323.   _key: null,
  324.   _root: null,
  325.   _path: null,
  326.  
  327.   ACCESS_READ:  nsIWindowsRegKey.ACCESS_READ,
  328.   ACCESS_WRITE: nsIWindowsRegKey.ACCESS_WRITE,
  329.   ACCESS_ALL:   nsIWindowsRegKey.ACCESS_ALL,
  330.  
  331.   ROOT_KEY_CURRENT_USER: nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
  332.   ROOT_KEY_LOCAL_MACHINE: nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
  333.  
  334.   close: function() {
  335.     this._key.close();
  336.     this._root = null;
  337.     this._path = null;
  338.   },
  339.  
  340.   open: function(rootKey, path, mode) {
  341.     this._key.open(rootKey, path, mode);
  342.     this._root = rootKey;
  343.     this._path = path;
  344.   },
  345.  
  346.   openChild: function(path, mode) {
  347.     var child = this._key.openChild(path, mode);
  348.     return new RegKey(child, this._root, this._path + "\\" + path);
  349.   },
  350.  
  351.   createChild: function(path, mode) {
  352.     var child = this._key.createChild(path, mode);
  353.     var key = new RegKey(child, this._root, this._path + "\\" + path);
  354.     if (installLogWriter)
  355.       installLogWriter.registryKeyCreated(key.toString());
  356.     return key;
  357.   },
  358.  
  359.   readStringValue: function(name) {
  360.     return this._key.readStringValue(name);
  361.   },
  362.  
  363.   writeStringValue: function(name, value) {
  364.     this._key.writeStringValue(name, value);
  365.     if (installLogWriter)
  366.       installLogWriter.registryKeyValueSet(this.toString(), name);
  367.   },
  368.  
  369.   writeIntValue: function(name, value) {
  370.     this._key.writeIntValue(name, value);
  371.     if (installLogWriter)
  372.       installLogWriter.registryKeyValueSet(this.toString(), name);
  373.   },
  374.  
  375.   hasValue: function(name) {
  376.     return this._key.hasValue(name);
  377.   },
  378.  
  379.   hasChild: function(name) {
  380.     return this._key.hasChild(name);
  381.   },
  382.  
  383.   get childCount() {
  384.     return this._key.childCount;
  385.   },
  386.  
  387.   getChildName: function(index) {
  388.     return this._key.getChildName(index);
  389.   },
  390.  
  391.   removeChild: function(name) {
  392.     this._key.removeChild(name);
  393.   },
  394.  
  395.   toString: function() {
  396.     var root;
  397.     switch (this._root) {
  398.     case this.ROOT_KEY_LOCAL_MACHINE:
  399.       root = "HKEY_LOCAL_MACHINE";
  400.       break;
  401.     case this.ROOT_KEY_CURRENT_USER:
  402.       root = "HKEY_CURRENT_USER";
  403.       break;
  404.     default:
  405.       LOG("unknown root key");
  406.       return "";
  407.     }
  408.     return root + "\\" + this._path;
  409.   }
  410. };
  411.  
  412. //-----------------------------------------------------------------------------
  413.  
  414. /*
  415. RegKey.prototype = {
  416.   // The name of the registry key
  417.   name: "";
  418.  
  419.   // An array of strings, where each even-indexed string names a value,
  420.   // and the subsequent string provides data for the value.
  421.   values: [];
  422.  
  423.   // An array of RegKey objects.
  424.   children: [];
  425. }
  426. */
  427.  
  428. /**
  429.  * This function creates a heirarchy of registry keys.  If any of the
  430.  * keys or values already exist, then they will be updated instead.
  431.  * @param rootKey
  432.  *        The root registry key from which to create the new registry
  433.  *        keys.
  434.  * @param data
  435.  *        A JS object with properties: "name", "values", and "children"
  436.  *        as defined above.  All children of this key will be created.
  437.  */
  438. function createRegistryKeys(rootKey, data) {
  439.   var key;
  440.   try {
  441.     key = rootKey.createChild(data.name, rootKey.ACCESS_WRITE);
  442.     var i;
  443.     if ("values" in data) {
  444.       for (i = 0; i < data.values.length; i += 2)
  445.         key.writeStringValue(data.values[i], data.values[i + 1]); 
  446.     }
  447.     if ("children" in data) {
  448.       for (i = 0; i < data.children.length; ++i)
  449.         createRegistryKeys(key, data.children[i]);
  450.     }
  451.     key.close();
  452.   } catch (e) {
  453.     LOG(e);
  454.     if (key)
  455.       key.close();
  456.   }
  457. }
  458.  
  459. /**
  460.  * This function deletes the specified registry key and optionally all of its
  461.  * children.
  462.  * @param rootKey
  463.  *        The parent nsIwindowRegKey of the key to delete.
  464.  * @param name
  465.  *        The name of the key to delete.
  466.  * @param recurse
  467.  *        Pass true to also delete all children of the named key.  Take care!
  468.  */
  469. function deleteRegistryKey(rootKey, name, recurse) {
  470.   if (!rootKey.hasChild(name)) {
  471.     LOG("deleteRegistryKey: rootKey does not have child: \"" + name + "\"");
  472.     return;
  473.   }
  474.   if (recurse) {
  475.     var key = rootKey.openChild(name, rootKey.ACCESS_ALL);
  476.     try {
  477.       for (var i = key.childCount - 1; i >= 0; --i)
  478.         deleteRegistryKey(key, key.getChildName(i), true);
  479.     } finally {
  480.       key.close();
  481.     }
  482.   }
  483.   rootKey.removeChild(name);
  484. }
  485.  
  486. /**
  487.  * This method walks the registry looking for the registry keys of
  488.  * the previous version of the application.
  489.  */
  490. function locateOldInstall(key, ourInstallDir) {
  491.   var result, childKey, productKey, mainKey;
  492.   try {
  493.     for (var i = 0; i < key.childCount; ++i) {
  494.       var childName = key.getChildName(i);
  495.       childKey = key.openChild(childName, key.ACCESS_READ);
  496.       if (childKey.hasValue("CurrentVersion")) {
  497.         for (var j = 0; j < childKey.childCount; ++j) {
  498.           var productVer = childKey.getChildName(j); 
  499.           productKey = childKey.openChild(productVer, key.ACCESS_READ);
  500.           if (productKey.hasChild("Main")) {
  501.             mainKey = productKey.openChild("Main", key.ACCESS_READ);
  502.             var installDir = mainKey.readStringValue("Install Directory");
  503.             var menuPath = mainKey.readStringValue("Program Folder Path");
  504.             mainKey.close();
  505.             if (newFile(installDir).equals(ourInstallDir)) {
  506.               result = new Object();
  507.               result.fullName = childName;
  508.               result.versionWithLocale = productVer;
  509.               result.version = productVer.split(" ")[0];
  510.               result.menuPath = menuPath;
  511.             }
  512.           }
  513.           productKey.close();
  514.           if (result)
  515.             break;
  516.         }
  517.       }
  518.       childKey.close();
  519.       if (result)
  520.         break;
  521.     }
  522.   } catch (e) {
  523.     result = null;
  524.     if (childKey)
  525.       childKey.close();
  526.     if (productKey)
  527.       productKey.close();
  528.     if (mainKey)
  529.       mainKey.close();
  530.   }
  531.   return result;
  532. }
  533.  
  534. /**
  535.  * Delete registry keys left-over from the previous version of the app
  536.  * installed at our location.
  537.  */
  538. function deleteOldRegKeys(key, info) {
  539.   deleteRegistryKey(key, info.fullName + " " + info.version, true);
  540.   var productKey = key.openChild(info.fullName, key.ACCESS_ALL);
  541.   var productCount;
  542.   try {
  543.     deleteRegistryKey(productKey, info.versionWithLocale, true);
  544.     productCount = productKey.childCount;
  545.   } finally {
  546.     productKey.close();
  547.   }
  548.   if (productCount == 0)
  549.     key.removeChild(info.fullName);  
  550. }
  551.  
  552. /**
  553.  * The installer sets various registry keys and values that may need to be
  554.  * updated.
  555.  *
  556.  * This operation is a bit tricky since we do not know the previous value of
  557.  * brandFullName.  As a result, we must walk the registry looking for an
  558.  * existing key that references the same install directory.  We assume that
  559.  * the value of vendorShortName does not change across updates.
  560.  */
  561. function updateRegistry(rootKey) {
  562.   LOG("updateRegistry");
  563.  
  564.   var ourInstallDir = getFile(KEY_APPDIR);
  565.  
  566.   var app =
  567.     Components.classes["@mozilla.org/xre/app-info;1"].
  568.     getService(Components.interfaces.nsIXULAppInfo).
  569.     QueryInterface(Components.interfaces.nsIXULRuntime);
  570.  
  571.   var sbs =
  572.       Components.classes["@mozilla.org/intl/stringbundle;1"].
  573.       getService(Components.interfaces.nsIStringBundleService);
  574.   var brandBundle = sbs.createBundle(URI_BRAND_PROPERTIES);
  575.   var brandFullName = brandBundle.GetStringFromName("brandFullName");
  576.  
  577.   var vendorShortName;
  578.   try {
  579.     var prefs =
  580.       Components.classes["@mozilla.org/preferences-service;1"].
  581.       getService(Components.interfaces.nsIPrefBranch);
  582.  
  583.     vendorShortName = prefs.getCharPref("app.update.vendorName.override");
  584.   }
  585.   catch (e) {
  586.     vendorShortName = brandBundle.GetStringFromName("vendorShortName");
  587.   }
  588.  
  589.   var key = new RegKey();
  590.  
  591.   var oldInstall;
  592.   try {
  593.     key.open(rootKey, "SOFTWARE\\" + vendorShortName, key.ACCESS_READ);
  594.     oldInstall = locateOldInstall(key, ourInstallDir);
  595.   } finally {
  596.     key.close();
  597.   }
  598.  
  599.   if (!oldInstall) {
  600.     LOG("no existing registry keys found");
  601.     return;
  602.   }
  603.  
  604.   // Maybe nothing needs to be changed...
  605.   if (oldInstall.fullName == brandFullName &&
  606.       oldInstall.version == app.version) {
  607.     LOG("registry is up-to-date");
  608.     return;
  609.   }
  610.  
  611.   // Delete the old keys:
  612.   try {
  613.     key.open(rootKey, "SOFTWARE\\" + vendorShortName, key.ACCESS_READ);
  614.     deleteOldRegKeys(key, oldInstall);
  615.   } finally {
  616.     key.close();
  617.   }
  618.  
  619.   // Create the new keys:
  620.  
  621.   var versionWithLocale = app.version + " (" + getLocale() + ")";
  622.   var installPath = ourInstallDir.path + "\\";
  623.   var pathToExe = getFile(KEY_EXECUTABLE_FILE).path;
  624.  
  625.   var Key_bin = {
  626.     name: "bin",
  627.     values: [
  628.       "PathToExe", pathToExe
  629.     ]
  630.   };
  631.   var Key_extensions = {
  632.     name: "Extensions",
  633.     values: [
  634.       "Components", getFile(KEY_COMPONENTS_DIR).path + "\\",
  635.       "Plugins", getFile(KEY_PLUGINS_DIR).path + "\\"
  636.     ]
  637.   };
  638.   var Key_nameWithVersion = {
  639.     name: brandFullName + " " + app.version,
  640.     values: [
  641.       "GeckoVer", app.platformVersion
  642.     ],
  643.     children: [
  644.       Key_bin,
  645.       Key_extensions
  646.     ]
  647.   };
  648.   var Key_main = {
  649.     name: "Main",
  650.     values: [
  651.       "Install Directory", installPath,
  652.       "PathToExe", pathToExe,
  653.       "Program Folder Path", oldInstall.menuPath
  654.     ]
  655.   };
  656.   var Key_uninstall = {
  657.     name: "Uninstall",
  658.     values: [
  659.       "Description", brandFullName + " (" + app.version + ")",
  660.       "Uninstall Log Folder", installPath + "uninstall"
  661.     ]
  662.   };
  663.   var Key_versionWithLocale = {
  664.     name: versionWithLocale,
  665.     children: [
  666.       Key_main,
  667.       Key_uninstall
  668.     ]
  669.   };
  670.   var Key_name = {
  671.     name: brandFullName,
  672.     values: [
  673.       "CurrentVersion", versionWithLocale
  674.     ],
  675.     children: [
  676.       Key_versionWithLocale
  677.     ]
  678.   };
  679.   var Key_brand = {
  680.     name: vendorShortName,
  681.     children: [
  682.       Key_name,
  683.       Key_nameWithVersion
  684.     ]
  685.   };
  686.  
  687.   try {
  688.     key.open(rootKey, "SOFTWARE", key.ACCESS_READ);
  689.     createRegistryKeys(key, Key_brand);
  690.   } finally {
  691.     key.close();
  692.   }
  693.  
  694.   if (rootKey != RegKey.prototype.ROOT_KEY_LOCAL_MACHINE)
  695.     return;
  696.  
  697.   // Now, do the same thing for the Add/Remove Programs control panel:
  698.  
  699.   const uninstallRoot =
  700.       "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
  701.  
  702.   try {
  703.     key.open(rootKey, uninstallRoot, key.ACCESS_READ);
  704.     var oldName = oldInstall.fullName + " (" + oldInstall.version + ")";
  705.     deleteRegistryKey(key, oldName, false);
  706.   } finally {
  707.     key.close();
  708.   }
  709.  
  710.   var uninstallBundle = sbs.createBundle(URI_UNINSTALL_PROPERTIES);
  711.  
  712.   var nameWithVersion = brandFullName + " (" + app.version + ")";
  713.   var uninstaller = getFile(KEY_WINDIR);
  714.   uninstaller.append(uninstallBundle.GetStringFromName("fileUninstall"));
  715.   // XXX copy latest uninstaller to this location
  716.  
  717.   var uninstallString =
  718.       uninstaller.path + " /ua \"" + versionWithLocale + "\"";
  719.  
  720.   Key_uninstall = {
  721.     name: nameWithVersion,
  722.     values: [
  723.       "Comment", brandFullName,
  724.       "DisplayIcon", pathToExe + ",0",        // XXX don't hardcode me!
  725.       "DisplayName", nameWithVersion,
  726.       "DisplayVersion", versionWithLocale, 
  727.       "InstallLocation", ourInstallDir.path,  // no trailing slash
  728.       "Publisher", vendorShortName,
  729.       "UninstallString", uninstallString,
  730.       "URLInfoAbout", uninstallBundle.GetStringFromName("URLInfoAbout"),
  731.       "URLUpdateInfo", uninstallBundle.GetStringFromName("URLUpdateInfo") 
  732.     ]
  733.   };
  734.  
  735.   var child;
  736.   try {
  737.     key.open(rootKey, uninstallRoot, key.ACCESS_READ);
  738.     createRegistryKeys(key, Key_uninstall);
  739.  
  740.     // Create additional DWORD keys for NoModify and NoRepair:
  741.     child = key.openChild(nameWithVersion, key.ACCESS_WRITE);
  742.     child.writeIntValue("NoModify", 1);
  743.     child.writeIntValue("NoRepair", 1);
  744.   } finally {
  745.     if (child)
  746.       child.close();
  747.     key.close();
  748.   }
  749. }
  750.  
  751. //-----------------------------------------------------------------------------
  752.  
  753. function nsPostUpdateWin() {
  754. }
  755.  
  756. nsPostUpdateWin.prototype = {
  757.   QueryInterface: function(iid) {
  758.     if (iid.equals(Components.interfaces.nsIRunnable) ||
  759.         iid.equals(Components.interfaces.nsISupports))
  760.       return this;
  761.     throw Components.results.NS_ERROR_NO_INTERFACE;
  762.   },
  763.  
  764.   run: function() {
  765.     try {
  766.       // We use a global object here for the install log writer, so that the
  767.       // registry updating code can easily inform it of registry keys that it
  768.       // may create.
  769.       installLogWriter = new InstallLogWriter();
  770.       try {
  771.         installLogWriter.begin();
  772.         updateRegistry(RegKey.prototype.ROOT_KEY_CURRENT_USER);
  773.         updateRegistry(RegKey.prototype.ROOT_KEY_LOCAL_MACHINE);
  774.       } finally {
  775.         installLogWriter.end();
  776.         installLogWriter = null;
  777.       }
  778.     } catch (e) {
  779.       LOG(e);
  780.     }
  781.   }
  782. };
  783.  
  784. //-----------------------------------------------------------------------------
  785.  
  786. var gModule = {
  787.   registerSelf: function(compMgr, fileSpec, location, type) {
  788.     compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  789.     
  790.     for (var key in this._objects) {
  791.       var obj = this._objects[key];
  792.       compMgr.registerFactoryLocation(obj.CID, obj.className, obj.contractID,
  793.                                       fileSpec, location, type);
  794.     }
  795.   },
  796.   
  797.   getClassObject: function(compMgr, cid, iid) {
  798.     if (!iid.equals(Components.interfaces.nsIFactory))
  799.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  800.  
  801.     for (var key in this._objects) {
  802.       if (cid.equals(this._objects[key].CID))
  803.         return this._objects[key].factory;
  804.     }
  805.     
  806.     throw Components.results.NS_ERROR_NO_INTERFACE;
  807.   },
  808.   
  809.   _makeFactory: #1= function(ctor) {
  810.     function ci(outer, iid) {
  811.       if (outer != null)
  812.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  813.       return (new ctor()).QueryInterface(iid);
  814.     } 
  815.     return { createInstance: ci };
  816.   },
  817.   
  818.   _objects: {
  819.     manager: { CID        : Components.ID("{d15b970b-5472-40df-97e8-eb03a04baa82}"),
  820.                contractID : "@mozilla.org/updates/post-update;1",
  821.                className  : "nsPostUpdateWin",
  822.                factory    : #1#(nsPostUpdateWin)
  823.              },
  824.   },
  825.   
  826.   canUnload: function(compMgr) {
  827.     return true;
  828.   }
  829. };
  830.  
  831. function NSGetModule(compMgr, fileSpec) {
  832.   return gModule;
  833. }
  834.