Exercising software freedom on Firefox

I’m a little unusual. I use Emacs.

That alone is unusual. But I get the impression that even amongst Emacs users, I’m in the minority in another way: I use the default keybindings. I love them. A lot of new Emacs users seem to insist on jamming vim keys into Emacs, but not me. These are my friends: C-p C-n C-f C-b C-a C-e C-k; down up left right start end kill.

I’m so gung-ho about Emacs keybindings that I made them the default keybinding of GTK+, which means that any application that uses GTK+ will respect Emacs keybindings for motion. They also work in anything that uses readline or readline-like input, like bash, python, or psql (postgresql’s default CLI client). Being used to Emacs keys has paid off for me. I have a consistent interface across the software that matters to me.

I’m becoming a minority in another way: I use Firefox. And Firefox uses GTK+. That means I can use Emacs keybindings in Firefox.

Ah, but there’s a rub. Firefox binds C-n (or as most people would call it, “ctrl-n”) to new window. This is probably okay for people who don’t have the intersectionality of Emacs keybindings everywhere and Firefox. But for me, it’s intolerable. If I want to move a cursor down, I have to instead perform a very unnatural-feeling motion of moving my right hand to the arrow keys and hit the arrow down button. For those accostumed to using arrow keys, imagine if every time you pressed the down arrow Firefox would open a new window. Imagine software reacting so at odds to your habituation.

Up until Firefox 56 there was an easy workaround. You could download extensions that would let you configure Firefox’s keyboard shorcuts, including disable some of them. I used to do this. The world, however, marches on and so does Firefox. Many extensions cannot do what they once did and the easy fix was gone.

I tried to cope, for a while. After all, it’s just one key. I can still use the arrow keys. I tried.

But no. It wouldn’t work. I couldn’t help myself. I often wanted to move the cursor down three or four rows and would accidentally open up three or four new windows. It was even worse because I could move in every other direction and it all felt natural, but if I made the mistake of going down, the software would react in the wrong way. Everything else did it right except Firefox. And one day, I had enough.

Software Freedom

Enough was enough. I had accidentally opened a new window for the last time. I want to go down, you donut! And you won’t stop me anymore!


I had the motivation. I have some skill. We can rebuild Firefox. Make it better. More consistent. We have the technology.

I didn’t want to get involved in Firefox’s build drama, though. I didn’t want to figure out how to clone its repo, how to setup a development environment, how to configure the build, what kinds of builds there are, and how to integrate all of this with my operating system. Luckily, someone else has already done all of this work for me: the Debian packagers.

A Debian package knows what dependencies are required to build a package and has all of the tooling ready to build that package and make it fit exactly with my operating system. Right system libraries, right compilation options, everything. I know how to build Debian packages:

  1. Get the source (apt-get source $packagename)
  2. Get the dependencies (sudo apt build-dep $packagename)
  3. Build the package (dpkg-buildpackage)

Easy enough.

Firefox, the behemoth

As I started following the steps above, something was immediately evident. Firefox is huge. Enormous. Gargantuan. The biggest codebase I have ever seen. At a glance I saw a mix of Python, C++, Rust, and XML which I later came to recognise as XUL (“XUL?” I hear you ask. Yes. XUL. More on this below.) I can see why few dare tread in here.

I, on the other hand, with my motivation going strong, felt undaunted. I would tame The Beast of oxidised metal.

But I wouldn’t do it alone. I know that the Mozilla project still has a fairly active IRC network over at irc.mozilla.org, so I headed down that way. I started talking about my problem, asking for advice. While I waited for replies, I tried to do it on my own. I figured, GTK+, keybindings, C. I was looking for some C or C++ source file that would define the GTK+ keybindings. I would find this file and destroy the keybinding. I have done something similar in the past for other GTK+ programs.

My solo search proved unfruitful. I couldn’t find anything about new window in C++ source files. I even tried the Rust files, maybe they’ve done something there, but again nothing. My grepping did find new window commands in XML files, but I figured that couldn’t still be of use. Everyone knows it, it’s all over the software news: Firefox disabled XUL as part of its move to a Rust engine.

In the meantime, helpful people from IRC pushed me along my quest and pointed me in the right direction. Yes, XUL is all I needed.

There is no Rust. There is only XUL!

Yep! Firefox has been lying to us! It’s still all XUL. All they’ve disabled is the external interface for extensions, but under the hood, Firefox is still the XUL mess it always was. They say they’re ripping it out, yet the process seems slow.

So I followed the advice. I changed a single XML file. I built the Debian package. I was expecting a long compilation time and I got it. I was worried I wouldn’t have enough RAM for the build, but looks like 16 gigabytes with four cores (Thinkpad X1 Carbon 5th gen) was enough. People in IRC reassured me that it would take about two hours. They were right! Two hours later, I had a new Firefox in a neat little Debian package. I installed it (dpkg -i *.deb) eager to see the results and…

XML parsing error. Undefined entity.

Oh no! I had made a mistake! All I could do was close this error window. Firefox just wouldn’t start.

However, this confirmed two things. One, the XUL really is still being used. In fact, it’s so important that Firefox won’t even start if you get it wrong. And two… I was on the right track. Modifying XUL could very well get me to my goal of disabling one key.

The error window reminded me a lot of similar errors I had seen in the past when XUL was available to 3rd party extension authors. It seems that not as much as advertised has changed.

XUL parsing error

I tried again. I had removed the key but I hadn’t removed a few references to that key. Another build. Another two hours. In the meantime, Mozilla employees and enthusiasts in IRC kept asking me if I was doing an artifact build. I said no, that I wanted to learn as little as possible about Firefox’s build process. Turns out that an artifact build is an interesting thing where you download pre-built Firefox components and the build just puts them together, greatly reducing the compilation times.

I had the very specific goals of building a Debian package and not wanting to get too involved in build drama, so I politely refused the suggestions of artifact builds.

I just want my cursor to move down, man.

My second try also didn’t work. I had neglected one further reference to the new window key. I didn’t think it was necessary, but the XML again failed to parse because the key for undoing closing a window is defined in terms of the key for opening a new window. I decided that if I wasn’t going to be opening new windows, I also wasn’t going to undo close them, so I also deleted this reference.

By now it was getting late, I had to sleep, and I couldn’t wait for another two-hour build. I made the change, started the build, and went to bed like a kid excited for Christmas morning.

Free at last!

The morning came. My new build was ready. I installed the third Debian package I built.

This time Firefox started. No more XML errors.

Could it be…?

I went to the first website I could think of that had a textarea element I could try to type in, paste.debian.net.

I typed some text. I hit enter a few times. I pressed C-p to go back up.

The moment of truth!

I hit C-n.

No new window.

The cursor moved down.


Great success!

The patch

So here’s the patch, for anyone else who wants it. I made it against ESR (currently Firefox 60) because that’s what’s packaged for Debian stable, but all of these modified files are still there in the current Mercurial repository I just checked right now.

diff --git a/firefox-esr-60.5.1esr/browser/base/content/browser-menubar.inc b/firefox-esr-60.5.1esr/browser/base/content/browser-menubar.inc
--- a/firefox-esr-60.5.1esr/browser/base/content/browser-menubar.inc
+++ b/firefox-esr-60.5.1esr/browser/base/content/browser-menubar.inc
@@ -27,7 +27,6 @@
                 <menuitem id="menu_newNavigator"
-                          key="key_newNavigator"
                 <menuitem id="menu_newPrivateWindow"
diff --git a/firefox-esr-60.5.1esr/browser/base/content/browser-sets.inc b/firefox-esr-60.5.1esr/browser/base/content/browser-sets.inc
--- a/firefox-esr-60.5.1esr/browser/base/content/browser-sets.inc
+++ b/firefox-esr-60.5.1esr/browser/base/content/browser-sets.inc
@@ -196,10 +196,6 @@
   <keyset id="mainKeyset">
-    <key id="key_newNavigator"
-         key="&newNavigatorCmd.key;"
-         command="cmd_newNavigator"
-         modifiers="accel" reserved="true"/>
     <key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel"
          command="cmd_newNavigatorTabNoEvent" reserved="true"/>
     <key id="focusURLBar" key="&openCmd.commandkey;" command="Browser:OpenLocation"
@@ -378,7 +374,6 @@
     <key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
-    <key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
 #ifdef XP_GNOME
diff --git a/firefox-esr-60.5.1esr/browser/components/customizableui/content/panelUI.inc.xul b/firefox-esr-60.5.1esr/browser/components/customizableui/content/panelUI.inc.xul
--- a/firefox-esr-60.5.1esr/browser/components/customizableui/content/panelUI.inc.xul
+++ b/firefox-esr-60.5.1esr/browser/components/customizableui/content/panelUI.inc.xul
@@ -205,7 +205,6 @@
         <toolbarbutton id="appMenu-new-window-button"
                        class="subviewbutton subviewbutton-iconic"
-                       key="key_newNavigator"
         <toolbarbutton id="appMenu-private-window-button"
                        class="subviewbutton subviewbutton-iconic"
diff --git a/firefox-esr-60.5.1esr/browser/locales/en-US/chrome/browser/browser.dtd b/firefox-esr-60.5.1esr/browser/locales/en-US/chrome/browser/browser.dtd
--- a/firefox-esr-60.5.1esr/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/firefox-esr-60.5.1esr/browser/locales/en-US/chrome/browser/browser.dtd
@@ -298,7 +298,6 @@ These should match what Safari and other
 <!ENTITY newUserContext.label             "New Container Tab">
 <!ENTITY newUserContext.accesskey         "B">
 <!ENTITY newNavigatorCmd.label        "New Window">
-<!ENTITY newNavigatorCmd.key        "N">
 <!ENTITY newNavigatorCmd.accesskey      "N">
 <!ENTITY newPrivateWindow.label     "New Private Window">
 <!ENTITY newPrivateWindow.accesskey "W">

So there you have it. You can still alter Firefox’s XUL. You just have to compile it in instead of doing an extension.

To Translate Is To Lie, So Weave A Good Yarn

I’m not a professional translator, but I know what I like in fiction.

When I was a Mexican kid in the 1980s we used to get old re-runs of the Flintstones in Spanish. Of course, my English wasn’t very good when I was very young, and I didn’t know them as “the Flintstones at all.” They were “Los Picapiedra” (something like “The Pickstones”), and not only that, but I had no idea who Fred or Barney were. Instead, I knew Pedro Picapiedra and Pablo Mármol (something like “Peter Pickstone” and “Paul Marble”). I liked them, and they felt familiar and comfortable. They spoke with a Spanish accent very close to mine and they used expressions that were similar to how my parents spoke.

It wasn’t until I got older and got more experienced that I realised I had been lied to, like many other lies we tell children. Pedro and Pablo weren’t a caricature of my Mexican lifestyle at all, but of a different, 1950s lifestyle from another country up north. I didn’t exactly feel cheated or lied to, but it was another cool new thing to learn about the world. I still felt much endeared to the original names and to this day, if I have to watch the Flintstones, I’d much rather view them as Los Picapiedra instead.

Other Lies I Grew Up With

This wasn’t the only time this happened. Calvin & Hobbes fooled me too. This time their names didn’t change, but their language did. Calvin spoke to me from the comic book pages with a hip, cool Mexico City slang like other kids my age would use to elevate themselves in the eyes of other kids. Calvin talked about the prices of candy and magazines in pesos, with peso amounts appropriate for the time of publication, and used phrases like “hecho la mocha” (something like “made a blur”) when he said he was gonna do something very quickly. His mother sounded like my mother. This time the deception was even better, and for the longest time I honestly thought Calvin was a Mexican kid like me.

And there were others. The Thundercats were Los Felinos Cósmicos (something like “Cosmic Felines”), the Carebears were Los Ositos Cariñositos (something like “The Little Loving Bears”), and The Little Mermaid was La Sirenita (interesting how mythological sirens and mermaids are different in English but not in Spanish).

Again, as I grew up, so did my languages, and I was able to experience the other side of the localisation. It was always a small revelation to realise that the names I had known were an alteration, that the translators had taken liberties, that the stories had been subtly tampered with. In some cases, like with Calvin, I was thoroughly fooled.

The Translator’s Task

I’m of the opinion that the translators and localisers of my youth performed their task admirably. A good translator should be a good illusionist. Making me believe that Calvin was Mexican or that the Flintstones could have been my neighbours is what a good translator should do. Translation is always far more than language, because languages are more than words. A language always comes with a culture, a people, habits and customs. You cannot just translate words alone; you have to translate everything else.

Only bad translators believe in the untranslateable. Despite differences in language, culture, and habits, a translator must seek out the closest points of contact across the divide and build bridges on those points. When no point of contact exists, a translator must build it. A new pun may be needed. The cultural references might need to be altered. If nothing else can be done and if there is time and space for it, a footnote can be the last resort, when a translator admits defeat and explains the terms of their surrender. Nothing went according to keikaku.

The world has changed a lot since I was a child. It has gotten a lot bigger. We have more ways to talk to each other. As a result, it’s getting harder for translators to perform their illusions.

Modern Difficulties of Translation

With the internet and other methods of communication, a more unified global presence has become more important. Translations now have to be more alike to the source material. Big alterations to characters’ names or, worse, to the title of the work, are now out of the question.

Thus we get The Ice Queen becoming Frozen, because it’s good marketing (things didn’t go so well last time we made a title about a princess or a queen), and Frozen she shall be in Spanish as well, leaving Spanish speakers to pronounce it as best they can. As a small concession, we will allow the forgettable and bilingually redundant subtitle “Una Aventura Congelada” (something like “A Frozen Adventure”), but overall, the trademark must be preserved. There’s now far too much communication between Spanish and English speakers to allow the possibility of losing brand recognition.

Something similar and strange happened with the localisation of Japanese pop culture. We went from Japanimation to anime, from comics to manga. The fans will no longer let a good lie in their stories, and while we will grandfather in Megaman instead of Rockman or Astroboy instead of Mighty Atom, from now on new material must retain as foreign of a feeling as possible, because we now crave the foreign. It doesn’t matter if we really can understand it as closely as the Japanese do, because we crave the experience of the foreign.

The reverse also happens and the Japanese try their best to assimilate the complicated consonants of English into their language, but they have had more practice with this assimilation. Their faux pas have been documented on the web for the amusment of English speakers.

When Lies Won’t do

I should be more fair to translators. Sometimes, a torrent of footnotes is all that will work. Of course, this should be reserved for the written word. Such is the case of the English translation of Master and Margarita. The endless stream of jokes making fun of Soviet propaganda and Soviet life are too much of a you-had-to-be-there. Explaining the jokes sadly makes them no longer funny, but there’s no other recourse except writing a completely different book, far removed from the experience of a modern Russian reading a Soviet satire.

But it doesn’t have to be this way. The Japanese translations of Don Quixote works without burdening the readers with the minutiae of life from a time long, long ago, in a country far, far away. Don Quixote’s exaggerated chivalric speech is rendered in Japanese translations as samurai speech. Tatamis suddenly appear in a place of La Mancha that I don’t care to call to mind.

And that’s the best kind of translation. The one that works and makes the fans love it, that makes them feel like they belong in this translated world.