home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 December / PCWorld_2005-12_cd.bin / komunikace / netscape / nsb-install-8-0.exe / components / nsExtensionManager.js < prev    next >
Text File  |  2005-09-26  |  178KB  |  4,543 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 Extension Manager.
  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 nsIExtensionManager             = Components.interfaces.nsIExtensionManager;
  39. const nsIUpdateService                = Components.interfaces.nsIUpdateService;
  40. const nsIUpdateItem                   = Components.interfaces.nsIUpdateItem;
  41.  
  42. const PREF_EM_APP_ID                  = "app.id";
  43. const PREF_EM_APP_VERSION             = "app.version";
  44. const PREF_EM_APP_EXTENSIONS_VERSION  = "app.extensions.version";
  45. const PREF_EM_APP_BUILDID             = "app.build_id";
  46. const PREF_EM_LAST_APP_VERSION        = "extensions.lastAppVersion";
  47. const PREF_UPDATE_COUNT               = "extensions.update.count";
  48. const PREF_UPDATE_DEFAULT_URL         = "extensions.update.url";
  49. const PREF_EM_WASINSAFEMODE           = "extensions.wasInSafeMode";
  50. const PREF_EM_DISABLEDOBSOLETE        = "extensions.disabledObsolete";
  51. const PREF_EM_LAST_SELECTED_SKIN      = "extensions.lastSelectedSkin";
  52. const PREF_EM_EXTENSION_FORMAT        = "extensions.%UUID%.";
  53. const PREF_EM_ITEM_UPDATE_ENABLED     = "extensions.%UUID%.update.enabled";
  54. const PREF_EM_ITEM_UPDATE_URL         = "extensions.%UUID%.update.url";
  55. const PREF_EM_DSS_ENABLED             = "extensions.dss.enabled";
  56.  
  57. const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
  58.  
  59. const DIR_EXTENSIONS                  = "extensions";
  60. const DIR_UNINSTALL                   = "uninstall";
  61. const DIR_TEMP                        = "temp";
  62. const DIR_CHROME                      = "chrome";
  63. const DIR_COMPONENTS                  = "components";
  64. const DIR_DEFAULTS                    = "defaults";
  65. const DIR_DEFAULTS_PREFS              = "preferences";
  66. const DIR_DEFAULTS_EXTENSIONS         = "extensions"; 
  67. const DIR_CR_CHROME                   = "chrome";
  68. const DIR_CR_OVERLAYINFO              = "overlayinfo";
  69. const FILE_CR_CHROMEDS                = "chrome.rdf";
  70. const FILE_EXTENSIONS                 = "Extensions.rdf";
  71. const FILE_UNINSTALL_LOG              = "Uninstall";
  72. const FILE_DEFAULTS                   = "defaults.ini";
  73. const FILE_COMPONENT_MANIFEST         = "components.ini";
  74. const FILE_COMPAT_MANIFEST            = "compatibility.ini";
  75. const FILE_INSTALL_MANIFEST           = "install.rdf";
  76. const FILE_CHROME_MANIFEST            = "contents.rdf";
  77. const FILE_WASINSAFEMODE              = "Safe Mode";
  78. const FILE_INSTALLED_EXTENSIONS       = "installed-extensions.txt"
  79. const FILE_INSTALLED_EXTENSIONS_PROCESSED = "installed-extensions-processed.txt"
  80.  
  81. const KEY_PROFILEDIR                  = "ProfD";
  82. const KEY_APPDIR                      = "XCurProcD";
  83. const KEY_DEFAULTS                    = "ProfDefNoLoc";
  84. const KEY_DEFAULT_THEME               = "winscape/1.0";
  85.  
  86. const ERROR_INVALID_VERSION           = -1;
  87. const ERROR_PHONED_HOME               = -2;
  88. const ERROR_EXTENSION_IS_THEME        = -3;
  89.  
  90. var gPref           = null;
  91. var gRDF            = null;
  92. var gOS             = null;
  93. var gVersionChecker = null;
  94.  
  95. function getVersionChecker()
  96. {
  97.   if (!gVersionChecker) {
  98.     gVersionChecker = Components.classes["@mozilla.org/updates/version-checker;1"]
  99.                                 .getService(Components.interfaces.nsIVersionChecker);
  100.   }
  101.   return gVersionChecker;
  102. }
  103.  
  104. ///////////////////////////////////////////////////////////////////////////////
  105. //
  106. // Utility Functions
  107. //
  108. const EM_NS_PREFIX      = "http://www.mozilla.org/2004/em-rdf#";
  109. const CHROME_NS_PREFIX  = "http://www.mozilla.org/rdf/chrome#";
  110.  
  111. function EM_NS(aProperty)
  112. {
  113.   return EM_NS_PREFIX + aProperty;
  114. }
  115.  
  116. function CHROME_NS(aProperty)
  117. {
  118.   return CHROME_NS_PREFIX + aProperty;
  119. }
  120.  
  121. // Returns the specified directory hierarchy under the special directory 
  122. // specified by aKey, creating directories along the way if necessary.
  123. function getDir(aKey, aSubDirs)
  124. {
  125.   return getDirInternal(aKey, aSubDirs, true);
  126. }
  127.  
  128. function getDirNoCreate(aKey, aSubDirs)
  129. {
  130.   return getDirInternal(aKey, aSubDirs, false);
  131. }
  132.  
  133. function getDirInternal(aKey, aSubDirs, aCreate)
  134. {
  135.   var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
  136.                               .getService(Components.interfaces.nsIProperties);
  137.   var dir = fileLocator.get(aKey, Components.interfaces.nsIFile);
  138.   for (var i = 0; i < aSubDirs.length; ++i) {
  139.     dir.append(aSubDirs[i]);
  140.     if (aCreate && !dir.exists())
  141.       dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
  142.   }
  143.   return dir;
  144. }
  145.  
  146. // Returns the file at the appropriate point in a directory hierarchy under
  147. // the specified key, creating directories along the way if necessary. Does
  148. // NOT create the file.
  149. function getFile(aKey, aPathToFile)
  150. {
  151.   var subdirs = [];
  152.   for (var i = 0; i < aPathToFile.length - 1; ++i)
  153.     subdirs.push(aPathToFile[i]);
  154.   var file = getDir(aKey, subdirs);
  155.   file.append(aPathToFile[aPathToFile.length - 1]);
  156.   return file;
  157. }
  158.  
  159. function getDirKey(aIsProfile)
  160. {
  161.   return aIsProfile ? KEY_PROFILEDIR : KEY_APPDIR;
  162. }
  163.  
  164. function dumpFile(aFile)
  165. {
  166.   dump("*** file = " + aFile.path + ", exists = " + aFile.exists() + "\n");
  167. }
  168.  
  169. // We use this to force RDF to bypass the cache when loading certain types
  170. // of files. 
  171. function getRandomFileName(aName, aExtension)
  172. {
  173.   var characters = "abcdefghijklmnopqrstuvwxyz0123456789";
  174.   var nameString = aName + "-";
  175.   for (var i = 0; i < 3; ++i) {
  176.     var index = Math.round((Math.random()) * characters.length);
  177.     nameString += characters.charAt(index);
  178.   }
  179.   return nameString + "." + aExtension;
  180. }
  181.  
  182. const PREFIX_EXTENSION  = "urn:mozilla:extension:";
  183. const PREFIX_THEME      = "urn:mozilla:theme:";
  184. const ROOT_EXTENSION    = "urn:mozilla:extension:root";
  185. const ROOT_THEME        = "urn:mozilla:theme:root";
  186.  
  187. function getItemPrefix(aItemType)
  188. {
  189.   var prefix = "";
  190.   if (aItemType & nsIUpdateItem.TYPE_EXTENSION) 
  191.     prefix = PREFIX_EXTENSION;
  192.   else if (aItemType & nsIUpdateItem.TYPE_THEME)
  193.     prefix = PREFIX_THEME;
  194.   return prefix;
  195. }
  196.  
  197. function getItemRoot(aItemType)
  198. {
  199.   var root = "";
  200.   if (aItemType & nsIUpdateItem.TYPE_EXTENSION) 
  201.     root = ROOT_EXTENSION;
  202.   else if (aItemType & nsIUpdateItem.TYPE_THEME)
  203.     root = ROOT_THEME;
  204.   return root;
  205. }
  206.  
  207. function getItemRoots(aItemType)
  208. {    
  209.   var roots = [];
  210.   if (aItemType == nsIUpdateItem.TYPE_ADDON)
  211.     roots = roots.concat([getItemRoot(nsIUpdateItem.TYPE_EXTENSION), 
  212.                           getItemRoot(nsIUpdateItem.TYPE_THEME)]);
  213.   else
  214.     roots.push(getItemRoot(aItemType));
  215.   return roots;
  216. }
  217.  
  218. function getItemType(aURI)
  219. {
  220.   var type = -1;
  221.   if (aURI.substr(0, PREFIX_EXTENSION.length) == PREFIX_EXTENSION)
  222.     type = nsIUpdateItem.TYPE_EXTENSION;
  223.   else if (aURI.substr(0, PREFIX_THEME.length) == PREFIX_THEME)
  224.     type = nsIUpdateItem.TYPE_THEME;
  225.   return type;
  226. }
  227.  
  228. function stripPrefix(aURI, aItemType)
  229. {
  230.   var val = aURI;
  231.   if (aItemType == nsIUpdateItem.TYPE_ADDON)
  232.     val = stripPrefix(aURI, getItemType(aURI));
  233.   else {
  234.     var prefix = getItemPrefix(aItemType);
  235.     if (prefix && aURI.substr(0, prefix.length) == prefix)  
  236.       val = aURI.substr(prefix.length, aURI.length);
  237.   }
  238.   return val;
  239. }
  240.  
  241. function stripPropertyPrefix(aProperty, aPrefix)
  242. {
  243.   return aProperty.substr(aPrefix.length, aProperty.length);
  244. }
  245.  
  246. function getURLSpecFromFile(aFile)
  247. {
  248.   var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
  249.                           .getService(Components.interfaces.nsIIOService);
  250.   var fph = ioServ.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  251.   return fph.getURLSpecFromFile(aFile);
  252. }
  253.  
  254. function ensureExtensionsFiles(aIsProfile)
  255. {
  256.   try {
  257.     var extensionsFile  = getFile(getDirKey(aIsProfile), 
  258.                                   [DIR_EXTENSIONS, FILE_EXTENSIONS]);
  259.   
  260.     // If the file does not exist at the current location, copy the default
  261.     // version over so we can access the various roots. 
  262.     // This is a sign also that something may have gone wrong, such as the user
  263.     // deleting /Extensions so we should remove the relative contents.rdf and
  264.     // overlayinfo hierarchies too. 
  265.     if (extensionsFile && !extensionsFile.exists()) {
  266.       var defaultFile = getFile(KEY_DEFAULTS, 
  267.                                 [DIR_DEFAULTS_EXTENSIONS, FILE_EXTENSIONS]);
  268.       defaultFile.copyTo(extensionsFile.parent, extensionsFile.leafName);
  269.  
  270.       // XXXben - do this only for profile until we have a better protection 
  271.       // mechanism for global items.
  272.       if (aIsProfile) {
  273.         try {      
  274.           var chromedsFile = getFile(getDirKey(aIsProfile), [DIR_CR_CHROME, FILE_CR_CHROMEDS]);
  275.           if (chromedsFile.exists())
  276.             chromedsFile.remove(false);
  277.           var overlayinfoDir = getDir(getDirKey(aIsProfile), [DIR_CR_CHROME, DIR_CR_OVERLAYINFO]);
  278.           if (overlayinfoDir.exists())
  279.             overlayinfoDir.remove(true);
  280.         }
  281.         catch (e) { 
  282.           dump("Extension System Warning: failed to remove chrome.rdf/overlay info because: " + e + "\n"); 
  283.         }
  284.       }
  285.     }
  286.   }
  287.   catch (e) { 
  288.     // Too early in the startup process to use the console, we may yet restart
  289.     // the app.
  290.     dump("Extension System Warning: Failed to set up default extensions" + 
  291.          " files probably because you do not have write privileges to this" + 
  292.          " location. While you can run Firefox like this, it is recommended" + 
  293.          " that you run it at least once with privileges that allow it to generate" + 
  294.          " these initial files to improve start performance. Running from a disk" + 
  295.          " image on MacOS X is not recommended.");
  296.   }
  297. }
  298.  
  299. function stringData(aLiteralOrResource)
  300. {
  301.   try {
  302.     var obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFLiteral);
  303.     return obj.Value;
  304.   }
  305.   catch (e) {
  306.     try {
  307.       obj = aLiteralOrResource.QueryInterface(Components.interfaces.nsIRDFResource);
  308.       return obj.Value;
  309.     }
  310.     catch (e) {}
  311.   }
  312.   return "--";
  313. }
  314.  
  315. function stackTraceFunctionFormat(aFunctionName)
  316. {
  317.   var classDelimiter = aFunctionName.indexOf("_");
  318.   var className = aFunctionName.substr(0, classDelimiter);
  319.   if (!className)
  320.     className == "<global>";
  321.   var functionName = aFunctionName.substr(classDelimiter + 1, aFunctionName.length);
  322.   if (!functionName) 
  323.     functionName == "<anonymous>";
  324.   return className + "::" + functionName;
  325. }
  326.  
  327. function stackTrace(aArguments, aMaxCount)
  328. {
  329.   dump("=[STACKTRACE]=====================================================\n");
  330.   dump("*** at: " + stackTraceFunctionFormat(aArguments.callee.name) + "()\n");
  331.   var temp = aArguments.callee.caller;
  332.   var count = 0;
  333.   while (temp) {
  334.     dump("***     " + stackTraceFunctionFormat(temp.name) + "()\n");
  335.     
  336.     temp = temp.arguments.callee.caller;
  337.     if (aMaxCount > 0 && ++count == aMaxCount)
  338.       break;
  339.   }
  340.   dump("==================================================================\n");
  341. }
  342.  
  343. ///////////////////////////////////////////////////////////////////////////////
  344. // Incompatible Item Error Message
  345. function showIncompatibleError(aDS)
  346. {
  347.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  348.                       .getService(Components.interfaces.nsIStringBundleService);
  349.   var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  350.   var params = [extensionStrings.GetStringFromName("extension")];
  351.   var title = extensionStrings.formatStringFromName("incompatibleTitle", 
  352.                                                     params, params.length);
  353.   
  354.   var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  355.   var brandShortName = brandStrings.GetStringFromName("brandShortName");
  356.  
  357.   var message;
  358.   var metadata = {};
  359.   getItemMetadata(aDS, metadata);
  360.   
  361.   if (undefined === metadata.minAppVersion) {
  362.     // getItemMetadata does not fill target application version range properties unless a 
  363.     // matching supported target application is found.
  364.     params = [metadata.name, metadata.version, brandShortName];
  365.     message = extensionStrings.formatStringFromName("incompatibleMessageNoApp", 
  366.                                                     params, params.length);
  367.   }
  368.   else if (metadata.minAppVersion == metadata.maxAppVersion) {
  369.     // If the min target app version and the max target app version are the same, don't show
  370.     // a message like, "Foo is only compatible with Firefox versions 0.7 to 0.7", rather just
  371.     // show, "Foo is only compatible with Firefox 0.7"
  372.     params = [metadata.name, metadata.version, brandShortName, metadata.name, 
  373.               metadata.version, brandShortName, metadata.minAppVersion];
  374.     message = extensionStrings.formatStringFromName("incompatibleMessageSingleAppVersion", 
  375.                                                     params, params.length);
  376.   }
  377.   else {
  378.     params = [metadata.name, metadata.version, brandShortName, metadata.name, 
  379.               metadata.version, brandShortName, metadata.minAppVersion, 
  380.               metadata.maxAppVersion];
  381.     message = extensionStrings.formatStringFromName("incompatibleMessage", params, params.length);
  382.   }
  383.   var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  384.                      .getService(Components.interfaces.nsIPromptService);
  385.   ps.alert(null, title, message);
  386. }
  387.  
  388. function showMalformedError(aFile)
  389. {
  390.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  391.                       .getService(Components.interfaces.nsIStringBundleService);
  392.   var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  393.   var title = extensionStrings.GetStringFromName("malformedTitle");
  394.  
  395.   var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  396.   var brandShortName = brandStrings.GetStringFromName("brandShortName");
  397.   var message = extensionStrings.formatStringFromName("malformedMessage", [brandShortName, aFile], 2);
  398.   
  399.   var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  400.                      .getService(Components.interfaces.nsIPromptService);
  401.   ps.alert(null, title, message);
  402. }
  403.  
  404. function showInvalidVersionError(aItemName, aVersion)
  405. {
  406.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  407.                       .getService(Components.interfaces.nsIStringBundleService);
  408.   var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  409.   var title = extensionStrings.GetStringFromName("invalidVersionTitle");
  410.  
  411.   var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  412.   var brandShortName = brandStrings.GetStringFromName("brandShortName");
  413.   var params = [brandShortName, aItemName, aVersion];
  414.   var message = extensionStrings.formatStringFromName("invalidVersionMessage", params, params.length);
  415.   
  416.   var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  417.                      .getService(Components.interfaces.nsIPromptService);
  418.   ps.alert(null, title, message);
  419. }
  420.  
  421. function showOldThemeError(aDS)
  422. {
  423.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  424.                       .getService(Components.interfaces.nsIStringBundleService);
  425.   var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  426.   var params = [extensionStrings.GetStringFromName("theme")];
  427.   var title = extensionStrings.formatStringFromName("incompatibleTitle", 
  428.                                                     params, params.length);
  429.  
  430.   var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  431.   var brandShortName = brandStrings.GetStringFromName("brandShortName");
  432.   var appVersion = extensionStrings.GetStringFromName("incompatibleOlder");
  433.   
  434.   try {  
  435.     var rdfc = Components.classes["@mozilla.org/rdf/container;1"]
  436.                          .createInstance(Components.interfaces.nsIRDFContainer);
  437.     rdfc.Init(aDS, gRDF.GetResource("urn:mozilla:skin:root"));
  438.     
  439.     var elts = rdfc.GetElements();
  440.     var nameArc = gRDF.GetResource(CHROME_NS("displayName"));
  441.     while (elts.hasMoreElements()) {
  442.       var elt = elts.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  443.       themeName = stringData(aDS.GetTarget(elt, nameArc, true));
  444.       if (themeName) 
  445.         break;
  446.     }
  447.   }
  448.   catch (e) {
  449.     themeName = extensionStrings.GetStringFromName("incompatibleThemeName");
  450.   }
  451.   
  452.   params = [themeName, "", brandShortName, themeName, "", brandShortName, appVersion];
  453.   var message = extensionStrings.formatStringFromName("incompatibleMessageSingleAppVersion",
  454.                                                       params, params.length);
  455.   var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  456.                      .getService(Components.interfaces.nsIPromptService);
  457.   ps.alert(null, title, message);
  458. }
  459.  
  460. function showMissingFileError(aSourceFile, aMissingFileName)
  461. {
  462.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  463.                       .getService(Components.interfaces.nsIStringBundleService);
  464.   var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  465.   var title = extensionStrings.GetStringFromName("missingFileTitle");
  466.  
  467.   var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  468.   var brandShortName = brandStrings.GetStringFromName("brandShortName");
  469.   var params = [brandShortName, aMissingFileName];
  470.   var message = extensionStrings.formatStringFromName("missingFileMessage", params, params.length);
  471.   
  472.   var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  473.                      .getService(Components.interfaces.nsIPromptService);
  474.   ps.alert(null, title, message);
  475.  
  476.   var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
  477.                                  .getService(Components.interfaces.nsIConsoleService);
  478.   params = [aSourceFile, aMissingFileName];
  479.   var consoleMessage = extensionStrings.formatStringFromName("missingFileConsoleMessage",
  480.                                                              params, params.length);
  481.   consoleService.logStringMessage(consoleMessage);
  482. }
  483.  
  484. function showMalformedRegistrationError(aCRException)
  485. {
  486.   var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  487.                       .getService(Components.interfaces.nsIStringBundleService);
  488.   var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  489.   var title = extensionStrings.GetStringFromName("malformedRegistrationTitle");
  490.  
  491.   var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  492.   var brandShortName = brandStrings.GetStringFromName("brandShortName");
  493.   var params = [brandShortName];
  494.   var message = extensionStrings.formatStringFromName("malformedRegistrationMessage", 
  495.                                                       params, params.length);
  496.   var detailsButtonMessage = extensionStrings.GetStringFromName("malformedRegistrationDetailsButton");
  497.   
  498.   var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  499.                      .getService(Components.interfaces.nsIPromptService);
  500.   var flags = (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1) +  
  501.               (ps.BUTTON_TITLE_OK * ps.BUTTON_POS_0);
  502.   var result = ps.confirmEx(null, title, message, flags, null, detailsButtonMessage, null, null, { } );
  503.   if (result == 1) {  
  504.     var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
  505.                                   .getService(Components.interfaces.nsIConsoleService);
  506.     params = [aCRException.extensionID, aCRException.functionName,
  507.               aCRException.chromePath, aCRException.isProfile, ];
  508.     var consoleMessage = extensionStrings.formatStringFromName("malformedRegistrationConsoleMessage",
  509.                                                                params, params.length);
  510.     consoleService.logStringMessage(consoleMessage);
  511.     
  512.     var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  513.                         .getService(Components.interfaces.nsIWindowWatcher);
  514.     ww.openWindow(null, "chrome://global/content/console.xul", 
  515.                   "", "chrome,modal,centerscreen,resizable", null);
  516.   }
  517. }
  518.  
  519. function getItemMetadata(aDS, aResult)
  520. {
  521.   var manifestRoot = gRDF.GetResource("urn:mozilla:install-manifest");
  522.  
  523.   // Extension Name and Version
  524.   var props = ["name", "version"];
  525.   for (var i = 0; i < props.length; ++i) {
  526.     var prop = gRDF.GetResource(EM_NS(props[i]));
  527.     aResult[props[i]] = stringData(aDS.GetTarget(manifestRoot, prop, true));
  528.   }
  529.   
  530.   // Target App Name and Version
  531.   var appID = gPref.getCharPref(PREF_EM_APP_ID);
  532.  
  533.   var targets = aDS.GetTargets(manifestRoot, gRDF.GetResource(EM_NS("targetApplication")), true);
  534.   var idRes = gRDF.GetResource(EM_NS("id"));
  535.   var minVersionRes = gRDF.GetResource(EM_NS("minVersion"));
  536.   var maxVersionRes = gRDF.GetResource(EM_NS("maxVersion"));
  537.   while (targets.hasMoreElements()) {
  538.     var targetApp = targets.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  539.     var id          = stringData(aDS.GetTarget(targetApp, idRes, true));
  540.     var minVersion  = stringData(aDS.GetTarget(targetApp, minVersionRes, true));
  541.     var maxVersion  = stringData(aDS.GetTarget(targetApp, maxVersionRes, true));
  542.  
  543.     if (id == appID) {
  544.       aResult.minAppVersion = minVersion;
  545.       aResult.maxAppVersion = maxVersion;
  546.       break;
  547.     }
  548.   }
  549. }
  550.  
  551. function getInstallManifest(aFile)
  552. {
  553.   var fileURL = getURLSpecFromFile(aFile);
  554.   var ds = gRDF.GetDataSourceBlocking(fileURL);
  555.   var manifestRoot = gRDF.GetResource("urn:mozilla:install-manifest");
  556.   var arcs = ds.ArcLabelsOut(manifestRoot);
  557.   if (!arcs.hasMoreElements()) {
  558.     ds = null;
  559.     var uri = Components.classes["@mozilla.org/network/standard-url;1"]
  560.                         .createInstance(Components.interfaces.nsIURI);
  561.     uri.spec = fileURL;
  562.     var url = uri.QueryInterface(Components.interfaces.nsIURL);
  563.     showMalformedError(url.fileName);
  564.   }
  565.   return ds;
  566. }
  567.  
  568. function ArrayEnumerator(aItems)
  569. {
  570.   this._index = 0;
  571.   
  572.   if (aItems) {
  573.     for (var i = 0; i < aItems.length; ++i) {    
  574.       if (!aItems[i])
  575.         aItems.splice(i, 1);      
  576.     }
  577.   }
  578.   
  579.   this._contents = aItems;
  580. }
  581.  
  582. ArrayEnumerator.prototype = {
  583.   _index: 0,
  584.   _contents: [],
  585.   
  586.   hasMoreElements: function ArrayEnumerator_hasMoreElements()
  587.   {
  588.     return this._index < this._contents.length;
  589.   },
  590.   
  591.   getNext: function ArrayEnumerator_getNext()
  592.   {
  593.     return this._contents[this._index++];      
  594.   }
  595. };
  596.   
  597. ///////////////////////////////////////////////////////////////////////////////
  598. //
  599. // nsInstallLogBase
  600. //
  601. function nsInstallLogBase()
  602. {
  603. }
  604.  
  605. nsInstallLogBase.prototype = {
  606.   CHROME_TYPE_PACKAGE   : "package",
  607.   CHROME_TYPE_SKIN      : "skin",
  608.   CHROME_TYPE_LOCALE    : "locale",
  609.  
  610.   TOKEN_ADD_FILE        : "add",
  611.   TOKEN_REGISTER_CHROME : "register",
  612.   TOKEN_PROFILE         : "profile",
  613.   TOKEN_GLOBAL          : "global",
  614.   TOKEN_SKIN            : "skin"
  615. };
  616.  
  617. ///////////////////////////////////////////////////////////////////////////////
  618. //
  619. // nsInstallLogWriter
  620. //
  621. function nsInstallLogWriter(aExtensionID, aIsProfile)
  622. {
  623.   this._isProfile = aIsProfile;
  624.   this._uninstallLog = getDir(getDirKey(aIsProfile),
  625.                               [DIR_EXTENSIONS, aExtensionID, DIR_UNINSTALL]);
  626.   this._uninstallLog.append(FILE_UNINSTALL_LOG);
  627. }
  628.  
  629. nsInstallLogWriter.prototype = {
  630.   __proto__       : nsInstallLogBase.prototype,
  631.   _uninstallLog   : null,
  632.   
  633.   open: function nsInstallLogWriter_open ()
  634.   {
  635.     this._fos = Components.classes["@mozilla.org/network/file-output-stream;1"]
  636.                           .createInstance(Components.interfaces.nsIFileOutputStream);
  637.     const MODE_WRONLY   = 0x02;
  638.     const MODE_CREATE   = 0x08;
  639.     const MODE_TRUNCATE = 0x20;
  640.     this._fos.init(this._uninstallLog, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, 0644, 0);
  641.   },
  642.   
  643.   close: function nsInstallLogWriter_close ()
  644.   {
  645.     this._fos.close();  
  646.   },
  647.   
  648.   addFile: function nsInstallLogWriter_addFile (aFile) 
  649.   {
  650.     var line = "add\t" + aFile.persistentDescriptor + "\n";
  651.     this._fos.write(line, line.length);
  652.   },
  653.   
  654.   registerChrome: function nsInstallLogWriter_registerChrome (aProviderName, aChromeType, aIsProfile)
  655.   {
  656.     var profile = aIsProfile ? "profile" : "global";
  657.     // register\tprofile\tpackage\t<provider_name>
  658.     var line = "register\t" + profile + "\t" + aChromeType + "\t" + aProviderName + "\n";
  659.     this._fos.write(line, line.length);
  660.   },
  661.   
  662.   installSkin: function nsInstallLogWriter_installSkin (aSkinName, aIsProfile)
  663.   {
  664.     var profile = aIsProfile ? "profile" : "global";
  665.     // register\tprofile\tpackage\t<provider_name>
  666.     var line = "skin\t" + profile + "\t" + aSkinName + "\n";
  667.     this._fos.write(line, line.length);
  668.   }
  669. };
  670.  
  671. ///////////////////////////////////////////////////////////////////////////////
  672. //
  673. // nsInstallLogReader
  674. //
  675. function nsInstallLogReader(aExtensionID, aIsProfile, aListener)
  676. {
  677.   this._isProfile = aIsProfile;
  678.   this.uninstallLog = getFile(getDirKey(aIsProfile),
  679.                               [DIR_EXTENSIONS, aExtensionID, 
  680.                                DIR_UNINSTALL, FILE_UNINSTALL_LOG]);
  681.   this._listener = aListener
  682. }
  683.  
  684. nsInstallLogReader.prototype = {
  685.   __proto__       : nsInstallLogBase.prototype,
  686.   uninstallLog    : null,
  687.   _listener       : null,
  688.   
  689.   read: function nsInstallLogReader_read ()
  690.   {
  691.     if (!this.uninstallLog.exists())
  692.       return;
  693.   
  694.     var fis = Components.classes["@mozilla.org/network/file-input-stream;1"]
  695.                         .createInstance(Components.interfaces.nsIFileInputStream);
  696.     fis.init(this.uninstallLog, -1, -1, false);
  697.     var lis = fis.QueryInterface(Components.interfaces.nsILineInputStream);
  698.     var line = { value: "" };
  699.     var more = false;
  700.     var lines = [];
  701.     do {
  702.       more = lis.readLine(line);
  703.       lines.push(line.value);
  704.     }
  705.     while (more);
  706.     fis.close();
  707.  
  708.     // Now that we've closed the stream we can remove all the files, unregister
  709.     // chrome, etc. 
  710.     //
  711.     // The list of lines we pass to the uninstall handler should be in this
  712.     // order:
  713.     // 1) File additions
  714.     // 2) Chrome Package Registrations
  715.     // 3) Chrome Skin and Locale Registrations
  716.     //
  717.     // They must be in this order since skins and locales rely on packages, and
  718.     // the packages they rely on is not stored in the registration line so we
  719.     // simply "deselect" for every package installed by the extension.
  720.     var dependentLines = [];
  721.     for (var i = 0; i < lines.length; ++i) {
  722.       var parts = lines[i].split("\t");
  723.       if (parts[1] == this.TOKEN_REGISTER_CHROME && 
  724.           (parts[2] == this.CHROME_TYPE_SKIN || 
  725.            parts[2] == this.CHROME_TYPE_LOCALE)) {
  726.         dependentLines.push(lines.splice(i, 1));
  727.       }
  728.     }
  729.     lines.concat(dependentLines);
  730.     
  731.     for (var i = 0; i < lines.length; ++i)
  732.       this._parseLine(lines[i]);
  733.   },
  734.   
  735.   _parseLine: function nsInstallLogReader__parseLine(aLine)
  736.   {
  737.     var parts = aLine.split("\t");
  738.     switch (parts[0]) {
  739.     case this.TOKEN_ADD_FILE:
  740.       var prefix = this.TOKEN_ADD_FILE + "\t";
  741.       var filePD = aLine.substr(prefix.length, aLine.length);
  742.       var lf = Components.classes["@mozilla.org/file/local;1"]
  743.                          .createInstance(Components.interfaces.nsILocalFile);
  744.       try {
  745.         lf.persistentDescriptor = filePD;
  746.         this._listener.onAddFile(lf);
  747.       }
  748.       catch (e) { 
  749.         dump("*** nsInstallLogReader::_parseLine - failed to remove file " + e + "\n"); 
  750.       }
  751.       break;
  752.     case this.TOKEN_REGISTER_CHROME:
  753.       var isProfile = parts[1] == this.TOKEN_PROFILE;
  754.       try {
  755.         this._listener.onRegisterChrome(parts[3], lf, parts[2], isProfile);
  756.       } 
  757.       catch (e) {
  758.         dump("*** nsInstallLogReader::_parseLine - failed to deregister chrome\n"); 
  759.       }
  760.       break;
  761.     case this.TOKEN_SKIN:
  762.       var isProfile = parts[1] == this.TOKEN_PROFILE;
  763.       try {
  764.         this._listener.onInstallSkin(parts[2], isProfile);
  765.       } 
  766.       catch (e) {
  767.         dump("*** nsInstallLogReader::_parseLine - failed to uninstall skin\n"); 
  768.       }
  769.       break;
  770.     }
  771.   }
  772. };
  773.  
  774. ///////////////////////////////////////////////////////////////////////////////
  775. //
  776. // nsInstalledExtensionReader
  777. //
  778. function nsInstalledExtensionReader(aManager)
  779. {
  780.   this._installedExtensions = getFile(KEY_APPDIR,
  781.                                       [DIR_EXTENSIONS, 
  782.                                        FILE_INSTALLED_EXTENSIONS]);
  783.   this._installedExtensionsProcessed = getFile(KEY_APPDIR,
  784.                                                [DIR_EXTENSIONS, 
  785.                                                 FILE_INSTALLED_EXTENSIONS_PROCESSED]);
  786.   this._manager = aManager;
  787. }
  788.  
  789. nsInstalledExtensionReader.prototype = {
  790.   _manager            : null,
  791.   _installedExtensions: null,
  792.   
  793.   read: function nsInstalledExtensionReader_read ()
  794.   {
  795.     if (this._installedExtensionsProcessed.exists())
  796.       return;
  797.     
  798.     if (!this._installedExtensions.exists()) {
  799.       var defaultsList = getFile(KEY_DEFAULTS, [DIR_DEFAULTS_EXTENSIONS, FILE_INSTALLED_EXTENSIONS]);
  800.       defaultsList.copyTo(getDir(KEY_APPDIR, [DIR_EXTENSIONS]), FILE_INSTALLED_EXTENSIONS);
  801.     }
  802.       
  803.     var fis = Components.classes["@mozilla.org/network/file-input-stream;1"]
  804.                         .createInstance(Components.interfaces.nsIFileInputStream);
  805.     fis.init(this._installedExtensions, -1, -1, false);
  806.     var lis = fis.QueryInterface(Components.interfaces.nsILineInputStream);
  807.     var line = { value: "" };
  808.     var more = false;
  809.     var lines = [];
  810.     do {
  811.       more = lis.readLine(line);
  812.       lines.push(line.value);
  813.     }
  814.     while (more);
  815.     fis.close();
  816.  
  817.     // Now that we've closed the stream we can remove all the files    
  818.     for (var i = 0; i < lines.length; ++i)
  819.       this._parseLine(lines[i]);
  820.     
  821.     this._installedExtensions.moveTo(getDir(KEY_APPDIR, [DIR_EXTENSIONS]), 
  822.                                      FILE_INSTALLED_EXTENSIONS_PROCESSED);
  823.   },
  824.   
  825.   TOKEN_EXTENSION : "extension",
  826.   TOKEN_THEME     : "theme",
  827.   
  828.   _parseLine: function nsInstalledExtensionReader__parseLine (aLine)
  829.   {
  830.     // extension,{GUID} or theme,{GUID}
  831.     var parts = aLine.split(",");
  832.     var manifest = getFile(KEY_APPDIR, 
  833.                            [DIR_EXTENSIONS, parts[1], FILE_INSTALL_MANIFEST]);
  834.     if (parts.length != 2)
  835.       return;
  836.       
  837.     if (!manifest.exists()) {
  838.       defaultManifest = defaultFile = getFile(KEY_DEFAULTS, 
  839.                                               [DIR_DEFAULTS_EXTENSIONS, parts[1], FILE_INSTALL_MANIFEST]);
  840.       var extensionDir = getDir(KEY_APPDIR, [DIR_EXTENSIONS, parts[1]]);
  841.       defaultManifest.copyTo(extensionDir, FILE_INSTALL_MANIFEST);
  842.       manifest = getFile(KEY_APPDIR, 
  843.                          [DIR_EXTENSIONS, parts[1], FILE_INSTALL_MANIFEST]);
  844.     }
  845.     switch (parts[0]) {
  846.     case this.TOKEN_EXTENSION:
  847.       this._manager.ensurePreConfiguredItem(parts[1], nsIUpdateItem.TYPE_EXTENSION, manifest);
  848.       break;
  849.     case this.TOKEN_THEME:
  850.       this._manager.ensurePreConfiguredItem(parts[1], nsIUpdateItem.TYPE_THEME, manifest);
  851.       break;
  852.     }
  853.   }
  854. };
  855.  
  856. ///////////////////////////////////////////////////////////////////////////////
  857. //
  858. // nsChromeRegistrationException
  859. //
  860. function nsChromeRegistrationException(aChromePath, aIsProfile, aFunctionName)
  861. {
  862.   this.chromePath = aChromePath;
  863.   this.isProfile = aIsProfile;
  864.   this.functionName = aFunctionName;
  865. }
  866. nsChromeRegistrationException.prototype = {
  867.   chromePath      : null,
  868.   isProfile       : true,
  869.   functionName    : null,
  870.   extensionID     : null
  871. };
  872.  
  873. ///////////////////////////////////////////////////////////////////////////////
  874. //
  875. // nsExtensionInstaller
  876. //
  877. function nsExtensionInstaller (aExtensionDS)
  878. {
  879.   this._extensionDS = aExtensionDS;
  880.  
  881.   this._provTypePackage = gRDF.GetResource(EM_NS("package"));
  882.   this._provTypeSkin    = gRDF.GetResource(EM_NS("skin"));
  883.   this._provTypeLocale  = gRDF.GetResource(EM_NS("locale"));
  884.   this._fileProperty    = gRDF.GetResource(EM_NS("file"));
  885.   this._sourceResource  = gRDF.GetResource("urn:mozilla:install-manifest");
  886. }
  887.  
  888. nsExtensionInstaller.prototype = {
  889.   // Utility services and helpers
  890.   _rdf              : null,
  891.   _writer           : null,
  892.  
  893.   // Extension metadata
  894.   _extensionID      : null,
  895.   _isProfile        : true,
  896.   _extDirKey        : KEY_PROFILEDIR,
  897.   
  898.   // Source and target datasources
  899.   _metadataDS       : null,
  900.   _extensionDS      : null,
  901.   
  902.   // RDF objects and properties
  903.   _provTypePackage  : null,
  904.   _provTypeSkin     : null,
  905.   _provTypeLocale   : null,
  906.   _sourceResource   : null,
  907.   _fileProperty     : null,
  908.   
  909.   install: function nsExtensionInstaller_install (aExtensionID, aIsProfile)
  910.   {
  911.     // Initialize the installer for this extension
  912.     this._extensionID = aExtensionID;
  913.     this._isProfile = aIsProfile;
  914.     this._extDirKey = getDirKey(this._isProfile);
  915.  
  916.     // Create a logger to log install operations for uninstall
  917.     this._writer = new nsInstallLogWriter(this._extensionID, this._isProfile);
  918.     this._writer.open();
  919.     
  920.     // Move files from the staging dir into the extension's final home.
  921.     // This function generates uninstall log files and creates backups of
  922.     // existing files. 
  923.     // XXXben - would like to add exception handling here to test for file
  924.     //          I/O failures on uninstall log so that if there's a crash
  925.     //          and the uninstall log is incorrectly/incompletely written 
  926.     //          we can roll back. It's not critical that we do so right now
  927.     //          since if this throws the extension's chrome is never 
  928.     //          registered. 
  929.     this._installExtensionFiles();
  930.     
  931.     // Load the metadata datasource
  932.     var metadataFile = getFile(this._extDirKey, 
  933.                                [DIR_EXTENSIONS, aExtensionID, FILE_INSTALL_MANIFEST]);
  934.     
  935.     this._metadataDS = getInstallManifest(metadataFile);
  936.     if (!this._metadataDS) return;
  937.     
  938.     // Add metadata for the extension to the global extension metadata set
  939.     this._extensionDS.addItemMetadata(this._extensionID, nsIUpdateItem.TYPE_EXTENSION, 
  940.                                       this._metadataDS, this._isProfile);
  941.     
  942.     // Register chrome packages for files specified in the extension manifest
  943.     try {
  944.       this._registerChromeForExtension();
  945.     }
  946.     catch (e) {
  947.       // Failed to register chrome, for any number of reasons - non-existent 
  948.       // contents.rdf file at the location specified, malformed contents.rdf, 
  949.       // etc. Set the "toBeUninstalled" flag so that the extension is uninstalled
  950.       // properly during the subsequent uninstall pass in 
  951.       // |nsExtensionManager::_finalizeOperations|
  952.  
  953.       this._extensionDS.setItemProperty(this._extensionID, 
  954.                                         this._extensionDS._emR("toBeUninstalled"),
  955.                                         this._extensionDS._emL("true"), this._isProfile,
  956.                                         nsIUpdateItem.TYPE_EXTENSION);
  957.       e.extensionID = this._extensionID;
  958.       showMalformedRegistrationError(e);
  959.     }
  960.     
  961.     this._writer.close();
  962.  
  963.     // Unset the "toBeInstalled" flag
  964.     this._extensionDS.setItemProperty(this._extensionID, 
  965.                                       this._extensionDS._emR("toBeInstalled"),
  966.                                       null, this._isProfile,
  967.                                       nsIUpdateItem.TYPE_EXTENSION);
  968.   },
  969.   
  970.   _installExtensionFiles: function nsExtensionInstaller__installExtensionFiles ()
  971.   {
  972.     var sourceXPI = getFile(this._extDirKey, 
  973.                             [DIR_EXTENSIONS, DIR_TEMP, 
  974.                              this._extensionID, 
  975.                              this._extensionID + ".xpi"]);
  976.     var zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
  977.                               .createInstance(Components.interfaces.nsIZipReader);
  978.     zipReader.init(sourceXPI);
  979.     zipReader.open();
  980.  
  981.     var entries = zipReader.findEntries("*");
  982.     while (entries.hasMoreElements()) {
  983.       var entry = entries.getNext().QueryInterface(Components.interfaces.nsIZipEntry);
  984.       
  985.       var parts = entry.name.split("/");
  986.       var subDirs = [DIR_EXTENSIONS, this._extensionID];
  987.       for (var i = 0; i < parts.length; ++i)
  988.         subDirs.push(parts[i]);
  989.       
  990.       var fileName = parts[parts.length-1];
  991.       if (fileName != "") {
  992.         var targetFile = getFile(this._extDirKey, subDirs);
  993.         zipReader.extract(entry.name, targetFile);
  994.         this._writer.addFile(targetFile.QueryInterface(Components.interfaces.nsILocalFile));
  995.       }
  996.     }
  997.     zipReader.close();
  998.     // Kick off the extraction on a new thread, then join to wait for it to
  999.     // complete. 
  1000.     // (new nsJarFileExtractor(aZipReader.file, dir)).extract();
  1001.     
  1002.     this._cleanUpStagedXPI();
  1003.   },
  1004.   
  1005.   _cleanUpStagedXPI: function nsExtensionInstaller__cleanUpStagedXPI ()
  1006.   {
  1007.     var stageDir = getDir(this._extDirKey, 
  1008.                           [DIR_EXTENSIONS, DIR_TEMP, this._extensionID]);
  1009.     var sourceXPI = stageDir.clone();
  1010.     sourceXPI.append(this._extensionID + ".xpi");
  1011.     sourceXPI.remove(false);
  1012.     
  1013.     // Remove the extension's stage dir
  1014.     if (!stageDir.directoryEntries.hasMoreElements()) 
  1015.       stageDir.remove(false);
  1016.       
  1017.     // If the parent "temp" dir is empty, remove it.
  1018.     try { // XXXben
  1019.       if (!stageDir.parent.directoryEntries.hasMoreElements())
  1020.         stageDir.parent.remove(false);
  1021.     }
  1022.     catch (e) { }
  1023.   },
  1024.   
  1025.   _registerChromeForExtension: function nsExtensionInstaller__registerChromeForExtension ()
  1026.   {
  1027.     // Enumerate the metadata datasource files collection and register chrome
  1028.     // for each file, calling _registerChrome for each.
  1029.     var chromeDir = getDir(this._extDirKey, 
  1030.                            [DIR_EXTENSIONS, this._extensionID, DIR_CHROME]);
  1031.     
  1032.     var files = this._metadataDS.GetTargets(this._sourceResource, this._fileProperty, true);
  1033.     while (files.hasMoreElements()) {
  1034.       var file = files.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1035.       var chromeFile = chromeDir.clone();
  1036.       var fileName = file.Value.substr("urn:mozilla:extension:file:".length, file.Value.length);
  1037.       chromeFile.append(fileName);
  1038.       
  1039.       var providers = [this._provTypePackage, this._provTypeSkin, this._provTypeLocale];
  1040.       for (var i = 0; i < providers.length; ++i) {
  1041.         var items = this._metadataDS.GetTargets(file, providers[i], true);
  1042.         while (items.hasMoreElements()) {
  1043.           var item = items.getNext().QueryInterface(Components.interfaces.nsIRDFLiteral);
  1044.           this._registerChrome(chromeFile, providers[i], item.Value);
  1045.         }
  1046.       }
  1047.     }
  1048.   },
  1049.   
  1050.   _registerChrome: function nsExtensionInstaller__registerChrome (aFile, aChromeType, aPath)
  1051.   { 
  1052.     var fileURL = getURLSpecFromFile(aFile);
  1053.     if (!aFile.isDirectory()) // .jar files
  1054.       fileURL = "jar:" + fileURL + "!/" + aPath;
  1055.     else                      // flat chrome hierarchies
  1056.       fileURL = fileURL + aPath;
  1057.     
  1058.     var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  1059.                        .getService(Components.interfaces.nsIXULChromeRegistry);
  1060.     var type;
  1061.     if (aChromeType.EqualsNode(this._provTypePackage)) {
  1062.       try {
  1063.         cr.installPackage(fileURL, this._isProfile);
  1064.       }
  1065.       catch (e) {
  1066.         throw new nsChromeRegistrationException(fileURL, this._isProfile, "installPackage");
  1067.       }
  1068.       type = this._writer.CHROME_TYPE_PACKAGE;
  1069.     }
  1070.     else if (aChromeType.EqualsNode(this._provTypeSkin)) {
  1071.       try {
  1072.         cr.installSkin(fileURL, this._isProfile, true); // Extension skins can execute scripts
  1073.       }
  1074.       catch (e) {
  1075.         throw new nsChromeRegistrationException(fileURL, this._isProfile, "installSkin");
  1076.       }
  1077.       type = this._writer.CHROME_TYPE_SKIN;
  1078.     }
  1079.     else if (aChromeType.EqualsNode(this._provTypeLocale)) {
  1080.       try {
  1081.         cr.installLocale(fileURL, this._isProfile);
  1082.       }
  1083.       catch (e) {
  1084.         throw new nsChromeRegistrationException(fileURL, this._isProfile, "installLocale");
  1085.       }
  1086.       type = this._writer.CHROME_TYPE_LOCALE;
  1087.     }
  1088.     var providerNames = this._getProviderNames(fileURL, type);
  1089.     for (var i = 0; i < providerNames.length; ++i) {
  1090.       this._writer.registerChrome(providerNames[i], type, this._isProfile);
  1091.       
  1092.       // Make sure we enable overlays for this extension so that if it is disabled by
  1093.       // mismatch checking, installing a newer version (as opposed to enabling as a 
  1094.       // result of a version compatibility update) makes the extension's overlaid UI 
  1095.       // appear immediately.
  1096.       cr.setAllowOverlaysForPackage(providerNames[i], true);
  1097.     }
  1098.   },
  1099.   
  1100.   _getProviderNames: function nsExtensionInstaller__getProviderNames (aBaseURL, aType)
  1101.   {
  1102.     if (aBaseURL.charAt(aBaseURL.length-1) != "/")
  1103.       aBaseURL += "/";
  1104.     var manifestURL = aBaseURL + "contents.rdf";
  1105.     
  1106.     var providerNames = [];
  1107.  
  1108.     try {
  1109.       // Discover the list of provider names to register for the location 
  1110.       // specified in the provider arc.
  1111.       //
  1112.       // The contents.rdf file will look like this:
  1113.       //
  1114.       //   <RDF:Seq about="urn:mozilla:<type>:root">
  1115.       //     <RDF:li resource="urn:mozilla:<type>:itemName1"/>
  1116.       //     <RDF:li resource="urn:mozilla:<type>:itemName2"/>
  1117.       //     ..
  1118.       //   </RDF:Seq>
  1119.       //
  1120.       // We need to explicitly walk this list here, we don't need to do so
  1121.       // for nsIXULChromeRegistry's |installPackage| method since that does
  1122.       // this same thing itself.
  1123.       
  1124.       var ds = gRDF.GetDataSourceBlocking(manifestURL);
  1125.       
  1126.       var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  1127.                           .createInstance(Components.interfaces.nsIRDFContainer);
  1128.       ctr.Init(ds, gRDF.GetResource("urn:mozilla:" + aType + ":root"));
  1129.       
  1130.       var items = ctr.GetElements();
  1131.       while (items.hasMoreElements()) {
  1132.         var item = items.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1133.         
  1134.         var nameArc = gRDF.GetResource(CHROME_NS("name"));
  1135.         var name;
  1136.         if (ds.hasArcOut(item, nameArc))
  1137.           name = stringData(ds.GetTarget(item, nameArc, true));
  1138.         else {
  1139.           var parts = item.Value.split(":");
  1140.           name = parts[parts.length-1];
  1141.         }
  1142.         providerNames.push(name);
  1143.       }
  1144.     }
  1145.     catch (e) { }
  1146.     
  1147.     return providerNames;
  1148.   }
  1149. };
  1150.  
  1151. ///////////////////////////////////////////////////////////////////////////////
  1152. //
  1153. // nsExtensionUninstaller
  1154. //
  1155. function nsExtensionUninstaller(aExtensionDS)
  1156. {
  1157.   this._cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  1158.                        .getService(Components.interfaces.nsIXULChromeRegistry);
  1159.   this._extensionDS = aExtensionDS;
  1160. }
  1161.  
  1162. nsExtensionUninstaller.prototype = {
  1163.   _extensionDS  : null,
  1164.   _cr           : null,
  1165.   _isProfile    : true,
  1166.   _extDirKey    : "",
  1167.   _extensionsDir: null,
  1168.   _extensionID  : "",
  1169.  
  1170.   uninstall: function nsExtensionUninstaller_uninstall (aExtensionID, aIsProfile)
  1171.   {
  1172.     // Initialize the installer for this extension
  1173.     this._extensionID = aExtensionID;
  1174.     this._isProfile = aIsProfile;
  1175.     this._extDirKey = getDirKey(this._isProfile);
  1176.     this._extensionsDir = getDir(this._extDirKey, [DIR_EXTENSIONS]);
  1177.  
  1178.     // Create a logger to log install operations for uninstall
  1179.     this._reader = new nsInstallLogReader(this._extensionID, 
  1180.                                           this._isProfile, 
  1181.                                           this);
  1182.     try { // XXXben don't let errors stop us. 
  1183.       this._reader.read();
  1184.       
  1185.       // Now remove the uninstall log file. 
  1186.       this._removeFile(this._reader.uninstallLog);
  1187.     }
  1188.     catch (e) {
  1189.       dump("******* Failed to remove extension uninstall log, with exception = " + e + "\n");
  1190.     }
  1191.     
  1192.     // Unset the "toBeUninstalled" flag
  1193.     this._extensionDS.setItemProperty(this._extensionID, 
  1194.                                       this._extensionDS._emR("toBeUninstalled"),
  1195.                                       null, this._isProfile,
  1196.                                       nsIUpdateItem.TYPE_EXTENSION);
  1197.   },
  1198.   
  1199.   ///////////////////////////////////////////////////////////////////////////////
  1200.   // nsIInstallLogReaderListener
  1201.   onAddFile: function nsExtensionUninstaller_onAddFile (aFile)
  1202.   {
  1203.     this._removeFile(aFile);
  1204.   },
  1205.   
  1206.   _removeFile: function nsExtensionUninstaller__removeFile (aFile)
  1207.   {
  1208.     if (aFile.exists()) {
  1209.       aFile.remove(false);
  1210.       
  1211.       // Clean up the parent hierarchy if possible  
  1212.       var parent = aFile.parent;
  1213.       var e = parent.directoryEntries;
  1214.       if (!e.hasMoreElements() && 
  1215.           !parent.equals(this._extensionsDir)) // stop at the extensions dir
  1216.         this._removeFile(parent);
  1217.     }
  1218.   },
  1219.   
  1220.   // XXXben - maybe we should find a way to 
  1221.   _packagesForExtension: [],
  1222.   
  1223.   onRegisterChrome: function nsExtensionUninstaller_onRegisterChrome (aProviderName, aFile, aChromeType, aIsProfile)
  1224.   {
  1225.     switch (aChromeType) {
  1226.     case this._reader.CHROME_TYPE_PACKAGE:
  1227.       this._packagesForExtension.push(aProviderName);
  1228.       this._cr.uninstallPackage(aProviderName, aIsProfile)
  1229.       break;
  1230.     case this._reader.CHROME_TYPE_SKIN:
  1231.       for (var i = 0; i < this._packagesForExtension.length; ++i) {
  1232.         this._cr.deselectSkinForPackage(aProviderName, 
  1233.                                         this._packagesForExtension[i], 
  1234.                                         aIsProfile);
  1235.       }
  1236.       // this._cr.uninstallSkin(aProviderName, aIsProfile)
  1237.       break;
  1238.     case this._reader.CHROME_TYPE_LOCALE:
  1239.       for (var i = 0; i < this._packagesForExtension.length; ++i) {
  1240.         this._cr.deselectLocaleForPackage(aProviderName, 
  1241.                                           this._packagesForExtension[i], 
  1242.                                           aIsProfile);
  1243.       }
  1244.       // this._cr.uninstallLocale(aProviderName, aIsProfile)
  1245.       break;
  1246.     }
  1247.   }
  1248. };
  1249.  
  1250. ///////////////////////////////////////////////////////////////////////////////
  1251. //
  1252. // nsExtensionEnabler
  1253. //
  1254. function nsExtensionEnabler(aExtensionDS)
  1255. {
  1256.   this._cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  1257.                        .getService(Components.interfaces.nsIXULChromeRegistry);
  1258.   this._extensionDS = aExtensionDS;
  1259. }
  1260.  
  1261. nsExtensionEnabler.prototype = {
  1262.   _extensionDS  : null,
  1263.   _cr           : null,
  1264.   _enable       : true,
  1265.   _isProfile    : true,
  1266.   _extDirKey    : "",
  1267.   _extensionsDir: null,
  1268.  
  1269.   enable: function nsExtensionEnabler_enable (aExtensionID, aIsProfile, aDisable)
  1270.   {
  1271.     // Initialize the installer for this extension
  1272.     this._enable = !aDisable;
  1273.     this._extensionID = aExtensionID;
  1274.     this._isProfile = aIsProfile;
  1275.     this._extDirKey = getDirKey(this._isProfile);
  1276.     this._extensionsDir = getDir(this._extDirKey, [DIR_EXTENSIONS]);
  1277.  
  1278.     // Create a logger to log install operations for uninstall
  1279.     this._reader = new nsInstallLogReader(this._extensionID, 
  1280.                                           this._isProfile, 
  1281.                                           this);
  1282.     this._reader.read();
  1283.   },
  1284.   
  1285.   onRegisterChrome: function nsExtensionEnabler_onRegisterChrome (aProviderName, aFile, aChromeType, aIsProfile)
  1286.   {
  1287.     if (aChromeType == this._reader.CHROME_TYPE_PACKAGE)
  1288.       this._cr.setAllowOverlaysForPackage(aProviderName, this._enable);
  1289.   },
  1290.   
  1291.   onAddFile: function nsExtensionEnabler_onAddFile (aFile)
  1292.   {
  1293.   }
  1294. };
  1295.  
  1296. ///////////////////////////////////////////////////////////////////////////////
  1297. //
  1298. // nsThemeInstaller
  1299. //
  1300. function nsThemeInstaller(aExtensionDS, aManager)
  1301. {
  1302.   this._extensionDS = aExtensionDS;
  1303.   this._em = aManager;
  1304. }
  1305.  
  1306. nsThemeInstaller.prototype = {
  1307.   _extensionDS  : null,
  1308.   _isProfile    : true,
  1309.   _extDirKey    : "",
  1310.  
  1311.   install: function nsThemeInstaller_install (aJARFile, aIsProfile)
  1312.   {
  1313.     var extDirKey = getDirKey(aIsProfile);
  1314.  
  1315.     // Since we're installing a "new type" theme, we assume a file layout
  1316.     // within the JAR like so:
  1317.     // foo.jar/
  1318.     //         install.rdf      <-- Theme Manager metadata
  1319.     //         contents.rdf   <-- Chrome Registry metadata
  1320.     //         browser/
  1321.     //         global/
  1322.     //         ...
  1323.     var zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
  1324.                               .createInstance(Components.interfaces.nsIZipReader);
  1325.     zipReader.init(aJARFile);
  1326.     zipReader.open();
  1327.     
  1328.     try {
  1329.       zipReader.getEntry(FILE_INSTALL_MANIFEST);
  1330.     }
  1331.     catch (e) {
  1332.       // If the zip reader returned an error code here it means that the install.rdf
  1333.       // file was not found in the theme jar file - i.e. it was an old style theme. 
  1334.       // There's no reason for people to be installing or maintaining such themes 
  1335.       // anymore as there is no practical use for them, so we should throw an error
  1336.       // here and bail. 
  1337.       try {
  1338.         zipReader.getEntry(FILE_CHROME_MANIFEST);
  1339.         
  1340.         // Load the contents.rdf file from the .jar file if present and show a detailed
  1341.         // error.         
  1342.         var tempChromeManifest = getFile(extDirKey,
  1343.                                          [DIR_EXTENSIONS, DIR_TEMP, FILE_CHROME_MANIFEST]);
  1344.         zipReader.extract(FILE_CHROME_MANIFEST, tempChromeManifest);
  1345.         var rdfs = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  1346.                              .getService(Components.interfaces.nsIRDFService);
  1347.         showOldThemeError(rdfs.GetDataSourceBlocking(getURLSpecFromFile(tempChromeManifest)));
  1348.         tempChromeManifest.remove(false);
  1349.       }
  1350.       catch (e) {
  1351.         showMissingFileError(aJARFile, FILE_CHROME_MANIFEST);
  1352.       }
  1353.     }
  1354.     
  1355.     var themeManifest = getFile(extDirKey,
  1356.                                 [DIR_EXTENSIONS, DIR_TEMP, getRandomFileName("install", "rdf")]);
  1357.     zipReader.extract(FILE_INSTALL_MANIFEST, themeManifest);
  1358.     
  1359.     var chromeManifest = getFile(extDirKey,
  1360.                                  [DIR_EXTENSIONS, DIR_TEMP, FILE_CHROME_MANIFEST]);
  1361.     zipReader.extract(FILE_CHROME_MANIFEST, chromeManifest);
  1362.     
  1363.     var themeMetadata = getInstallManifest(themeManifest);
  1364.     if (!themeMetadata) return;
  1365.     var chromeMetadata = gRDF.GetDataSourceBlocking(getURLSpecFromFile(chromeManifest));
  1366.     
  1367.     // We do a basic version check first just to make sure we somehow weren't 
  1368.     // tricked into installing an incompatible theme...
  1369.     this._themeID = this._em.canInstallItem(themeMetadata);
  1370.     if (isNaN(parseInt(this._themeID))) {
  1371.       var canInstall = true;
  1372.  
  1373.       // Copy the file to its final location
  1374.       var destinationDir = getDir(extDirKey, 
  1375.                                   [DIR_EXTENSIONS, this._themeID, DIR_CHROME]);
  1376.       var destinationFile = destinationDir.clone();
  1377.       destinationFile.append(aJARFile.leafName);
  1378.       if (destinationFile.exists())
  1379.         destinationFile.remove(false);
  1380.       aJARFile.copyTo(destinationDir, aJARFile.leafName);
  1381.  
  1382.       var nameArc = gRDF.GetResource(CHROME_NS("name"));
  1383.       var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  1384.                           .createInstance(Components.interfaces.nsIRDFContainer);
  1385.       ctr.Init(chromeMetadata, gRDF.GetResource("urn:mozilla:skin:root"));
  1386.       
  1387.       var elts = ctr.GetElements();
  1388.       while (elts.hasMoreElements()) {
  1389.         var elt = elts.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1390.         var chromeSkinPrefix = "urn:mozilla:skin:";
  1391.         if (elt.Value.substr(0, chromeSkinPrefix.length) == chromeSkinPrefix) {
  1392.           var name = chromeMetadata.GetTarget(elt, nameArc, true);
  1393.           
  1394.           // Check to see if the em:internalName property on the theme install
  1395.           // manifest matches the chrome:name property on the theme's CR entry. 
  1396.           var manifestRoot = gRDF.GetResource("urn:mozilla:install-manifest");
  1397.           var internalName = themeMetadata.GetTarget(manifestRoot, 
  1398.                                                      gRDF.GetResource(EM_NS("internalName")),
  1399.                                                      true);
  1400.           if (!internalName.EqualsNode(name)) {
  1401.             var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
  1402.                                            .getService(Components.interfaces.nsIConsoleService);
  1403.             consoleService.logStringMessage("Could not install theme because chrome:name arc in " +
  1404.                                             "the theme's contents.rdf file (" + stringData(name) +
  1405.                                             ") does not match the em:internalName arc in the theme's " + 
  1406.                                             "install.rdf file (" + stringData(internalName) + ")");
  1407.             var fileURL = getURLSpecFromFile(themeManifest);
  1408.             var uri = Components.classes["@mozilla.org/network/standard-url;1"]
  1409.                                 .createInstance(Components.interfaces.nsIURI);
  1410.             uri.spec = fileURL;
  1411.             var url = uri.QueryInterface(Components.interfaces.nsIURL);
  1412.             showMalformedError(url.fileName);
  1413.             
  1414.             destinationFile.remove(false);
  1415.             destinationDir.remove(true);
  1416.             this._em._cleanDirs();
  1417.             
  1418.             canInstall = false;
  1419.           }
  1420.  
  1421.           if (canInstall) {
  1422.             name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1423.  
  1424.             // Create a logger to log install operations for uninstall
  1425.             this._writer = new nsInstallLogWriter(this._themeID, aIsProfile);
  1426.             this._writer.open();
  1427.             this._writer.installSkin(name, aIsProfile);
  1428.           }
  1429.         }
  1430.       }
  1431.  
  1432.       if (canInstall) {
  1433.         this._writer.addFile(destinationFile.QueryInterface(Components.interfaces.nsILocalFile));
  1434.         this._writer.close();
  1435.  
  1436.         // Use the Chrome Registry API to install the theme there
  1437.         var filePath = "jar:" + getURLSpecFromFile(destinationFile) + "!/";      
  1438.         var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  1439.                           .getService(Components.interfaces.nsIXULChromeRegistry);
  1440.         cr.installSkin(filePath, aIsProfile, false);
  1441.  
  1442.         // Insert the theme into the theme list. 
  1443.         this._extensionDS.insertForthcomingItem(this._themeID, nsIUpdateItem.TYPE_THEME, 
  1444.                                                 aIsProfile);
  1445.  
  1446.         // Add metadata for the extension to the global extension metadata set
  1447.         this._extensionDS.addItemMetadata(this._themeID, nsIUpdateItem.TYPE_THEME,
  1448.                                           themeMetadata, aIsProfile);
  1449.       }
  1450.       
  1451.       this._extensionDS.doneInstallingTheme(this._themeID);
  1452.     }
  1453.     else if (this._themeID == 0)
  1454.       showIncompatibleError(themeMetadata);
  1455.     
  1456.     zipReader.close();
  1457.     themeManifest.remove(false);
  1458.     chromeManifest.remove(false);
  1459.   }
  1460. };
  1461.  
  1462. ///////////////////////////////////////////////////////////////////////////////
  1463. //
  1464. // nsThemeUninstaller
  1465. //
  1466. function nsThemeUninstaller(aExtensionDS)
  1467. {
  1468.   this._cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  1469.                        .getService(Components.interfaces.nsIXULChromeRegistry);
  1470. }
  1471.  
  1472. nsThemeUninstaller.prototype = {
  1473.   _extensionsDir : null,
  1474.   
  1475.   uninstall: function nsThemeUninstaller_uninstall (aThemeID, aIsProfile)
  1476.   {
  1477.     this._extensionsDir = getDir(getDirKey(aIsProfile), [DIR_EXTENSIONS]);
  1478.  
  1479.     // Create a logger to log install operations for uninstall
  1480.     this._reader = new nsInstallLogReader(aThemeID, aIsProfile, this);
  1481.     try { // XXXben don't let errors stop us. 
  1482.       this._reader.read();
  1483.       
  1484.       // Now remove the uninstall log file. 
  1485.       this._removeFile(this._reader.uninstallLog);
  1486.     }
  1487.     catch (e) {
  1488.       dump("******* Failed to remove theme uninstall log, with exception = " + e + "\n");
  1489.     }
  1490.   },
  1491.   
  1492.   ///////////////////////////////////////////////////////////////////////////////
  1493.   // nsIInstallLogReaderListener
  1494.   onAddFile: function nsThemeUninstaller_onAddFile (aFile)
  1495.   {
  1496.     this._removeFile(aFile);
  1497.   },
  1498.   
  1499.   _removeFile: function nsThemeUninstaller__removeFile (aFile)
  1500.   {
  1501.     if (aFile.exists()) {
  1502.       aFile.remove(false);
  1503.       
  1504.       // Clean up the parent hierarchy if possible  
  1505.       var parent = aFile.parent;
  1506.       var e = parent.directoryEntries;
  1507.       if (!e.hasMoreElements() && 
  1508.           !parent.equals(this._extensionsDir)) // stop at the extensions dir
  1509.         this._removeFile(parent);
  1510.     }
  1511.   },
  1512.   
  1513.   onInstallSkin: function nsThemeUninstaller_onInstallSkin (aSkinName, aIsProfile)
  1514.   {
  1515.     this._cr.uninstallSkin(aSkinName, aIsProfile);
  1516.   }
  1517. };
  1518.  
  1519. ///////////////////////////////////////////////////////////////////////////////
  1520. //
  1521. // nsExtensionManager
  1522. //
  1523. function nsExtensionManager()
  1524. {
  1525.   gPref = Components.classes["@mozilla.org/preferences-service;1"]
  1526.                     .getService(Components.interfaces.nsIPrefBranch);
  1527.   gRDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  1528.                    .getService(Components.interfaces.nsIRDFService);
  1529.   gOS = Components.classes["@mozilla.org/observer-service;1"]
  1530.                   .getService(Components.interfaces.nsIObserverService);
  1531.  
  1532.   gOS.addObserver(this, "xpcom-shutdown", false);
  1533.  
  1534.   ensureExtensionsFiles(false);
  1535. }
  1536.  
  1537. nsExtensionManager.prototype = {
  1538.   _extInstaller     : null,
  1539.   _extUninstaller   : null,
  1540.   _extEnabler       : null,
  1541.   _started          : false,
  1542.   
  1543.   /////////////////////////////////////////////////////////////////////////////
  1544.   // nsIObserver
  1545.   observe: function nsExtensionManager_observe (aSubject, aTopic, aData)
  1546.   {
  1547.     switch (aTopic) {
  1548.     case "quit-application-requested":
  1549.       if (this._downloadCount > 0) {
  1550.         var result;
  1551.         result = this._confirmCancelDownloads(this._downloadCount, 
  1552.                                               "quitCancelDownloadsAlertTitle",
  1553.                                               "quitCancelDownloadsAlertMsgMultiple",
  1554.                                               "quitCancelDownloadsAlertMsg",
  1555.                                               "dontQuitButtonWin");
  1556.         if (!result)
  1557.           this._cancelDownloads();
  1558.         var PRBool = aSubject.QueryInterface(Components.interfaces.nsISupportsPRBool);
  1559.         PRBool.data = result;
  1560.       }
  1561.       break;
  1562.     case "offline-requested":
  1563.       if (this._downloadCount > 0) {
  1564.         result = this._confirmCancelDownloads(this._downloadCount,
  1565.                                               "offlineCancelDownloadsAlertTitle",
  1566.                                               "offlineCancelDownloadsAlertMsgMultiple",
  1567.                                               "offlineCancelDownloadsAlertMsg",
  1568.                                               "dontGoOfflineButton");
  1569.         if (!result)
  1570.           this._cancelDownloads();
  1571.         var PRBool = aSubject.QueryInterface(Components.interfaces.nsISupportsPRBool);
  1572.         PRBool.data = result;
  1573.       }
  1574.       break;  
  1575.     case "xpcom-shutdown":
  1576.       gOS.removeObserver(this, "xpcom-shutdown");    
  1577.  
  1578.       // Release strongly held services.
  1579.       gPref           = null;
  1580.       gRDF            = null;
  1581.       gOS             = null;
  1582.       gVersionChecker = null;
  1583.       break;
  1584.     }
  1585.   },
  1586.   
  1587.   start: function nsExtensionManager_start (aIsDirty)
  1588.   {
  1589.     this._started = true;
  1590.     
  1591.     var needsRestart = false;
  1592.   
  1593.     ensureExtensionsFiles(true);
  1594.     
  1595.     // Somehow the component list went away, and for that reason the new one
  1596.     // generated by this function is going to result in a different compreg.
  1597.     // We must force a restart.
  1598.     var componentList = getFile(KEY_PROFILEDIR, [FILE_COMPONENT_MANIFEST]);
  1599.     if (!componentList.exists())
  1600.       needsRestart = true;
  1601.     
  1602.     // XXXben - a bit of a hack - clean up any empty dirs that may not have been
  1603.     //          properly removed by [un]install... I should really investigate those
  1604.     //          cases to see what is stopping these dirs from being removed, but no
  1605.     //          time now.
  1606.     this._cleanDirs();
  1607.   
  1608.     var cmdLineSvc = Components.classes["@mozilla.org/appshell/commandLineService;1"]
  1609.                                 .getService(Components.interfaces.nsICmdLineService);
  1610.     var safeMode = cmdLineSvc.getCmdLineValue("-safe-mode") != null;
  1611.     if (!safeMode) {
  1612.       var wasInSafeModeFile = getFile(KEY_PROFILEDIR, [DIR_EXTENSIONS, FILE_WASINSAFEMODE]);
  1613.       if (wasInSafeModeFile.exists()) {
  1614.         // Clean up after we were in safe mode
  1615.         var win = this._showProgressWindow();
  1616.         try {
  1617.           this._ensureDS();
  1618.           
  1619.           // Retrieve the skin that was selected prior to entering safe mode
  1620.           // and select it. 
  1621.           var lastSelectedSkin = KEY_DEFAULT_THEME;
  1622.           try {
  1623.             lastSelectedSkin = gPref.getCharPref(PREF_EM_LAST_SELECTED_SKIN);
  1624.             gPref.clearUserPref(PREF_EM_LAST_SELECTED_SKIN);
  1625.             gPref.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, lastSelectedSkin);
  1626.           } 
  1627.           catch (e) { }
  1628.           
  1629.           // Walk the list of extensions and re-activate overlays for packages 
  1630.           // that aren't disabled.
  1631.           var items = this._ds.getItemsWithFlagUnset("disabled", nsIUpdateItem.TYPE_EXTENSION);
  1632.           for (var i = 0; i < items.length; ++i)
  1633.             this._finalizeEnableDisable(items[i], false);
  1634.             
  1635.           wasInSafeModeFile.remove(false);
  1636.           
  1637.           this._writeDefaults(true);
  1638.           try {
  1639.             this._writeDefaults(false);
  1640.           }
  1641.           catch (e) { }
  1642.         }
  1643.         catch (e) {
  1644.           dump("*** nsExtensionManager::start - failure, catching exception so finalize window can close = " + e + "\n");
  1645.         }
  1646.         win.close();
  1647.         
  1648.         needsRestart = true;
  1649.       }
  1650.  
  1651.       if (aIsDirty) 
  1652.         needsRestart = this._finishOperations();
  1653.     }
  1654.     else {
  1655.       var win = this._showProgressWindow();
  1656.       try {    
  1657.         // Enter safe mode
  1658.         this._ensureDS();
  1659.  
  1660.         // Save the current theme (assumed to be the theme that styles the global
  1661.         // package) and re-select the default theme ("classic/1.0")
  1662.         if (!gPref.prefHasUserValue(PREF_EM_LAST_SELECTED_SKIN)) {
  1663.           gPref.setCharPref(PREF_EM_LAST_SELECTED_SKIN,
  1664.                             gPref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN));
  1665.           if (gPref.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN))
  1666.             gPref.clearUserPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
  1667.         }
  1668.  
  1669.         var items = this._ds.getItemList(null, nsIUpdateItem.TYPE_EXTENSION, {});
  1670.         for (var i = 0; i < items.length; ++i)
  1671.           this._finalizeEnableDisable(items[i].id, true);
  1672.           
  1673.         this._ds.safeMode = true;
  1674.         
  1675.         this._writeDefaults(true);
  1676.         try {
  1677.           this._writeDefaults(false);
  1678.         }
  1679.         catch (e) { }
  1680.  
  1681.         needsRestart = true;
  1682.  
  1683.         var wasInSafeModeFile = getFile(KEY_PROFILEDIR, [DIR_EXTENSIONS, FILE_WASINSAFEMODE]);
  1684.         if (!wasInSafeModeFile.exists())
  1685.           wasInSafeModeFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
  1686.         else {
  1687.           // If the "Safe Mode" file already exists, then we are in the second launch of an
  1688.           // app launched with -safe-mode and so we don't want to provoke any further
  1689.           // restarts or re-create the file, just continue starting normally.
  1690.           needsRestart = false;
  1691.         }
  1692.       }
  1693.       catch (e) {
  1694.         dump("*** nsExtensionManager::start - (safe mode) failure, catching exception so finalize window can close = " + e + "\n");
  1695.       }
  1696.       win.close();
  1697.       
  1698.     }
  1699.     return needsRestart;
  1700.   },
  1701.   
  1702.   handleCommandLineArgs: function nsExtensionManager_handleCommandLineArgs ()
  1703.   {
  1704.     var cmdLineSvc = Components.classes["@mozilla.org/appshell/commandLineService;1"]
  1705.                               .getService(Components.interfaces.nsICmdLineService);
  1706.     var globalExtension = cmdLineSvc.getCmdLineValue("-install-global-extension");
  1707.     if (globalExtension)
  1708.       this._checkForGlobalInstalls(globalExtension, nsIUpdateItem.TYPE_EXTENSION);
  1709.       
  1710.     var globalTheme = cmdLineSvc.getCmdLineValue("-install-global-theme");
  1711.     if (globalTheme)
  1712.       this._checkForGlobalInstalls(globalTheme, nsIUpdateItem.TYPE_THEME);
  1713.     
  1714.     var showList = cmdLineSvc.getCmdLineValue("-list-global-items");
  1715.     if (showList)
  1716.       this._showGlobalItemList();
  1717.       
  1718.     var locked = cmdLineSvc.getCmdLineValue("-lock-item");
  1719.     if (locked) {
  1720.       this._ensureDS();
  1721.       this._ds.lockUnlockItem(locked, true);
  1722.     }
  1723.  
  1724.     var unlocked = cmdLineSvc.getCmdLineValue("-unlock-item");
  1725.     if (unlocked) {
  1726.       this._ensureDS();
  1727.       this._ds.lockUnlockItem(unlocked, false);
  1728.     }
  1729.     
  1730.     this._finishOperations();
  1731.   },
  1732.   
  1733.   register: function nsExtensionManager_register ()
  1734.   {
  1735.     if (!this._started) {
  1736.       // Loads the datasource and installs any pre-configured items.
  1737.       this._ds = new nsExtensionsDataSource();
  1738.       this._ds.loadExtensions(false);
  1739.       
  1740.       // Write bin/extensions/Extensions.rdf
  1741.       //       bin/extensions/installed-extensions-processed.txt
  1742.       (new nsInstalledExtensionReader(this)).read();
  1743.  
  1744.       // Write bin/components.ini
  1745.       var manifest = getFile(KEY_APPDIR, [FILE_COMPONENT_MANIFEST]);
  1746.       this._writeProfileFile(manifest, this._getComponentsDir, false);
  1747.     }
  1748.   },
  1749.  
  1750.   _cancelDownloads: function nsExtensionManager__cancelDownloads ()
  1751.   {
  1752.     for (var i = 0; i < this._transactions.length; ++i)
  1753.       gOS.notifyObservers(this._transactions[i], "xpinstall-progress", "cancel");
  1754.     gOS.removeObserver(this, "offline-requested");
  1755.     gOS.removeObserver(this, "quit-application-requested");
  1756.  
  1757.     this._removeAllDownloads();
  1758.   },
  1759.  
  1760.   _confirmCancelDownloads: function nsExtensionManager__confirmCancelDownloads(aCount, 
  1761.     aTitle, aCancelMessageMultiple, aCancelMessageSingle, aDontCancelButton)
  1762.   {
  1763.     var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  1764.                         .getService(Components.interfaces.nsIStringBundleService);
  1765.     var bundle = sbs.createBundle("chrome://mozapps/locale/downloads/downloads.properties");
  1766.     var title = bundle.GetStringFromName(aTitle);
  1767.     var message, quitButton;
  1768.     if (aCount > 1) {
  1769.       message = bundle.formatStringFromName(aCancelMessageMultiple, [aCount], 1);
  1770.       quitButton = bundle.formatStringFromName("cancelDownloadsOKTextMultiple", [aCount], 1);
  1771.     }
  1772.     else {
  1773.       message = bundle.GetStringFromName(aCancelMessageSingle);
  1774.       quitButton = bundle.GetStringFromName("cancelDownloadsOKText");
  1775.     }
  1776.     var dontQuitButton = bundle.GetStringFromName(aDontCancelButton);
  1777.     
  1778.     var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  1779.                        .getService(Components.interfaces.nsIWindowMediator);
  1780.     var win = wm.getMostRecentWindow("Extension:Manager");
  1781.     const nsIPromptService = Components.interfaces.nsIPromptService;
  1782.     var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  1783.                        .getService(nsIPromptService);
  1784.     var flags = (nsIPromptService.BUTTON_TITLE_IS_STRING * nsIPromptService.BUTTON_POS_0) +
  1785.                 (nsIPromptService.BUTTON_TITLE_IS_STRING * nsIPromptService.BUTTON_POS_1);
  1786.     var rv = ps.confirmEx(win, title, message, flags, quitButton, dontQuitButton, null, null, { });
  1787.     return rv == 1;
  1788.   },
  1789.   
  1790.   // This function checks for and disables any "old-style" extensions 
  1791.   // from Firefox 0.8 and earlier created using the "chrome:extension=true" flag. 
  1792.   _disableObsoleteExtensions: function nsExtensionManager__disableObsoleteExtensions ()
  1793.   {
  1794.     if (!gPref.prefHasUserValue(PREF_EM_DISABLEDOBSOLETE) || !gPref.getBoolPref(PREF_EM_DISABLEDOBSOLETE)) {
  1795.       var win = this._showProgressWindow();
  1796.       try {
  1797.         var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  1798.                           .getService(Components.interfaces.nsIXULChromeRegistry);
  1799.         var crDS = gRDF.GetDataSource("rdf:chrome");
  1800.         var disabled = false;
  1801.         var sources = crDS.GetSources(gRDF.GetResource(CHROME_NS("extension")), gRDF.GetLiteral("true"), true);
  1802.         while (sources.hasMoreElements()) {
  1803.           disabled = true;
  1804.           
  1805.           var source = sources.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1806.           var name = crDS.GetTarget(source, gRDF.GetResource(CHROME_NS("name")), true);
  1807.           if (name) {
  1808.             name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1809.             cr.setAllowOverlaysForPackage(name, false);
  1810.           }
  1811.         }
  1812.  
  1813.         // Re-select the default theme to prevent any incompatibilities with old-style
  1814.         // themes.
  1815.         cr.selectSkin(KEY_DEFAULT_THEME, true);
  1816.       }
  1817.       catch (e) {
  1818.         dump("*** nsExtensionManager::_disableObsoleteExtensions - failure, catching exception so finalize window can close\n");
  1819.       }
  1820.       win.close();
  1821.       
  1822.       if (disabled) {
  1823.         const nsIPromptService = Components.interfaces.nsIPromptService;
  1824.         var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  1825.                            .getService(nsIPromptService);
  1826.         var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  1827.                             .getService(Components.interfaces.nsIStringBundleService);
  1828.         var bundle = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  1829.         var title = bundle.GetStringFromName("disabledObsoleteTitle");
  1830.         var message = bundle.GetStringFromName("disabledObsoleteMessage");
  1831.         ps.alert(null, title, message);
  1832.       }
  1833.             
  1834.       gPref.setBoolPref(PREF_EM_DISABLEDOBSOLETE, true);
  1835.     }
  1836.   },
  1837.   
  1838.   _checkForGlobalInstalls: function nsExtensionManager__checkForGlobalInstalls (aPath, aItemType)
  1839.   {
  1840.  
  1841.     // First see if the path supplied is a file path
  1842.     var file = Components.classes["@mozilla.org/file/local;1"]
  1843.                          .createInstance(Components.interfaces.nsILocalFile);
  1844.     try {
  1845.       file.initWithPath(aPath);
  1846.     }
  1847.     catch (e) {
  1848.       // Try appending the path to the current proc dir. 
  1849.       file = getDir(KEY_APPDIR, []);
  1850.       try {
  1851.         file.append(aPath);
  1852.       }
  1853.       catch (e) { /* can't handle this */ }
  1854.     }
  1855.     
  1856.     if (file.exists()) {
  1857.       if (aItemType & nsIUpdateItem.TYPE_EXTENSION)
  1858.         this.installExtension(file, nsIExtensionManager.FLAG_INSTALL_GLOBAL);
  1859.       else if (aItemType & nsIUpdateItem.TYPE_THEME)
  1860.         this.installTheme(file, nsIExtensionManager.FLAG_INSTALL_GLOBAL);
  1861.     }
  1862.     else
  1863.       dump("Invalid XPI/JAR Path: " + aPath + "\n");
  1864.   },
  1865.   
  1866.   _showGlobalItemList: function nsExtensionManager__showGlobalItemList ()
  1867.   {
  1868.     this._ensureDS();
  1869.     
  1870.     var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  1871.                         .getService(Components.interfaces.nsIStringBundleService);
  1872.     var bundle = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  1873.  
  1874.     dump(bundle.GetStringFromName("globalItemList"));
  1875.     dump(bundle.GetStringFromName("globalItemListExtensions"));
  1876.     var items = this.getItemList(null, nsIUpdateItem.TYPE_EXTENSION, {});
  1877.     for (var i = 0; i < items.length; ++i)
  1878.       dump(" " + items[i].id + "   " + items[i].name + " " + items[i].version + "\n");
  1879.     dump(bundle.GetStringFromName("globalItemListThemes"));
  1880.     items = this.getItemList(null, nsIUpdateItem.TYPE_THEME, {});
  1881.     for (var i = 0; i < items.length; ++i)
  1882.       dump(" " + items[i].id + "   " + items[i].name + " " + items[i].version + "\n");
  1883.       
  1884.     dump("\n\n");
  1885.   },
  1886.   
  1887.   _finishOperations: function nsExtensionManager__finishOperations ()
  1888.   {
  1889.     var win = this._showProgressWindow();
  1890.   
  1891.     try {
  1892.       // An existing autoreg file is an indication that something major has 
  1893.       // happened to the extensions datasource (install/uninstall/enable/disable)
  1894.       // and as such we must load it now and see what needs to happen.
  1895.       this._ensureDS();
  1896.       
  1897.       // Look for items that need to be installed
  1898.       var items = this._ds.getItemsWithFlagSet("toBeInstalled");
  1899.       for (var i = 0; i < items.length; ++i)
  1900.         this._finalizeInstall(items[i]);
  1901.  
  1902.       // If there were any install operations, we need to restart (again!) after 
  1903.       // the component files have been properly installed are registered...
  1904.       var needsRestart = items.length > 0;
  1905.       
  1906.       // Look for extensions that need to be enabled
  1907.       items = this._ds.getItemsWithFlagSet("toBeEnabled");
  1908.       for (var i = 0; i < items.length; ++i)
  1909.         this._finalizeEnableDisable(items[i], false);
  1910.       
  1911.       // Look for extensions that need to be disabled
  1912.       items = this._ds.getItemsWithFlagSet("toBeDisabled");
  1913.       for (var i = 0; i < items.length; ++i)
  1914.         this._finalizeEnableDisable(items[i], true);
  1915.       
  1916.       // Look for extensions that need to be removed. This MUST be done after
  1917.       // the install operations since extensions to be installed may have to be
  1918.       // uninstalled if there are errors during the installation process!
  1919.       items = this._ds.getItemsWithFlagSet("toBeUninstalled");
  1920.       for (var i = 0; i < items.length; ++i)
  1921.         this._finalizeUninstall(items[i]);
  1922.         
  1923.       // Clean up any helper objects
  1924.       delete this._extInstaller;
  1925.       delete this._extUninstaller;
  1926.       delete this._extEnabler;
  1927.       
  1928.       this._updateManifests();
  1929.       // If no additional restart is required, it implies that there are
  1930.       // no new components that need registering so we can inform the app
  1931.       // not to do any extra startup checking next time round.    
  1932.       this._writeCompatibilityManifest(needsRestart);
  1933.     }
  1934.     catch (e) {
  1935.       dump("*** nsExtensionManager::_finishOperations - failure, catching exception so finalize window can close " + e +"\n");
  1936.     }
  1937.     win.close();
  1938.     
  1939.     return needsRestart;
  1940.   },
  1941.   
  1942.   // XXXben - this is actually a cheap stunt to load all the chrome registry 
  1943.   //          services required to register/unregister packages... the synchronous
  1944.   //          nature of this code ensures the window will never actually appear
  1945.   //          on screen. 
  1946.   _showProgressWindow: function nsExtensionManager__showProgressWindow ()
  1947.   {
  1948.     var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  1949.                        .getService(Components.interfaces.nsIWindowWatcher);
  1950.     return ww.openWindow(null, "chrome://mozapps/content/extensions/finalize.xul", 
  1951.                          "", "chrome,centerscreen,dialog", null);
  1952.   },
  1953.   
  1954.   _loadDefaults: function nsExtensionManager__loadDefaults ()
  1955.   {
  1956.     // Load default preferences files for all extensions
  1957.     var defaultsManifest = getFile(KEY_PROFILEDIR, 
  1958.                                    [DIR_EXTENSIONS, FILE_DEFAULTS]);
  1959.     if (defaultsManifest.exists()) {
  1960.       var fis = Components.classes["@mozilla.org/network/file-input-stream;1"]
  1961.                           .createInstance(Components.interfaces.nsIFileInputStream);
  1962.       fis.init(defaultsManifest, -1, -1, false);
  1963.       var lis = fis.QueryInterface(Components.interfaces.nsILineInputStream);
  1964.       var line = { value: "" };
  1965.       var more = false;
  1966.       do {
  1967.         more = lis.readLine(line);
  1968.         var lf = Components.classes["@mozilla.org/file/local;1"]
  1969.                             .createInstance(Components.interfaces.nsILocalFile);
  1970.         var path = line.value;
  1971.         if (path) {
  1972.           lf.initWithPath(path);
  1973.           
  1974.           if (lf.exists())
  1975.             gPref.readUserPrefs(lf);
  1976.         }
  1977.       }
  1978.       while (more);
  1979.       fis.close();
  1980.     }
  1981.   },
  1982.   
  1983.   ensurePreConfiguredItem: function nsExtensionManager_ensurePreConfiguredItem (aItemID, aItemType, aManifest)
  1984.   {
  1985.     this._ds.insertForthcomingItem(aItemID, aItemType, false);
  1986.     var metadataDS = getInstallManifest(aManifest);
  1987.     this._ds.addItemMetadata(aItemID, aItemType, metadataDS, false);
  1988.   },
  1989.   
  1990.   checkForMismatches: function nsExtensionManager_checkForMismatches () 
  1991.   {
  1992.     var needsRestart = false;
  1993.         
  1994.     this._disableObsoleteExtensions();
  1995.  
  1996.     // Check to see if the version of the application that is being started
  1997.     // now is the same one that was started last time. 
  1998.     var currAppVersion = gPref.getCharPref(PREF_EM_APP_VERSION);
  1999.     //JA test var currAppVersion = gPref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
  2000.     try {
  2001.       var lastAppVersion = gPref.getCharPref(PREF_EM_LAST_APP_VERSION);
  2002.     }
  2003.     catch (e) {}
  2004.     if (currAppVersion != lastAppVersion) {
  2005.       // Version mismatch, we're have to load the extensions datasource
  2006.       // and do version checking. Time hit here doesn't matter since this 
  2007.       // doesn't happen all that often.
  2008.       this._ensureDS();
  2009.       var currAppID = gPref.getCharPref(PREF_EM_APP_ID);
  2010.       var items = this._ds.getIncompatibleItemList(currAppID, currAppVersion,
  2011.                                                    nsIUpdateItem.TYPE_ADDON);
  2012.       if (items.length > 0) {
  2013.         for (var i = 0; i < items.length; ++i) {
  2014.           // Now disable the extension so it won't hurt anything. 
  2015.           var itemType = getItemType(this._ds._getResourceForItem(items[i].id).Value);
  2016.           if (itemType != -1 && itemType & nsIUpdateItem.TYPE_EXTENSION)
  2017.             this.disableExtension(items[i].id);
  2018.           else if (itemType & nsIUpdateItem.TYPE_THEME) {
  2019.             if (gPref.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN))
  2020.               gPref.clearUserPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
  2021.             this._ds.disableTheme(items[i].id);
  2022.           }
  2023.         }
  2024.         var updates = Components.classes["@mozilla.org/updates/update-service;1"]
  2025.                                 .getService(Components.interfaces.nsIUpdateService);
  2026.         updates.checkForUpdates(items, items.length, nsIUpdateItem.TYPE_ADDON, 
  2027.                                 nsIUpdateService.SOURCE_EVENT_MISMATCH,
  2028.                                 null);
  2029.         
  2030.         needsRestart = true;
  2031.       }
  2032.     }
  2033.     
  2034.     // Somehow the component list went away, and for that reason the new one
  2035.     // generated by this function is going to result in a different compreg.
  2036.     // We must force a restart.
  2037.     var componentList = getFile(KEY_PROFILEDIR, [FILE_COMPONENT_MANIFEST]);
  2038.     if (!componentList.exists())
  2039.       needsRestart = true;
  2040.     
  2041.     // Now update the last app version so we don't do this checking 
  2042.     // again. 
  2043.     gPref.setCharPref(PREF_EM_LAST_APP_VERSION, currAppVersion);
  2044.  
  2045.     // XXXben - I am not entirely sure this is needed, since components and 
  2046.     // defaults manifests are written by the disabling function. Not going to
  2047.     // rock the boat now however. 
  2048.     this._updateManifests();
  2049.     
  2050.     return needsRestart;
  2051.   },
  2052.   
  2053.   get inSafeMode() 
  2054.   {
  2055.     return this._ds.safeMode;
  2056.   },
  2057.   
  2058.   _updateManifests: function nsExtensionManager__updateManifests ()
  2059.   {
  2060.     // Update the components manifests with paths for compatible, enabled, 
  2061.     // extensions.
  2062.     try {
  2063.       // Wrap this in try..catch so that if the account is restricted we don't
  2064.       // completely fail here for lack of permissions to write to the bin
  2065.       // dir (and cause apprunner to go into a restart loop). 
  2066.       //
  2067.       // This means that making changes to install-dir extensions only possible
  2068.       // for people with write access to bin dir (i.e. uninstall, disable, 
  2069.       // enable)
  2070.       this._writeComponentManifest(false);
  2071.       this._writeDefaults(false);
  2072.     }
  2073.     catch (e) { 
  2074.       dump("*** ExtensionManager:_updateManifests: no access privileges to application directory, skipping.\n"); 
  2075.     };
  2076.     this._writeComponentManifest(true);
  2077.     this._writeDefaults(true);
  2078.   },
  2079.   
  2080.   // XXXben write to temporary file then move to final when done.
  2081.   _writeProfileFile: function nsExtensionManager__writeProfileFile (aFile, aGetDirFunc, aIsProfile)
  2082.   {
  2083.     // When an operation is performed that requires a component re-registration
  2084.     // (extension enabled/disabled, installed, uninstalled), we must write the
  2085.     // set of registry-relative paths of components to register to an .autoreg 
  2086.     // file which lives in the profile folder. 
  2087.     //
  2088.     // To do this we must enumerate all installed extensions and write data 
  2089.     // about all valid items to the file. 
  2090.     this._ensureDS();
  2091.     
  2092.     var fos = Components.classes["@mozilla.org/network/file-output-stream;1"]
  2093.                         .createInstance(Components.interfaces.nsIFileOutputStream);
  2094.     const MODE_WRONLY   = 0x02;
  2095.     const MODE_CREATE   = 0x08;
  2096.     const MODE_TRUNCATE = 0x20;
  2097.     fos.init(aFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, 0644, 0);
  2098.  
  2099.     var extensions = this.getItemList(null, nsIUpdateItem.TYPE_EXTENSION, { });
  2100.     var validExtensions = [];
  2101.     for (var i = 0; i < extensions.length; ++i) {
  2102.       var extension = extensions[i];
  2103.     
  2104.       // An extension entry is valid only if it is not disabled, not about to 
  2105.       // be disabled, and not about to be uninstalled.
  2106.       var toBeDisabled = this._ds.getItemProperty(extension.id, "toBeDisabled");
  2107.       var toBeUninstalled = this._ds.getItemProperty(extension.id, "toBeUninstalled");
  2108.       var toBeInstalled = this._ds.getItemProperty(extension.id, "toBeInstalled");
  2109.       var disabled = this._ds.getItemProperty(extension.id, "disabled");
  2110.       if (toBeDisabled == "true" || toBeUninstalled == "true" || 
  2111.           disabled == "true" || toBeInstalled == "true")
  2112.         continue;
  2113.       
  2114.       var isProfile = this._ds.isProfileItem(extension.id);
  2115.       var sourceDir = aGetDirFunc(isProfile, extension.id);
  2116.       if (sourceDir.exists() && (aIsProfile == isProfile))
  2117.         validExtensions.push({ sourceDir: sourceDir, isProfile: isProfile });
  2118.     }
  2119.     
  2120.     var lines = ["[Extra Files]\r\n",
  2121.                  "Count=" + validExtensions.length + "\r\n"];
  2122.     for (i = 0; i < lines.length; ++i)
  2123.       fos.write(lines[i], lines[i].length);
  2124.       
  2125.     for (i = 0; i < validExtensions.length; ++i) {
  2126.       var e = validExtensions[i];
  2127.       var relativeDir = getDir(e.isProfile ? KEY_PROFILEDIR : KEY_APPDIR, []);
  2128.       var lf = e.sourceDir.QueryInterface(Components.interfaces.nsILocalFile);
  2129.       var relDesc = lf.getRelativeDescriptor(relativeDir);
  2130.       var line = "File" + i + "=" + relDesc + "\r\n";
  2131.       fos.write(line, line.length);
  2132.     }
  2133.     fos.close();
  2134.   },
  2135.   
  2136.   _getComponentsDir: function nsExtensionManager__getComponentsDir (aIsProfile, aExtensionID)
  2137.   {
  2138.     return getDirNoCreate(getDirKey(aIsProfile), 
  2139.                           [DIR_EXTENSIONS, aExtensionID, DIR_COMPONENTS]);
  2140.   },
  2141.  
  2142.   _getPreferencesDir: function nsExtensionManager__getPreferencesDir (aIsProfile, aExtensionID)
  2143.   {
  2144.     return getDirNoCreate(getDirKey(aIsProfile), 
  2145.                           [DIR_EXTENSIONS, aExtensionID, 
  2146.                            DIR_DEFAULTS, DIR_DEFAULTS_PREFS]);
  2147.   },
  2148.  
  2149.   _writeComponentManifest: function nsExtensionManager__writeComponentManifest (aIsProfile)
  2150.   {
  2151.     var manifest = aIsProfile ? getFile(KEY_PROFILEDIR, [FILE_COMPONENT_MANIFEST]) : 
  2152.                                 getFile(KEY_APPDIR, [FILE_COMPONENT_MANIFEST]);
  2153.     this._writeProfileFile(manifest, this._getComponentsDir, aIsProfile);
  2154.  
  2155.     // Now refresh the compatibility manifest.
  2156.     this._writeCompatibilityManifest(true);
  2157.   },
  2158.   
  2159.   _writeCompatibilityManifest: function nsExtensionManager__writeCompatibilityManifest (aComponentListUpdated)
  2160.   {
  2161.     var fos = Components.classes["@mozilla.org/network/file-output-stream;1"]
  2162.                         .createInstance(Components.interfaces.nsIFileOutputStream);
  2163.     const MODE_WRONLY   = 0x02;
  2164.     const MODE_CREATE   = 0x08;
  2165.     const MODE_TRUNCATE = 0x20;
  2166.  
  2167.     // The compat file only lives in the Profile dir because we make the 
  2168.     // assumption that you can never have extensions prior to profile
  2169.     // startup.
  2170.     var compat = getFile(KEY_PROFILEDIR, [FILE_COMPAT_MANIFEST]);
  2171.     fos.init(compat, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, 0644, 0);
  2172.  
  2173.     var currAppBuildID = gPref.getCharPref(PREF_EM_APP_BUILDID);
  2174.  
  2175.     var val = aComponentListUpdated ? 1 : 0;
  2176.     var lines = ["[Compatibility]\r\n",
  2177.                  "Build ID=" + currAppBuildID + "\r\n",
  2178.                  "Components List Changed=" + val + "\r\n"];
  2179.     for (var i = 0; i < lines.length; ++i)
  2180.       fos.write(lines[i], lines[i].length);
  2181.  
  2182.     fos.close();
  2183.   },
  2184.   
  2185.   _writeDefaults: function nsExtensionManager__writeDefaults (aIsProfile)
  2186.   {
  2187.     var manifest = aIsProfile ? getFile(KEY_PROFILEDIR, [FILE_DEFAULTS]) : 
  2188.                                 getFile(KEY_APPDIR, [FILE_DEFAULTS]);
  2189.     this._writeProfileFile(manifest, this._getPreferencesDir, aIsProfile);
  2190.   },
  2191.   
  2192.   _cleanDirs: function nsExtensionManager__cleanDirs ()
  2193.   {
  2194.     var keys = [KEY_PROFILEDIR, KEY_APPDIR];
  2195.     for (var i = 0; i < keys.length; ++i) {
  2196.       var extensions = getDir(keys[i], [DIR_EXTENSIONS]);
  2197.       var entries = extensions.directoryEntries;
  2198.       while (entries.hasMoreElements()) {
  2199.         var entry = entries.getNext().QueryInterface(Components.interfaces.nsIFile);
  2200.         if (entry.isDirectory() && !entry.directoryEntries.hasMoreElements()) {
  2201.           try {
  2202.             entry.remove(false);
  2203.           }
  2204.           catch (e) { }
  2205.         }
  2206.       }
  2207.     }
  2208.   },
  2209.   
  2210.   /////////////////////////////////////////////////////////////////////////////  
  2211.   // nsIExtensionManager
  2212.   installExtension: function nsExtensionManager_installExtension (aXPIFile, aFlags)
  2213.   {
  2214.     // Since we're installing a "new type" extension, we assume a file layout
  2215.     // within the XPI like so:
  2216.     // foo.xpi/
  2217.     //         extension.rdf
  2218.     //         chrome/
  2219.     //         components/ 
  2220.     //         defaults/
  2221.     //                  prefs/
  2222.     var installProfile = aFlags & nsIExtensionManager.FLAG_INSTALL_PROFILE;
  2223.  
  2224.     var tempDir = getDir(getDirKey(installProfile), [DIR_EXTENSIONS, DIR_TEMP]);
  2225.     var fileName = getRandomFileName("temp", "xpi");
  2226.     aXPIFile.copyTo(tempDir, fileName);
  2227.     var xpiFile = tempDir.clone();
  2228.     xpiFile.append(fileName);
  2229.  
  2230.     // if the source file was read-only, fix permissions
  2231.     if (!xpiFile.isWritable()) {
  2232.       xpiFile.permissions = 0644;
  2233.     }
  2234.  
  2235.     var zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
  2236.                               .createInstance(Components.interfaces.nsIZipReader);
  2237.     zipReader.init(xpiFile);
  2238.     zipReader.open();
  2239.     
  2240.     var tempManifest = getFile(getDirKey(installProfile),
  2241.                                [DIR_EXTENSIONS, DIR_TEMP, getRandomFileName("install", "rdf")]);
  2242.     if (!tempManifest.exists())
  2243.       tempManifest.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
  2244.     zipReader.extract(FILE_INSTALL_MANIFEST, tempManifest);
  2245.     
  2246.     var extensionID = this.installExtensionInternal(xpiFile, tempManifest, installProfile);
  2247.     switch (extensionID) {
  2248.     case ERROR_EXTENSION_IS_THEME:
  2249.       this.installTheme(aXPIFile, aFlags);
  2250.       break;
  2251.     case ERROR_INVALID_VERSION:
  2252.     case ERROR_PHONED_HOME:
  2253.       break;
  2254.     default:
  2255.       // Then we stage the extension's XPI into a temporary directory so we 
  2256.       // can extract them after the next restart. 
  2257.       this._stageExtensionXPI(zipReader, extensionID, installProfile);
  2258.  
  2259.       this._writeComponentManifest(installProfile);
  2260.     }
  2261.     
  2262.     zipReader.close();
  2263.     tempManifest.remove(false);
  2264.     
  2265.     if (extensionID != ERROR_PHONED_HOME)
  2266.       xpiFile.remove(false);
  2267.   },
  2268.   
  2269.   installExtensionInternal: function nsExtensionManager_installExtensionInternal (aXPIFile, aManifest, aIsProfile)
  2270.   {
  2271.     var ds = getInstallManifest(aManifest);
  2272.     if (!ds) return;
  2273.     
  2274.     // XXXben - this is a hack until we properly fix xpinstall to be able to install
  2275.     //          different chrome types from trusted script. At the moment, when we
  2276.     //          call initManagerFromChrome, we can only install extensions, since
  2277.     //          the code path that installs themes is not utilized. To minimize the
  2278.     //          level of changes happening at the lower level in xpinstall at this
  2279.     //          point I am inserting this hack which checks for a theme-only property
  2280.     //          in the install manifest.
  2281.     var manifestRoot = gRDF.GetResource("urn:mozilla:install-manifest");
  2282.     var internalName = gRDF.GetResource(EM_NS("internalName"));
  2283.     if (stringData(ds.GetTarget(manifestRoot, internalName, true)) != "--")
  2284.       return ERROR_EXTENSION_IS_THEME;
  2285.     
  2286.     // We do a basic version check first just to make sure we somehow weren't 
  2287.     // tricked into installing an incompatible extension...
  2288.     this._ensureDS();
  2289.     var extensionID = this.canInstallItem(ds);
  2290.     // |extensionID| must be a GUID string, not a number - a number means failure.
  2291.     if (isNaN(parseInt(extensionID)))
  2292.       this._configureForthcomingItem(ds, extensionID, aIsProfile);
  2293.     else if (extensionID == 0) {
  2294.       var io = new this.IncompatibleObserver(this);
  2295.       var isChecking = io.checkForUpdates(ds, nsIUpdateItem.TYPE_EXTENSION,
  2296.                                           aXPIFile, aIsProfile);
  2297.       if (!isChecking)
  2298.         showIncompatibleError(ds);
  2299.       else {
  2300.         extensionID = ERROR_PHONED_HOME; // caller uses this to distinguish 
  2301.                                          // phone-home attempt.
  2302.       }
  2303.     }
  2304.     
  2305.     return extensionID;
  2306.   },
  2307.   
  2308.   IncompatibleObserver: function nsExtensionManager_IncompatibleObserver (aEM) 
  2309.   {
  2310.     this._item = null;
  2311.     this._em = aEM;
  2312.     this._ds = null;
  2313.     this._xpi = null;
  2314.     this._extensionID = 0;
  2315.     this._isProfile = true;
  2316.     
  2317.     this.checkForUpdates = function nsExtensionManager__iOcheckForUpdates (aDataSource, aType, 
  2318.                                                                            aXPIFile, aIsProfile)
  2319.     {
  2320.       // Construct a nsIUpdateItem for this extension...
  2321.       var item = this._em._getItemForIncompatibleID(aDataSource, aType);
  2322.       if (item) {
  2323.         this._item      = item;
  2324.         this._ds        = aDataSource;
  2325.         this._xpi       = aXPIFile;
  2326.         this._isProfile = true;
  2327.         
  2328.         gOS.addObserver(this, "Update:Extension:Started", false);
  2329.         gOS.addObserver(this, "Update:Extension:Item-Ended", false);
  2330.         gOS.addObserver(this, "Update:Extension:Item-Error", false);
  2331.         gOS.addObserver(this, "Update:Extension:Ended", false);
  2332.  
  2333.         this._em.update([item], 1, true);
  2334.         
  2335.         return true;
  2336.       }
  2337.       return false;
  2338.     }
  2339.     
  2340.     this.observe = function nsExtensionManager__iOobserve (aSubject, aTopic, aData)
  2341.     {
  2342.       switch (aTopic) {
  2343.       case "Update:Extension:Started":
  2344.         break;
  2345.       case "Update:Extension:Item-Ended":
  2346.         if (aSubject) {
  2347.           var item = aSubject.QueryInterface(Components.interfaces.nsIUpdateItem);
  2348.           this._em._ds.setTargetApplicationInfo(item.id, 
  2349.                                                 item.minAppVersion,
  2350.                                                 item.maxAppVersion, 
  2351.                                                 this._ds, 
  2352.                                                 this._item.type);
  2353.           this._extensionID = this._em.canInstallItem(this._ds);
  2354.         }
  2355.         break;
  2356.       case "Update:Extension:Item-Error":
  2357.         break;
  2358.       case "Update:Extension:Ended":
  2359.         gOS.removeObserver(this, "Update:Extension:Started");
  2360.         gOS.removeObserver(this, "Update:Extension:Item-Ended");
  2361.         gOS.removeObserver(this, "Update:Extension:Item-Error");
  2362.         gOS.removeObserver(this, "Update:Extension:Ended");
  2363.         
  2364.         if (isNaN(this._extensionID)) {
  2365.           var zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
  2366.                                     .createInstance(Components.interfaces.nsIZipReader);
  2367.           zipReader.init(this._xpi);
  2368.           zipReader.open();
  2369.  
  2370.           // Add the item after all
  2371.           this._em._configureForthcomingItem(this._ds, this._extensionID, 
  2372.                                              this._isProfile);
  2373.           this._em._stageExtensionXPI(zipReader, this._extensionID, this._isProfile);
  2374.           this._em._writeComponentManifest(this._isProfile);
  2375.           
  2376.           zipReader.close();
  2377.         }
  2378.         else 
  2379.           showIncompatibleError(this._ds);
  2380.         
  2381.         // Now really delete the temporary XPI file
  2382.         this._xpi.remove(false);
  2383.         break;
  2384.       }
  2385.     }
  2386.   },
  2387.   
  2388.   _configureForthcomingItem: function nsExtensionManager__configureForthcomingItem (aDataSource, 
  2389.                                                                                     aExtensionID, 
  2390.                                                                                     aIsProfile)
  2391.   {
  2392.     // Clear any "disabled" flags that may have been set by the mismatch 
  2393.     // checking code at startup.
  2394.     var props = { toBeDisabled  : null,
  2395.                   disabled      : null,
  2396.                   toBeInstalled : this._ds._emL("true"),
  2397.                   name          : this.getManifestProperty(aDataSource, "name"),
  2398.                   version       : this.getManifestProperty(aDataSource, "version") };
  2399.     for (var p in props) {
  2400.       this._ds.setItemProperty(aExtensionID, this._ds._emR(p),
  2401.                                props[p], aIsProfile,
  2402.                                nsIUpdateItem.TYPE_EXTENSION);
  2403.     }
  2404.  
  2405.     // Insert it into the child list NOW rather than later because:
  2406.     // - extensions installed using the command line need to be a member
  2407.     //   of a container during the install phase for the code to be able
  2408.     //   to identify profile vs. global
  2409.     // - extensions installed through the UI should show some kind of
  2410.     //   feedback to indicate their presence is forthcoming (i.e. they
  2411.     //   will be available after a restart).
  2412.     this._ds.insertForthcomingItem(aExtensionID, nsIUpdateItem.TYPE_EXTENSION, 
  2413.                                    aIsProfile);
  2414.   },
  2415.   
  2416.   _getItemForIncompatibleID: function nsExtensionManager__getItemForID (aDataSource, aType)
  2417.   {
  2418.     var newItem = null;
  2419.     var id, version, targetAppInfo, name, updateURL;
  2420.     var manifestRoot = gRDF.GetResource("urn:mozilla:install-manifest");
  2421.     try {
  2422.       function getProperty (aDataSource, aSourceResource, aProperty)
  2423.       {
  2424.         var rv;
  2425.         try {
  2426.           var property = gRDF.GetResource(EM_NS(aProperty));
  2427.           rv = stringData(aDataSource.GetTarget(aSourceResource, property, true));
  2428.           if (rv == "--")
  2429.             throw Components.results.NS_ERROR_FAILURE;
  2430.         }
  2431.         catch (e) { }
  2432.         return rv;
  2433.       }
  2434.       
  2435.       var root = gRDF.GetResource("urn:mozilla:install-manifest");
  2436.       id            = getProperty(aDataSource, root, "id");
  2437.       version       = getProperty(aDataSource, root, "version");
  2438.       targetAppInfo = this._ds.getTargetApplicationInfo(id, aDataSource, aType);
  2439.       name          = getProperty(aDataSource, root, "name");
  2440.       updateURL     = getProperty(aDataSource, root, "updateURL");
  2441.       if (updateURL == "--")
  2442.         updateURL = "";
  2443.       
  2444.       newItem = Components.classes["@mozilla.org/updates/item;1"]
  2445.                           .createInstance(Components.interfaces.nsIUpdateItem);
  2446.       newItem.init(id, version, targetAppInfo.minVersion, 
  2447.                    targetAppInfo.maxVersion,
  2448.                    name, -1, "", "", updateURL, aType);
  2449.     }
  2450.     catch (e) {
  2451.       return null;
  2452.     }
  2453.     return newItem;
  2454.   },
  2455.   
  2456.   canInstallItem: function nsExtensionManager_canInstallItem (aDataSource)
  2457.   {
  2458.     var manifestRoot = gRDF.GetResource("urn:mozilla:install-manifest");
  2459.     // First make sure the item has a valid "version" property. 
  2460.     var version = gRDF.GetResource(EM_NS("version"));
  2461.     var versionLiteral = stringData(aDataSource.GetTarget(manifestRoot, version, true));
  2462.     if (!getVersionChecker().isValidVersion(versionLiteral)) {
  2463.       var name = gRDF.GetResource(EM_NS("name"));
  2464.       var nameLiteral = stringData(aDataSource.GetTarget(manifestRoot, name, true));
  2465.       showInvalidVersionError(nameLiteral, versionLiteral);
  2466.       return ERROR_INVALID_VERSION;
  2467.     }
  2468.         
  2469.     // Check the target application range specified by the extension metadata.
  2470.     if (this._ds.isCompatible(aDataSource, manifestRoot)) {
  2471.       var id = gRDF.GetResource(EM_NS("id"));
  2472.       var idLiteral = aDataSource.GetTarget(manifestRoot, id, true);
  2473.       return idLiteral.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  2474.     }
  2475.     return 0;
  2476.   },
  2477.   
  2478.   getManifestProperty: function nsExtensionManager_getManifestProperty (aDataSource, aProperty)
  2479.   {
  2480.     var manifestRoot = gRDF.GetResource("urn:mozilla:install-manifest");
  2481.     var arc = gRDF.GetResource(EM_NS(aProperty));
  2482.     return aDataSource.GetTarget(manifestRoot, arc, true);
  2483.   },
  2484.   
  2485.   _stageExtensionXPI: function nsExtensionManager__stageExtensionXPI (aZipReader, aExtensionID, aInstallProfile)
  2486.   {
  2487.     // Get the staging dir
  2488.     var dir = getDir(getDirKey(aInstallProfile),
  2489.                      [DIR_EXTENSIONS, DIR_TEMP, aExtensionID]);
  2490.     var extensionFileName = aExtensionID + ".xpi";
  2491.     var extensionFile = dir.clone();
  2492.     extensionFile.append(extensionFileName);
  2493.     if (extensionFile.exists())
  2494.       extensionFile.remove(false);
  2495.     aZipReader.file.copyTo(dir, extensionFileName);
  2496.  
  2497.     // if the source file was readonly, fix the permissions
  2498.     if (!extensionFile.isWritable()) {
  2499.       extensionFile.permissions = 0644;
  2500.     }
  2501.   },
  2502.   
  2503.   // This function is called on the next startup 
  2504.   _finalizeInstall: function nsExtensionManager__finalizeInstall (aExtensionID)
  2505.   {
  2506.     var isProfile = this._ds.isProfileItem(aExtensionID);
  2507.     if (aExtensionID == 0 || aExtensionID == -1) {
  2508.       this._ds.removeCorruptItem(aExtensionID, 
  2509.                                  nsIUpdateItem.TYPE_EXTENSION, 
  2510.                                  isProfile);
  2511.       return;
  2512.     }
  2513.     
  2514.     if (!this._extInstaller)
  2515.       this._extInstaller = new nsExtensionInstaller(this._ds);
  2516.       
  2517.     this._extInstaller.install(aExtensionID, isProfile);
  2518.     
  2519.     // Update the Components Manifest
  2520.     this._writeComponentManifest(isProfile);
  2521.     
  2522.     // Update the Defaults Manifest
  2523.     this._writeDefaults(isProfile);
  2524.   },
  2525.   
  2526.   _finalizeEnableDisable: function nsExtensionManager__finalizeEnableDisable (aExtensionID, aDisable)
  2527.   {
  2528.     if (!this._extEnabler)
  2529.       this._extEnabler = new nsExtensionEnabler(this._ds);
  2530.       
  2531.     var isProfile = this._ds.isProfileItem(aExtensionID);
  2532.     this._extEnabler.enable(aExtensionID, isProfile, aDisable);
  2533.  
  2534.     // clear temporary flags
  2535.     this._ds.setItemProperty(aExtensionID, 
  2536.                              this._ds._emR("toBeEnabled"),
  2537.                              null, isProfile,
  2538.                              nsIUpdateItem.TYPE_EXTENSION);
  2539.     this._ds.setItemProperty(aExtensionID, 
  2540.                              this._ds._emR("toBeDisabled"),
  2541.                              null, isProfile,
  2542.                              nsIUpdateItem.TYPE_EXTENSION);
  2543.   },
  2544.   
  2545.   _finalizeUninstall: function nsExtensionManager__finalizeUninstall (aExtensionID)
  2546.   {
  2547.     if (!this._extUninstaller)
  2548.       this._extUninstaller = new nsExtensionUninstaller(this._ds);
  2549.     var isProfile = this._ds.isProfileItem(aExtensionID);
  2550.     this._extUninstaller.uninstall(aExtensionID, isProfile);
  2551.  
  2552.     // Clean the extension resource
  2553.     this._ds.removeItemMetadata(aExtensionID, nsIUpdateItem.TYPE_EXTENSION);
  2554.     
  2555.     // Do this LAST since inferences are made about an item based on
  2556.     // what container it's in.
  2557.     this._ds.removeItemFromContainer(aExtensionID, 
  2558.                                      nsIUpdateItem.TYPE_EXTENSION,
  2559.                                      isProfile);
  2560.   },
  2561.       
  2562.   uninstallExtension: function nsExtensionManager_uninstallExtension (aExtensionID)
  2563.   {
  2564.     if (!this._ds.isDownloadItem(aExtensionID)) {
  2565.       this._ds.uninstallExtension(aExtensionID);
  2566.  
  2567.       var isProfile = this._ds.isProfileItem(aExtensionID);
  2568.  
  2569.       // Update the Components Manifest
  2570.       this._writeComponentManifest(isProfile);
  2571.  
  2572.       // Update the Defaults Manifest
  2573.       this._writeDefaults(isProfile);
  2574.     }
  2575.     else {
  2576.       // Bad download entry - uri is url, e.g. "http://www.foo.com/test.xpi"
  2577.       // ... just remove it from the list. 
  2578.       this._ds.removeCorruptDLItem(aExtensionID, nsIUpdateItem.TYPE_EXTENSION);
  2579.     }
  2580.   },
  2581.   
  2582.   enableExtension: function nsExtensionManager_enableExtension (aExtensionID)
  2583.   {
  2584.     this._ds.enableExtension(aExtensionID);
  2585.  
  2586.     var isProfile = this._ds.isProfileItem(aExtensionID);
  2587.  
  2588.     // Update the Components Manifest
  2589.     this._writeComponentManifest(isProfile);
  2590.  
  2591.     // Update the Defaults Manifest
  2592.     this._writeDefaults(isProfile);
  2593.   },
  2594.   
  2595.   disableExtension: function nsExtensionManager_disableExtension (aExtensionID)
  2596.   {
  2597.     this._ds.disableExtension(aExtensionID);
  2598.  
  2599.     var isProfile = this._ds.isProfileItem(aExtensionID);
  2600.  
  2601.     // Update the Components Manifest
  2602.     this._writeComponentManifest(isProfile);
  2603.  
  2604.     // Update the Defaults Manifest
  2605.     this._writeDefaults(isProfile);
  2606.   },
  2607.   
  2608.   enableTheme: function nsExtensionsDataSource_enableTheme (aThemeID)
  2609.   {
  2610.     this._ds.enableTheme(aThemeID);
  2611.   },
  2612.     
  2613.   disableTheme: function nsExtensionsDataSource_disableTheme (aThemeID)
  2614.   {
  2615.     this._ds.disableTheme(aThemeID);
  2616.   },
  2617.   
  2618.   update: function nsExtensionManager_update (aItems, aItemCount, aVersionUpdateOnly)
  2619.   {
  2620.     var appID = gPref.getCharPref(PREF_EM_APP_ID);
  2621.         dump ("get extensions version\n");
  2622.     var appVersion = gPref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
  2623.         dump ("after extensions version\n");
  2624.  
  2625.     if (aItems.length == 0) {
  2626.       var addonType = nsIUpdateItem.TYPE_ADDON;
  2627.       aItems = this.getItemList(null, addonType, { });
  2628.     }
  2629.     var updater = new nsExtensionItemUpdater(appID, appVersion, this);
  2630.     updater.checkForUpdates(aItems, aItems.length, aVersionUpdateOnly);
  2631.   },
  2632.   
  2633.   getItemList: function nsExtensionManager_getItemList (aItemID, aType, aCountRef)
  2634.   {
  2635.     this._ensureDS();
  2636.     return this._ds.getItemList(aItemID, aType, aCountRef);
  2637.   },    
  2638.  
  2639.   /////////////////////////////////////////////////////////////////////////////  
  2640.   // Themes
  2641.   installTheme: function nsExtensionManager_installTheme (aJARFile, aFlags)
  2642.   {
  2643.     this._ensureDS();
  2644.     
  2645.     var isProfile = aFlags & nsIExtensionManager.FLAG_INSTALL_PROFILE;
  2646.     var installer = new nsThemeInstaller(this._ds, this);
  2647.     installer.install(aJARFile, isProfile);
  2648.     // XPInstall selects the theme, if necessary.
  2649.   },
  2650.   
  2651.   uninstallTheme: function nsExtensionManager_uninstallTheme (aThemeID)
  2652.   {
  2653.     if (!this._ds.isDownloadItem(aThemeID)) {
  2654.       this._ensureDS();
  2655.       this._ds.uninstallTheme(aThemeID);
  2656.     }
  2657.     else {
  2658.       // Bad download entry - uri is url, e.g. "http://www.foo.com/test.jar"
  2659.       // ... just remove it from the list. 
  2660.       this._ds.removeCorruptDLItem(aThemeID, nsIUpdateItem.TYPE_THEME);
  2661.     }
  2662.   },
  2663.   
  2664.   moveTop: function nsExtensionManager_moveTop (aItemID)
  2665.   {
  2666.     this._ds.moveTop(aItemID);
  2667.   },
  2668.   
  2669.   moveUp: function nsExtensionManager_moveUp (aItemID)
  2670.   {
  2671.     this._ds.moveUp(aItemID);
  2672.   },
  2673.   
  2674.   moveDown: function nsExtensionManager_moveDown (aItemID)
  2675.   {
  2676.     this._ds.moveDown(aItemID);
  2677.   },
  2678.  
  2679.   get datasource()
  2680.   {
  2681.     this._ensureDS();
  2682.     return this._ds;
  2683.   },
  2684.   
  2685.   /////////////////////////////////////////////////////////////////////////////    
  2686.   // Downloads
  2687.   _transactions: [],
  2688.   _downloadCount: 0,
  2689.   addDownloads: function nsExtensionManager_addDownloads (aItems, aItemCount)
  2690.   {
  2691.     this._downloadCount += aItemCount;
  2692.     
  2693.     var txn = new nsItemDownloadTransaction(this);
  2694.     for (var i = 0; i < aItemCount; ++i) {
  2695.       var currItem = aItems[i];
  2696.       var txnID = Math.round(Math.random() * 100);
  2697.       txn.addDownload(currItem.name, currItem.xpiURL, currItem.iconURL, 
  2698.                       currItem.type, txnID);
  2699.       this._transactions.push(txn);
  2700.     }
  2701.  
  2702.     // Kick off the download process for this transaction
  2703.     gOS.addObserver(this, "offline-requested", false);
  2704.     gOS.addObserver(this, "quit-application-requested", false);
  2705.     gOS.notifyObservers(txn, "xpinstall-progress", "open");  
  2706.   },
  2707.   
  2708.   removeDownload: function nsExtensionManager_removeDownload (aURL, aType)
  2709.   {
  2710.     for (var i = 0; i < this._transactions.length; ++i) {
  2711.       if (this._transactions[i].containsURL(aURL)) {
  2712.         this._transactions[i].removeDownload(aURL, aType);
  2713.         return;
  2714.       }
  2715.     } 
  2716.   },
  2717.   
  2718.   _removeAllDownloads: function nsExtensionManager__removeAllDownloads ()
  2719.   {
  2720.     for (var i = 0; i < this._transactions.length; ++i)
  2721.       this._transactions[i].removeAllDownloads();
  2722.   },
  2723.   
  2724.   // The nsIXPIProgressDialog implementation in the download transaction object
  2725.   // forwards notifications through these methods which we then pass on to any
  2726.   // front end objects implementing nsIExtensionDownloadProgressListener that 
  2727.   // are listening. We maintain the master state of download operations HERE, 
  2728.   // not in the front end, because if the user closes the extension or theme 
  2729.   // managers during the downloads we need to maintain state and not terminate
  2730.   // the download/install process. 
  2731.   onStateChange: function nsExtensionManager_onStateChange (aTransaction, aURL, aState, aValue)
  2732.   {
  2733.     if (!(aURL in this._progressData)) 
  2734.       this._progressData[aURL] = { };
  2735.     this._progressData[aURL].state = aState;
  2736.     
  2737.     for (var i = 0; i < this._downloadObservers.length; ++i)
  2738.       this._downloadObservers[i].onStateChange(aURL, aState, aValue);
  2739.  
  2740.     const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog;
  2741.     switch (aState) {
  2742.     case nsIXPIProgressDialog.INSTALL_DONE:
  2743.       --this._downloadCount;
  2744.       break;
  2745.     case nsIXPIProgressDialog.DIALOG_CLOSE:
  2746.       for (var i = 0; i < this._transactions.length; ++i) {
  2747.         if (this._transactions[i].id == aTransaction.id) {
  2748.           this._transactions.splice(i, 1);
  2749.           delete aTransaction;
  2750.           break;
  2751.         }
  2752.       }
  2753.       break;
  2754.     }
  2755.   },
  2756.   
  2757.   _progressData: { },
  2758.   onProgress: function nsExtensionManager_onProgress (aURL, aValue, aMaxValue)
  2759.   {
  2760.     for (var i = 0; i < this._downloadObservers.length; ++i)
  2761.       this._downloadObservers[i].onProgress(aURL, aValue, aMaxValue);
  2762.     
  2763.     if (!(aURL in this._progressData)) 
  2764.       this._progressData[aURL] = { };
  2765.     this._progressData[aURL].progress = Math.round((aValue / aMaxValue) * 100);
  2766.   },
  2767.  
  2768.   _downloadObservers: [],
  2769.   addDownloadObserver: function nsExtensionManager_addDownloadObserver (aXPIProgressDialog)
  2770.   {
  2771.     for (var i = 0; i < this._downloadObservers.length; ++i) {
  2772.       if (this._downloadObservers[i] == aXPIProgressDialog)
  2773.         return i;
  2774.     }
  2775.     this._downloadObservers.push(aXPIProgressDialog);
  2776.     return this._downloadObservers.length - 1;
  2777.   },
  2778.   
  2779.   removeDownloadObserverAt: function nsExtensionManager_removeDownloadObserverAt (aIndex)
  2780.   {
  2781.     this._downloadObservers.splice(aIndex, 1);
  2782.     if (this._downloadCount != 0)
  2783.       this._ds.flushProgressInfo(this._progressData);
  2784.   },
  2785.  
  2786.   //
  2787.   _ds: null,
  2788.  
  2789.   /////////////////////////////////////////////////////////////////////////////    
  2790.   // Other
  2791.   
  2792.   // This should NOT be called until after the window is shown! 
  2793.   _ensureDS: function nsExtensionManager__ensureDS ()
  2794.   {
  2795.     if (!this._ds) {
  2796.       dump("*** loading the extensions datasource\n");
  2797.       this._ds = new nsExtensionsDataSource();
  2798.       if (this._ds) {
  2799.         this._ds.loadExtensions(false);
  2800.         this._ds.loadExtensions(true);
  2801.       }
  2802.       
  2803.       // Ensure any pre-configured items are initialized.
  2804.       (new nsInstalledExtensionReader(this)).read();
  2805.     }
  2806.   },
  2807.  
  2808.   /////////////////////////////////////////////////////////////////////////////
  2809.   // nsIClassInfo
  2810.   getInterfaces: function nsExtensionManager_getInterfaces (aCount)
  2811.   {
  2812.     var interfaces = [Components.interfaces.nsIExtensionManager,
  2813.                       Components.interfaces.nsIXPIProgressDialog,
  2814.                       Components.interfaces.nsIObserver];
  2815.     aCount.value = interfaces.length;
  2816.     return interfaces;
  2817.   },
  2818.   
  2819.   getHelperForLanguage: function nsExtensionManager_getHelperForLanguage (aLanguage)
  2820.   {
  2821.     return null;
  2822.   },
  2823.   
  2824.   get contractID() 
  2825.   {
  2826.     return "@mozilla.org/extensions/manager;1";
  2827.   },
  2828.   
  2829.   get classDescription()
  2830.   {
  2831.     return "Extension Manager";
  2832.   },
  2833.   
  2834.   get classID() 
  2835.   {
  2836.     return Components.ID("{8A115FAA-7DCB-4e8f-979B-5F53472F51CF}");
  2837.   },
  2838.   
  2839.   get implementationLanguage()
  2840.   {
  2841.     return Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT;
  2842.   },
  2843.   
  2844.   get flags()
  2845.   {
  2846.     return Components.interfaces.nsIClassInfo.SINGLETON;
  2847.   },
  2848.  
  2849.   /////////////////////////////////////////////////////////////////////////////
  2850.   // nsISupports
  2851.   QueryInterface: function nsExtensionManager_QueryInterface (aIID) 
  2852.   {
  2853.     if (!aIID.equals(Components.interfaces.nsIExtensionManager) &&
  2854.         !aIID.equals(Components.interfaces.nsIObserver) &&
  2855.         !aIID.equals(Components.interfaces.nsISupports))
  2856.       throw Components.results.NS_ERROR_NO_INTERFACE;
  2857.     return this;
  2858.   }
  2859. };
  2860.  
  2861. ///////////////////////////////////////////////////////////////////////////////
  2862. //
  2863. // nsItemDownloadTransaction
  2864. //
  2865. //   This object implements nsIXPIProgressDialog and represents a collection of
  2866. //   XPI/JAR download and install operations. There is one 
  2867. //   nsItemDownloadTransaction per back-end XPInstallManager object. We maintain
  2868. //   a collection of separate transaction objects because it's possible to have
  2869. //   multiple separate XPInstall download/install operations going on 
  2870. //   simultaneously, each with its own XPInstallManager instance. For instance
  2871. //   you could start downloading two extensions and then download a theme. Each
  2872. //   of these operations would open the appropriate FE and have to be able to
  2873. //   track each operation independently.
  2874. //
  2875. function nsItemDownloadTransaction(aManager)
  2876. {
  2877.   this._manager = aManager;
  2878.   this._downloads = [];
  2879. }
  2880.  
  2881. nsItemDownloadTransaction.prototype = {
  2882.   _manager    : null,
  2883.   _downloads  : [],
  2884.   id          : -1,
  2885.   
  2886.   addDownload: function nsItemDownloadTransaction_addDownload (aName, aURL, aIconURL, aItemType, aID)
  2887.   {
  2888.     this._downloads.push({ url: aURL, type: aItemType, waiting: true });
  2889.     this._manager._ds.addDownload(aName, aURL, aIconURL, aItemType);
  2890.     this.id = aID;
  2891.   },
  2892.   
  2893.   removeDownload: function nsItemDownloadTransaction_removeDownload (aURL, aItemType)
  2894.   {
  2895.     this._manager._ds.removeDownload(aURL, aItemType);
  2896.   },
  2897.   
  2898.   removeAllDownloads: function nsItemDownloadTransaction_removeAllDownloads ()
  2899.   {
  2900.     for (var i = 0; i < this._downloads.length; ++i)
  2901.       this.removeDownload(this._downloads[i].url, this._downloads[i].type);
  2902.   },
  2903.   
  2904.   containsURL: function nsItemDownloadTransaction_containsURL (aURL)
  2905.   {
  2906.     for (var i = 0; i < this._downloads.length; ++i) {
  2907.       if (this._downloads[i].url == aURL)
  2908.         return true;
  2909.     }
  2910.     return false;
  2911.   },
  2912.  
  2913.   /////////////////////////////////////////////////////////////////////////////  
  2914.   // nsIXPIProgressDialog
  2915.   onStateChange: function nsItemDownloadTransaction_onStateChange (aIndex, aState, aValue)
  2916.   {
  2917.     this._manager.onStateChange(this, this._downloads[aIndex].url, aState, aValue);
  2918.   },
  2919.   
  2920.   onProgress: function nsItemDownloadTransaction_onProgress (aIndex, aValue, aMaxValue)
  2921.   {
  2922.     this._manager.onProgress(this._downloads[aIndex].url, aValue, aMaxValue);
  2923.   },
  2924.   
  2925.   /////////////////////////////////////////////////////////////////////////////
  2926.   // nsISupports
  2927.   QueryInterface: function nsItemDownloadTransaction_QueryInterface (aIID) 
  2928.   {
  2929.     if (!aIID.equals(Components.interfaces.nsIXPIProgressDialog) &&
  2930.         !aIID.equals(Components.interfaces.nsISupports))
  2931.       throw Components.results.NS_ERROR_NO_INTERFACE;
  2932.     return this;
  2933.   }
  2934. };
  2935.  
  2936. ///////////////////////////////////////////////////////////////////////////////
  2937. //
  2938. // nsExtensionItemUpdater
  2939. //
  2940. function nsExtensionItemUpdater(aTargetAppID, aTargetAppVersion, aEM) 
  2941. {
  2942.   this._appID = aTargetAppID;
  2943.   this._appVersion = aTargetAppVersion;
  2944.   this._emDS = aEM._ds;
  2945.   this._em = aEM;
  2946.  
  2947.   getVersionChecker();
  2948. }
  2949.  
  2950. nsExtensionItemUpdater.prototype = {
  2951.   _appID              : "",
  2952.   _appVersion         : "",
  2953.   _emDS               : null,
  2954.   _em                 : null,
  2955.   _versionUpdateOnly  : 0,
  2956.   _items              : [],
  2957.   
  2958.   /////////////////////////////////////////////////////////////////////////////
  2959.   // nsIExtensionItemUpdater
  2960.   //
  2961.   // When we check for updates to an item, there are two pieces of information
  2962.   // that are returned - 1) info about the newest available version, if any,
  2963.   // and 2) info about the currently installed version. The latter is provided
  2964.   // primarily to inform the client of changes to the application compatibility 
  2965.   // metadata for the current item. Depending on the situation, either 2 or 
  2966.   // 1&2 may be what is required.
  2967.   //
  2968.   // Callers:
  2969.   //  1 - nsUpdateService.js, user event
  2970.   //      User clicked on the update icon to invoke an update check, 
  2971.   //      user clicked on an Extension/Theme and clicked "Update". In this
  2972.   //      case we want to update compatibility metadata about the installed
  2973.   //      version, and look for newer versions to offer. 
  2974.   //  2 - nsUpdateService.js, background event
  2975.   //      Timer fired, background update is being performed. In this case
  2976.   //      we also want to update compatibility metadata and look for newer
  2977.   //      versions.
  2978.   //  3 - Mismatch
  2979.   //      User upgraded to a newer version of the app, update compatibility
  2980.   //      metadata and look for newer versions.
  2981.   //  4 - Install Phone Home
  2982.   //      User installed an item that was deemed incompatible based only
  2983.   //      on the information provided in the item's install.rdf manifest, 
  2984.   //      we look ONLY for compatibility updates in this case to determine
  2985.   //      whether or not the item can be installed.
  2986.   //  
  2987.   checkForUpdates: function nsExtensionItemUpdater_checkForUpdates (aItems, aItemCount, 
  2988.                                                                     aVersionUpdateOnly) 
  2989.   {
  2990.     gOS.notifyObservers(null, "Update:Extension:Started", "");
  2991.     this._versionUpdateOnly = aVersionUpdateOnly;
  2992.     this._items = aItems;
  2993.     this._responseCount = aItemCount;
  2994.     
  2995.     // This is the number of extensions/themes/etc that we found updates for.
  2996.     this._updateCount = 0;
  2997.  
  2998.     for (var i = 0; i < aItemCount; ++i) {
  2999.       var e = this._items[i];
  3000.       gOS.notifyObservers(e, "Update:Extension:Item-Started", "");
  3001.       (new nsRDFItemUpdater(this)).checkForUpdates(e, aVersionUpdateOnly);
  3002.     }
  3003.   },
  3004.   
  3005.   /////////////////////////////////////////////////////////////////////////////
  3006.   // nsExtensionItemUpdater
  3007.   _applyVersionUpdates: function nsExtensionItemUpdater__applyVersionUpdates (aLocalItem, aRemoteItem)
  3008.   {
  3009.     var r = this._emDS._getResourceForItem(aLocalItem.id);
  3010.     if (!r) return;
  3011.     var targetAppInfo = this._emDS.getTargetApplicationInfo(aLocalItem.id, this._emDS, 
  3012.                                                             getItemType(r.Value));
  3013.     if (gVersionChecker.compare(targetAppInfo.maxVersion, aRemoteItem.maxAppVersion) < 0) {
  3014.       // Remotely specified maxVersion is newer than the maxVersion 
  3015.       // for the installed Extension. Apply that change to the datasource.
  3016.       this._emDS.setTargetApplicationInfo(aLocalItem.id, 
  3017.                                           aRemoteItem.minAppVersion, 
  3018.                                           aRemoteItem.maxAppVersion,
  3019.                                           null, aLocalItem.type);
  3020.       
  3021.       // If we got here through |checkForMismatches|, this extension has
  3022.       // already been disabled, re-enable it.
  3023.       if (this._emDS.getItemProperty(aLocalItem.id, "disabled") == "true") 
  3024.         this._em.enableExtension(aLocalItem.id);
  3025.     }
  3026.   },
  3027.   
  3028.   _isValidUpdate: function nsExtensionItemUpdater__isValidUpdate (aLocalItem, aRemoteItem) 
  3029.   {
  3030.     var appExtensionsVersion = gPref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
  3031.  
  3032.     // Check if the update will only run on a newer version of Firefox. 
  3033.     if (aRemoteItem.minAppVersion && 
  3034.         gVersionChecker.compare(appExtensionsVersion, aRemoteItem.minAppVersion) < 0) 
  3035.       return false;
  3036.  
  3037.     // Check if the update will only run on an older version of Firefox. 
  3038.     if (aRemoteItem.maxAppVersion && 
  3039.         gVersionChecker.compare(appExtensionsVersion, aRemoteItem.maxAppVersion) > 0) 
  3040.       return false;
  3041.     
  3042.     return true;
  3043.   },
  3044.   
  3045.   _checkForDone: function nsExtensionItemUpdater__checkForDone ()
  3046.   {
  3047.     if (--this._responseCount == 0) {
  3048.       if (!this._versionUpdateOnly)
  3049.         gPref.setIntPref(PREF_UPDATE_COUNT, this._updateCount); 
  3050.           
  3051.       gOS.notifyObservers(null, "Update:Extension:Ended", "");
  3052.     }
  3053.   },
  3054.   
  3055.   /////////////////////////////////////////////////////////////////////////////
  3056.   // nsISupports
  3057.   QueryInterface: function nsExtensionItemUpdater_QueryInterface (aIID) 
  3058.   {
  3059.     if (!aIID.equals(Components.interfaces.nsIExtensionItemUpdater) &&
  3060.         !aIID.equals(Components.interfaces.nsISupports))
  3061.       throw Components.results.NS_ERROR_NO_INTERFACE;
  3062.     return this;
  3063.   }  
  3064. };
  3065.  
  3066. function nsRDFItemUpdater(aUpdater)
  3067. {
  3068.   this._updater = aUpdater;
  3069. }
  3070.  
  3071. nsRDFItemUpdater.prototype = {
  3072.   _updater            : null,
  3073.   _versionUpdateOnly  : 0,
  3074.   _item               : null,
  3075.   
  3076.   checkForUpdates: function (aItem, aVersionUpdateOnly)
  3077.   {
  3078.     // A preference setting can disable updating for this item
  3079.     try {
  3080.       if (!gPref.getBoolPref(PREF_EM_ITEM_UPDATE_ENABLED.replace(/%UUID%/, aItem.id))) {
  3081.         gOS.notifyObservers(null, "Update:Extension:Item-Ended", "");
  3082.         this._updater._checkForDone();
  3083.         return;
  3084.       }
  3085.     }
  3086.     catch (e) { }
  3087.  
  3088.     this._versionUpdateOnly = aVersionUpdateOnly;
  3089.     this._item = aItem;
  3090.   
  3091.     // Look for a custom update URI: 1) supplied by a pref, 2) supplied by the
  3092.     // install manifest, 3) the default configuration
  3093.     try {
  3094.       var dsURI = gPref.getComplexValue(PREF_EM_ITEM_UPDATE_URL.replace(/%UUID%/, aItem.id),
  3095.                                         Components.interfaces.nsIPrefLocalizedString).data;
  3096.     }
  3097.     catch (e) { }
  3098.     if (!dsURI)
  3099.       dsURI = aItem.updateRDF;
  3100.     if (!dsURI) {
  3101.       dsURI = gPref.getComplexValue(PREF_UPDATE_DEFAULT_URL,
  3102.                                     Components.interfaces.nsIPrefLocalizedString).data;
  3103.     }
  3104.     dsURI = dsURI.replace(/%ITEM_ID%/g, aItem.id);
  3105.     dsURI = dsURI.replace(/%ITEM_VERSION%/g, aItem.version);
  3106.     dsURI = dsURI.replace(/%ITEM_MAXAPPVERSION%/g, aItem.maxAppVersion);
  3107.     dsURI = dsURI.replace(/%APP_ID%/g, this._updater._appID);
  3108.     dsURI = dsURI.replace(/%APP_VERSION%/g, this._updater._appVersion);
  3109.     dsURI = dsURI.replace(/%REQ_VERSION%/g, 1);
  3110.     
  3111.     // escape() does not properly encode + symbols in any embedded FVF strings.
  3112.     dsURI = dsURI.replace(/\+/g, "%2B");
  3113.  
  3114.     var ds = gRDF.GetDataSource(dsURI);
  3115.     var rds = ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
  3116.     if (rds.loaded)
  3117.       this.onDatasourceLoaded(ds, aItem);
  3118.     else {
  3119.       var sink = ds.QueryInterface(Components.interfaces.nsIRDFXMLSink);
  3120.       sink.addXMLSinkObserver(this);
  3121.     }
  3122.   },
  3123.  
  3124.   onDatasourceLoaded: function nsExtensionItemUpdater_onDatasourceLoaded (aDatasource, aLocalItem)
  3125.   {
  3126.     ///////////////////////////////////////////////////////////////////////////    
  3127.     // The extension update RDF file looks something like this:
  3128.     //
  3129.     //  <RDF:Description about="urn:mozilla:extension:{GUID}">
  3130.     //    <em:updates>
  3131.     //      <RDF:Seq>
  3132.     //        <RDF:li resource="urn:mozilla:extension:{GUID}:4.9"/>
  3133.     //        <RDF:li resource="urn:mozilla:extension:{GUID}:5.0"/>
  3134.     //      </RDF:Seq>
  3135.     //    </em:updates>
  3136.     //    <!-- the version of the extension being offered -->
  3137.     //    <em:version>5.0</em:version>
  3138.     //    <em:updateLink>http://www.mysite.com/myext-50.xpi</em:updateLink>
  3139.     //  </RDF:Description>
  3140.     //
  3141.     //  <RDF:Description about="urn:mozilla:extension:{GUID}:4.9">
  3142.     //    <em:version>4.9</em:version>
  3143.     //    <em:targetApplication>
  3144.     //      <RDF:Description>
  3145.     //        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
  3146.     //        <em:minVersion>0.9</em:minVersion>
  3147.     //        <em:maxVersion>1.0</em:maxVersion>
  3148.     //        <em:updateLink>http://www.mysite.com/myext-49.xpi</em:updateLink>
  3149.     //      </RDF:Description>
  3150.     //    </em:targetApplication>
  3151.     //  </RDF:Description>  
  3152.     //
  3153.     // If we get here because the following happened:
  3154.     // 1) User was using Firefox 0.9 with ExtensionX 0.5 (minVersion 0.8, 
  3155.     //    maxVersion 0.9 for Firefox)
  3156.     // 2) User upgraded Firefox to 1.0
  3157.     // 3) |checkForMismatches| deems ExtensionX 0.5 incompatible with this
  3158.     //    new version of Firefox on the basis of its maxVersion
  3159.     // 4) ** We reach this point **
  3160.     //
  3161.     // If the version of ExtensionX (0.5) matches that provided by the 
  3162.     // server, then this is a cue that the author updated the rdf file
  3163.     // or central repository to say "0.5 is ALSO compatible with Firefox 1.0,
  3164.     // no changes are necessary." In this event, the local metadata for
  3165.     // installed ExtensionX (0.5) is freshened with the new maxVersion, 
  3166.     // and we advance to the next item WITHOUT any download/install 
  3167.     // updates.
  3168.   
  3169.     // Parse the response RDF
  3170.     function UpdateData() {}; 
  3171.     UpdateData.prototype = { version: "0.0", updateLink: null, 
  3172.                              minVersion: "0.0", maxVersion: "0.0" };
  3173.     
  3174.     var versionUpdate = new UpdateData();
  3175.     var newestUpdate  = new UpdateData();
  3176.  
  3177.     var newerItem, sameItem;
  3178.     
  3179.     // Firefox 1.0PR+ update.rdf format
  3180.     if (!this._versionUpdateOnly) {
  3181.       // Look for newer versions of this item, we only do this in "normal" 
  3182.       // mode... see comment by nsExtensionItemUpdater_checkForUpdates 
  3183.       // about how we do this in all cases but Install Phone Home - which 
  3184.       // only needs to do a version check.
  3185.       this._parseV20UpdateInfo(aDatasource, aLocalItem, newestUpdate, false);
  3186.       if (!newestUpdate.updateLink) {
  3187.         // Firefox 0.9 update.rdf format - does not contain any metadata
  3188.         // that can be used for version updates, so performed in the "all updates"
  3189.         // mode only. 
  3190.         this._parseV10UpdateInfo(aDatasource, aLocalItem, newestUpdate);
  3191.       }
  3192.  
  3193.       newerItem = Components.classes["@mozilla.org/updates/item;1"]
  3194.                             .createInstance(Components.interfaces.nsIUpdateItem);
  3195.       newerItem.init(aLocalItem.id, 
  3196.                      newestUpdate.version, 
  3197.                      newestUpdate.minVersion, 
  3198.                      newestUpdate.maxVersion, 
  3199.                      aLocalItem.name, 
  3200.                      -1, newestUpdate.updateLink, "", "", 
  3201.                      aLocalItem.type);
  3202.       if (this._updater._isValidUpdate(aLocalItem, newerItem))
  3203.         ++this._updater._updateCount;
  3204.       else
  3205.         newerItem = null;
  3206.     }
  3207.     
  3208.     // Now look for updated version compatibility metadata for the currently
  3209.     // installed version...
  3210.     this._parseV20UpdateInfo(aDatasource, aLocalItem, versionUpdate, true);
  3211.  
  3212.     var result = gVersionChecker.compare(versionUpdate.version, 
  3213.                                           aLocalItem.version);
  3214.     if (result == 0) {
  3215.       // Local version exactly matches the "Version Update" remote version, 
  3216.       // Apply changes into local datasource.
  3217.       sameItem = Components.classes["@mozilla.org/updates/item;1"]
  3218.                            .createInstance(Components.interfaces.nsIUpdateItem);
  3219.       sameItem.init(aLocalItem.id, 
  3220.                     versionUpdate.version, 
  3221.                     versionUpdate.minVersion, 
  3222.                     versionUpdate.maxVersion, 
  3223.                     aLocalItem.name, 
  3224.                     -1, "", "", "", 
  3225.                     aLocalItem.type);
  3226.       if (!this._versionUpdateOnly) {
  3227.         if (this._updater._isValidUpdate(aLocalItem, sameItem)) {
  3228.           // Install-time updates are not written to the DS because there is no
  3229.           // entry yet, EM just uses the notifications to ascertain (by hand)
  3230.           // whether or not there is a remote maxVersion tweak that makes the 
  3231.           // item being installed compatible.
  3232.           this._updater._applyVersionUpdates(aLocalItem, sameItem);
  3233.         }
  3234.         else 
  3235.           sameItem = null;
  3236.       }
  3237.     }
  3238.     
  3239.     gOS.notifyObservers(!this._versionUpdateOnly ? newerItem : sameItem, 
  3240.                         "Update:Extension:Item-Ended", "");
  3241.     
  3242.     // Only one call of this._updater._checkForDone is needed for RDF 
  3243.     // responses, since there is only one response per item.
  3244.     this._updater._checkForDone();
  3245.   },
  3246.  
  3247.   // Parses Firefox 0.9 update.rdf format  
  3248.   _parseV10UpdateInfo: function nsExtensionItemUpdater__parseV10UpdateInfo (aDataSource, aLocalItem, aUpdateData)
  3249.   {
  3250.     var extensionRes  = gRDF.GetResource(getItemPrefix(aLocalItem.type) + aLocalItem.id);
  3251.     
  3252.     aUpdateData.version     = this._getPropertyFromResource(aDataSource, extensionRes, 
  3253.                                                             "version", aLocalItem);
  3254.     aUpdateData.updateLink  = this._getPropertyFromResource(aDataSource, extensionRes, 
  3255.                                                             "updateLink", aLocalItem);
  3256.   },
  3257.   
  3258.   // Get a compulsory property from a resource. Reports an error if the 
  3259.   // property was not present. 
  3260.   _getPropertyFromResource: function nsExtensionItemUpdater__getPropertyFromResource (aDataSource,
  3261.                                                                                       aSourceResource, 
  3262.                                                                                       aProperty, 
  3263.                                                                                       aLocalItem)
  3264.   {
  3265.     var rv;
  3266.     try {
  3267.       var property = gRDF.GetResource(EM_NS(aProperty));
  3268.       rv = stringData(aDataSource.GetTarget(aSourceResource, property, true));
  3269.       if (rv == "--")
  3270.         throw Components.results.NS_ERROR_FAILURE;
  3271.     }
  3272.     catch (e) { 
  3273.       // XXXben show console message "aProperty" not found on aSourceResource. 
  3274.       return null;
  3275.     }
  3276.     return rv;
  3277.   },
  3278.   
  3279.   // Parses Firefox 1.0RC1+ update.rdf format
  3280.   _parseV20UpdateInfo: function nsExtensionItemUpdater__parseV20UpdateInfo (aDataSource, 
  3281.                                                                             aLocalItem, 
  3282.                                                                             aUpdateData, 
  3283.                                                                             aVersionUpdatesOnly)
  3284.   {
  3285.     var extensionRes  = gRDF.GetResource(getItemPrefix(aLocalItem.type) + aLocalItem.id);
  3286.  
  3287.     var updatesArc = gRDF.GetResource(EM_NS("updates"));
  3288.     var updates = aDataSource.GetTarget(extensionRes, updatesArc, true);
  3289.     
  3290.     try {
  3291.       updates = updates.QueryInterface(Components.interfaces.nsIRDFResource);
  3292.     }
  3293.     catch (e) { return; }
  3294.     
  3295.     var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
  3296.                        .getService(Components.interfaces.nsIRDFContainerUtils);
  3297.     if (cu.IsContainer(aDataSource, updates)) {
  3298.       var c = Components.classes["@mozilla.org/rdf/container;1"]
  3299.                         .getService(Components.interfaces.nsIRDFContainer);
  3300.       c.Init(aDataSource, updates);
  3301.  
  3302.       // In "all update types" mode, we look for newer versions, starting with the 
  3303.       // current installed version.
  3304.       if (!aVersionUpdatesOnly) 
  3305.         aUpdateData.version = aLocalItem.version;
  3306.  
  3307.       var versions = c.GetElements();
  3308.       while (versions.hasMoreElements()) {
  3309.         // There are two different methodologies for collecting version 
  3310.         // information depending on whether or not we've bene invoked in 
  3311.         // "version updates only" mode or "version+newest" mode. 
  3312.         var version = versions.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3313.         this._parseV20Update(aDataSource, version, aLocalItem, aUpdateData, aVersionUpdatesOnly);
  3314.         if (aVersionUpdatesOnly && aUpdateData.updateLink)
  3315.           break;
  3316.       }
  3317.     }
  3318.   },
  3319.   
  3320.   _parseV20Update: function nsExtensionItemUpdater__parseV20Update (aDataSource,
  3321.                                                                     aUpdateResource,
  3322.                                                                     aLocalItem,
  3323.                                                                     aUpdateData,
  3324.                                                                     aVersionUpdatesOnly)
  3325.   {
  3326.     var version = this._getPropertyFromResource(aDataSource, aUpdateResource, 
  3327.                                                 "version", aLocalItem);
  3328.     var taArc = gRDF.GetResource(EM_NS("targetApplication"));
  3329.     var targetApps = aDataSource.GetTargets(aUpdateResource, taArc, true);
  3330.     while (targetApps.hasMoreElements()) {
  3331.       var targetApp = targetApps.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3332.       var id = this._getPropertyFromResource(aDataSource, targetApp, "id", aLocalItem);
  3333.       if (id != this._updater._appID)
  3334.         continue;
  3335.       
  3336.       var result = gVersionChecker.compare(version, aLocalItem.version);
  3337.       if (aVersionUpdatesOnly ? result == 0 : result > 0) {
  3338.         aUpdateData.version = version;
  3339.         aUpdateData.updateLink = this._getPropertyFromResource(aDataSource, targetApp, "updateLink", aLocalItem);
  3340.         aUpdateData.minVersion = this._getPropertyFromResource(aDataSource, targetApp, "minVersion", aLocalItem);
  3341.         aUpdateData.maxVersion = this._getPropertyFromResource(aDataSource, targetApp, "maxVersion", aLocalItem);
  3342.       }
  3343.     }
  3344.   },
  3345.   
  3346.   onDatasourceError: function nsExtensionItemUpdater_onDatasourceError (aItem, aError)
  3347.   {
  3348.     gOS.notifyObservers(aItem, "Update:Extension:Item-Error", aError);
  3349.     this._updater._checkForDone();
  3350.   },
  3351.  
  3352.   /////////////////////////////////////////////////////////////////////////////
  3353.   // nsIRDFXMLSinkObserver
  3354.   onBeginLoad: function(aSink)
  3355.   {
  3356.   },
  3357.   onInterrupt: function(aSink)
  3358.   {
  3359.   },
  3360.   onResume: function(aSink)
  3361.   {
  3362.   },
  3363.   
  3364.   onEndLoad: function(aSink)
  3365.   {
  3366.     try {
  3367.       aSink.removeXMLSinkObserver(this);
  3368.       
  3369.       var ds = aSink.QueryInterface(Components.interfaces.nsIRDFDataSource);
  3370.       this.onDatasourceLoaded(ds, this._item);
  3371.     }
  3372.     catch (e) { }
  3373.   },
  3374.   
  3375.   onError: function(aSink, aStatus, aErrorMsg)
  3376.   {
  3377.     try {
  3378.       aSink.removeXMLSinkObserver(this);
  3379.       
  3380.       this.onDatasourceError(this._item, aStatus.toString());
  3381.     }
  3382.     catch (e) { }
  3383.   }
  3384. };
  3385.  
  3386. ///////////////////////////////////////////////////////////////////////////////
  3387. //
  3388. // nsExtensionsDataSource
  3389. //
  3390. function nsExtensionsDataSource()
  3391. {
  3392. }
  3393.  
  3394. nsExtensionsDataSource.prototype = {
  3395.   _appExtensions      : null,
  3396.   _profileExtensions  : null,  
  3397.   _composite          : null,
  3398.   safeMode            : false,
  3399.   
  3400.   _emR: function nsExtensionsDataSource__emR (aProperty) 
  3401.   {
  3402.     return gRDF.GetResource(EM_NS(aProperty));
  3403.   },
  3404.   
  3405.   _emL: function nsExtensionsDataSource__emL (aLiteral)
  3406.   {
  3407.     return gRDF.GetLiteral(aLiteral);
  3408.   },
  3409.   
  3410.   isCompatible: function nsExtensionsDataSource__isCompatible (aDS, aSource)
  3411.   {
  3412.     // XXXben - cheap hack. Our bundled items are always compatible. 
  3413.     if (aSource.EqualsNode(gRDF.GetResource(getItemPrefix(nsIUpdateItem.TYPE_THEME) + "{f799a0d0-641d-11d9-9669-0800200c9a66}")) || 
  3414.         aSource.EqualsNode(gRDF.GetResource(getItemPrefix(nsIUpdateItem.TYPE_EXTENSION) + "{641d8d09-7dda-4850-8228-ac0ab65e2ac9}")))
  3415.       return true;
  3416.       
  3417.     var appVersion = gPref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
  3418.     var appID = gPref.getCharPref(PREF_EM_APP_ID);
  3419.     
  3420.     var targets = aDS.GetTargets(aSource, this._emR("targetApplication"), true);
  3421.     var idRes = this._emR("id");
  3422.     var minVersionRes = this._emR("minVersion");
  3423.     var maxVersionRes = this._emR("maxVersion");
  3424.     while (targets.hasMoreElements()) {
  3425.       var targetApp = targets.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3426.       var id          = stringData(aDS.GetTarget(targetApp, idRes, true));
  3427.       var minVersion  = stringData(aDS.GetTarget(targetApp, minVersionRes, true));
  3428.       var maxVersion  = stringData(aDS.GetTarget(targetApp, maxVersionRes, true));
  3429.  
  3430.       if (id == appID) {
  3431.         var versionChecker = getVersionChecker();
  3432.         return ((versionChecker.compare(appVersion, minVersion) >= 0) &&
  3433.                 (versionChecker.compare(appVersion, maxVersion) <= 0));
  3434.       }
  3435.     }
  3436.     return false;
  3437.   },
  3438.   
  3439.   getIncompatibleItemList: function nsExtensionsDataSource_getIncompatibleItemList (aAppID, aAppVersion, aItemType)
  3440.   {
  3441.     var items = [];
  3442.     var roots = getItemRoots(aItemType);
  3443.     for (var i = 0; i < roots.length; ++i) {    
  3444.       var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  3445.                           .createInstance(Components.interfaces.nsIRDFContainer);
  3446.       ctr.Init(this._composite, gRDF.GetResource(roots[i]));
  3447.       
  3448.       var elements = ctr.GetElements();
  3449.       while (elements.hasMoreElements()) {
  3450.         var e = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3451.         var itemType = getItemType(e.Value);
  3452.         if (itemType != -1 && !this.isCompatible(this, e))
  3453.           items.push(this.getItemForID(stripPrefix(e.Value, itemType)));
  3454.       }
  3455.     }
  3456.     return items;
  3457.   },
  3458.   
  3459.   getItemList: function nsExtensionsDataSource_getItemList(aItemID, aType, aCountRef)
  3460.   {
  3461.     var items = [];
  3462.     if (aItemID)
  3463.       items.push(this.getItemForID(aItemID));
  3464.     else {
  3465.       var roots = getItemRoots(aType);
  3466.       for (var i = 0; i < roots.length; ++i) {
  3467.         var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  3468.                             .createInstance(Components.interfaces.nsIRDFContainer);
  3469.         ctr.Init(this, gRDF.GetResource(roots[i]));
  3470.         
  3471.         var elements = ctr.GetElements();
  3472.         while (elements.hasMoreElements()) {
  3473.           var e = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3474.           var itemType = getItemType(e.Value);
  3475.           if (itemType != -1)
  3476.             items.push(this.getItemForID(stripPrefix(e.Value, itemType)));
  3477.         }
  3478.       }
  3479.     }
  3480.     aCountRef.value = items.length;
  3481.     return items;
  3482.   },
  3483.  
  3484.   // XXXben this function is a little weird since it returns an array of strings, not
  3485.   // an array of nsIUpdateItems...  
  3486.   getItemsWithFlagSet: function nsExtensionsDataSource_getItemsWithFlagSet (aFlag)
  3487.   {
  3488.     var items = [];
  3489.     var sources = this.GetSources(this._emR(aFlag), this._emL("true"), true);
  3490.     while (sources.hasMoreElements()) {
  3491.       var e = sources.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3492.       
  3493.       items.push(stripPrefix(e.Value, getItemType(e.Value)));
  3494.     }
  3495.     return items;
  3496.   },
  3497.   
  3498.   getItemsWithFlagUnset: function nsExtensionsDataSource_getItemsWithFlagUnset (aFlag, aItemType)
  3499.   {
  3500.     var items = [];
  3501.     
  3502.     var roots = getItemRoots(aItemType);
  3503.     for (var i = 0; i < roots.length; ++i) {
  3504.       var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  3505.                           .createInstance(Components.interfaces.nsIRDFContainer);
  3506.       ctr.Init(this, gRDF.GetResource(roots[i]));
  3507.       
  3508.       var elements = ctr.GetElements();
  3509.       while (elements.hasMoreElements()) {
  3510.         var e = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3511.         if (getItemType(e.Value) != -1) {
  3512.           var value = this.GetTarget(e, this._emR(aFlag), true);
  3513.           if (!value)
  3514.             items.push(stripPrefix(e.Value, getItemType(e.Value)));
  3515.         }
  3516.       }
  3517.     }
  3518.     return items;
  3519.   },
  3520.   
  3521.   getItemForID: function nsExtensionsDataSource_getItemForID (aItemID)
  3522.   {
  3523.     var item = Components.classes["@mozilla.org/updates/item;1"]
  3524.                          .createInstance(Components.interfaces.nsIUpdateItem);
  3525.                          
  3526.     var r = this._getResourceForItem(aItemID);
  3527.     if (!r)
  3528.       return null;
  3529.     
  3530.     var targetAppInfo = this.getTargetApplicationInfo(aItemID, this, getItemType(r.Value));
  3531.     item.init(aItemID, 
  3532.               this.getItemProperty(aItemID, "version"), 
  3533.               targetAppInfo ? targetAppInfo.minVersion : "",
  3534.               targetAppInfo ? targetAppInfo.maxVersion : "",
  3535.               this.getItemProperty(aItemID, "name"),
  3536.               -1, 
  3537.               "", /* XPI Update URL */
  3538.               this.getItemProperty(aItemID, "iconURL"), 
  3539.               this.getItemProperty(aItemID, "updateURL"), 
  3540.               getItemType(r.Value));
  3541.     return item;
  3542.   },
  3543.   
  3544.   isProfileItem: function nsExtensionsDataSource_isProfileItem (aItemID)
  3545.   {
  3546.     return this.getItemProperty(aItemID, "installLocation") != "global";
  3547.   },
  3548.   
  3549.   _setProperty: function nsExtensionsDataSource__setProperty (aDS, aSource, aProperty, aNewValue)
  3550.   {
  3551.     var oldValue = aDS.GetTarget(aSource, aProperty, true);
  3552.     if (oldValue) {
  3553.       if (aNewValue)
  3554.         aDS.Change(aSource, aProperty, oldValue, aNewValue);
  3555.       else
  3556.         aDS.Unassert(aSource, aProperty, oldValue);
  3557.     }
  3558.     else if (aNewValue)
  3559.       aDS.Assert(aSource, aProperty, aNewValue, true);
  3560.   },
  3561.   
  3562.   // Given a GUID, get the RDF resource representing the item. This
  3563.   // will be of the form urn:mozilla:extension:{GUID} or 
  3564.   // urn:mozilla:theme:{GUID} depending on the item type. 
  3565.   _getResourceForItem: function nsExtensionsDataSource__getResourceForItem(aItemID)
  3566.   {
  3567.     var res = null;
  3568.     
  3569.     // We can try and infer the resource URI from presence in one of the 
  3570.     // item lists.
  3571.     var rdfc = Components.classes["@mozilla.org/rdf/container;1"]
  3572.                          .createInstance(Components.interfaces.nsIRDFContainer);
  3573.     rdfc.Init(this, gRDF.GetResource(ROOT_EXTENSION));
  3574.     res = gRDF.GetResource(PREFIX_EXTENSION + aItemID);
  3575.     if (rdfc.IndexOf(res) != -1)
  3576.       return res;
  3577.     
  3578.     rdfc.Init(this, gRDF.GetResource(ROOT_THEME));
  3579.     res = gRDF.GetResource(PREFIX_THEME + aItemID);
  3580.     if (rdfc.IndexOf(res) != -1)
  3581.       return res;
  3582.  
  3583.     return null;
  3584.   },
  3585.  
  3586.   getTargetApplicationInfo: function nsExtensionsDataSource_getTargetApplicationInfo (aExtensionID, aDataSource, aType)
  3587.   {
  3588.     var internalName = this.getItemProperty(aExtensionID, "internalName");
  3589.     // The default theme is always compatible. 
  3590.     if (internalName == KEY_DEFAULT_THEME) {
  3591.       var ver = gPref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
  3592.       return { minVersion: ver, maxVersion: ver };
  3593.     }
  3594.  
  3595.     var appID = gPref.getCharPref(PREF_EM_APP_ID);
  3596.     var r = gRDF.GetResource(getItemPrefix(aType) + aExtensionID);
  3597.     var targetApps = aDataSource.GetTargets(r, this._emR("targetApplication"), true);
  3598.     if (!targetApps.hasMoreElements()) {
  3599.       r = gRDF.GetResource("urn:mozilla:install-manifest"); 
  3600.       targetApps = aDataSource.GetTargets(r, this._emR("targetApplication"), true); 
  3601.     }
  3602.     while (targetApps.hasMoreElements()) {
  3603.       var targetApp = targetApps.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3604.       if (targetApp) {
  3605.         try {
  3606.           var id = stringData(aDataSource.GetTarget(targetApp, this._emR("id"), true));
  3607.           if (id != appID) // Different target application
  3608.             continue;
  3609.           
  3610.           return { minVersion: stringData(aDataSource.GetTarget(targetApp, this._emR("minVersion"), true)),
  3611.                    maxVersion: stringData(aDataSource.GetTarget(targetApp, this._emR("maxVersion"), true)) };
  3612.         }
  3613.         catch (e) { 
  3614.           continue;
  3615.         }
  3616.       }
  3617.     }
  3618.     return null;
  3619.   },
  3620.   
  3621.   setTargetApplicationInfo: function nsExtensionsDataSource_setTargetApplicationInfo(aExtensionID, aMinVersion, 
  3622.                                                                                      aMaxVersion, aDataSource, aType)
  3623.   {
  3624.     var targetDataSource = aDataSource;
  3625.     if (!targetDataSource)
  3626.       targetDataSource = this;
  3627.       
  3628.     var appID = gPref.getCharPref(PREF_EM_APP_ID);
  3629.     var r = gRDF.GetResource(getItemPrefix(aType) + aExtensionID);
  3630.     var targetApps = targetDataSource.GetTargets(r, this._emR("targetApplication"), true);
  3631.     if (!targetApps.hasMoreElements()) {
  3632.       r = gRDF.GetResource("urn:mozilla:install-manifest"); 
  3633.       targetApps = aDataSource.GetTargets(r, this._emR("targetApplication"), true); 
  3634.     }
  3635.     while (targetApps.hasMoreElements()) {
  3636.       var targetApp = targetApps.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3637.       if (targetApp) {
  3638.         var id = stringData(targetDataSource.GetTarget(targetApp, this._emR("id"), true));
  3639.         if (id != appID) // Different target application
  3640.           continue;
  3641.         
  3642.         if (!aDataSource) {
  3643.           var isProfile = this.isProfileItem(aExtensionID);
  3644.           targetDataSource = isProfile ? this._profileExtensions : this._appExtensions;
  3645.         }
  3646.         this._setProperty(targetDataSource, targetApp, this._emR("minVersion"), this._emL(aMinVersion));
  3647.         this._setProperty(targetDataSource, targetApp, this._emR("maxVersion"), this._emL(aMaxVersion));
  3648.         
  3649.         if (!aDataSource)
  3650.           this._flush(isProfile);
  3651.       }
  3652.     }
  3653.   },
  3654.   
  3655.   getItemProperty: function nsExtensionsDataSource_getItemProperty (aItemID, aProperty)
  3656.   { 
  3657.     var item = this._getResourceForItem(aItemID);
  3658.     if (!item) {
  3659.       dump("*** getItemProperty failing for lack of an item. This means getResourceForItem \
  3660.                 failed to locate a resource for aItemID (item ID = " + aItemID + ", property = " + aProperty + ")\n");
  3661.     }
  3662.     else 
  3663.       return this._getItemProperty(item, aProperty);
  3664.     return undefined;
  3665.   },
  3666.   
  3667.   _getItemProperty: function nsExtensionsDataSource__getItemProperty (aItemResource, aProperty)
  3668.   {
  3669.     try {
  3670.       return this.GetTarget(aItemResource, this._emR(aProperty), true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  3671.     }
  3672.     catch (e) {}
  3673.     return "";
  3674.   },
  3675.   
  3676.   setItemProperty: function nsExtensionsDataSource_setItemProperty(
  3677.     aItemID, aPropertyArc, aPropertyValue, aIsProfile, aItemType)
  3678.   {
  3679.     var item = gRDF.GetResource(getItemPrefix(aItemType) + aItemID);
  3680.     var ds = aIsProfile ? this._profileExtensions : this._appExtensions;
  3681.     this._setProperty(ds, item, aPropertyArc, aPropertyValue);
  3682.  
  3683.     this._flush(aIsProfile);  
  3684.   },
  3685.  
  3686.   insertForthcomingItem: function nsExtensionsDataSource_insertForthcomingItem (aItemID, aItemType, aIsProfile)
  3687.   {
  3688.     // Get the target container and resource
  3689.     var targetDS = aIsProfile ? this._profileExtensions : this._appExtensions;
  3690.     var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  3691.                         .createInstance(Components.interfaces.nsIRDFContainer);
  3692.     ctr.Init(targetDS, gRDF.GetResource(getItemRoot(aItemType)));
  3693.  
  3694.     var targetRes = gRDF.GetResource(getItemPrefix(aItemType) + aItemID);
  3695.     // Don't bother adding the extension to the list if it's already there. 
  3696.     // (i.e. we're upgrading)
  3697.     var oldIndex = ctr.IndexOf(targetRes);
  3698.     if (oldIndex == -1)
  3699.       ctr.AppendElement(targetRes);
  3700.  
  3701.     this._flush(aIsProfile);
  3702.   }, 
  3703.  
  3704.   removeItemFromContainer: function nsExtensionsDataSource_removeItemFromContainer(aItemID, aItemType, aIsProfile)
  3705.   {
  3706.     var targetDS = aIsProfile ? this._profileExtensions : this._appExtensions;
  3707.     var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  3708.                         .createInstance(Components.interfaces.nsIRDFContainer);
  3709.     ctr.Init(targetDS, gRDF.GetResource(getItemRoot(aItemType)));
  3710.     
  3711.     var item = gRDF.GetResource(getItemPrefix(aItemType) + aItemID);
  3712.     ctr.RemoveElement(item, true);
  3713.     
  3714.     this._flush(aIsProfile);
  3715.   },
  3716.  
  3717.   // Removes a corrupt item entry from the extension list added due to 
  3718.   // buggy code in previous EM versions!  
  3719.   removeCorruptItem: function nsExtensionsDataSource_removeCorruptItem (aItemID, aItemType, aIsProfile)
  3720.   {
  3721.     this.removeItemMetadata(aItemID, aItemType);
  3722.     this.removeItemFromContainer(aItemID, aItemType, aIsProfile);
  3723.   },
  3724.  
  3725.   // Removes a corrupt download entry from the list.   
  3726.   removeCorruptDLItem: function nsExtensionsDataSource_removeCorruptDLItem (aItemURI, aItemType)
  3727.   {
  3728.     var itemResource = gRDF.GetResource(aItemURI);
  3729.     var itemRoot = gRDF.GetResource(getItemRoot(aItemType));
  3730.     var dses = [this._profileExtensions, this._appExtensions];
  3731.     var isProfile = [true, false];
  3732.     for (var i = 0; i < dses.length; ++i) {
  3733.       var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  3734.                           .createInstance(Components.interfaces.nsIRDFContainer);
  3735.       ctr.Init(dses[i], itemRoot);
  3736.       if (ctr.IndexOf(itemResource) != -1) {
  3737.         ctr.RemoveElement(itemResource, true);
  3738.         this._cleanResource(itemResource, dses[i]);
  3739.         this._flush(isProfile[i]);
  3740.         break;
  3741.       }
  3742.     }
  3743.   },
  3744.   
  3745.   addItemMetadata: function nsExtensionsDataSource_addItemMetadata (aItemID, aItemType, aSourceDS, aIsProfile)
  3746.   {
  3747.     var targetDS = aIsProfile ? this._profileExtensions : this._appExtensions;
  3748.     var targetRes = gRDF.GetResource(getItemPrefix(aItemType) + aItemID);
  3749.  
  3750.     // Copy the assertions over from the source datasource. 
  3751.     
  3752.     // Assert properties with single values
  3753.     var singleProps = ["version", "name", "description", "creator", "homepageURL", 
  3754.                        "updateURL", "updateService", "optionsURL", "aboutURL", 
  3755.                        "iconURL", "internalName"];
  3756.  
  3757.     // Global extensions and themes can also be locked (can't be removed or disabled).
  3758.     if (!aIsProfile)
  3759.       singleProps = singleProps.concat(["locked"]);
  3760.     var sourceRes = gRDF.GetResource("urn:mozilla:install-manifest");
  3761.     for (var i = 0; i < singleProps.length; ++i) {
  3762.       var property = this._emR(singleProps[i]);
  3763.       var literal = aSourceDS.GetTarget(sourceRes, property, true);
  3764.       if (!literal)
  3765.         continue; // extension didn't specify this property, no big deal, continue.
  3766.         
  3767.       var val = literal.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  3768.       
  3769.       var oldValue = targetDS.GetTarget(targetRes, property, true);
  3770.       if (!oldValue)
  3771.         targetDS.Assert(targetRes, property, literal, true);
  3772.       else
  3773.         targetDS.Change(targetRes, property, oldValue, literal);
  3774.     }    
  3775.  
  3776.     // Assert properties with multiple values    
  3777.     var manyProps = ["contributor"];
  3778.     for (var i = 0; i < singleProps.length; ++i) {
  3779.       var property = this._emR(manyProps[i]);
  3780.       var literals = aSourceDS.GetTargets(sourceRes, property, true);
  3781.       
  3782.       var oldValues = targetDS.GetTargets(targetRes, property, true);
  3783.       while (oldValues.hasMoreElements()) {
  3784.         var oldValue = oldValues.getNext().QueryInterface(Components.interfaces.nsIRDFNode);
  3785.         targetDS.Unassert(targetRes, property, oldValue);
  3786.       }
  3787.       while (literals.hasMoreElements()) {
  3788.         var literal = literals.getNext().QueryInterface(Components.interfaces.nsIRDFNode);
  3789.         targetDS.Assert(targetRes, property, literal, true);
  3790.       }
  3791.     }
  3792.     
  3793.     // Version/Dependency Info
  3794.     var versionProps = ["targetApplication", "requires"];
  3795.     var idRes = this._emR("id");
  3796.     var minVersionRes = this._emR("minVersion");
  3797.     var maxVersionRes = this._emR("maxVersion");
  3798.     for (var i = 0; i < versionProps.length; ++i) {
  3799.       var property = this._emR(versionProps[i]);
  3800.       var newVersionInfos = aSourceDS.GetTargets(sourceRes, property, true);
  3801.       
  3802.       var oldVersionInfos = targetDS.GetTargets(targetRes, property, true);
  3803.       while (oldVersionInfos.hasMoreElements()) {
  3804.         var oldVersionInfo = oldVersionInfos.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3805.         this._cleanResource(oldVersionInfo, targetDS);
  3806.         targetDS.Unassert(targetRes, property, oldVersionInfo);
  3807.       }
  3808.       while (newVersionInfos.hasMoreElements()) {
  3809.         var newVersionInfo = newVersionInfos.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3810.         var anon = gRDF.GetAnonymousResource();
  3811.         targetDS.Assert(anon, idRes, aSourceDS.GetTarget(newVersionInfo, idRes, true), true);
  3812.         targetDS.Assert(anon, minVersionRes, aSourceDS.GetTarget(newVersionInfo, minVersionRes, true), true);
  3813.         targetDS.Assert(anon, maxVersionRes, aSourceDS.GetTarget(newVersionInfo, maxVersionRes, true), true);
  3814.         targetDS.Assert(targetRes, property, anon, true);
  3815.       }
  3816.     }
  3817.     
  3818.     this._flush(aIsProfile);
  3819.   },
  3820.   
  3821.   lockUnlockItem: function nsExtensionsDataSource_lockUnlockItem (aItemID, aLocked)
  3822.   {
  3823.     var item = this._getResourceForItem(aItemID);
  3824.     if (item) {
  3825.       var val = aLocked ? this._emL("true") : this._emL("false");
  3826.       this.setItemProperty(aItemID, this._emR("locked"), val, false, getItemType(item.Value));
  3827.       this._flush(false);
  3828.     }
  3829.   },  
  3830.   
  3831.   enableExtension: function nsExtensionsDataSource_enableExtension (aExtensionID)
  3832.   {
  3833.     this.setItemProperty(aExtensionID, this._emR("toBeEnabled"), 
  3834.                          this._emL("true"), this.isProfileItem(aExtensionID), 
  3835.                          nsIUpdateItem.TYPE_EXTENSION);
  3836.     this.setItemProperty(aExtensionID, this._emR("toBeDisabled"), 
  3837.                          null, this.isProfileItem(aExtensionID), 
  3838.                          nsIUpdateItem.TYPE_EXTENSION);
  3839.     this.setItemProperty(aExtensionID, this._emR("disabled"), 
  3840.                          null, this.isProfileItem(aExtensionID), 
  3841.                          nsIUpdateItem.TYPE_EXTENSION);
  3842.   },
  3843.   
  3844.   disableExtension: function nsExtensionsDataSource_disableExtension (aExtensionID)
  3845.   {
  3846.     this.setItemProperty(aExtensionID, this._emR("toBeDisabled"), 
  3847.                          this._emL("true"), this.isProfileItem(aExtensionID), 
  3848.                          nsIUpdateItem.TYPE_EXTENSION);
  3849.     this.setItemProperty(aExtensionID, this._emR("toBeEnabled"), 
  3850.                          null, this.isProfileItem(aExtensionID), 
  3851.                          nsIUpdateItem.TYPE_EXTENSION);
  3852.     this.setItemProperty(aExtensionID, this._emR("disabled"), 
  3853.                          this._emL("true"), this.isProfileItem(aExtensionID), 
  3854.                          nsIUpdateItem.TYPE_EXTENSION);
  3855.   },
  3856.   
  3857.   uninstallExtension: function nsExtensionsDataSource_uninstallExtension (aExtensionID)
  3858.   {
  3859.     // We have to do this check BEFORE we unhook all the metadata from this 
  3860.     // extension's resource, otherwise we'll think it's a global extension.
  3861.     var isProfile = this.isProfileItem(aExtensionID);
  3862.     
  3863.     this.setItemProperty(aExtensionID, this._emR("toBeInstalled"), 
  3864.                          null, isProfile, 
  3865.                          nsIUpdateItem.TYPE_EXTENSION);
  3866.     this.setItemProperty(aExtensionID, this._emR("toBeUninstalled"), 
  3867.                          this._emL("true"), isProfile, 
  3868.                          nsIUpdateItem.TYPE_EXTENSION);
  3869.     this._flush(isProfile);
  3870.   },
  3871.   
  3872.   doneInstallingTheme: function nsExtensionsDataSource_doneInstallingTheme (aThemeID)
  3873.   {
  3874.     // Notify observers of a change in the iconURL property to cause the UI to
  3875.     // refresh.
  3876.     var theme = this._getResourceForItem(aThemeID);
  3877.     var iconURLArc = this._emR("iconURL");
  3878.     var iconURL = this.GetTarget(theme, iconURLArc, true);
  3879.     if (theme, iconURLArc, iconURL) {
  3880.       for (var i = 0; i < this._observers.length; ++i)
  3881.         this._observers[i].onAssert(this, theme, iconURLArc, iconURL);
  3882.     }
  3883.   },
  3884.   
  3885.   enableTheme: function nsExtensionsDataSource_enableTheme (aThemeID)
  3886.   {
  3887.     this.setItemProperty(aThemeID, this._emR("disabled"), 
  3888.                          null, this.isProfileItem(aThemeID), 
  3889.                          nsIUpdateItem.TYPE_THEME);
  3890.   },
  3891.     
  3892.   disableTheme: function nsExtensionsDataSource_disableTheme (aThemeID)
  3893.   {
  3894.     this.setItemProperty(aThemeID, this._emR("disabled"), 
  3895.                          this._emL("true"), this.isProfileItem(aThemeID), 
  3896.                          nsIUpdateItem.TYPE_THEME);
  3897.   },
  3898.   
  3899.   uninstallTheme: function nsExtensionsDataSource_uninstallTheme(aThemeID)
  3900.   {
  3901.     // We have to do this check BEFORE we unhook all the metadata from this 
  3902.     // extension's resource, otherwise we'll think it's a global extension.
  3903.     var isProfile = this.isProfileItem(aThemeID);
  3904.         
  3905.     // Clean the extension resource
  3906.     this.removeItemMetadata(aThemeID, nsIUpdateItem.TYPE_THEME);
  3907.     
  3908.     var uninstaller = new nsThemeUninstaller(this);
  3909.     uninstaller.uninstall(aThemeID, isProfile);  
  3910.     
  3911.     // Do this LAST since inferences are made about an item based on
  3912.     // what container it's in.
  3913.     this.removeItemFromContainer(aThemeID, nsIUpdateItem.TYPE_THEME, isProfile);
  3914.   },
  3915.   
  3916.   // Cleans the resource of all its assertionss
  3917.   removeItemMetadata: function nsExtensionsDataSource_removeItemMetadata (aItemID, aItemType)
  3918.   {
  3919.     var item = gRDF.GetResource(getItemPrefix(aItemType) + aItemID);
  3920.     var isProfile = this.isProfileItem(aItemID);
  3921.     var ds = isProfile ? this._profileExtensions : this._appExtensions;
  3922.     
  3923.     var resources = ["targetApplication", "requires"];
  3924.     for (var i = 0; i < resources.length; ++i) {
  3925.       var targetApps = ds.GetTargets(item, this._emR(resources[i]), true);
  3926.       while (targetApps.hasMoreElements()) {
  3927.         var targetApp = targetApps.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3928.         this._cleanResource(targetApp, ds);
  3929.       }
  3930.     }
  3931.  
  3932.     this._cleanResource(item, ds);
  3933.   },
  3934.   
  3935.   _cleanResource: function nsExtensionsDataSource__cleanResource (aResource, aDS)
  3936.   {
  3937.     // Remove outward arcs
  3938.     var arcs = aDS.ArcLabelsOut(aResource);
  3939.     while (arcs.hasMoreElements()) {
  3940.       var arc = arcs.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  3941.       var value = aDS.GetTarget(aResource, arc, true);
  3942.       if (value)
  3943.         aDS.Unassert(aResource, arc, value);
  3944.     }
  3945.   },
  3946.   
  3947.   moveTop: function nsExtensionsDataSource_moveTop (aItemID)
  3948.   {
  3949.     var extensions = gRDF.GetResource("urn:mozilla:extension:root");
  3950.     var item = this._getResourceForItem(aItemID);
  3951.     var ds = this._getTargetDSFromSource(item);
  3952.     var container = Components.classes["@mozilla.org/rdf/container;1"]
  3953.                               .createInstance(Components.interfaces.nsIRDFContainer);
  3954.     container.Init(ds, extensions);
  3955.     
  3956.     var index = container.IndexOf(item);
  3957.     if (index > 1) {
  3958.       container.RemoveElement(item, false);
  3959.       container.InsertElementAt(item, 1, true);
  3960.     }
  3961.     this._flush(this.isProfileItem(aItemID));
  3962.   },
  3963.   
  3964.   moveUp: function nsExtensionsDataSource_moveUp (aItemID)
  3965.   {
  3966.     var extensions = gRDF.GetResource("urn:mozilla:extension:root");
  3967.     var item = this._getResourceForItem(aItemID);
  3968.     var ds = this._getTargetDSFromSource(item);
  3969.     var container = Components.classes["@mozilla.org/rdf/container;1"]
  3970.                               .createInstance(Components.interfaces.nsIRDFContainer);
  3971.     container.Init(ds, extensions);
  3972.     
  3973.     var item = this._getResourceForItem(aItemID);
  3974.     var index = container.IndexOf(item);
  3975.     if (index > 1) {
  3976.       container.RemoveElement(item, false);
  3977.       container.InsertElementAt(item, index - 1, true);
  3978.     }
  3979.     this._flush(this.isProfileItem(aItemID));
  3980.   },
  3981.   
  3982.   moveDown: function nsExtensionsDataSource_moveDown (aItemID)
  3983.   {
  3984.     var extensions = gRDF.GetResource("urn:mozilla:extension:root");
  3985.     var item = this._getResourceForItem(aItemID);
  3986.     var ds = this._getTargetDSFromSource(item);
  3987.     var container = Components.classes["@mozilla.org/rdf/container;1"]
  3988.                               .createInstance(Components.interfaces.nsIRDFContainer);
  3989.     container.Init(ds, extensions);
  3990.     
  3991.     var item = this._getResourceForItem(aItemID);
  3992.     var index = container.IndexOf(item);
  3993.     var count = container.GetCount();
  3994.     if (index < count) {
  3995.       container.RemoveElement(item, true);
  3996.       container.InsertElementAt(item, index + 1, true);
  3997.     }
  3998.     this._flush(this.isProfileItem(aItemID));
  3999.   },
  4000.   
  4001.   isDownloadItem: function nsExtensionsDataSource_isDownloadItem (aItemID)
  4002.   {
  4003.     return this.getItemProperty(aItemID, "downloadURL") != "";
  4004.   },
  4005.  
  4006.   addDownload: function nsExtensionsDataSource_addDownload (aName, aURL, aIconURL, aItemType)
  4007.   {
  4008.     var root = gRDF.GetResource(getItemRoot(aItemType));
  4009.     
  4010.     var res = gRDF.GetResource(aURL);
  4011.     this._setProperty(this._profileExtensions, res, 
  4012.                       this._emR("name"),
  4013.                       gRDF.GetLiteral(aName))
  4014.     this._setProperty(this._profileExtensions, res, 
  4015.                       this._emR("version"),
  4016.                       gRDF.GetLiteral(" "));
  4017.     this._setProperty(this._profileExtensions, res, 
  4018.                       this._emR("iconURL"),
  4019.                       gRDF.GetLiteral(aIconURL));
  4020.     this._setProperty(this._profileExtensions, res, 
  4021.                       this._emR("downloadURL"),
  4022.                       gRDF.GetLiteral(aURL));
  4023.  
  4024.     var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  4025.                         .createInstance(Components.interfaces.nsIRDFContainer);
  4026.     ctr.Init(this._profileExtensions, root);
  4027.     if (ctr.IndexOf(res) == -1)
  4028.       ctr.InsertElementAt(res, 1, true);
  4029.     
  4030.     this._flush(true);
  4031.   },
  4032.   
  4033.   removeDownload: function nsExtensionsDataSource_removeDownload (aURL, aItemType)
  4034.   {
  4035.     var root = gRDF.GetResource(getItemRoot(aItemType));
  4036.     var res = gRDF.GetResource(aURL);
  4037.     var ctr = Components.classes["@mozilla.org/rdf/container;1"]
  4038.                         .createInstance(Components.interfaces.nsIRDFContainer);
  4039.     ctr.Init(this._profileExtensions, root);
  4040.     ctr.RemoveElement(res, true);
  4041.     this._cleanResource(res, this._profileExtensions);
  4042.     
  4043.     this._flush(true);
  4044.   },
  4045.     
  4046.   flushProgressInfo: function nsExtensionsDataSource_flushProgressInfo (aData)
  4047.   {
  4048.     for (var url in aData) {
  4049.       var res = gRDF.GetResource(url);
  4050.       this._setProperty(this._profileExtensions, res, 
  4051.                         this._emR("state"),
  4052.                         gRDF.GetIntLiteral(aData[url].state));
  4053.       this._setProperty(this._profileExtensions, res, 
  4054.                         this._emR("progress"),
  4055.                         gRDF.GetIntLiteral(aData[url].progress));
  4056.     }
  4057.     this._flush(true);
  4058.   },   
  4059.    
  4060.   loadExtensions: function nsExtensionsDataSource_loadExtensions (aProfile)
  4061.   {
  4062.     var extensionsFile  = getFile(getDirKey(aProfile), 
  4063.                                   [DIR_EXTENSIONS, FILE_EXTENSIONS]);
  4064.     ensureExtensionsFiles(aProfile);
  4065.  
  4066.     var ds = gRDF.GetDataSourceBlocking(getURLSpecFromFile(extensionsFile));
  4067.     if (aProfile) {
  4068.       this._profileExtensions = ds;
  4069.       if (!this._composite) 
  4070.         this._composite = Components.classes["@mozilla.org/rdf/datasource;1?name=composite-datasource"]
  4071.                                     .createInstance(Components.interfaces.nsIRDFDataSource);
  4072.       if (this._appExtensions)
  4073.         this._composite.RemoveDataSource(this._appExtensions);
  4074.       this._composite.AddDataSource(this._profileExtensions);
  4075.       if (this._appExtensions)
  4076.         this._composite.AddDataSource(this._appExtensions);  
  4077.     }
  4078.     else {
  4079.       this._appExtensions = ds;
  4080.       
  4081.       if (!this._composite)
  4082.         this._composite = Components.classes["@mozilla.org/rdf/datasource;1?name=composite-datasource"]
  4083.                                     .createInstance(Components.interfaces.nsIRDFCompositeDataSource);
  4084.       this._composite.AddDataSource(this._appExtensions);
  4085.     }
  4086.   },
  4087.   
  4088.   /////////////////////////////////////////////////////////////////////////////
  4089.   // nsIRDFDataSource
  4090.   get URI()
  4091.   {
  4092.     return "rdf:extensions";
  4093.   },
  4094.   
  4095.   GetSource: function nsExtensionsDataSource_GetSource (aProperty, aTarget, aTruthValue)
  4096.   {
  4097.     return this._composite.GetSource(aProperty, aTarget, aTruthValue);
  4098.   },
  4099.   
  4100.   GetSources: function nsExtensionsDataSource_GetSources (aProperty, aTarget, aTruthValue)
  4101.   {
  4102.     return this._composite.GetSources(aProperty, aTarget, aTruthValue);
  4103.   },
  4104.   
  4105.   _getThemeJARURL: function nsExtensionsDataSource__getThemeJARURL (aSource, aFileName, aFallbackURL)
  4106.   {
  4107.   dump ("ExtensionManager, in _getThemeJARURL with params: " + aSource + ", " + aFileName + ", " + aFallbackURL + "\n\n");
  4108.     var id = stripPrefix(aSource.Value, nsIUpdateItem.TYPE_THEME);
  4109.     var chromeDir = getDir(this.isProfileItem(id) ? KEY_PROFILEDIR : KEY_APPDIR, 
  4110.                             [DIR_EXTENSIONS, id, DIR_CHROME]);
  4111.  
  4112.     var jarFile = null;
  4113.     // XXXben hack for pre-configured classic.jar
  4114.     // MERC (ccampbell): changed to the id for the new fusion theme
  4115.     if ((!chromeDir.exists() || !chromeDir.directoryEntries.hasMoreElements()) &&
  4116.         aSource.EqualsNode(gRDF.GetResource("urn:mozilla:theme:{f799a0d0-641d-11d9-9669-0800200c9a66}")))
  4117.       jarFile = getFile(KEY_APPDIR, ["chrome", "fusion.jar"]);
  4118. // JMC - adding winscape theme support
  4119.     if ((!chromeDir.exists() || !chromeDir.directoryEntries.hasMoreElements()) &&
  4120.         aSource.EqualsNode(gRDF.GetResource("urn:mozilla:theme:{8803789A-23EB-44b4-BD48-6762FD320242}")))
  4121.       jarFile = getFile(KEY_APPDIR, ["chrome", "winscape.jar"]);
  4122.  
  4123.     if (chromeDir.directoryEntries.hasMoreElements() || jarFile) {
  4124.       if (!jarFile)
  4125.         jarFile = chromeDir.directoryEntries.getNext().QueryInterface(Components.interfaces.nsIFile);
  4126.  
  4127.       if (jarFile.exists()) {
  4128.         var zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
  4129.                                   .createInstance(Components.interfaces.nsIZipReader);
  4130.         zipReader.init(jarFile);
  4131.         zipReader.open();
  4132.         var url = aFallbackURL;
  4133.         try {
  4134.           zipReader.getEntry(aFileName);
  4135.           url = "jar:" + getURLSpecFromFile(jarFile) + "!/" + aFileName; 
  4136.         }
  4137.         catch (e) { }
  4138.         zipReader.close();
  4139.         
  4140.         if (url)
  4141.           return gRDF.GetResource(url);
  4142.       }
  4143.     }
  4144.     return null;
  4145.   },
  4146.   
  4147.   GetTarget: function nsExtensionsDataSource_GetTarget(aSource, aProperty, aTruthValue)
  4148.   {
  4149.     if (!aSource)
  4150.       return null;
  4151.       
  4152.     if (aProperty.EqualsNode(this._emR("iconURL"))) {
  4153.       var itemType = getItemType(aSource.Value);
  4154.       if (itemType != -1 && itemType & nsIUpdateItem.TYPE_EXTENSION) {
  4155.         var hasIconURL = this._composite.hasArcOut(aSource, aProperty);
  4156.         // If the download entry doesn't have a IconURL property, use a
  4157.         // generic icon URL instead.
  4158.         if (!hasIconURL)
  4159.           return gRDF.GetResource("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png");
  4160.         else {
  4161.           var iconURL = this._composite.GetTarget(aSource, aProperty, true);
  4162.           iconURL = iconURL.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  4163.           var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
  4164.                              .getService(Components.interfaces.nsIChromeRegistry);
  4165.           var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
  4166.                                  .getService(Components.interfaces.nsIIOService);
  4167.           var uri = ioServ.newURI(iconURL, null, null);
  4168.           try {
  4169.             cr.convertChromeURL(uri);
  4170.           }
  4171.           catch(e) {
  4172.             // bogus URI, supply a generic icon. 
  4173.             return gRDF.GetResource("chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png");
  4174.           }
  4175.         }
  4176.       }
  4177.       else if (itemType != -1 && itemType & nsIUpdateItem.TYPE_THEME) {
  4178.         var res = this._getThemeJARURL(aSource, "icon.png", "chrome://mozapps/skin/extensions/themeGeneric.png");
  4179.         if (res)
  4180.           return res;
  4181.       }
  4182.     }
  4183.     else if (aProperty.EqualsNode(this._emR("previewImage"))) {
  4184.       var itemType = getItemType(aSource.Value);
  4185.       if (itemType != -1 && itemType & nsIUpdateItem.TYPE_THEME) {
  4186.         var res = this._getThemeJARURL(aSource, "preview.png", null);
  4187.         if (res)
  4188.           return res;
  4189.       }
  4190.     }
  4191.     else if (aProperty.EqualsNode(this._emR("installLocation"))) {
  4192.       var arcs = this._profileExtensions.ArcLabelsOut(aSource);
  4193.       return arcs.hasMoreElements() ? this._emL("profile") : this._emL("global");
  4194.     }
  4195.     else if (aProperty.EqualsNode(this._emR("disabled"))) {
  4196.       if (this.safeMode) 
  4197.         return this._emL("true");
  4198.       // fall through to default.
  4199.     }
  4200.     else if (aProperty.EqualsNode(this._emR("itemType"))) {
  4201.       // We can try and infer the type from presence in one of the 
  4202.       // item lists.
  4203.       var rdfc = Components.classes["@mozilla.org/rdf/container;1"]
  4204.                           .createInstance(Components.interfaces.nsIRDFContainer);
  4205.       rdfc.Init(this, gRDF.GetResource(ROOT_EXTENSION));
  4206.       if (rdfc.IndexOf(aSource) != -1) 
  4207.         return this._emL("extension");
  4208.     
  4209.       rdfc.Init(this, gRDF.GetResource(ROOT_THEME));
  4210.       if (rdfc.IndexOf(aSource) != -1) 
  4211.         return this._emL("theme");
  4212.     }
  4213.     else if (aProperty.EqualsNode(this._emR("compatible"))) {
  4214.       var type = getItemType(aSource.Value);
  4215.       var id = stripPrefix(aSource.Value, type);
  4216.       var targetAppInfo = this.getTargetApplicationInfo(id, this, type);
  4217.       if (!targetAppInfo)
  4218.         return this._emL("false");
  4219.       getVersionChecker();
  4220.       
  4221.       var appVersion = gPref.getCharPref(PREF_EM_APP_EXTENSIONS_VERSION);
  4222.       if (gVersionChecker.compare(targetAppInfo.maxVersion, appVersion) < 0 || 
  4223.           gVersionChecker.compare(appVersion, targetAppInfo.minVersion) < 0) {
  4224.         // OK, this item is incompatible. 
  4225.         return this._emL("false");
  4226.       }
  4227.       return this._emL("true");
  4228.     }
  4229.     else if (aProperty.EqualsNode(this._emR("displayDescription"))) {
  4230.       // We have a separate property for the description of the extension items
  4231.       // which is displayed in the EM list - because we overload this value with
  4232.       // alternative messages when the extension is disabled because of 
  4233.       // incompatibility.
  4234.       var disabled = this.getItemProperty(stripPrefix(aSource.Value, getItemType(aSource.Value)), 
  4235.                                           "disabled");
  4236.       if (disabled == "true") {
  4237.         // See if this item was disabled because it was incompatible. 
  4238.         // XXXben potential visual-glitch bug here with extensions whose install.rdf 
  4239.         //        manifests state that they are incompatible but when phone home checking
  4240.         //        reveals that they are compatible and they are installed the 
  4241.         //        incompatible metadata is written anyway and will remain in the ds
  4242.         //        until the next background update check corrects it - this means that
  4243.         //        when a compatible extension is installed in this manner it is 
  4244.         //        likely that when it is disabled it will show this special-case
  4245.         //        error message.
  4246.         var compatible = this.getItemProperty(stripPrefix(aSource.Value, getItemType(aSource.Value)), 
  4247.                                               "compatible");
  4248.         if (compatible != "true") {
  4249.           var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  4250.                               .getService(Components.interfaces.nsIStringBundleService);
  4251.           var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
  4252.           var brandStrings = sbs.createBundle("chrome://global/locale/brand.properties");
  4253.           var brandShortName = brandStrings.GetStringFromName("brandShortName");
  4254.           var appVersion = gPref.getCharPref(PREF_EM_APP_VERSION);
  4255.           var incompatibleMessage = extensionStrings.formatStringFromName("incompatibleExtension", 
  4256.                                                                           [brandShortName, appVersion], 2);
  4257.           return this._emL(incompatibleMessage);
  4258.         }
  4259.       }
  4260.       // Use the "description" property. 
  4261.       return this.GetTarget(aSource, this._emR("description"), aTruthValue);
  4262.     }
  4263.     else if (aProperty.EqualsNode(this._emR("name")) || 
  4264.              aProperty.EqualsNode(this._emR("description")) || 
  4265.              aProperty.EqualsNode(this._emR("creator")) || 
  4266.              aProperty.EqualsNode(this._emR("homepageURL"))) {
  4267.       // These are localizable properties that a language pack supplied by the 
  4268.       // Extension may override.          
  4269.       var prefName = PREF_EM_EXTENSION_FORMAT.replace(/%UUID%/, 
  4270.                                                       stripPrefix(aSource.Value, 
  4271.                                                                   nsIUpdateItem.TYPE_EXTENSION)) + 
  4272.                       stripPropertyPrefix(aProperty.Value, EM_NS_PREFIX);
  4273.       try {
  4274.         var value = gPref.getComplexValue(prefName, 
  4275.                                           Components.interfaces.nsIPrefLocalizedString);
  4276.         if (value.data) 
  4277.           return this._emL(value.data);
  4278.       }
  4279.       catch (e) {
  4280.       }
  4281.     }
  4282.     
  4283.     return this._composite.GetTarget(aSource, aProperty, aTruthValue);
  4284.   },
  4285.   
  4286.   GetTargets: function nsExtensionsDataSource_GetTargets (aSource, aProperty, aTruthValue)
  4287.   {
  4288.     if (aProperty.EqualsNode(this._emR("name")) ||
  4289.         aProperty.EqualsNode(this._emR("contributor"))) {
  4290.       // These are localizable properties that a language pack supplied by the 
  4291.       // Extension may override.          
  4292.       var contributors = [];
  4293.       var prefName = PREF_EM_EXTENSION_FORMAT.replace(/%UUID%/, 
  4294.                                                       stripPrefix(aSource.Value, 
  4295.                                                                   nsIUpdateItem.TYPE_EXTENSION)) + 
  4296.                       stripPropertyPrefix(aProperty.Value, EM_NS_PREFIX);
  4297.       var i = 0;
  4298.       do {
  4299.         try {
  4300.           var value = gPref.getComplexValue(prefName + "." + ++i, 
  4301.                                             Components.interfaces.nsIPrefLocalizedString);
  4302.           if (value.data) 
  4303.             contributors.push(this._emL(value.data));
  4304.         }
  4305.         catch (e) {
  4306.           try {
  4307.             var value = gPref.getComplexValue(prefName, 
  4308.                                               Components.interfaces.nsIPrefLocalizedString);
  4309.             if (value.data) 
  4310.               contributors.push(this._emL(value.data));
  4311.           }
  4312.           catch (e) {
  4313.           }
  4314.           break;
  4315.         }
  4316.       }
  4317.       while (1);
  4318.       if (contributors.length > 0)
  4319.         return new ArrayEnumerator(contributors);
  4320.     }
  4321.     return this._composite.GetTargets(aSource, aProperty, aTruthValue);
  4322.   },
  4323.   
  4324.   _getTargetDSFromSource: function nsExtensionsDataSource__getTargetDSFromSource (aSource)
  4325.   {
  4326.     var itemID = stripPrefix(aSource.Value, nsIUpdateItem.TYPE_ADDON);
  4327.     return this.isProfileItem(itemID) ? this._profileExtensions : this._appExtensions;
  4328.   },
  4329.   
  4330.   Assert: function nsExtensionsDataSource_Assert (aSource, aProperty, aTarget, aTruthValue)
  4331.   {
  4332.     var targetDS = this._getTargetDSFromSource(aSource);
  4333.     targetDS.Assert(aSource, aProperty, aTarget, aTruthValue);
  4334.   },
  4335.   
  4336.   Unassert: function nsExtensionsDataSource_Unassert (aSource, aProperty, aTarget)
  4337.   {
  4338.     var targetDS = this._getTargetDSFromSource(aSource);
  4339.     targetDS.Unassert(aSource, aProperty, aTarget);
  4340.   },
  4341.   
  4342.   Change: function nsExtensionsDataSource_Change (aSource, aProperty, aOldTarget, aNewTarget)
  4343.   {
  4344.     var targetDS = this._getTargetDSFromSource(aSource);
  4345.     targetDS.Change(aSource, aProperty, aOldTarget, aNewTarget);
  4346.   },
  4347.  
  4348.   Move: function nsExtensionsDataSource_Move (aSource, aNewSource, aProperty, aTarget)
  4349.   {
  4350.     var targetDS = this._getTargetDSFromSource(aSource);
  4351.     targetDS.Move(aSource, aNewSource, aProperty, aTarget);
  4352.   },
  4353.   
  4354.   HasAssertion: function nsExtensionsDataSource_HasAssertion (aSource, aProperty, aTarget, aTruthValue)
  4355.   {
  4356.     if (!aSource || !aProperty || !aTarget)
  4357.       return false;
  4358.     return this._composite.HasAssertion(aSource, aProperty, aTarget, aTruthValue);
  4359.   },
  4360.   
  4361.   _observers: [],
  4362.   AddObserver: function nsExtensionsDataSource_AddObserver (aObserver)
  4363.   {
  4364.     for (var i = 0; i < this._observers.length; ++i) {
  4365.       if (this._observers[i] == aObserver) 
  4366.         return;
  4367.     }
  4368.     this._observers.push(aObserver);
  4369.     this._composite.AddObserver(aObserver);
  4370.   },
  4371.   
  4372.   RemoveObserver: function nsExtensionsDataSource_RemoveObserver (aObserver)
  4373.   {
  4374.     for (var i = 0; i < this._observers.length; ++i) {
  4375.       if (this._observers[i] == aObserver) 
  4376.         this._observers.splice(i, 1);
  4377.     }
  4378.     this._composite.RemoveObserver(aObserver);
  4379.   },
  4380.   
  4381.   ArcLabelsIn: function nsExtensionsDataSource_ArcLabelsIn (aNode)
  4382.   {
  4383.     return this._composite.ArcLabelsIn(aNode);
  4384.   },
  4385.   
  4386.   ArcLabelsOut: function nsExtensionsDataSource_ArcLabelsOut (aSource)
  4387.   {
  4388.     return this._composite.ArcLabelsOut(aSource);
  4389.   },
  4390.   
  4391.   GetAllResources: function nsExtensionsDataSource_GetAllResources ()
  4392.   {
  4393.     return this._composite.GetAllResources();
  4394.   },
  4395.   
  4396.   IsCommandEnabled: function nsExtensionsDataSource_IsCommandEnabled (aSources, aCommand, aArguments)
  4397.   {
  4398.     return this._composite.IsCommandEnabled(aSources, aCommand, aArguments);
  4399.   },
  4400.   
  4401.   DoCommand: function nsExtensionsDataSource_DoCommand (aSources, aCommand, aArguments)
  4402.   {
  4403.     this._composite.DoCommand(aSources, aCommand, aArguments);
  4404.   },
  4405.   
  4406.   GetAllCmds: function nsExtensionsDataSource_GetAllCmds (aSource)
  4407.   {
  4408.     return this._composite.GetAllCmds(aSource);
  4409.   },
  4410.   
  4411.   hasArcIn: function nsExtensionsDataSource_hasArcIn (aNode, aArc)
  4412.   {
  4413.     return this._composite.hasArcIn(aNode, aArc);
  4414.   },
  4415.   
  4416.   hasArcOut: function nsExtensionsDataSource_hasArcOut (aSource, aArc)
  4417.   {
  4418.     return this._composite.hasArcOut(aSource, aArc);
  4419.   },
  4420.   
  4421.   beginUpdateBatch: function nsExtensionsDataSource_beginUpdateBatch ()
  4422.   {
  4423.     return this._composite.beginUpdateBatch();
  4424.   },
  4425.   
  4426.   endUpdateBatch: function nsExtensionsDataSource_endUpdateBatch ()
  4427.   {
  4428.     return this._composite.endUpdateBatch();
  4429.   },
  4430.   
  4431.   /////////////////////////////////////////////////////////////////////////////
  4432.   // nsIRDFRemoteDataSource
  4433.   
  4434.   get loaded()
  4435.   {
  4436.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  4437.   },
  4438.   
  4439.   Init: function nsExtensionsDataSource_Init (aURI)
  4440.   {
  4441.   },
  4442.   
  4443.   Refresh: function nsExtensionsDataSource_Refresh (aBlocking)
  4444.   {
  4445.   },
  4446.   
  4447.   Flush: function nsExtensionsDataSource_Flush ()
  4448.   {
  4449.     this._flush(false);
  4450.     this._flush(true);
  4451.   },
  4452.   
  4453.   FlushTo: function nsExtensionsDataSource_FlushTo (aURI)
  4454.   {
  4455.   },
  4456.   
  4457.   _flush: function nsExtensionsDataSource__flush (aIsProfile)
  4458.   { 
  4459.     var ds = aIsProfile ? this._profileExtensions : this._appExtensions;
  4460.     var rds = ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  4461.     rds.Flush();
  4462.   },
  4463.  
  4464.   /////////////////////////////////////////////////////////////////////////////
  4465.   // nsISupports
  4466.   QueryInterface: function nsExtensionsDataSource_QueryInterface (aIID) 
  4467.   {
  4468.     if (!aIID.equals(Components.interfaces.nsIRDFDataSource) &&
  4469.         !aIID.equals(Components.interfaces.nsIRDFRemoteDataSource) && 
  4470.         !aIID.equals(Components.interfaces.nsISupports))
  4471.       throw Components.results.NS_ERROR_NO_INTERFACE;
  4472.     return this;
  4473.   }
  4474. };
  4475.  
  4476.  
  4477. var gModule = {
  4478.   _firstTime: true,
  4479.   
  4480.   registerSelf: function (aComponentManager, aFileSpec, aLocation, aType) 
  4481.   {
  4482.     if (this._firstTime) {
  4483.       this._firstTime = false;
  4484.       throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  4485.     }
  4486.     aComponentManager = aComponentManager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  4487.     
  4488.     for (var key in this._objects) {
  4489.       var obj = this._objects[key];
  4490.       aComponentManager.registerFactoryLocation(obj.CID, obj.className, obj.contractID,
  4491.                                                 aFileSpec, aLocation, aType);
  4492.     }
  4493.  
  4494. /*    
  4495.     // Make the Extension Manager a startup observer
  4496.     var categoryManager = Components.classes["@mozilla.org/categorymanager;1"]
  4497.                                     .getService(Components.interfaces.nsICategoryManager);
  4498.     categoryManager.addCategoryEntry("app-startup", this._objects.manager.className,
  4499.                                      "service," + this._objects.manager.contractID, 
  4500.                                      true, true, null);
  4501.  */
  4502.   },
  4503.   
  4504.   getClassObject: function (aComponentManager, aCID, aIID) 
  4505.   {
  4506.     if (!aIID.equals(Components.interfaces.nsIFactory))
  4507.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  4508.  
  4509.     for (var key in this._objects) {
  4510.       if (aCID.equals(this._objects[key].CID))
  4511.         return this._objects[key].factory;
  4512.     }
  4513.     
  4514.     throw Components.results.NS_ERROR_NO_INTERFACE;
  4515.   },
  4516.   
  4517.   _objects: {
  4518.     manager: { CID        : nsExtensionManager.prototype.classID,
  4519.                contractID : nsExtensionManager.prototype.contractID,
  4520.                className  : nsExtensionManager.prototype.classDescription,
  4521.                factory    : {
  4522.                               createInstance: function (aOuter, aIID) 
  4523.                               {
  4524.                                 if (aOuter != null)
  4525.                                   throw Components.results.NS_ERROR_NO_AGGREGATION;
  4526.                                 
  4527.                                 return (new nsExtensionManager()).QueryInterface(aIID);
  4528.                               }
  4529.                             }
  4530.              }
  4531.    },
  4532.   
  4533.   canUnload: function (aComponentManager) 
  4534.   {
  4535.     return true;
  4536.   }
  4537. };
  4538.  
  4539. function NSGetModule(compMgr, fileSpec) 
  4540. {
  4541.   return gModule;
  4542. }
  4543.