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>
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
33 /* This is the Gecko wrapping for the Browser D-Bus Bridge API
34 * You can use the XPCOM API directly, but the Bridge API
36 * - has few utility features over the "raw" API (like introspection)
37 * - generally feels a bit more JavaScriptish (hopefully)
41 var ep = netscape.security.PrivilegeManager.enablePrivilege;
42 ep("UniversalXPConnect");
43 var cc = Components.classes;
44 var ci = Components.interfaces;
46 /* Utility function that creates a wrapper function which handles signature
47 * conversion from wrapper(argarray) to method(arg1, ..., argN) and sets
48 * 'this' to point to the user-specified object. This is mainly used to
49 * convert the variant arrays from the XPCOM service to callback arguments.
51 var unWrap = function(method, user_data) {
54 method.apply(user_data, arguments[0]);
59 var makeVariantMaker = function(variant_type) {
60 return function(val) {
61 ep("UniversalXPConnect");
62 var variant = cc["@mozilla.org/variant;1"].createInstance(ci.nsIWritableVariant);
63 variant['setAs' + variant_type](val);
69 // Bridge API: The DBus singleton class
70 window.DBus = function() {
72 ep("UniversalXPConnect");
73 this.dbus = cc["@movial.fi/dbus/service;1"].getService();
74 this.dbus = this.dbus.QueryInterface(Components.interfaces.IDBusService);
76 // Mirror static properties
77 this.SYSTEM = this.dbus.SYSTEM;
78 this.SESSION = this.dbus.SESSION;
80 // Bridge API: DBus.[some type]()
81 // These are types we can wrap to variants directly for the
82 // DBus.Int32() etc conversion methods
94 for (var type in types) {
96 this[type] = makeVariantMaker(types[type]);
100 /* TODO: These need their own XPCOM types/handling:
109 // Bridge API: dbus.getMethod()
110 window.DBus.prototype.getMethod = function(bustype,
118 /* A wrapper function for method calls
119 * Executes the actual call through the XPCOM interface
121 var method = function() {
122 ep("UniversalXPConnect");
123 // FIXME: Is the copy really necessary? Why?
124 method.dbusmethod.doCall(Array.prototype.slice.call(arguments),
129 // Store the user specified "this" object
130 method.user_data = user_data;
132 // Create the XPCOM method object
133 ep("UniversalXPConnect");
134 method.dbusmethod = this.dbus.getMethod(bustype,
141 /* XXX: Hmm? onreply and onerror are undefined here, no? */
142 method.dbusmethod.onReply = unWrap(method.onreply, method.user_data);
143 method.dbusmethod.onError = unWrap(method.onerror, method.user_data);
145 // Watch the onreply property and assign the callback through our
147 method.watch("onreply", function(id, oldval, newval) {
148 ep("UniversalXPConnect");
149 method.dbusmethod.onReply = unWrap(newval, method.user_data);
153 method.watch("onerror", function(id, oldval, newval) {
154 ep("UniversalXPConnect");
155 method.dbusmethod.onError = unWrap(newval, method.user_data);
158 // Mirror the async property
160 method.watch("async", function(id, oldval, newval) {
161 ep("UniversalXPConnect");
162 method.dbusmethod.async = newval;
168 // Bridge API: dbus.getSignal()
169 window.DBus.prototype.getSignal = function(bustype,
177 // Create the XPCOM signal object
178 ep("UniversalXPConnect");
179 signal.dbussignal = this.dbus.getSignal(bustype,
185 // Store user-specified "this" for callbacks
186 signal.user_data = user_data;
188 // XXX: ummm.... onemit == undefined?
189 signal.dbussignal.onEmit = unWrap(signal.onemit, signal.user_data);
191 // Watch the onemit property for wrapping
192 signal.watch("onemit", function(id, oldval, newval) {
193 ep("UniversalXPConnect");
194 signal.dbussignal.onEmit = unWrap(newval, signal.user_data);
197 // Watch the enabled property for wrapping
198 signal.enabled = false;
199 signal.watch("enabled", function(id, oldval, newval) {
200 ep("UniversalXPConnect");
201 signal.dbussignal.enabled = newval;
207 // Bridge API: dbus.emitSignal()
208 window.DBus.prototype.emitSignal = function() {
209 var args = arguments.splice(5);
210 ep("UniversalXPConnect");
211 this.dbus.emitSignal(arguments[0], arguments[1],
212 arguments[2], arguments[3],
213 arguments[4], args, args.length);
218 // Instantiate the Bridge API singleton
220 window.dbus = new window.DBus();
229 DBus.prototype.parseInterface = function(doc, dest, path, inter){
231 var temp = doc.getElementsByTagName("interface");
232 //interesting interface is last
233 for( var i = temp.length - 1; i >=0; i--){
234 if( temp[i].getAttribute("name") != inter ) { continue; }
236 var methods = temp[i].getElementsByTagName("method");
237 for ( var j = 0, jl = methods.length; j < jl; j ++){
238 var name = methods[j].getAttribute("name");
240 var args = methods[j].getElementsByTagName("arg");
243 for( var z = 0, zl = args.length; z < zl; z ++){
245 if( args[z].getAttribute("direction") == "in"){
246 type.push(args[z].getAttribute("type") );
250 ret[name] = dbus.getMethod(window.DBus.SESSION, dest, path,
251 name, inter, type.join(""), ret );
255 var signals = temp[i].getElementsByTagName("signal");
256 for ( var j = 0, jl = signals.length; j < jl; j ++){
257 name = signals[j].getAttribute("name");
258 ret[name] = dbus.getSignal(window.DBus.SESSION,
260 this.object_path, ret);
261 ret[name].enabled = false;
262 ret[name].onemit = null;
264 ret.xml = (new XMLSerializer()).serializeToString(doc);
266 // break; //interface found
272 * @class This method uses the <code>Introspect</code> method on the standard
273 * <code>org.freedesktop.DBus.Introspectable</code> interface to map a given
274 * D-Bus interface to a JavaScript object. The produced object works as
275 * <code>this</code> in all its methods.<br/> Note that introspecting is
276 * not done asynchronously so the function call will block until the service
281 * var myinterface = DBus.getInterface( mydest, mypath, myinterface);<br/>
282 * var myname = "";<br/>
283 * myinterface.getName.onreply = function(name){<br/>
284 * myname = name;<br/>
286 * myinterface.getName.onerror = function(message){<br/>
287 * alert('name getting failed:'+message );<br/>
289 * myinterface.getName( me );<br/>
291 * @param dest {String} destination
292 * @param path {String} object path
293 * @param inter {String} interface
295 DBus.prototype.getInterface = function(dest, path, inter){
297 var introspect = dbus.getMethod(dbus.SESSION, dest, path,
299 "org.freedesktop.DBus.Introspectable" );
300 introspect.onreply = function(s){
301 doc = s.replace(/^\s*|\s*$/g,"");
303 introspect.onerror = function(s){
306 introspect.async = false;
313 doc = (new DOMParser()).parseFromString(doc, "text/xml");
315 return this.parseInterface(doc, dest, path, inter);