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:
const NM_IFACE_INTERFACE = {
name: "org.freedesktop.NetworkManager.Devices",
methods: [ 'getName', 'getIP4Address',
'getActiveNetwork' ]
};
const NM_NETWORK_INTERFACE = {
name: "org.freedesktop.NetworkManager.Devices",
methods: [ 'getName' ]
};
var bus = DBUS.getSystemBus();
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.
The code is available via mercurial at
http://eric.extremeboredom.net/hg/mozjs_dbus/
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.
Obligatory screenshots:
http://orion.extremeboredom.net/~eric/DropBox/libnotify-js.png
http://orion.extremeboredom.net/~eric/DropBox/mozjs_dbus_avahi_2.png
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!
Thanks,
Eric
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:
NS_IMETHODIMP MozJSDBusCoreComponent::RequestService(const nsACString
&busName,
...
NS_CStringGetData(busName, &cBusName);
connection = GetConnection((char*)cBusName);
dbus_bus_request_name(connection, cServiceName, 0, &dbus_error);
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.
Might take a longer look later when I have time.
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:
>
> NS_IMETHODIMP MozJSDBusCoreComponent::RequestService(const nsACString
> &busName,
> ...
> NS_CStringGetData(busName, &cBusName);
> connection = GetConnection((char*)cBusName);
> dbus_bus_request_name(connection, cServiceName, 0, &dbus_error);
>
> 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.
Great, thanks again!
- Eric
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.
Thanks,
Sandy
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:
http://dbus.freedesktop.org/doc/dbus-glib/dbus-glib-DBusGProxy.html#dbus-g-proxy-new-for-name
http://dbus.freedesktop.org/doc/dbus-glib/dbus-glib-DBusGProxy.html#dbus-g-proxy-new-for-name-owner
> 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.
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:
528 c_value = (unsigned char *)
529 NS_ConvertUTF16toUTF8(c_ptr).get();
530 if (!dbus_message_iter_append_basic(&sub,
531 dbus_type, &c_value)) {
-Boris
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.
Thanks,
Eric
> _______________________________________________
> dev-platforms-linux mailing list
> dev-platf...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-platforms-linux