home *** CD-ROM | disk | FTP | other *** search
/ Freelog 100 / FreelogNo100-NovembreDecembre2010.iso / Multimedia / Songbird / Songbird_1.8.0-1800_windows-i686-msvc8.exe / components / sbDataRemote.js < prev    next >
Text File  |  2010-08-30  |  20KB  |  633 lines

  1. /*
  2.  *=BEGIN SONGBIRD GPL
  3.  *
  4.  * This file is part of the Songbird web player.
  5.  *
  6.  * Copyright(c) 2005-2009 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. /**
  26.  * \file sbDataRemote.js
  27.  * \brief Implementation of the interface sbIDataRemote
  28.  * This implementation of sbIDataRemote uses the mozilla pref system as a
  29.  *   backend for storing key-value pairs.
  30.  * \sa sbIDataRemote.idl  sbIDataRemote.js
  31.  */
  32.  
  33. // Enable this to log number of active dataremotes for each key
  34. var DEBUG_DATAREMOTES = false;
  35.  
  36. var Cr = Components.results;
  37. var Ci = Components.interfaces;
  38. var Cc = Components.classes;
  39.  
  40. // This object should not be instantiated by user code.  Instead, use 
  41. // the original contract id "@songbirdnest.com/Songbird/DataRemote;1"
  42. // which will give a wrapper around this object.  See sbPIDataRemote2
  43. // and sbDataRemoteWrapper for details.
  44. const SONGBIRD_DATAREMOTE_CONTRACTID = null;
  45. const SONGBIRD_DATAREMOTE_CLASSNAME = "Songbird Data Remote Instance";
  46. const SONGBIRD_DATAREMOTE_CID = Components.ID("{e0990420-e9c0-11dd-ba2f-0800200c9a66}");
  47. const SONGBIRD_DATAREMOTE_IID = Ci.sbPIDataRemote2;
  48.  
  49. function DataRemote() {
  50.   // Nothing here...
  51. }
  52.  
  53. // Define the prototype block before adding to the prototype object or the
  54. //   additions will get blown away (at least the setters/getters were).
  55. DataRemote.prototype = {
  56.   _initialized: false,     // has init been called
  57.   _observing: false,       // are we hooked up to the pref branch as a listener
  58.   _prefBranch: null,       // the pref branch associated with the root
  59.   _root: null,             // the root used to retrieve the pref branch
  60.   _key: null,              // the section of the branch we care about ("Domain")
  61.   _boundObserver: null,    // the object observing the change
  62.   _boundElement: null,     // the element containing the linked prop/attr.
  63.   _boundProperty: null,    // the property linked to the data (of boundElement)
  64.   _boundAttribute: null,   // the attribute linked to the data (of boundElement)
  65.   _isBool: false,          // Is the data a yes/no true/false chunk of data?
  66.   _isNot: false,           // Is the linked data state opposite of target data?
  67.   _evalString: "",         // a string of js to evaluate when the data changes
  68.  
  69.   init: function(aKey, aRoot) {
  70.     // Only allow initialization once per object
  71.     if (this._initialized)
  72.       throw Cr.NS_ERROR_UNEXPECTED;
  73.       
  74.     // Set the strings
  75.     if (!aRoot) {
  76.       // The prefapi hashes fully qualified prefs, so using a simple root does not
  77.       //   hurt us. Callbacks are in a (BIG) linked-list (ew), which sucks. Having
  78.       //   a shorter root saves some strncmp() time.
  79.       this._root = "songbird.";
  80.       this._key = aKey;
  81.     } else {
  82.       // If a root is specified use that.
  83.       this._root = aRoot;
  84.       this._key = aKey;
  85.     }
  86.  
  87.     // get the prefbranch for our root from the pref service
  88.     var prefsService = Cc["@mozilla.org/preferences-service;1"]
  89.                        .getService(Ci.nsIPrefService);
  90.     this._prefBranch = prefsService.getBranch(this._root)
  91.                   .QueryInterface(Ci.nsIPrefBranch2);
  92.     if (!this._prefBranch)
  93.       throw Cr.NS_ERROR_FAILURE;
  94.  
  95.     this._initialized = true;
  96.   },
  97.  
  98.   // only needs to be called if we have bound an attribute, property or observer
  99.   unbind: function() {
  100.     if (!this._initialized)
  101.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  102.     if (this._prefBranch && this._observing)
  103.       this._prefBranch.removeObserver( this._key, this );
  104.  
  105.     // clear the decks
  106.     this._observing = false;
  107.     this._boundObserver = null;
  108.     this._boundAttribute = null;
  109.     this._boundProperty = null;
  110.     this._boundElement = null;
  111.   },
  112.       
  113.   bindObserver: function(aObserver, aSuppressFirst) {
  114.     if (!this._initialized)
  115.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  116.  
  117.     // Clear and reinsert ourselves as an observer.
  118.     if ( this._observing )
  119.       this._prefBranch.removeObserver(this._key, this);
  120.  
  121.     this._prefBranch.addObserver(this._key, this, true);
  122.     this._observing = true;
  123.  
  124.     // Now we are linked to an nsIObserver object
  125.     this._boundObserver = aObserver;
  126.     this._boundElement = null;
  127.     this._boundProperty = null;
  128.     this._boundAttribute = null;
  129.     this._isBool = false;
  130.     this._isNot = false;
  131.     this._evalString = "";
  132.     
  133.     // If the caller wants to be notified, fire on attachment
  134.     if (!aSuppressFirst)
  135.       this.observe(null, null, this._key);
  136.   },
  137.   
  138.   bindRemoteObserver: function(aRemoteObserver, aSuppressFirst) {
  139.     this.bindObserver(aRemoteObserver, aSuppressFirst);
  140.   },
  141.      
  142.   bindProperty: function(aElement, aProperty, aIsBool, aIsNot, aEvalString) {
  143.     if (!this._initialized)
  144.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  145.  
  146.     if (!aIsBool)
  147.       aIsBool = false;
  148.     if (!aIsNot)
  149.       aIsNot = false;
  150.     if (!aEvalString)
  151.       aEvalString = "";
  152.  
  153.     // Clear and reinsert ourselves as an observer.
  154.     if ( this._observing )
  155.       this._prefBranch.removeObserver(this._key, this);
  156.  
  157.     this._prefBranch.addObserver(this._key, this, true);
  158.     this._observing = true;
  159.  
  160.     // Now we are linked to property on an element
  161.     this._boundObserver = null;
  162.     this._boundElement = aElement;
  163.     this._boundProperty = aProperty;
  164.     this._boundAttribute = null;
  165.     this._isBool = aIsBool;
  166.     this._isNot = aIsNot;
  167.     this._evalString = aEvalString;
  168.     
  169.     // Set the value once
  170.     this.observe(null, null, this._key);
  171.   },
  172.         
  173.   bindAttribute: function(aElement, aAttribute, aIsBool, aIsNot, aEvalString) {
  174.     if (!this._initialized)
  175.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  176.  
  177.     if (!aIsBool)
  178.       aIsBool = false;
  179.     if (!aIsNot)
  180.       aIsNot = false;
  181.     if (!aEvalString)
  182.       aEvalString = "";
  183.  
  184.     // Clear and reinsert ourselves as an observer.
  185.     if ( this._observing )
  186.       this._prefBranch.removeObserver(this._key, this);
  187.  
  188.     this._prefBranch.addObserver(this._key, this, true);
  189.     this._observing = true;
  190.  
  191.     // Now we are linked to an attribute on an element
  192.     this._boundObserver = null;
  193.     this._boundElement = aElement;
  194.     this._boundProperty = null;
  195.     this._boundAttribute = aAttribute;
  196.     this._isBool = aIsBool;
  197.     this._isNot = aIsNot;
  198.     this._evalString = aEvalString;
  199.     
  200.     // Set the value once
  201.     this.observe(null, null, this._key);
  202.   },
  203.  
  204.   deleteBranch: function() {
  205.     this._prefBranch.deleteBranch(this._key);
  206.   },
  207.  
  208.  
  209.   // Original attribute getter/setters
  210.  
  211.   get stringValue() {
  212.     if (!this._initialized)
  213.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  214.  
  215.     return this._getValue();
  216.   },
  217.  
  218.   set stringValue(aStringValue) {
  219.     if (!this._initialized)
  220.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  221.  
  222.     // Make sure there is a string object to pass.
  223.     if (aStringValue == null)
  224.       aStringValue = "";
  225.     this._setValue(aStringValue);
  226.   },
  227.  
  228.   get boolValue() {
  229.     if (!this._initialized)
  230.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  231.  
  232.     return (this._makeIntValue(this._getValue()) != 0);
  233.   },
  234.  
  235.   set boolValue(aBoolValue) {
  236.     if (!this._initialized)
  237.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  238.  
  239.     // convert the bool to a numeric string for easy getBoolValue calls.
  240.     aBoolValue = aBoolValue ? "1" : "0";
  241.     this._setValue(aBoolValue);
  242.   },
  243.  
  244.   get intValue() {
  245.     if (!this._initialized)
  246.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  247.  
  248.     return this._makeIntValue(this._getValue());
  249.   },
  250.  
  251.   set intValue(aIntValue) {
  252.     if (!this._initialized)
  253.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  254.  
  255.     this._setValue(aIntValue + "");
  256.   },
  257.  
  258.  
  259.   // Newer method getter/setters, used to work around
  260.   // BMO 304048. See sbIDataRemote for details.
  261.   // These are temporary and internal, but in general
  262.   // this entire implementation should go away.
  263.   getAsString: function() {
  264.     return this.stringValue;
  265.   },
  266.  
  267.   setAsString: function(aStringValue) {
  268.     this.stringValue = aStringValue;
  269.   },
  270.  
  271.   setAsBool: function(aBoolValue) {
  272.     this.boolValue = aBoolValue;
  273.   },
  274.  
  275.   getAsBool: function(aBoolValue) {
  276.     return this.boolValue;
  277.   },
  278.  
  279.   getAsInt: function() {
  280.     return this.intValue;
  281.   },
  282.  
  283.   setAsInt: function(aIntValue) {
  284.     this.intValue = aIntValue;
  285.   },
  286.  
  287.   // internal helper function - all setters ultimately call this
  288.   _setValue: function(aValueStr) {
  289.     // assume we are being called after the init check in another method.
  290.  
  291.     // Make a unicode string, assign the value, set it into the preferences.
  292.     var sString = Cc["@mozilla.org/supports-string;1"]
  293.                             .createInstance(Ci.nsISupportsString);
  294.     sString.data = aValueStr;
  295.     this._prefBranch.setComplexValue(this._key,
  296.                                 Ci.nsISupportsString,
  297.                                 sString);
  298.   },
  299.  
  300.   // internal helper function - all getters ultimately call this
  301.   _getValue: function() {
  302.     // assume we are being called after the init check in another method.
  303.  
  304.     var retval = "";
  305.     try {
  306.       var prefValue = this._prefBranch.getComplexValue(this._key, Ci.nsISupportsString);
  307.       if (prefValue != "") {
  308.         retval = prefValue.data;
  309.       }
  310.     } catch (err) {
  311.       // when the pref does not exist in the branch the pref system returns an
  312.       //   error code which changes to an exception. We do not want to throw
  313.       //   here but return an empty string.
  314.     }
  315.     return retval;
  316.   },
  317.       
  318.   // internal function for converting to an integer.
  319.   _makeIntValue: function(aValueStr) {
  320.     var retval = 0;
  321.     if (aValueStr && aValueStr.length)
  322.       retval = parseInt(aValueStr);
  323.     return retval;
  324.   },
  325.  
  326.   // observe - Called when someone updates the remote data
  327.   // aSubject: The prefbranch object
  328.   // aTopic:   NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
  329.   // aData:    the domain (key)
  330.   observe: function(aSubject, aTopic, aData) {
  331.     if (!this._initialized)
  332.       throw Cr.NS_ERROR_NOT_INITIALIZED;
  333.     
  334.     // Early bail conditions
  335.     if (aData != this._key)
  336.       return;
  337.     
  338.     // Get the value as a string - this must be called value to not break
  339.     //    evalStrings that do an eval of the value.
  340.     var value = this._getValue();
  341.     
  342.     // Run the optional evaluation
  343.     if (this._evalString.length)
  344.       value = eval(this._evalString);
  345.     
  346.     // Handle boolean and not states
  347.     if (this._isBool) {
  348.       // If we were not a bool, make us one (_evalString could change value)
  349.       if (typeof(value) != "boolean") {
  350.         if (value == "true")
  351.           value = true;
  352.         else if (value == "false")
  353.           value = false;
  354.         else
  355.           value = (this._makeIntValue(value) != 0);
  356.       }
  357.       // reverse ourself if neccessary
  358.       if (this._isNot)
  359.         value = !value;
  360.     }
  361.  
  362.     // Handle callback states
  363.     if (this._boundObserver) {
  364.       try {
  365.         // check if we're dealing with a remote api observer instead of a
  366.         // normal application level data remote observer.
  367.         if (this._boundObserver instanceof Ci.sbIRemoteObserver) {
  368.           // pass useful information to the observer.
  369.           this._boundObserver.observe( this._key, value );
  370.         }
  371.         else {
  372.           // pass useful information to the observer.
  373.           this._boundObserver.observe( this, this._key, value );
  374.         }
  375.       }
  376.       catch (err) {
  377.         dump("ERROR! Could not call boundObserver.observe(). Key = " + this._key + "\n" + err + "\n");
  378.       }
  379.     }
  380.     else if (this._boundElement && this._boundProperty) {
  381.       // Set the property of the callback object
  382.       this._boundElement[this._boundProperty] = value;
  383.     }
  384.     else if (this._boundElement && this._boundAttribute) {
  385.       // Set the attribute of the callback object
  386.       var valStr = value;
  387.       // If bool-type, convert to string.
  388.       if (this._isBool) {
  389.         if (value)
  390.           valStr = "true";
  391.         else
  392.           valStr = "false";
  393.       }
  394.       try {
  395.         this._boundElement.setAttribute(this._boundAttribute, valStr);
  396.       }
  397.       catch (err) {
  398.         dump("ERROR! Could not setAttribute in sbDataRemote.js\n " + err + "\n");
  399.       }
  400.     }
  401.   },
  402.  
  403.   // nsIClassInfo
  404.   getInterfaces: function( count ) {
  405.      var ifaces = [ SONGBIRD_DATAREMOTE_IID,
  406.                     Ci.nsIClassInfo,
  407.                     Ci.nsIObserver,
  408.                     Ci.nsISecurityCheckedComponent,
  409.                     Ci.sbISecurityAggregator,
  410.                     Ci.nsISupportsWeakReference ];
  411.       count.value = ifaces.length;
  412.       return ifaces;
  413.   },
  414.  
  415.   get classDescription() {
  416.       return SONGBIRD_DATAREMOTE_CLASSNAME;
  417.   },
  418.  
  419.   get contractID() {
  420.       return SONGBIRD_DATAREMOTE_CONTRACTID;
  421.   },
  422.  
  423.   get classID() {
  424.       return SONGBIRD_DATAREMOTE_CID;
  425.   },
  426.  
  427.   getHelperForLanguage: function( language ) { return null; },
  428.  
  429.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  430.  
  431.   // needs to be DOM_OBJECT to allow remoteAPI to access it.
  432.   flags: Ci.nsIClassInfo.DOM_OBJECT,
  433.  
  434.   // nsISecurityCheckedComponent -- implemented by the security mixin
  435.   _securityMixin: null,
  436.   _initializedSCC: false,
  437.   _publicWProps: [ ],
  438.   _publicRProps: [ "classinfo:classDescription",
  439.                    "classinfo:contractID",
  440.                    "classinfo:classID",
  441.                    "classinfo:implementationLanguage",
  442.                    "classinfo:flags" ],
  443.   _publicMethods: [ "internal:bindAttribute",
  444.                     "internal:deleteBranch",
  445.                     "internal:bindObserver",
  446.                     "internal:bindProperty",
  447.                     "internal:setAsString",
  448.                     "internal:getAsString",
  449.                     "internal:setAsInt",
  450.                     "internal:getAsInt",
  451.                     "internal:setAsBool",
  452.                     "internal:getAsBool",
  453.                     "internal:unbind",
  454.                     "internal:init" ],
  455.   _publicInterfaces: [ Ci.nsISupports,
  456.                        Ci.nsIClassInfo,
  457.                        Ci.nsIObserver,
  458.                        Ci.nsISecurityCheckedComponent,
  459.                        SONGBIRD_DATAREMOTE_IID ],
  460.  
  461.   _initSCC: function() {
  462.     this._securityMixin = Cc["@songbirdnest.com/remoteapi/security-mixin;1"]
  463.                          .createInstance(Ci.nsISecurityCheckedComponent);
  464.  
  465.     // initialize the security mixin with the cleared methods and props
  466.     this._securityMixin
  467.              .init(this, this._publicInterfaces, this._publicInterfaces.length,
  468.                          this._publicMethods, this._publicMethods.length,
  469.                          this._publicRProps, this._publicRProps.length,
  470.                          this._publicWProps, this._publicWProps.length,
  471.                          false);
  472.  
  473.     this._initializedSCC = true;
  474.   },
  475.  
  476.   canCreateWrapper: function(iid) {
  477.     if (! this._initializedSCC)
  478.       this._initSCC();
  479.     return this._securityMixin.canCreateWrapper(iid);
  480.   },
  481.   canCallMethod: function(iid, methodName) {
  482.     if (! this._initializedSCC)
  483.       this._initSCC();
  484.     return this._securityMixin.canCallMethod(iid, methodName);
  485.   },
  486.   canGetProperty: function(iid, propertyName) {
  487.     if (! this._initializedSCC)
  488.       this._initSCC();
  489.     return this._securityMixin.canGetProperty(iid, propertyName);
  490.   },
  491.   canSetProperty: function(iid, propertyName) {
  492.     if (! this._initializedSCC)
  493.       this._initSCC();
  494.     return this._securityMixin.canSetProperty(iid, propertyName);
  495.   },
  496.  
  497.   // nsISupports
  498.   QueryInterface: function(iid) {
  499.     if (!iid.equals(SONGBIRD_DATAREMOTE_IID) &&
  500.         !iid.equals(Ci.nsIClassInfo) && 
  501.         !iid.equals(Ci.nsIObserver) && 
  502.         !iid.equals(Ci.nsISecurityCheckedComponent) &&
  503.         !iid.equals(Ci.sbISecurityAggregator) &&
  504.         !iid.equals(Ci.nsISupportsWeakReference) &&
  505.         !iid.equals(Ci.nsISupports)) {
  506.       throw Cr.NS_ERROR_NO_INTERFACE;
  507.     }
  508.     return this;
  509.   }
  510. }; // DataRemote.prototype
  511.  
  512. // be specific
  513. DataRemote.prototype.constructor = DataRemote;
  514.  
  515.  
  516. /**
  517.  * ----------------------------------------------------------------------------
  518.  * Debug Wrapper.  Subclasses and replaces DateRemote to keep 
  519.  * track of how many dataremotes are alive, and for which keys.
  520.  * Enable DEBUG_DATAREMOTES to track down leaks.
  521.  * ----------------------------------------------------------------------------
  522.  */
  523.  
  524. if (DEBUG_DATAREMOTES) {
  525.   var RealDataRemote = DataRemote;
  526.   function DataRemote() {
  527.     dump("DEBUG_DATAREMOTE: NEW DATAREMOTE\n");      
  528.   }
  529.   DataRemote.prototype = {
  530.     __proto__: RealDataRemote.prototype,
  531.     _activeDataremotes: {}, // Shared by all instances
  532.  
  533.     // Overload some methods with debug logging
  534.  
  535.     unbind: function() {
  536.       if (this._observing) {
  537.           this._activeDataremotes[this._key]--;
  538.         dump("DEBUG_DATAREMOTE: UNBIND " + this._key + ". Remaining:\n");      
  539.           for (var key in this._activeDataremotes) {
  540.             dump("DEBUG_DATAREMOTE:\t" + key + "\t\t\t" +
  541.                  this._activeDataremotes[key] + "\n");
  542.           }
  543.       }
  544.       // Call original unbind
  545.       this.__proto__.__proto__.unbind.call(this);
  546.     },
  547.  
  548.     _logBind: function() {
  549.       dump("DEBUG_DATAREMOTE: BIND " + this._key + "\n");
  550.       if (!(this._key in this._activeDataremotes)) {
  551.         this._activeDataremotes[this._key] = 0;
  552.       }
  553.       this._activeDataremotes[this._key]++;
  554.     },
  555.  
  556.     bindObserver: function() {
  557.       this.__proto__.__proto__.bindObserver.apply(this, arguments);
  558.       this._logBind();
  559.     },
  560.  
  561.     bindProperty: function() {
  562.       this.__proto__.__proto__.bindProperty.apply(this, arguments);
  563.       this._logBind();    
  564.     },
  565.  
  566.     bindAttribute: function() {
  567.       this.__proto__.__proto__.bindAttribute.apply(this, arguments);
  568.       this._logBind();
  569.     }
  570.   } 
  571.   DataRemote.prototype.constructor = DataRemote;
  572. }
  573.  
  574.  
  575. /**
  576.  * ----------------------------------------------------------------------------
  577.  * Registration for XPCOM
  578.  * ----------------------------------------------------------------------------
  579.  */
  580.  
  581. const gDataRemoteModule = {
  582.   registerSelf: function(compMgr, fileSpec, location, type) {
  583.     compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  584.     compMgr.registerFactoryLocation(SONGBIRD_DATAREMOTE_CID,
  585.                                     SONGBIRD_DATAREMOTE_CLASSNAME,
  586.                                     SONGBIRD_DATAREMOTE_CONTRACTID,
  587.                                     fileSpec,
  588.                                     location,
  589.                                     type);
  590.   },
  591.  
  592.   unregisterSelf : function (compMgr, location, type) {
  593.     compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  594.     compMgr.unregisterFactoryLocation(SONGBIRD_DATAREMOTE_CID, location);
  595.   },
  596.  
  597.   getClassObject : function (compMgr, cid, iid) {
  598.     if (!cid.equals(SONGBIRD_DATAREMOTE_CID))
  599.       throw Cr.NS_ERROR_NO_INTERFACE;
  600.  
  601.     if (!iid.equals(Ci.nsIFactory))
  602.       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  603.  
  604.     return this.mFactory;
  605.   },
  606.  
  607.   mFactory : {
  608.     createInstance : function (outer, iid) {
  609.       if (outer != null)
  610.         throw Cr.NS_ERROR_NO_AGGREGATION;
  611.       
  612.       return (new DataRemote()).QueryInterface(iid);
  613.     }
  614.   },
  615.  
  616.   canUnload: function(compMgr) { 
  617.     return true; 
  618.   },
  619.  
  620.   QueryInterface : function (iid) {
  621.     if ( !iid.equals(Ci.nsIModule) ||
  622.          !iid.equals(Ci.nsISupports) )
  623.       throw Cr.NS_ERROR_NO_INTERFACE;
  624.     return this;
  625.   }
  626.  
  627. }; // gDataRemoteModule
  628.  
  629. function NSGetModule(compMgr, fileSpec) {
  630.   return gDataRemoteModule;
  631. } // NSGetModule
  632.  
  633.