I've been working on writing DBUS bindings for Mozilla Javscript. The goal of this project is to make it possible for XUL and extension developers to integrate with the Linux desktop.
The bindings are still in an early state and need lots of cleanup, but already supports consuming existing services, creating new services, signals, and xml introspection.
The API is very similar to the python bindings, here's an example of how to query NetworkManager for interface information:
var nm = bus.getObject("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", NM_INTERFACE);
var devices = nm.getDevices();
for (var x = 0; x < devices.length; x++) { var device = bus.getObject("org.freedesktop.NetworkManager", devices[x], NM_IFACE_INTERFACE); var name = device.getName(); alert('Found interface: ' + name); }
Specifying the interface methods is required because NM does not support XML introspection. When querying a service that does, you can pass the interface name as a string to getObject instead.
One of the goals from the start was to use XPCOM only as a bridge to the C DBUS library. Everything else is implemented using Javascript.
Right now I'm looking for both general feedback, as well as someone to review my code and suggest ways to clean it up.
A sample program is included that demonstrates network-manager, notify-daemon, and avahi. To run, clone the code into top-level 'extensions' folder in an up to date mozilla-central and run from dist/xpi-stage/mozjs_dbus/ using xulrunner. You'll need to pass --enable-dbus to the moz configure script so that the library paths are included.
There was a project a while back called DBuzilla, which has been abandoned. My library is much simpler because in addition to being much less reliant on XPCOM, it makes use of glib, rather than re-implementing all of the threading/mainloop integration stuff. I was, however, able to re-use some of his data marshaling code, so it was not all for nothing.
I look forward to hearing back from people on this list!
On Dec 3, 5:30 pm, Eric Butler <e...@extremeboredom.net> wrote:
> Hey everyone,
> I've been working on writing DBUS bindings for Mozilla Javscript. The > goal of this project is to make it possible for XUL and extension > developers to integrate with the Linux desktop.
Awesome, this is definitely useful.
> The API is very similar to the python bindings,
The Python bindings are a good source of inspiration, though one thing I might suggest to include "out of the box" that the Python bindings don't is a service tracker. Basically what DBusGProxy does for the GLib bindings.
> Specifying the interface methods is required because NM does not support > XML introspection.
This is fixed in NM 0.7 by the way (shipped with Fedora 8).
> One of the goals from the start was to use XPCOM only as a bridge to the > C DBUS library. Everything else is implemented using Javascript.
Makes sense.
> Right now I'm looking for both general feedback, as well as someone to > review my code and suggest ways to clean it up.
I took a quick look. Some initial comments:
* In some places you're checking for OOM from dbus, others not. For example, dbus_message_new_* can return NULL. * From looking at the XPCOM string guide, it's not clear to me this is safe:
Do we know that cBusName will always be null-terminated UTF-8?
* For signals, you have a global map:
// XXX: This should be a hashtable! std::map<string, SignalCallbackInfo*> signalCallbacks;
Shouldn't this be a weak reference map? Otherwise we'll keep alive every JS callback that ever connected to a signal, right?
* One part of the Python bindings honestly I wouldn't copy is the magic to do proxy.MethodName(arg1, arg2). I don't think it's a good thing to have remote invocation to be so transparent. Just make people type proxy.dbuscall("MethodName", arg1, arg2) or something. Also, you can't do the magic method->attribute unless you have introspection data, which means your proxy creation blocks on getting introspection...yuck.
> it makes use of glib, rather than re-implementing > all of the threading/mainloop integration stuff.
Makes sense - though I would double-check that this is safe with how Mozilla uses main loops.
I'm extremely sorry I haven't been able to reply sooner - I really appreciate that you took time to look through this. I'm finally going to have time this week to continue working on this project.
Comments inline below.
Colin Walters wrote: > The Python bindings are a good source of inspiration, though one thing > I might suggest to include "out of the box" that the Python bindings > don't is a service tracker. Basically what DBusGProxy does for the > GLib bindings.
Could you please point me to some documentation about how DBusGProxy does this? I've been having trouble finding a good example of the exact feature(s) you're referring to.
> * In some places you're checking for OOM from dbus, others not. For > example, > dbus_message_new_* can return NULL.
Whoops, I think I found and fixed all these now.
> * From looking at the XPCOM string guide, it's not clear to me this is > safe:
> Do we know that cBusName will always be null-terminated UTF-8?
Hm, looking at the string guide, it sounds like this could actually be UTF-16. Can someone help me get this right?
On the topic of strings, there's one other place I need help, see line 563 in MozJSDbusMarshalling.cpp. When I pass what I think is valid UTF-8 data over to DBUS, it comes across as jibberish. As noted in the source, I think this may be related to the fact that mozilla is compiled with -fshort-wchar and dbus is not - however I don't have any string evidence that this is related to the problem at all.
> * For signals, you have a global map:
> // XXX: This should be a hashtable! > std::map<string, SignalCallbackInfo*> signalCallbacks;
> Shouldn't this be a weak reference map? Otherwise we'll keep alive > every JS callback that ever connected to a signal, right?
Can you clarify "weak reference map"?
I'm intentionally holding onto the JS callback when connecToSignal is called (MozJSDBusCoreComponent.cpp:256), I was planning to add a disconnectFromSignal() method that will remove/release everything. Are you suggesting a different approach?
> * One part of the Python bindings honestly I wouldn't copy is the > magic to do proxy.MethodName(arg1, arg2). I don't think it's a good > thing to have remote invocation to be so transparent. Just make > people type proxy.dbuscall("MethodName", arg1, arg2) or something. > Also, you can't do the magic method->attribute unless you have > introspection data, which means your proxy creation blocks on getting > introspection...yuck.
I agree that this isn't great, I'll consider removing it.
>> it makes use of glib, rather than re-implementing >> all of the threading/mainloop integration stuff.
> Makes sense - though I would double-check that this is safe with how > Mozilla uses main loops.
I'm pretty sure this is safe, but I'll look into it again. Does anyone on this list know?
> Might take a longer look later when I have time.
On Dec 3 2007, 2:30 pm, Eric Butler <e...@extremeboredom.net> wrote:
> Hey everyone,
> I've been working on writing DBUS bindings for Mozilla Javscript. The > goal of this project is to make it possible for XUL and extension > developers to integrate with the Linux desktop.
This looks absolutely awesome. I would love to be able to access DBUS from a Firefox extension the way I can from an Epiphany extension. Any clues on when this might show up in Firefox? Is there a relevant entry in bugzilla? If this functionality is going to be in FF3 I think I might wet myself.
On Dec 17 2007, 4:23 pm, Eric Butler <e...@extremeboredom.net> wrote:
> Could you please point me to some documentation about how DBusGProxy > does this? I've been having trouble finding a good example of the exact > feature(s) you're referring to.
It's not critical for a release - it's more of in the "nice to have" category. Here's the docs for the dbus-glib version:
> Hm, looking at the string guide, it sounds like this could actually be > UTF-16. Can someone help me get this right?
Yeah, from looking at the string guide it looks like you probably need to call NS_ConvertUTF16toUTF8 if the input is a nsACString. I could be wrong though.
> On the topic of strings, there's one other place I need help, see line > 563 in MozJSDbusMarshalling.cpp. When I pass what I think is valid UTF-8 > data over to DBUS, it comes across as jibberish. As noted in the source, > I think this may be related to the fact that mozilla is compiled with > -fshort-wchar and dbus is not - however I don't have any string evidence > that this is related to the problem at all.
Ah...I'm almost certain DBus doesn't use the wchar type internally. It expects UTF-8. If you're getting garbage, it seems far more likely to me that you're not sending valid UTF-8 (say UTF-16 instead), or you forgot to use & on the passed parameter so you're getting some random memory.
> > * For signals, you have a global map:
> > // XXX: This should be a hashtable! > > std::map<string, SignalCallbackInfo*> signalCallbacks;
> > Shouldn't this be a weak reference map? Otherwise we'll keep alive > > every JS callback that ever connected to a signal, right?
> Can you clarify "weak reference map"?
Conceptually you have an app-lifetime singleton that is holding references to any object which requests to receive D-Bus signals, right? This may not be a problem in practice if the consumers of DBus inside Mozilla are themselves scoped to the app lifetime, like network connection monitoring would be.
> I'm intentionally holding onto the JS callback when connecToSignal is > called (MozJSDBusCoreComponent.cpp:256), I was planning to add a > disconnectFromSignal() method that will remove/release everything. Are > you suggesting a different approach?
I think it would be more ideal to hold a weak reference, and remove the signal watch when an object is garbage collected, if this is possible. I'm not sure if it is.
Colin Walters wrote: >> Hm, looking at the string guide, it sounds like this could actually be >> UTF-16. Can someone help me get this right?
> Yeah, from looking at the string guide it looks like you probably need > to call NS_ConvertUTF16toUTF8 if the input is a nsACString. I could > be wrong though.
An nsACString is just a byte container. It could be in any encoding. The interface being used should specify the encoding.
An nsAString would be a 2-byte unit container encoded in UTF-16.
> or you > forgot to use & on the passed parameter so you're getting some random > memory.
Does dbus make a copy of the string at the point when dbus_message_iter_append_basic is called? Looking at MozJSDBusMarshalling.cpp, the const char** being passed to this function in the VTYPE_WSTRING_SIZE_IS case of marshallVariant is a pointer to a pointer to a stack-allocated buffer that goes out of scope right after the dbus_message_iter_append_basic call.
Of course there are worse situations in that code. For example the VTYPE_ARRAY case when the type is VTYPE_WCHAR_STR actually passes a pointer to a buffer that _has_ gone out of scope to dbus_message_iter_append_basic:
> Yeah, from looking at the string guide it looks like you probably need > to call NS_ConvertUTF16toUTF8 if the input is a nsACString. I could > be wrong though.
> > On the topic of strings, there's one other place I need help, see > line > > 563 in MozJSDbusMarshalling.cpp. When I pass what I think is valid > UTF-8 > > data over to DBUS, it comes across as jibberish. As noted in the > source, > > I think this may be related to the fact that mozilla is compiled > with > > -fshort-wchar and dbus is not - however I don't have any string > evidence > > that this is related to the problem at all.
> Ah...I'm almost certain DBus doesn't use the wchar type internally. > It expects UTF-8. If you're getting garbage, it seems far more likely > to me that you're not sending valid UTF-8 (say UTF-16 instead), or you > forgot to use & on the passed parameter so you're getting some random > memory.
I'll check on all this and report back. I didn't write most of the code in MozJSDbusMarshalling.cpp, so I'm not completely familiar with what's going on. Boris suspects the same problem as well.
> I think it would be more ideal to hold a weak reference, and remove > the signal watch when an object is garbage collected, if this is > possible. I'm not sure if it is.
If someone could help me out with this, it does sounds like the right thing to do.
If anyone on this list has time to contribute patches, that would be very helpful. My time is spread very thin across a bunch of projects.