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 / sbDataRemote.js < prev    next >
Text File  |  2007-12-21  |  17KB  |  524 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.  * \file sbDataRemote.js
  29.  * \brief Implementation of the interface sbIDataRemote
  30.  * This implementation of sbIDataRemote uses the mozilla pref system as a
  31.  *   backend for storing key-value pairs.
  32.  * \sa sbIDataRemote.idl  sbIDataRemote.js
  33.  */
  34.  
  35. var Cr = Components.results;
  36. var Ci = Components.interfaces;
  37. var Cc = Components.classes;
  38.  
  39. const SONGBIRD_DATAREMOTE_CONTRACTID = "@songbirdnest.com/Songbird/DataRemote;1";
  40. const SONGBIRD_DATAREMOTE_CLASSNAME = "Songbird Data Remote Service";
  41. const SONGBIRD_DATAREMOTE_CID = Components.ID("{950952aa-cb6b-4806-b39a-307b1b1eadb6}");
  42. const SONGBIRD_DATAREMOTE_IID = Ci.sbIDataRemote;
  43.  
  44. function DataRemote() {
  45.   // Nothing here...
  46. }
  47.  
  48. // Define the prototype block before adding to the prototype object or the
  49. //   additions will get blown away (at least the setters/getters were).
  50. DataRemote.prototype = {
  51.   _initialized: false,     // has init been called
  52.   _observing: false,       // are we hooked up to the pref branch as a listener
  53.   _prefBranch: null,       // the pref branch associated with the root
  54.   _root: null,             // the root used to retrieve the pref branch
  55.   _key: null,              // the section of the branch we care about ("Domain")
  56.   _boundObserver: null,    // the object observing the change
  57.   _boundElement: null,     // the element containing the linked prop/attr.
  58.   _boundProperty: null,    // the property linked to the data (of boundElement)
  59.   _boundAttribute: null,   // the attribute linked to the data (of boundElement)
  60.   _isBool: false,          // Is the data a yes/no true/false chunk of data?
  61.   _isNot: false,           // Is the linked data state opposite of target data?
  62.   _evalString: "",         // a string of js to evaluate when the data changes
  63.  
  64.   init: function(aKey, aRoot) {
  65.     // Only allow initialization once per object
  66.     if (this._initialized)
  67.       throw Cr.NS_ERROR_UNEXPECTED;
  68.       
  69.     // Set the strings
  70.     if (!aRoot) {
  71.       // The prefapi hashes fully qualified prefs, so using a simple root does not
  72.       //   hurt us. Callbacks are in a (BIG) linked-list (ew), which sucks. Having
  73.       //   a shorter root saves some strncmp() time.
  74.       this._root = "songbird.";
  75.       this._key = aKey;
  76.     } else {
  77.       // If a root is specified use that.
  78.       this._root = aRoot;
  79.       this._key = aKey;
  80.     }
  81.  
  82.     // get the prefbranch for our root from the pref service
  83.     var prefsService = Cc["@mozilla.org/preferences-service;1"]
  84.                        .getService(Ci.nsIPrefService);
  85.     this._prefBranch = prefsService.getBranch(this._root)
  86.                   .QueryInterface(Ci.nsIPrefBranch2);
  87.     if (!this._prefBranch)
  88.       throw Cr.NS_ERROR_FAILURE;
  89.  
  90.     this._initialized = true;
  91.   },
  92.  
  93.   // only needs to be called if we have bound an attribute, property or observer
  94.   unbind: function() {
  95.     if (!this._initialized)
  96.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  97.     if (this._prefBranch && this._observing)
  98.       this._prefBranch.removeObserver( this._key, this );
  99.  
  100.     // clear the decks
  101.     this._observing = false;
  102.     this._boundObserver = null;
  103.     this._boundAttribute = null;
  104.     this._boundProperty = null;
  105.     this._boundElement = null;
  106.   },
  107.       
  108.   bindObserver: function(aObserver, aSuppressFirst) {
  109.     if (!this._initialized)
  110.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  111.  
  112.     // Clear and reinsert ourselves as an observer.
  113.     if ( this._observing )
  114.       this._prefBranch.removeObserver(this._key, this);
  115.  
  116.     // Temporary change in how we attach as observers
  117.     //this._prefBranch.addObserver(this._key, this, true);
  118.     this._prefBranch.addObserver(this._key, this, false);
  119.     this._observing = true;
  120.  
  121.     // Now we are linked to an nsIObserver object
  122.     this._boundObserver = aObserver;
  123.     this._boundElement = null;
  124.     this._boundProperty = null;
  125.     this._boundAttribute = null;
  126.     this._isBool = false;
  127.     this._isNot = false;
  128.     this._evalString = "";
  129.     
  130.     // If the caller wants to be notified, fire on attachment
  131.     if (!aSuppressFirst)
  132.       this.observe(null, null, this._key);
  133.   },
  134.      
  135.   bindProperty: function(aElement, aProperty, aIsBool, aIsNot, aEvalString) {
  136.     if (!this._initialized)
  137.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  138.  
  139.     if (!aIsBool)
  140.       aIsBool = false;
  141.     if (!aIsNot)
  142.       aIsNot = false;
  143.     if (!aEvalString)
  144.       aEvalString = "";
  145.  
  146.     // Clear and reinsert ourselves as an observer.
  147.     if ( this._observing )
  148.       this._prefBranch.removeObserver(this._key, this);
  149.  
  150.     // Temporary change in how we attach as observers
  151.     //this._prefBranch.addObserver(this._key, this, true);
  152.     this._prefBranch.addObserver(this._key, this, false);
  153.     this._observing = true;
  154.  
  155.     // Now we are linked to property on an element
  156.     this._boundObserver = null;
  157.     this._boundElement = aElement;
  158.     this._boundProperty = aProperty;
  159.     this._boundAttribute = null;
  160.     this._isBool = aIsBool;
  161.     this._isNot = aIsNot;
  162.     this._evalString = aEvalString;
  163.     
  164.     // Set the value once
  165.     this.observe(null, null, this._key);
  166.   },
  167.         
  168.   bindAttribute: function(aElement, aAttribute, aIsBool, aIsNot, aEvalString) {
  169.     if (!this._initialized)
  170.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  171.  
  172.     if (!aIsBool)
  173.       aIsBool = false;
  174.     if (!aIsNot)
  175.       aIsNot = false;
  176.     if (!aEvalString)
  177.       aEvalString = "";
  178.  
  179.     // Clear and reinsert ourselves as an observer.
  180.     if ( this._observing )
  181.       this._prefBranch.removeObserver(this._key, this);
  182.  
  183.     // Temporary change in how we attach as observers
  184.     //this._prefBranch.addObserver(this._key, this, true);
  185.     this._prefBranch.addObserver(this._key, this, false);
  186.     this._observing = true;
  187.     
  188.     // Now we are linked to an attribute on an element
  189.     this._boundObserver = null;
  190.     this._boundElement = aElement;
  191.     this._boundProperty = null;
  192.     this._boundAttribute = aAttribute;
  193.     this._isBool = aIsBool;
  194.     this._isNot = aIsNot;
  195.     this._evalString = aEvalString;
  196.     
  197.     // Set the value once
  198.     this.observe(null, null, this._key);
  199.   },
  200.  
  201.   get stringValue() {
  202.     if (!this._initialized)
  203.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  204.  
  205.     return this._getValue();
  206.   },
  207.  
  208.   set stringValue(aStringValue) {
  209.     if (!this._initialized)
  210.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  211.  
  212.     // Make sure there is a string object to pass.
  213.     if (aStringValue == null)
  214.       aStringValue = "";
  215.     this._setValue(aStringValue);
  216.   },
  217.  
  218.   get boolValue() {
  219.     if (!this._initialized)
  220.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  221.  
  222.     return (this._makeIntValue(this._getValue()) != 0);
  223.   },
  224.  
  225.   set boolValue(aBoolValue) {
  226.     if (!this._initialized)
  227.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  228.  
  229.     // convert the bool to a numeric string for easy getBoolValue calls.
  230.     aBoolValue = aBoolValue ? "1" : "0";
  231.     this._setValue(aBoolValue);
  232.   },
  233.  
  234.   get intValue() {
  235.     if (!this._initialized)
  236.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  237.  
  238.     return this._makeIntValue(this._getValue());
  239.   },
  240.  
  241.   set intValue(aIntValue) {
  242.     if (!this._initialized)
  243.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  244.  
  245.     this._setValue(aIntValue + "");
  246.   },
  247.  
  248.   // internal helper function - all setters ultimately call this
  249.   _setValue: function(aValueStr) {
  250.     // assume we are being called after the init check in another method.
  251.  
  252.     // Make a unicode string, assign the value, set it into the preferences.
  253.     var sString = Cc["@mozilla.org/supports-string;1"]
  254.                             .createInstance(Ci.nsISupportsString);
  255.     sString.data = aValueStr;
  256.     this._prefBranch.setComplexValue(this._key,
  257.                                 Ci.nsISupportsString,
  258.                                 sString);
  259.   },
  260.  
  261.   // internal helper function - all getters ultimately call this
  262.   _getValue: function() {
  263.     // assume we are being called after the init check in another method.
  264.  
  265.     var retval = "";
  266.     try {
  267.       var prefValue = this._prefBranch.getComplexValue(this._key, Ci.nsISupportsString);
  268.       if (prefValue != "") {
  269.         retval = prefValue.data;
  270.       }
  271.     } catch (err) {
  272.       // when the pref does not exist in the branch the pref system returns an
  273.       //   error code which changes to an exception. We do not want to throw
  274.       //   here but return an empty string.
  275.     }
  276.     return retval;
  277.   },
  278.       
  279.   // internal function for converting to an integer.
  280.   _makeIntValue: function(aValueStr) {
  281.     var retval = 0;
  282.     if (aValueStr && aValueStr.length)
  283.       retval = parseInt(aValueStr);
  284.     return retval;
  285.   },
  286.  
  287.   // observe - Called when someone updates the remote data
  288.   // aSubject: The prefbranch object
  289.   // aTopic:   NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
  290.   // aData:    the domain (key)
  291.   observe: function(aSubject, aTopic, aData) {
  292.     if (!this._initialized)
  293.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  294.     
  295.     // Early bail conditions
  296.     if (aData != this._key)
  297.       return;
  298.     
  299.     // Get the value as a string - this must be called value to not break
  300.     //    evalStrings that do an eval of the value.
  301.     var value = this._getValue();
  302.     
  303.     // Run the optional evaluation
  304.     if (this._evalString.length)
  305.       value = eval(this._evalString);
  306.     
  307.     // Handle boolean and not states
  308.     if (this._isBool) {
  309.       // If we were not a bool, make us one (_evalString could change value)
  310.       if (typeof(value) != "boolean") {
  311.         if (value == "true")
  312.           value = true;
  313.         else if (value == "false")
  314.           value = false;
  315.         else
  316.           value = (this._makeIntValue(value) != 0);
  317.       }
  318.       // reverse ourself if neccessary
  319.       if (this._isNot)
  320.         value = !value;
  321.     }
  322.  
  323.     // Handle callback states
  324.     if (this._boundObserver) {
  325.       try {
  326.         // pass useful information to the observer.
  327.         this._boundObserver.observe( this, this._key, value );
  328.       }
  329.       catch (err) {
  330.         dump("ERROR! Could not call boundObserver.observe(). Key = " + this._key + "\n" + err + "\n");
  331.       }
  332.     }
  333.     else if (this._boundElement && this._boundProperty) {
  334.       // Set the property of the callback object
  335.       this._boundElement[this._boundProperty] = value;
  336.     }
  337.     else if (this._boundElement && this._boundAttribute) {
  338.       // Set the attribute of the callback object
  339.       var valStr = value;
  340.       // If bool-type, convert to string.
  341.       if (this._isBool) {
  342.         if (value)
  343.           valStr = "true";
  344.         else
  345.           valStr = "false";
  346.       }
  347.       try {
  348.         this._boundElement.setAttribute(this._boundAttribute, valStr);
  349.       }
  350.       catch (err) {
  351.         dump("ERROR! Could not setAttribute in sbDataRemote.js\n " + err + "\n");
  352.       }
  353.     }
  354.   },
  355.  
  356.   // nsIClassInfo
  357.   getInterfaces: function( count ) {
  358.      var ifaces = [ SONGBIRD_DATAREMOTE_IID,
  359.                     Ci.nsIClassInfo,
  360.                     Ci.nsIObserver,
  361.                     Ci.nsISecurityCheckedComponent,
  362.                     Ci.sbISecurityAggregator,
  363.                     Ci.nsISupportsWeakReference ];
  364.       count.value = ifaces.length;
  365.       return ifaces;
  366.   },
  367.  
  368.   get classDescription() {
  369.       return SONGBIRD_DATAREMOTE_CLASSNAME;
  370.   },
  371.  
  372.   get contractID() {
  373.       return SONGBIRD_DATAREMOTE_CONTRACTID;
  374.   },
  375.  
  376.   get classID() {
  377.       return SONGBIRD_DATAREMOTE_CID;
  378.   },
  379.  
  380.   getHelperForLanguage: function( language ) { return null; },
  381.  
  382.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  383.  
  384.   // needs to be DOM_OBJECT to allow remoteAPI to access it.
  385.   flags: Ci.nsIClassInfo.DOM_OBJECT,
  386.  
  387.   // nsISecurityCheckedComponent -- implemented by the security mixin
  388.   _securityMixin: null,
  389.   _initializedSCC: false,
  390.   _publicWProps: [ "internal:stringValue",
  391.                    "internal:boolValue",
  392.                    "internal:intValue" ],
  393.   _publicRProps: [ "internal:stringValue",
  394.                    "internal:boolValue",
  395.                    "internal:intValue",
  396.                    "classinfo:classDescription",
  397.                    "classinfo:contractID",
  398.                    "classinfo:classID",
  399.                    "classinfo:implementationLanguage",
  400.                    "classinfo:flags" ],
  401.   _publicMethods: [ "internal:bindAttribute",
  402.                     "internal:bindObserver",
  403.                     "internal:bindProperty",
  404.                     "internal:unbind",
  405.                     "internal:init" ],
  406.   _publicInterfaces: [ Ci.nsISupports,
  407.                        Ci.nsIClassInfo,
  408.                        Ci.nsIObserver,
  409.                        Ci.nsISecurityCheckedComponent,
  410.                        SONGBIRD_DATAREMOTE_IID ],
  411.  
  412.   _initSCC: function() {
  413.     this._securityMixin = Cc["@songbirdnest.com/remoteapi/security-mixin;1"]
  414.                          .createInstance(Ci.nsISecurityCheckedComponent);
  415.  
  416.     // initialize the security mixin with the cleared methods and props
  417.     this._securityMixin
  418.              .init(this, this._publicInterfaces, this._publicInterfaces.length,
  419.                          this._publicMethods, this._publicMethods.length,
  420.                          this._publicRProps, this._publicRProps.length,
  421.                          this._publicWProps, this._publicWProps.length,
  422.                          false);
  423.  
  424.     this._initializedSCC = true;
  425.   },
  426.  
  427.   canCreateWrapper: function(iid) {
  428.     if (! this._initializedSCC)
  429.       this._initSCC();
  430.     return this._securityMixin.canCreateWrapper(iid);
  431.   },
  432.   canCallMethod: function(iid, methodName) {
  433.     if (! this._initializedSCC)
  434.       this._initSCC();
  435.     return this._securityMixin.canCallMethod(iid, methodName);
  436.   },
  437.   canGetProperty: function(iid, propertyName) {
  438.     if (! this._initializedSCC)
  439.       this._initSCC();
  440.     return this._securityMixin.canGetProperty(iid, propertyName);
  441.   },
  442.   canSetProperty: function(iid, propertyName) {
  443.     if (! this._initializedSCC)
  444.       this._initSCC();
  445.     return this._securityMixin.canSetProperty(iid, propertyName);
  446.   },
  447.  
  448.   // nsISupports
  449.   QueryInterface: function(iid) {
  450.     if (!iid.equals(SONGBIRD_DATAREMOTE_IID) &&
  451.         !iid.equals(Ci.nsIClassInfo) && 
  452.         !iid.equals(Ci.nsIObserver) && 
  453.         !iid.equals(Ci.nsISecurityCheckedComponent) &&
  454.         !iid.equals(Ci.nsISecurityAggregator) &&
  455.         !iid.equals(Ci.nsISupportsWeakReference) &&
  456.         !iid.equals(Ci.nsISupports)) {
  457.       throw Cr.NS_ERROR_NO_INTERFACE;
  458.     }
  459.     return this;
  460.   }
  461. }; // DataRemote.prototype
  462.  
  463. // be specific
  464. DataRemote.prototype.constructor = DataRemote;
  465.  
  466. /**
  467.  * ----------------------------------------------------------------------------
  468.  * Registration for XPCOM
  469.  * ----------------------------------------------------------------------------
  470.  */
  471.  
  472. const gDataRemoteModule = {
  473.   registerSelf: function(compMgr, fileSpec, location, type) {
  474.     compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  475.     compMgr.registerFactoryLocation(SONGBIRD_DATAREMOTE_CID,
  476.                                     SONGBIRD_DATAREMOTE_CLASSNAME,
  477.                                     SONGBIRD_DATAREMOTE_CONTRACTID,
  478.                                     fileSpec,
  479.                                     location,
  480.                                     type);
  481.   },
  482.  
  483.   unregisterSelf : function (compMgr, location, type) {
  484.     compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  485.     compMgr.unregisterFactoryLocation(SONGBIRD_DATAREMOTE_CID, location);
  486.   },
  487.  
  488.   getClassObject : function (compMgr, cid, iid) {
  489.     if (!cid.equals(SONGBIRD_DATAREMOTE_CID))
  490.       throw Cr.NS_ERROR_NO_INTERFACE;
  491.  
  492.     if (!iid.equals(Ci.nsIFactory))
  493.       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  494.  
  495.     return this.mFactory;
  496.   },
  497.  
  498.   mFactory : {
  499.     createInstance : function (outer, iid) {
  500.       if (outer != null)
  501.         throw Cr.NS_ERROR_NO_AGGREGATION;
  502.       
  503.       return (new DataRemote()).QueryInterface(iid);
  504.     }
  505.   },
  506.  
  507.   canUnload: function(compMgr) { 
  508.     return true; 
  509.   },
  510.  
  511.   QueryInterface : function (iid) {
  512.     if ( !iid.equals(Ci.nsIModule) ||
  513.          !iid.equals(Ci.nsISupports) )
  514.       throw Cr.NS_ERROR_NO_INTERFACE;
  515.     return this;
  516.   }
  517.  
  518. }; // gDataRemoteModule
  519.  
  520. function NSGetModule(compMgr, fileSpec) {
  521.   return gDataRemoteModule;
  522. } // NSGetModule
  523.  
  524.