2 * DBus.js, a JS wrapper for the Browser D-Bus Bridge
3 * Copyright (c) 2008-2009 Movial Creative Technologies Inc
5 * Contact: Movial Creative Technologies Inc, <info@movial.com>
6 * Authors: Lauri Mylläri, <lauri.myllari@movial.fi>
7 * Kalle Vahlman, <kalle.vahlman@movial.com>
8 * Ehsun Amanolahi, <ehsun.amanolahi@movial.com>
10 * Permission is hereby granted, free of charge, to any person
11 * obtaining a copy of this software and associated documentation
12 * files (the "Software"), to deal in the Software without
13 * restriction, including without limitation the rights to use,
14 * copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following
19 * The above copyright notice and this permission notice shall be
20 * included in all copies or substantial portions of the Software.
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
29 * OTHER DEALINGS IN THE SOFTWARE.
34 /* This is the Gecko wrapping for the Browser D-Bus Bridge API
35 * You can use the XPCOM API directly, but the Bridge API
37 * - has few utility features over the "raw" API (like introspection)
38 * - generally feels a bit more JavaScriptish (hopefully)
42 var ep = netscape.security.PrivilegeManager.enablePrivilege;
43 ep("UniversalXPConnect");
44 var cc = Components.classes;
45 var ci = Components.interfaces;
47 /* Utility function that creates a wrapper function which handles signature
48 * conversion from wrapper(argarray) to method(arg1, ..., argN) and sets
49 * 'this' to point to the user-specified object. This is mainly used to
50 * convert the variant arrays from the XPCOM service to callback arguments.
52 var unWrap = function(method, user_data) {
55 method.apply(user_data, arguments[0]);
60 var makeVariantMaker = function(variant_type) {
61 return function(val) {
62 ep("UniversalXPConnect");
63 var variant = cc["@mozilla.org/variant;1"].createInstance(ci.nsIWritableVariant);
64 variant['setAs' + variant_type](val);
70 // Bridge API: The DBus singleton class
71 window.DBus = function() {
73 ep("UniversalXPConnect");
74 this.dbus = cc["@movial.fi/dbus/service;1"].getService();
75 this.dbus = this.dbus.QueryInterface(Components.interfaces.IDBusService);
77 // Mirror static properties
78 this.SYSTEM = this.dbus.SYSTEM;
79 this.SESSION = this.dbus.SESSION;
81 // Bridge API: DBus.[some type]()
82 // These are types we can wrap to variants directly for the
83 // DBus.Int32() etc conversion methods
95 for (var type in types) {
97 this[type] = makeVariantMaker(types[type]);
103 // Methods for non-variant and non-automatic type conversions
104 window.DBus.prototype.Signature = function(val) {
105 ep("UniversalXPConnect");
106 var carrier = cc["@movial.fi/dbus/datacarrier;1"].createInstance(ci.IDBusDataCarrier);
112 window.DBus.prototype.ObjectPath = function(val) {
113 ep("UniversalXPConnect");
114 var carrier = cc["@movial.fi/dbus/datacarrier;1"].createInstance(ci.IDBusDataCarrier);
120 window.DBus.prototype.Variant = function(sig, val) {
121 ep("UniversalXPConnect");
122 var carrier = cc["@movial.fi/dbus/datacarrier;1"].createInstance(ci.IDBusDataCarrier);
124 carrier.signature = sig;
129 window.DBus.prototype.Struct = function(val) {
130 ep("UniversalXPConnect");
131 var carrier = cc["@movial.fi/dbus/datacarrier;1"].createInstance(ci.IDBusDataCarrier);
137 // Bridge API: dbus.getMethod()
138 window.DBus.prototype.getMethod = function(bustype,
146 /* A wrapper function for method calls
147 * Executes the actual call through the XPCOM interface
149 var method = function() {
150 ep("UniversalXPConnect");
151 // FIXME: Is the copy really necessary? Why?
152 method.dbusmethod.doCall(Array.prototype.slice.call(arguments),
156 // Store the user specified "this" object
157 method.user_data = user_data;
159 // Create the XPCOM method object
160 ep("UniversalXPConnect");
161 method.dbusmethod = this.dbus.getMethod(bustype,
168 /* XXX: Hmm? onreply and onerror are undefined here, no? */
169 method.dbusmethod.onReply = unWrap(method.onreply, method.user_data);
170 method.dbusmethod.onError = unWrap(method.onerror, method.user_data);
172 // Watch the onreply property and assign the callback through our
174 method.watch("onreply", function(id, oldval, newval) {
175 ep("UniversalXPConnect");
176 method.dbusmethod.onReply = unWrap(newval, method.user_data);
180 method.watch("onerror", function(id, oldval, newval) {
181 ep("UniversalXPConnect");
182 method.dbusmethod.onError = unWrap(newval, method.user_data);
185 // Mirror the async property
187 method.watch("async", function(id, oldval, newval) {
188 ep("UniversalXPConnect");
189 method.dbusmethod.async = newval;
195 // Bridge API: dbus.getSignal()
196 window.DBus.prototype.getSignal = function(bustype,
204 // Create the XPCOM signal object
205 ep("UniversalXPConnect");
206 signal.dbussignal = this.dbus.getSignal(bustype,
212 // Store user-specified "this" for callbacks
213 signal.user_data = user_data;
215 // XXX: ummm.... onemit == undefined?
216 signal.dbussignal.onEmit = unWrap(signal.onemit, signal.user_data);
218 // Watch the onemit property for wrapping
219 signal.watch("onemit", function(id, oldval, newval) {
220 ep("UniversalXPConnect");
221 signal.dbussignal.onEmit = unWrap(newval, signal.user_data);
224 // Watch the enabled property for wrapping
225 signal.enabled = false;
226 signal.watch("enabled", function(id, oldval, newval) {
227 ep("UniversalXPConnect");
228 signal.dbussignal.enabled = newval;
234 // Bridge API: dbus.emitSignal()
235 window.DBus.prototype.emitSignal = function() {
236 var args = arguments.splice(5);
237 ep("UniversalXPConnect");
238 this.dbus.emitSignal(arguments[0], arguments[1],
239 arguments[2], arguments[3],
240 arguments[4], args, args.length);
245 // Instantiate the Bridge API singleton
247 window.dbus = new window.DBus();
256 DBus.prototype.parseInterface = function(doc, dest, path, inter){
258 var temp = doc.getElementsByTagName("interface");
259 //interesting interface is last
260 for( var i = temp.length - 1; i >=0; i--){
261 if( temp[i].getAttribute("name") != inter ) { continue; }
263 var methods = temp[i].getElementsByTagName("method");
264 for ( var j = 0, jl = methods.length; j < jl; j ++){
265 var name = methods[j].getAttribute("name");
267 var args = methods[j].getElementsByTagName("arg");
270 for( var z = 0, zl = args.length; z < zl; z ++){
272 if( args[z].getAttribute("direction") == "in"){
273 type.push(args[z].getAttribute("type") );
277 ret[name] = dbus.getMethod(window.DBus.SESSION, dest, path,
278 name, inter, type.join(""), ret );
282 var signals = temp[i].getElementsByTagName("signal");
283 for ( var j = 0, jl = signals.length; j < jl; j ++){
284 name = signals[j].getAttribute("name");
285 ret[name] = dbus.getSignal(window.DBus.SESSION,
287 this.object_path, ret);
288 ret[name].enabled = false;
289 ret[name].onemit = null;
291 ret.xml = (new XMLSerializer()).serializeToString(doc);
293 // break; //interface found
300 * @class This method uses the <code>Introspect</code> method on the standard
301 * <code>org.freedesktop.DBus.Introspectable</code> interface to map a given
302 * D-Bus interface to a JavaScript object. The produced object works as
303 * <code>this</code> in all its methods.<br/> Note that introspecting is
304 * not done asynchronously so the function call will block until the service
309 * var myinterface = DBus.getInterface( mydest, mypath, myinterface);<br/>
310 * var myname = "";<br/>
311 * myinterface.getName.onreply = function(name){<br/>
312 * myname = name;<br/>
314 * myinterface.getName.onerror = function(message){<br/>
315 * alert('name getting failed:'+message );<br/>
317 * myinterface.getName( me );<br/>
319 * @param dest {String} destination
320 * @param path {String} object path
321 * @param inter {String} interface
323 DBus.prototype.getInterface = function(dest, path, inter){
325 var introspect = dbus.getMethod(dbus.SESSION, dest, path,
327 "org.freedesktop.DBus.Introspectable" );
328 introspect.onreply = function(s){
329 doc = s.replace(/^\s*|\s*$/g,"");
331 introspect.onerror = function(s){
334 introspect.async = false;
341 doc = (new DOMParser()).parseFromString(doc, "text/xml");
343 return this.parseInterface(doc, dest, path, inter);
346 /* Conversion functions for default conversion types
347 * These are offered mainly just for completeness, there is usually no point in
348 * actually using them...
350 DBus.prototype.Boolean = function (val) { return val; };
351 DBus.prototype.Double = function (val) { return val; };
352 DBus.prototype.String = function (val) { return val; };
353 DBus.prototype.Array = function (val) { return val; };
354 DBus.prototype.Dict = function (val) { return val; };