home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Multimedia / Songbird / Songbird_2.0.0-2311_windows-i686-msvc8.exe / components / sbDisplayPanes.js < prev    next >
Text File  |  2012-06-08  |  18KB  |  569 lines

  1. /*
  2.  *=BEGIN SONGBIRD GPL
  3.  *
  4.  * This file is part of the Songbird web player.
  5.  *
  6.  * Copyright(c) 2005-2010 POTI, Inc.
  7.  * http://www.songbirdnest.com
  8.  *
  9.  * This file may be licensed under the terms of of the
  10.  * GNU General Public License Version 2 (the ``GPL'').
  11.  *
  12.  * Software distributed under the License is distributed
  13.  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
  14.  * express or implied. See the GPL for the specific language
  15.  * governing rights and limitations.
  16.  *
  17.  * You should have received a copy of the GPL along with this
  18.  * program. If not, go to http://www.gnu.org/licenses/gpl.html
  19.  * or write to the Free Software Foundation, Inc.,
  20.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21.  *
  22.  *=END SONGBIRD GPL
  23.  */
  24.  
  25. const Cc = Components.classes;
  26. const Ci = Components.interfaces;
  27. const Cu = Components.utils;
  28.  
  29. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  30. Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
  31. Cu.import("resource://app/jsmodules/RDFHelper.jsm");
  32.  
  33. /**
  34.  * sbIContentPaneInfo
  35.  */
  36.  
  37. function PaneInfo() {};
  38.  
  39. PaneInfo.prototype = {
  40.   requiredProperties: [ "contentUrl", 
  41.                         "contentTitle",
  42.                         "contentIcon",
  43.                         "suggestedContentGroups",
  44.                         "defaultWidth", 
  45.                         "defaultHeight" ],
  46.   optionalProperties: [ "showOnInstall" ],
  47.     
  48.   verify: function() {
  49.     var errorList = [];
  50.     for (var i = 0; i < this.requiredProperties.length; i++) {
  51.       var property = this.requiredProperties[i];
  52.       if (! (typeof(this[property]) == 'string'
  53.                && this[property].length > 0)) 
  54.       {
  55.         errorList.push("Invalid description. '" + property + "' is a required property.");
  56.       }
  57.     }
  58.     try {
  59.       this.defaultWidth = parseInt(this.defaultWidth);
  60.       this.defaultHeight = parseInt(this.defaultHeight);
  61.       this.showOnInstall = this.showOnInstall == "true";
  62.     } catch (e) {
  63.       errorList.push(e.toString());
  64.     }
  65.     return(errorList);
  66.   },
  67.   
  68.   QueryInterface: XPCOMUtils.generateQI([Ci.sbIDisplayPaneContentInfo])
  69. };
  70.  
  71. /**
  72.  * /class DisplayPaneMetadataReader
  73.  * Responsible for reading addon metadata and performing 
  74.  * registration with DisplayPaneManager
  75.  */
  76. function DisplayPaneMetadataReader() {
  77.   //debug("DisplayPaneMetadataReader: ctor\n");
  78.   this._manager = Cc["@songbirdnest.com/Songbird/DisplayPane/Manager;1"]
  79.                     .getService(Ci.sbIDisplayPaneManager);
  80. }
  81. DisplayPaneMetadataReader.prototype = {
  82.   _manager: null,
  83.  
  84.   /**
  85.    * Populate DisplayPaneManager using addon metadata
  86.    */
  87.   loadPanes: function loadPanes() {
  88.     //debug("DisplayPaneMetadataReader: loadPanes\n");
  89.     var addons = RDFHelper.help("rdf:addon-metadata", "urn:songbird:addon:root", RDFHelper.DEFAULT_RDF_NAMESPACES);
  90.     
  91.     for (var i = 0; i < addons.length; i++) {
  92.       // skip addons with no panes.
  93.       var panes;
  94.       if (addons[i].displayPanes) {
  95.         // TODO: remove this some time post 0.5 and before 1.0
  96.         Cu.reportError(
  97.           "DisplayPanes: Use of the <displayPanes> element in install.rdf " +
  98.           "is deprecated. Remove that element and leave the contents as-is."
  99.         );
  100.         panes = addons[i].displayPanes[0].displayPane;
  101.       }
  102.       else {
  103.         panes = addons[i].displayPane;
  104.       }
  105.       
  106.       if (!panes) {
  107.         // no display panes in this addon.
  108.         continue;
  109.       }
  110.       
  111.       try {
  112.         for (var j = 0; j < panes.length; j++) {
  113.           this._registerDisplayPane(addons[i], panes[j]) 
  114.         }
  115.       } catch (e) {
  116.         this._reportErrors("", [  "An error occurred while processing " +
  117.                   "extension " + addons[i].Value + ".  Exception: " + e  ]);
  118.       }
  119.     }
  120.   },
  121.   
  122.   /**
  123.    * Extract pane metadata and register it with the manager.
  124.    */
  125.   _registerDisplayPane: function _registerDisplayPane(addon, pane) {
  126.     // create and validate our pane info
  127.     var info = new PaneInfo();
  128.     for (property in pane) {
  129.       if (pane[property])
  130.        info[property] = pane[property][0];
  131.     }
  132.     var errorList = info.verify();
  133.     
  134.     // If errors were encountered, then do not submit 
  135.     // to the Display Pane Manager
  136.     if (errorList.length > 0) {
  137.       this._reportErrors(
  138.           "Ignoring display pane addon in the install.rdf of extension " +
  139.           addon.Value + " due to these error(s):\n", errorList);
  140.       return;
  141.     }
  142.     
  143.     // Submit description
  144.     this._manager.registerContent( info.contentUrl, 
  145.                                    info.contentTitle,
  146.                                    info.contentIcon,
  147.                                    info.defaultWidth,
  148.                                    info.defaultHeight,
  149.                                    info.suggestedContentGroups,
  150.                                    info.showOnInstall );
  151.     
  152.     //debug("DisplayPaneMetadataReader: registered pane " + info.contentTitle
  153.     //        + " from addon " + addon.Value + " \n");
  154.   },
  155.   
  156.   
  157.   /**
  158.    * \brief Dump a list of errors to the console and jsconsole
  159.    *
  160.    * \param contextMessage Additional prefix to use before every line
  161.    * \param errorList Array of error messages
  162.    */
  163.   _reportErrors: function _reportErrors(contextMessage, errorList) {
  164.     var consoleService = Cc["@mozilla.org/consoleservice;1"]
  165.                            .getService(Ci.nsIConsoleService);
  166.     for (var i = 0; i  < errorList.length; i++) {
  167.       consoleService.logStringMessage("Display Pane Metadata Reader: " 
  168.                                        + contextMessage + errorList[i]);
  169.       dump("DisplayPaneMetadataReader: " + contextMessage + errorList[i] + "\n");
  170.     }
  171.   }
  172. }
  173.  
  174. /**
  175.  * /class DisplayPaneManager
  176.  * /brief Coordinates display pane content
  177.  *
  178.  * Acts as a registry for display panes and available content.
  179.  *
  180.  * \sa sbIDisplayPaneManager
  181.  */
  182. function DisplayPaneManager() {
  183.  
  184.   // Components that implement sbIDisplayPaneContentInfo can indicate they
  185.   // provide display pane content by adding themselves to the
  186.   // "display-pane-provider" category.
  187.   var catMgr = Cc["@mozilla.org/categorymanager;1"]
  188.                  .getService(Ci.nsICategoryManager);
  189.   var entries = catMgr.enumerateCategory("display-pane-provider");
  190.   while (entries.hasMoreElements()) {
  191.     var entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
  192.     this._registerContentFromCategoryEntry(entry, catMgr);
  193.   }
  194.  
  195.   this._initialized = true;
  196. }
  197.  
  198. DisplayPaneManager.prototype = {
  199.   classDescription: "Songbird Display Pane Manager Service Interface",
  200.   classID:          Components.ID("{6aef120f-d7ad-414d-a93d-3ac945e64301}"),
  201.   contractID:       "@songbirdnest.com/Songbird/DisplayPane/Manager;1",
  202.   constructor:      DisplayPaneManager,
  203.  
  204.   LOG: function(str) {
  205.     var consoleService = Cc['@mozilla.org/consoleservice;1']
  206.                            .getService(Ci.nsIConsoleService);
  207.     consoleService.logStringMessage(str);
  208.   },
  209.  
  210.   _contentList: [],
  211.   _instantiatorsList: [],
  212.   _delayedInstantiations: [],
  213.   _listenersList: [],
  214.   _initialized: false,
  215.   
  216.   _addonMetadataLoaded: false,
  217.  
  218.   _getString: function(aName, aDefault) {
  219.     if (!this._stringbundle) {
  220.       var src = "chrome://branding/locale/brand.properties";
  221.       var stringBundleService = Cc["@mozilla.org/intl/stringbundle;1"]
  222.                                   .getService(Ci.nsIStringBundleService);
  223.       this._stringbundle = stringBundleService.createBundle(src);
  224.     }
  225.         
  226.     try {
  227.       return this._stringbundle.GetStringFromName(aName);
  228.     }
  229.     catch(e) {
  230.       return aDefault;
  231.     }
  232.  
  233.   },
  234.   
  235.   _defaultPaneInfo: null,
  236.   
  237.   _cleanupInstantiatorsList: function DPM_cleanupInstantiatorsList() {
  238.     for (var i = this._instantiatorsList.length - 1; i >= 0; --i) {
  239.       if (!(this._instantiatorsList[i] instanceof
  240.             Ci.sbIDisplayPaneInstantiator)) {
  241.         Cu.reportError("Warning: found bad instantiator; "+
  242.                        "possibly via removal from DOM");
  243.         this._instantiatorsList.splice(i, 1);
  244.       }
  245.     }
  246.   },
  247.   
  248.   get defaultPaneInfo() {
  249.     if (!this._defaultPaneInfo) {
  250.       var paneInfo = {
  251.         contentUrl: this._getString("displaypane.default.url",  "chrome://songbird/content/xul/defaultDisplayPane.xul"),
  252.         contentTitle: null, // automatically set by host depending on where the default pane is instantiated
  253.         contentIcon: this._getString("displaypane.default.icon",  "chrome://branding/content/branding.ico"),
  254.         defaultWidth: this._getString("displaypane.default.width",  "150"),
  255.         defaultHeight: this._getString("displaypane.default.height",  "75"),
  256.         suggestedContentGroups: this._getString("displaypane.default.groups",  "")
  257.       };
  258.       this._defaultPaneInfo = paneInfo;
  259.     }
  260.     return this._defaultPaneInfo;
  261.   },
  262.  
  263.   /**
  264.    * Make sure that we've read display pane registration
  265.    * metadata from all extension install.rdf files.
  266.    */
  267.   ensureAddonMetadataLoaded: function() {
  268.     if (!this._initialized || this._addonMetadataLoaded) {
  269.       return;
  270.     }
  271.     this._addonMetadataLoaded = true;
  272.     
  273.     // Load the addon metadata
  274.     var metadataReader = new DisplayPaneMetadataReader();
  275.     metadataReader.loadPanes();
  276.   },
  277.   
  278.  
  279.   /**
  280.    * given a list of pane parameters, return a new sbIDisplayPaneContentInfo
  281.    */
  282.   makePaneInfo: function(aContentUrl,
  283.                          aContentTitle,
  284.                          aContentIcon,
  285.                          aSuggestedContentGroups,
  286.                          aDefaultWidth,
  287.                          aDefaultHeight) {
  288.     var paneInfo = new PaneInfo();
  289.     paneInfo.contentUrl = aContentUrl;
  290.     paneInfo.contentTitle = aContentTitle;
  291.     paneInfo.contentIcon = aContentIcon;
  292.     paneInfo.suggestedContentGroups = aSuggestedContentGroups;
  293.     paneInfo.defaultWidth = aDefaultWidth;
  294.     paneInfo.defaultHeight = aDefaultHeight;
  295.     
  296.     return paneInfo;
  297.   },
  298.  
  299.   /**
  300.    * \see sbIDisplayPaneManager
  301.    */
  302.   getPaneInfo: function(aContentUrl) {
  303.     this.ensureAddonMetadataLoaded();
  304.   
  305.     for each (var pane in this._contentList) {
  306.       ////debug("PANE: " + pane.contentTitle  + " XXX " + aContentUrl + "\n\n");
  307.       if (pane.contentUrl == aContentUrl) 
  308.         return pane;
  309.     }
  310.     return null;
  311.   },
  312.   
  313.   /**
  314.    * \see sbIDisplayPaneManager
  315.    */
  316.   getInstantiatorForWindow:
  317.   function sbDisplayPaneMgr_getInstantiatorForWindow(aWindow) {
  318.     this._cleanupInstantiatorsList();
  319.     for each (var instantiator in this._instantiatorsList) {
  320.       if (instantiator.contentWindow === aWindow) {
  321.         return instantiator;
  322.       }
  323.     }
  324.     return null;
  325.   },
  326.  
  327.   /**
  328.    * \see sbIDisplayPaneManager
  329.    */
  330.   get contentList() {
  331.     this.ensureAddonMetadataLoaded();
  332.     return ArrayConverter.enumerator(this._contentList);
  333.   },
  334.   
  335.   /**
  336.    * \see sbIDisplayPaneManager
  337.    */
  338.   get instantiatorsList() {
  339.     this._cleanupInstantiatorsList();
  340.     return ArrayConverter.enumerator(this._instantiatorsList);
  341.   },
  342.   
  343.   /**
  344.    * \see sbIDisplayPaneManager
  345.    */
  346.   registerContent: function(aContentUrl,
  347.                             aContentTitle,
  348.                             aContentIcon,
  349.                             aDefaultWidth,
  350.                             aDefaultHeight,
  351.                             aSuggestedContentGroups,
  352.                             aAutoShow) {
  353.                             
  354.     ////debug("REGISTER: " + aContentUrl + "\n");
  355.     
  356.     var info = this.getPaneInfo(aContentUrl);
  357.     if (info) {
  358.       throw Components.results.NS_ERROR_ALREADY_INITIALIZED;
  359.     }
  360.     info = this.makePaneInfo(aContentUrl,
  361.                              aContentTitle,
  362.                              aContentIcon,
  363.                              aSuggestedContentGroups,
  364.                              aDefaultWidth,
  365.                              aDefaultHeight);
  366.     this._contentList.push(info);
  367.     for each (var listener in this._listenersList) {
  368.       listener.onRegisterContent(info);
  369.     }
  370.     // if we have never seen this pane, show it in its prefered group
  371.     var SB_NewDataRemote = Components.Constructor("@songbirdnest.com/Songbird/DataRemote;1",
  372.                                                   "sbIDataRemote",
  373.                                                   "init");
  374.     var known = SB_NewDataRemote("displaypane.known." + aContentUrl, null);
  375.     if (!known.boolValue) {
  376.       if (aAutoShow) {
  377.         if (!this.tryInstantiation(info)) {
  378.           this._delayedInstantiations.push(info);
  379.         }
  380.       }
  381.       // remember we've seen this pane, let the pane hosts reload on their own if they need to
  382.       known.boolValue = true;
  383.     }
  384.   },
  385.   
  386.   /**
  387.    * \see sbIDisplayPaneManager
  388.    */
  389.   unregisterContent: function(aContentUrl) {
  390.     for (var contentIndex = 0; contentIndex < this._contentList.length; contentIndex++) {
  391.       if (this._contentList[contentIndex].contentUrl != aContentUrl) {
  392.         continue;
  393.       }
  394.  
  395.       // any instantiator currently hosting this url should be emptied
  396.       for each (var instantiator in this._instantiatorsList) {
  397.         if (instantiator.contentUrl == aContentUrl) {
  398.           instantiator.hide();
  399.         }
  400.       }
  401.       // also remove it from the delayed instantiation list
  402.       for (instantiatorIndex = this._delayedInstantiations.length - 1; instantiatorIndex >= 0; --instantiatorIndex) {
  403.         if (this._delayedInstantiations[instantiatorIndex].contentUrl == aContentUrl) {
  404.           this._delayedInstantiations.splice(instantiatorIndex, 1);
  405.         }
  406.       }
  407.  
  408.       var [info] = this._contentList.splice(contentIndex, 1);
  409.       
  410.       for each (var listener in this._listenersList) {
  411.         listener.onUnregisterContent(info);
  412.       }
  413.       return;
  414.     }
  415.   },
  416.   
  417.   /**
  418.    * \see sbIDisplayPaneManager
  419.    */
  420.   registerInstantiator: function(aInstantiator) {
  421.     this.ensureAddonMetadataLoaded();
  422.     
  423.     if (this._instantiatorsList.indexOf(aInstantiator) > -1) {
  424.       Cu.reportError("Attempt to re-register instantiator ignored\n" +
  425.                      (new Error()).stack);
  426.       return;
  427.     }
  428.     this._instantiatorsList.push(aInstantiator);
  429.     for each (var listener in this._listenersList) {
  430.       listener.onRegisterInstantiator(aInstantiator);
  431.     }
  432.     this.processDelayedInstantiations();
  433.   },
  434.  
  435.   /**
  436.    * \see sbIDisplayPaneManager
  437.    */
  438.   unregisterInstantiator: function(aInstantiator) {
  439.     var index = this._instantiatorsList.indexOf(aInstantiator);
  440.     if (index < 0) {
  441.       // not found
  442.       return;
  443.     }
  444.     this._instantiatorsList.splice(index, 1);
  445.     for each (var listener in this._listenersList) {
  446.       listener.onUnregisterInstantiator(aInstantiator);
  447.     }
  448.   },
  449.   
  450.   /**
  451.    * given a content group list (from a sbIDisplayPaneContentInfo),
  452.    * return the the first instantiator that matches the earliest possible
  453.    * content group
  454.    */
  455.   getFirstInstantiatorForGroupList: function(aContentGroupList) {
  456.     var groups = aContentGroupList.toUpperCase().split(";");
  457.     for each (var group in groups) {
  458.       for each (var instantiator in this._instantiatorsList) {
  459.         if (instantiator.contentGroup.toUpperCase() == group) {
  460.           return instantiator;
  461.         }
  462.       }
  463.     }
  464.     return null;
  465.   },
  466.   
  467.   processDelayedInstantiations: function() {
  468.     var table = [];
  469.     for each (var info in this._delayedInstantiations) {
  470.       if (!this.isValidPane(info) || this.tryInstantiation(info)) {
  471.         continue;
  472.       }
  473.       table.push(info);
  474.     }
  475.     this._delayedInstantiations = table;
  476.   },
  477.   
  478.   tryInstantiation: function(info) {
  479.     var instantiator = this.getFirstInstantiatorForGroupList(info.suggestedContentGroups);
  480.     if (instantiator) {
  481.       instantiator.loadContent(info);
  482.       return true;
  483.     }
  484.     return false;
  485.   },
  486.   
  487.   isValidPane: function(aPane) {
  488.     this.ensureAddonMetadataLoaded();
  489.     for each (var pane in this._contentList) {
  490.       if (pane == aPane) return true;
  491.     }
  492.     return false;
  493.   },
  494.  
  495.   showPane: function(aContentUrl) {
  496.     for each (var instantiator in this._instantiatorsList) {
  497.       if (instantiator.contentUrl == aContentUrl) {
  498.         // we already have a pane with this content
  499.         instantiator.collapsed = false;
  500.         return;
  501.       }
  502.     }
  503.     var info = this.getPaneInfo(aContentUrl);
  504.     if (info) {
  505.       if (!this.tryInstantiation(info)) {
  506.         this._delayedInstantiations.push(info);
  507.       }
  508.     } else {
  509.       throw new Error("Content URL was not found in list of registered panes");
  510.     }
  511.   },
  512.   
  513.   addListener: function(aListener) {
  514.     this._listenersList.push(aListener);
  515.   },
  516.   
  517.   removeListener: function(aListener) {
  518.     var index = this._listenersList.indexOf(aListener);
  519.     if (index > -1)
  520.       this._listenersList.splice(index, 1);
  521.   },
  522.   
  523.   updateContentInfo: function(aContentUrl, aNewContentTitle, aNewContentIcon) {
  524.     var info = this.getPaneInfo(aContentUrl);
  525.     if (!info) {
  526.       throw Components.results.NS_ERROR_NOT_INITIALIZED;
  527.     }
  528.  
  529.     info.contentTitle = aNewContentTitle;
  530.     info.contentIcon  = aNewContentIcon;
  531.     
  532.     // change the live title for every instance of this content
  533.     for each (var instantiator in this._instantiatorsList) {
  534.       if (instantiator.contentUrl == aContentUrl) {
  535.         instantiator.contentTitle = aNewContentTitle;
  536.         instantiator.contentIcon = aNewContentIcon;
  537.       }
  538.     }
  539.     for each (var listener in this._listenersList) {
  540.       listener.onPaneInfoChanged(info);
  541.     }
  542.   },
  543.  
  544.   _registerContentFromCategoryEntry: function(aEntry, aCatMgr) {
  545.     var catMgr = aCatMgr || Cc["@mozilla.org/categorymanager;1"]
  546.                               .getService(Ci.nsICategoryManager);
  547.     var contractId = catMgr.getCategoryEntry("display-pane-provider", aEntry);
  548.     var contentInfo = Cc[contractId]
  549.                         .createInstance(Ci.sbIDisplayPaneContentInfo);
  550.     this.registerContent(contentInfo.contentUrl,
  551.                          contentInfo.contentTitle,
  552.                          contentInfo.contentIcon,
  553.                          contentInfo.defaultWidth,
  554.                          contentInfo.defaultHeight,
  555.                          contentInfo.suggestedContentGroups);
  556.   },
  557.  
  558.   /**
  559.    * \see nsISupports.idl
  560.    */
  561.   QueryInterface:
  562.     XPCOMUtils.generateQI([Ci.sbIDisplayPaneManager])
  563. }; // DisplayPaneManager.prototype
  564.  
  565. function NSGetModule(compMgr, fileSpec) {
  566.   return XPCOMUtils.generateModule([DisplayPaneManager]);
  567. }
  568.  
  569.