2018-08-03
Firefox now implements its remote control partly over D-Bus
On Unix, Firefox has had a long standing feature where you could remote control a running Firefox instance. This has traditionally worked through X properties (with two generations of protocols), which has the nice advantage that it works from remote machines as well as your local one, provided that you're forwarding X. Since I read my mail through exmh that's running on one of our servers, not my desktop, this is pretty useful for me; I can click on a link in mail in exmh, and it opens in my desktop Firefox. However, working through X properties also has the disadvantage that it naturally doesn't work at all on Wayland. Since Wayland is increasingly important, last November or so the Mozilla people fixed this by adding a new D-Bus based protocol (it landed in bug 1360560 and bug 1360566 but has evolved in various ways since then).
On current versions of Firefox, you will find this service on the session bus under the name org.mozilla.firefox.<something>, where the <something> is often 'ZGVmYXVsdA__'. In general this weird thing is the base64 encoded name of your Firefox profile with a few special characters turned into _, and that particular name is, well:
; echo -n default | base64 ZGVmYXVsdA==
Because this directly encodes the profile name in something that
you have to get right, the D-Bus based version of Firefox remote
control will reliably restrict itself to talking to a running Firefox
that's using the same profile; the X properties based version doesn't
always (or didn't always, at any rate). You can force a new Firefox
to not try to talk to an existing Firefox by using --new-instance
,
as before.
(One case where you might need this is if you're testing an
alternate version of Firefox by manually setting your $HOME
to, eg, /tmp/ffox-test
.)
It turns out that which protocol Firefox uses when is a bit tangled.
If Firefox is built with D-Bus support, a running Firefox on X will
be listening for incoming requests using both D-Bus and the X
properties based protocol; you can talk to this Firefox with either.
In the current Firefox code, if you built with both D-Bus and Wayland
support, the client Firefox always uses D-Bus to try to talk to the
running 'server' Firefox; it doesn't fall back to X properties if
there's no D-Bus available. If you built Firefox without Wayland
support, it always uses the X properties based protocol (even if
you built with D-Bus, and so the running Firefox is listening there).
You can see this sausage being made in StartRemoteClient()
here.
This logic was introduced in the change for bug 1465371. Before then Firefox tried to use the X properties based remote control if it was running on X, and fell back to the D-Bus protocol otherwise. In thinking about it I've come to believe that the logic here is sound, because in a Wayland session you may have some programs that think they're running in X and then pass this view on to things run from them. D-Bus is more session type agnostic, although it only works on the local machine.
Note that this implies that you can no longer use Firefox itself
as a client on a second machine, at least not if your second machine
Firefox is a modern one that was built with Wayland support; it'll
try to talk D-Bus and fail because your running Firefox isn't on
that machine. If you want to remote control Firefox from a second
machine, you now want a dedicated client like my ffox-remote
program.
(Hopefully Mozilla will leave the X properties based protocol there for many years to come, so my cross-machine remote control will still keep working.)
Sidebar: some D-Bus protocol details
The D-Bus object path is /org/mozilla/firefox/Remote, which has one
org.mozilla.firefox method, OpenURL()
, all of which you can see
by using a D-Bus browsing program such as d-feet. In the Firefox source code,
what you want to look at is widget/xremoteclient/DBusRemoteClient.cpp
(the client side, ie the firefox
command you just ran that is
going to pass your URL or whatever to the currently running one)
and toolkit/components/remote/nsDBusRemoteService.cpp (the server
side, ie the running Firefox).
Despite the fact that D-Feet will tell you that the argument to
OpenURL()
is a string, in actuality it's an entire command line
encoded in the same annoying binary encoding that is used in the
current X property based protocol, which you can read a concise
description of in nsRemoteService.cpp.
Presumably this minimizes code changes, although it's not the most
natural D-Bus interface. This encoding does mean that you're going
to need some moderately tangled code to remote-control Firefox over
D-Bus; you can't fire up just any old D-Bus client program for it.
The client code for this is in toolkit/xre/nsAppRunner.cpp,
in the StartRemoteClient()
function.
How I want to use Go's versioned modules
I thought that I understood Go's 'vgo' versioned modules after reading things like Russ Cox's "Go & Versioning" series. Then I started poking at them for the usage cases I'm interested in, and now I'm more confused than before.
The available Go documentation makes it pretty clear how to work
with Go modules for your own code, and there are walk-throughs like
Dave Cheney's Taking Go modules for a spin. But
much of my use of Go is in fetching and building other people's Go
programs (eg, a handy TLS certificate
inspector, this handy program
that I should use more often, and of course
my favorite Let's Encrypt client).
It's not clear how Go modules interact with this in a future world
where these packages have go.mod
files that specify what versions
of their dependencies they should be built with, and certainly there
doesn't seem to be any interaction now.
When I'm building Go programs like this, I'm acting as what I've
called an infrequent developer and I
basically want things to just work. If a program has a go.mod
file, the most likely way to have things just work is to use the
dependency version information from go.mod
(it's what the program
is advertising as right, after all). For usability I want this to
happen automatically on plain 'go get <package>
' or something
very like it, because that's what I and many other people are going
to use.
(I absolutely will not be manually cloning VCS repos to somewhere,
cd
ing to it, and running 'go build
' just so I can have Go respect
the package's go.mod
. It's an extremely useful feature of Go that
I can go from nothing to an installed program with a single command.)
All of this leads me to want a model of go.mod
usage where Go
commands respect go.mod
if one is present but still work and
behave traditionally if there isn't one. I want this to happen
whether or not the package in question is in $GOPATH/src
, partly
because that means I don't have to care whether any particular
program has added a go.mod
yet. The Go developers don't seem to
have any interest in supporting this approach, though; perhaps they
consider it too unpredictable.
(I consider it very predictable; I will get whatever the authors
of the module think is best. If they like go.mod
, I'll automatically
use that; if they vendor some or all things, I'll use that; otherwise,
I'll use the Go default of 'the latest version of everything', which
they're presumably fine with since they left their program that
way.)
PS: Given that the latest Go tip still doesn't seem to have any way
of using a package's go.mod
if you just do 'go get <package>
',
I suspect that the Go developers consider handling this in any way
to be out of scope for the first version of Go modules. These days
I'm not sure they even like 'go get <package>
', or if they've
switched over to considering it a mistake that they're more or less
locked in to supporting to some degree.
PPS: For existing packages you have fetched, you could get what I
want by writing a cover script for go
that manipulates $GO111MODULE
based on whether or not there's a go.mod
file in an appropriate
spot. Having to write this cover script seems wasteful, though,
since Go is already perfectly capable of checking for itself.
(At least according to the documentation, setting $GO111MODULE
to
on
doesn't quite do this. Instead it claims to make use of go.mod
mandatory, at least as I'm reading the 'go help modules
'
documentation. The actual behavior in my tests doesn't necessarily
match this, so the whole thing leaves me confused.)