sbDataRemote.js
Go to the documentation of this file.
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 
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.
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() {
417  },
418 
419  get contractID() {
421  },
422 
423  get classID() {
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,
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 
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 
582  registerSelf: function(compMgr, fileSpec, location, type) {
583  compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
584  compMgr.registerFactoryLocation(SONGBIRD_DATAREMOTE_CID,
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 
SafebrowsingApplicationMod prototype registerSelf
const SONGBIRD_DATAREMOTE_CID
Definition: sbDataRemote.js:46
const SONGBIRD_DATAREMOTE_CONTRACTID
Definition: sbDataRemote.js:44
sbDeviceFirmwareAutoCheckForUpdate prototype flags
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
const SONGBIRD_DATAREMOTE_IID
Definition: sbDataRemote.js:47
sidebarFactory createInstance
Definition: nsSidebar.js:351
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
var language
Definition: Info.js:44
systray _prefBranch
Definition: mainwin.js:150
function DataRemote()
Definition: sbDataRemote.js:49
var Cr
Definition: sbDataRemote.js:36
sbDeviceFirmwareAutoCheckForUpdate prototype getHelperForLanguage
var DEBUG_DATAREMOTES
Definition: sbDataRemote.js:34
_window init
Definition: FeedWriter.js:1144
SafebrowsingApplicationMod prototype getClassObject
var count
Definition: test_bug7406.js:32
function NSGetModule(compMgr, fileSpec)
return null
Definition: FeedWriter.js:1143
return!aWindow arguments!aWindow arguments[0]
const SONGBIRD_DATAREMOTE_CLASSNAME
Definition: sbDataRemote.js:45
const gDataRemoteModule
countRef value
Definition: FeedWriter.js:1423
var Ci
Definition: sbDataRemote.js:37
if(DEBUG_DATAREMOTES)
sbDeviceFirmwareAutoCheckForUpdate prototype classID
sbDeviceFirmwareAutoCheckForUpdate prototype getInterfaces
classDescription implementationLanguage
Definition: FeedWriter.js:1427
_getWindowDimension aAttribute
var Cc
Definition: sbDataRemote.js:38
_updateTextAndScrollDataForFrame aData
sbDeviceFirmwareAutoCheckForUpdate prototype observe