home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 February / PCWorld_2008-02_cd.bin / temacd / songbird / Songbird_0.4_windows-i686.exe / components / sbAddonMetadata.js < prev    next >
Text File  |  2007-12-21  |  15KB  |  475 lines

  1. /**
  2. //
  3. // BEGIN SONGBIRD GPL
  4. // 
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright(c) 2005-2008 POTI, Inc.
  8. // http://songbirdnest.com
  9. // 
  10. // This file may be licensed under the terms of of the
  11. // GNU General Public License Version 2 (the "GPL").
  12. // 
  13. // Software distributed under the License is distributed 
  14. // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
  15. // express or implied. See the GPL for the specific language 
  16. // governing rights and limitations.
  17. //
  18. // You should have received a copy of the GPL along with this 
  19. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  20. // or write to the Free Software Foundation, Inc., 
  21. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. // 
  23. // END SONGBIRD GPL
  24. //
  25.  */
  26.  
  27.  
  28. /**
  29.  * \file sbAddonMetadata.js
  30.  * \brief Provides an nsIRDFDataSource with the contents of all 
  31.  *        addon install.rdf files.
  32.  */ 
  33.  
  34. const CONTRACTID = "@mozilla.org/rdf/datasource;1?name=addon-metadata";
  35. const CLASSNAME = "Songbird Addon Metadata Datasource";
  36. const CID = Components.ID("{a1edd551-0f29-4ce9-aebc-92fbee77f37e}");
  37. const IID = Components.interfaces.nsIRDFDataSource;
  38.  
  39. const FILE_INSTALL_MANIFEST = "install.rdf";
  40. const FILE_EXTENSIONS       = "extensions.rdf";
  41. const FILE_ADDONMETADATA    = "addon-metadata.rdf";
  42.  
  43. const PREF_LASTMODIFIED = "songbird.addonmetadata.lastModifiedTime";
  44.  
  45. const KEY_PROFILEDIR                  = "ProfD";
  46.  
  47. const RDFURI_INSTALL_MANIFEST_ROOT    = "urn:mozilla:install-manifest";
  48. const RDFURI_ADDON_ROOT               = "urn:songbird:addon:root"
  49. const PREFIX_ADDON_URI                = "urn:songbird:addon:";
  50. const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
  51. const PREFIX_NS_SONGBIRD              = "http://www.songbirdnest.com/2007/addon-metadata-rdf#";
  52. const PREFIX_ITEM_URI                 = "urn:mozilla:item:";
  53.  
  54. function SONGBIRD_NS(property) {
  55.   return PREFIX_NS_SONGBIRD + property;
  56. }
  57.  
  58. function EM_NS(property) {
  59.   return PREFIX_NS_EM + property;
  60. }
  61.  
  62. function ADDON_NS(id) {
  63.   return PREFIX_ADDON_URI + id;
  64. }
  65.  
  66. function ITEM_NS(id) {
  67.   return PREFIX_ITEM_URI + id;
  68. }
  69.  
  70.  
  71. /**
  72.  * /class AddonMetadata
  73.  * /brief Provides an nsIRDFDataSource with the contents of all 
  74.  *        addon install.rdf files.
  75.  */
  76. function AddonMetadata() {
  77.   //debug("\nAddonMetadata: constructed\n");
  78.   
  79.   this._RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  80.                    .getService(Components.interfaces.nsIRDFService);
  81.  
  82.   try {
  83.  
  84.     // If possible, load the cached datasource from disk. 
  85.     // Otherwise rebuild it by reading the metadata from all addons
  86.     if (!this._loadDatasource() || this._isRebuildRequired()) {
  87.       //debug("AddonMetadata: rebuilding addon metadata datasource\n");
  88.       this._purgeDatasource();
  89.       this._buildDatasource();
  90.     }
  91.     
  92.   } catch (e) {
  93.     dump("AddonMetadata: Constructor Error: " + e.toString());
  94.   }
  95.   
  96.   var os  = Components.classes["@mozilla.org/observer-service;1"]
  97.                       .getService(Components.interfaces.nsIObserverService);
  98.   os.addObserver(this, "xpcom-shutdown", false);
  99. };
  100.  
  101. AddonMetadata.prototype = {
  102.  
  103.   constructor: AddonMetadata,
  104.  
  105.   _RDF: null,
  106.   _datasource: null,
  107.  
  108.   
  109.   /**
  110.    * Return true if cached addons metadata datasource is no longer valid.
  111.    * Uses the last modified date on the extension manager's data file to
  112.    * determine if extensions have changed.
  113.    */
  114.   _isRebuildRequired: function _isRebuildRequired() {
  115.     var emDataFile = this._getProfileFile(FILE_EXTENSIONS);
  116.     
  117.     if (!emDataFile.exists()) {      
  118.       //debug("AddonMetadata._isRebuildRequired: " + FILE_EXTENSIONS + " not found\n");
  119.       return true;
  120.     }
  121.  
  122.     var newLastModified = emDataFile.lastModifiedTime.toString();
  123.  
  124.     // When was the last time we noticed that extensions.rdf had been modified
  125.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  126.                           .getService(Components.interfaces.nsIPrefBranch);
  127.     var lastModified = 0;
  128.     try {  
  129.       lastModified = prefs.getCharPref(PREF_LASTMODIFIED);
  130.     } catch (e) {}    
  131.     
  132.     // If the extensions.rdf hasn't changed, then we don't need to rebuild
  133.     //debug("AddonMetadata._isRebuildRequired: " + lastModified + " == " + newLastModified + "\n");
  134.     if (lastModified == newLastModified) {
  135.       return false;
  136.     }
  137.         
  138.     // Store the new last modified time
  139.     prefs.setCharPref(PREF_LASTMODIFIED, newLastModified);
  140.  
  141.     //debug("AddonMetadata._isRebuildRequired: true\n");
  142.  
  143.     // Extensions.rdf has changed, so we will need to rebuild the addons datasource.
  144.     return true;
  145.   },
  146.   
  147.   
  148.   
  149.   /**
  150.    * Attempt to load the data source from disk.  Return false 
  151.    * if the datasource needs populating. 
  152.    */
  153.   _loadDatasource: function _loadDatasource() {
  154.     //debug("\nAddonMetadata: _loadDatasource \n");
  155.     
  156.     var file = this._getProfileFile(FILE_ADDONMETADATA);
  157.     
  158.     try { 
  159.       this._datasource = this._getDatasource(file);
  160.     } catch (e) {
  161.       // Load did not go ok.  Will need to rebuild
  162.       return false;
  163.     }
  164.     
  165.     if (!file.exists()) {
  166.       return false;
  167.     }
  168.     
  169.     return true;
  170.   },
  171.   
  172.  
  173.  
  174.   /**
  175.    * Clean out the contents of the datasource
  176.    */
  177.   _purgeDatasource: function _purgeDatasource() {
  178.     //debug("\nAddonMetadata: _purgeDatasource \n");
  179.     
  180.     var file = this._getProfileFile(FILE_ADDONMETADATA);
  181.     
  182.     // Make sure the RDF service isn't caching our ds
  183.     this._RDF.UnregisterDataSource(this._datasource);
  184.     
  185.     // Kill the file
  186.     try { 
  187.       if (file.exists()) {
  188.         file.remove(false);
  189.       }
  190.     } catch (e) {
  191.       debug("\nAddonMetadata: _purgeDatasource: Could not remove " 
  192.             + "addon-metadata.rdf. Bad things may happen.\n");
  193.     }
  194.  
  195.     // Reload the datasource.  Since the file no longer exists
  196.     // this will result in a new empty DS.
  197.     this._loadDatasource();
  198.   },
  199.   
  200.     
  201.   
  202.  
  203.   /**
  204.    * Given a filename, returns an nsIFile pointing to 
  205.    * profile/filename
  206.    */
  207.   _getProfileFile: function _getProfileFile(filename) {
  208.     // get profile directory
  209.     var file = Components.classes["@mozilla.org/file/directory_service;1"]
  210.                          .getService(Components.interfaces.nsIProperties)
  211.                          .get("ProfD", Components.interfaces.nsIFile);
  212.     var localFile = file.QueryInterface(Components.interfaces.nsILocalFile);                         
  213.     localFile.appendRelativePath(filename)
  214.     return file;
  215.   },
  216.  
  217.  
  218.   
  219.   /**
  220.    * Populate the datasource with the contents of all 
  221.    * addon install.rdf files.
  222.    */
  223.   _buildDatasource: function _buildDatasource() {
  224.     //debug("\nAddonMetadata: _buildDatasource \n");
  225.  
  226.     // Make a container to list all installed extensions
  227.     var itemRoot = this._RDF.GetResource(RDFURI_ADDON_ROOT);    
  228.     var cu = Components.classes["@mozilla.org/rdf/container-utils;1"]
  229.                        .getService(Components.interfaces.nsIRDFContainerUtils);
  230.     var container = cu.MakeSeq(this._datasource, itemRoot);
  231.  
  232.     var extManager = Components.classes["@mozilla.org/extensions/manager;1"]
  233.                            .getService(Components.interfaces.nsIExtensionManager);
  234.    
  235.     // Read the install.rdf for every addon
  236.     var addons = extManager.getItemList(Components.interfaces.nsIUpdateItem.TYPE_ADDON, {});
  237.     for (var i = 0; i < addons.length; i++)
  238.     {
  239.       var id = addons[i].id;
  240.             
  241.       // If the extension is disabled, do not include it in our datasource 
  242.       if (this._isExtensionDisabled(id))  {
  243.         //debug("\nAddonMetadata:  id {" + id +  "} is disabled.\n");
  244.         continue;
  245.       }
  246.       
  247.       //debug("\nAddonMetadata:  loading install.rdf for id {" + id +  "}\n");
  248.       
  249.       var location = extManager.getInstallLocation(id);
  250.       var installManifestFile = location.getItemFile(id, FILE_INSTALL_MANIFEST);
  251.  
  252.       if (!installManifestFile.exists()) {
  253.         this._reportErrors(["install.rdf for id " + id +  " was not found " + 
  254.                   "at location " + installManifestFile.path]);
  255.       }
  256.       
  257.       var manifestDS = this._getDatasource(installManifestFile);
  258.       var itemNode = this._RDF.GetResource(ADDON_NS(id));
  259.       // Copy the install.rdf metadata into the master datasource
  260.       this._copyManifest(itemNode, manifestDS);
  261.       
  262.       // Add the new install.rdf root to the list of extensions
  263.       container.AppendElement(itemNode);
  264.     }
  265.  
  266.     // Save changes  
  267.     this._datasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource)
  268.                     .Flush();
  269.     //debug("\nAddonMetadata: _buildDatasource complete \n");
  270.   },
  271.   
  272.   
  273.   
  274.   /**
  275.    * Return true if the given extension GUID has been disabled in the EM
  276.    */
  277.   _isExtensionDisabled: function _isExtensionDisabled(id) {
  278.     var item = this._RDF.GetResource(ITEM_NS(id));
  279.     var extManager = Components.classes["@mozilla.org/extensions/manager;1"]
  280.                         .getService(Components.interfaces.nsIExtensionManager);
  281.     var userDisabled = this._RDF.GetResource(EM_NS("userDisabled"));
  282.     if (extManager.datasource.hasArcOut(item, userDisabled)) {
  283.       var target = extManager.datasource.GetTarget(item, userDisabled, true);
  284.       if (target instanceof Components.interfaces.nsIRDFLiteral){
  285.         target = target.QueryInterface(Components.interfaces.nsIRDFLiteral);
  286.         return target.Value == "true";
  287.       }
  288.     }                      
  289.     var appDisabled = this._RDF.GetResource(EM_NS("appDisabled"));
  290.     if (extManager.datasource.hasArcOut(item, appDisabled)) {
  291.       var target = extManager.datasource.GetTarget(item, appDisabled, true);
  292.       if (target instanceof Components.interfaces.nsIRDFLiteral){
  293.         target = target.QueryInterface(Components.interfaces.nsIRDFLiteral);
  294.         return target.Value == "true";
  295.       }
  296.     }
  297.     return false;
  298.   },
  299.  
  300.   
  301.   
  302.   /**
  303.    * Copy all nodes and assertions into the main datasource,
  304.    * renaming the install manifest to the id of the extension.
  305.    */
  306.   _copyManifest:  function _copyManifest(itemNode, manifestDS) {
  307.   
  308.     // Copy all resources from the manifest to the main DS
  309.     var resources = manifestDS.GetAllResources();
  310.     while (resources.hasMoreElements()) {
  311.       var resource = resources.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  312.       
  313.       // Get all arcs out of the resource
  314.       var arcs = manifestDS.ArcLabelsOut(resource);
  315.       while (arcs.hasMoreElements()) {
  316.         var arc = arcs.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  317.               
  318.         // For each arc, get all targets
  319.         var targets = manifestDS.GetTargets(resource, arc, true);
  320.         while (targets.hasMoreElements()) {
  321.           var target = targets.getNext().QueryInterface(Components.interfaces.nsIRDFNode);
  322.           
  323.           // If this resource is the manifest root, replace it
  324.           // with a resource representing the current addon
  325.           newResource = resource;
  326.           if (resource.Value == RDFURI_INSTALL_MANIFEST_ROOT) {
  327.             newResource = itemNode;
  328.           }
  329.                     
  330.           // Otherwise, assert into the main ds
  331.           this._datasource.Assert(newResource, arc, target, true);
  332.         }
  333.       }                   
  334.     }
  335.   },
  336.  
  337.   
  338.  
  339.  
  340.  
  341.   /**
  342.    * Gets a datasource from a file.
  343.    * @param   file
  344.    *          The nsIFile that containing RDF/XML
  345.    * @returns RDF datasource
  346.    */
  347.   _getDatasource: function _getDatasource(file) {
  348.     var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
  349.                            .getService(Components.interfaces.nsIIOService);
  350.     var fph = ioServ.getProtocolHandler("file")
  351.                     .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  352.  
  353.     var fileURL = fph.getURLSpecFromFile(file);
  354.     var ds = this._RDF.GetDataSourceBlocking(fileURL);
  355.  
  356.     return ds;
  357.   },
  358.  
  359.  
  360.   
  361.   _deinit: function _deinit() {
  362.     //debug("\nAddonMetadata: deinit\n");
  363.  
  364.     this._RDF = null;
  365.     this._datasource = null;
  366.   },
  367.     
  368.   
  369.  
  370.   // watch for XRE startup and shutdown messages 
  371.   observe: function(subject, topic, data) {
  372.     var os = Components.classes["@mozilla.org/observer-service;1"]
  373.                        .getService(Components.interfaces.nsIObserverService);
  374.     switch (topic) {
  375.     case "xpcom-shutdown":
  376.       os.removeObserver(this, "xpcom-shutdown");
  377.       this._deinit();
  378.       break;
  379.     }
  380.   },
  381.   
  382.   
  383.   
  384.   _reportErrors: function _reportErrors(errorList) {
  385.     var consoleService = Components.classes["@mozilla.org/consoleservice;1"].
  386.          getService(Components.interfaces.nsIConsoleService);
  387.     for (var i = 0; i  < errorList.length; i++) {
  388.       consoleService.logStringMessage("Addon Metadata Reader: " + errorList[i]);
  389.       debug("AddonMetadataReader: " + errorList[i] + "\n");
  390.     }
  391.   },
  392.  
  393.  
  394.  
  395.   /**
  396.    * See nsISupports.idl
  397.    */
  398.   QueryInterface: function(iid) {
  399.     if (!iid.equals(IID) &&
  400.         !iid.equals(Components.interfaces.nsIObserver) && 
  401.         !iid.equals(Components.interfaces.nsISupports))
  402.       throw Components.results.NS_ERROR_NO_INTERFACE;
  403.     
  404.     // THIS IS SO WRONG! 
  405.     // We can get away with it though since we really only want the datasource
  406.     // and never the object that builds it.
  407.     if (iid.equals(IID)) {
  408.       return this._datasource;
  409.     }
  410.     
  411.     return this;
  412.   }
  413. }; // AddonMetadata.prototype
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421. /**
  422.  * ----------------------------------------------------------------------------
  423.  * Registration for XPCOM
  424.  * ----------------------------------------------------------------------------
  425.  */
  426. var gModule = {
  427.   registerSelf: function(componentManager, fileSpec, location, type) {
  428.     componentManager = componentManager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  429.     for (var key in this._objects) {
  430.       var obj = this._objects[key];
  431.       componentManager.registerFactoryLocation(obj.CID, obj.className, obj.contractID,
  432.                                                fileSpec, location, type);
  433.     }
  434.   },
  435.  
  436.   getClassObject: function(componentManager, cid, iid) {
  437.     if (!iid.equals(Components.interfaces.nsIFactory))
  438.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  439.  
  440.     for (var key in this._objects) {
  441.       if (cid.equals(this._objects[key].CID))
  442.         return this._objects[key].factory;
  443.     }
  444.     
  445.     throw Components.results.NS_ERROR_NO_INTERFACE;
  446.   },
  447.  
  448.   _makeFactory: #1= function(ctor) {
  449.     function ci(outer, iid) {
  450.       if (outer != null)
  451.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  452.       return (new ctor()).QueryInterface(iid);
  453.     } 
  454.     return { createInstance: ci };
  455.   },
  456.   
  457.   _objects: {
  458.     AddonMetadata:     {   CID        : CID,
  459.                            contractID : CONTRACTID,
  460.                            className  : CLASSNAME,
  461.                            factory    : #1#(AddonMetadata)
  462.                          },
  463.   },
  464.  
  465.   canUnload: function(componentManager) { 
  466.     return true; 
  467.   }
  468. }; // gModule
  469.  
  470. function NSGetModule(comMgr, fileSpec) {
  471.   return gModule;
  472. } // NSGetModule
  473.  
  474.  
  475.