XULRunner: Electron, который умер раньше Electron

Как я полюбил Mozilla-стек для десктопных приложений — и что от него осталось

Где-то в 2008-м

В те годы Firefox был на четвёртой версии и казался идеальным выбором: Chrome ещё не вышел, IE приходилось терпеть, а у Firefox были open source, расширения, человеческий UI и AwesomeBar. И где-то в недрах mozilla.org у них лежал XULRunner.

Идея была такая: тот же движок, на котором работает Firefox, можно использовать как платформу для собственных приложений — не браузер, а приложение. UI пишется на XUL, логика на JavaScript, доступ к системе через XPCOM. Получается толстый клиент, но кросс-платформенный, со всей мощью Gecko под капотом — HTML, CSS, SVG, XSLT, рендеринг, сеть, шрифты, OS-интеграция.

По сути, Electron за десять лет до Electron — только сделанный не стартапом из Сан-Франциско, а Mozilla, с их документацией, MDN и поддержкой Linux/Mac/Windows из коробки.

Что было можно

На XULRunner были написаны вполне серьёзные вещи:

  • Komodo Edit / Komodo IDE от ActiveState — настоящая IDE с автодополнением, отладкой, поддержкой десятка языков. До сих пор лежит на GitHub как OpenKomodoIDE, но не обновлялась давно.
  • Songbird — медиа-плеер, который пытался быть «iTunes, но открытым». Закрылся в 2013.
  • Miro Player — видеоплеер с поддержкой подкастов и торрентов.
  • Thunderbird — да, почтовик Mozilla. Он формально не XULRunner, но построен по той же схеме, и до сих пор живёт по этой архитектуре.
  • Zotero — менеджер библиографии для учёных. Один из немногих, кто пережил всё это.

Я и сам игрался. Где-то лежал xul-minesweeper — копия классического Сапёра на XUL и JavaScript, запускался поверх XULRunner: обычное Windows-окно, никакого браузера, а внутри <xul:hbox>, <xul:button>, <xul:menupopup> и логика в JavaScript-файлах в components/, зарегистрированных через XPCOM.

Никаких 200-мегабайтных Chromium-бинарников — тонкий слой нативного кода, толстый JS, Gecko как универсальный рендерер. Я тогда скачал себе несколько версий — xulrunner-1.8.0.1, xulrunner-1.9.1, xulrunner-8.0, xulrunner-23.0 — все они до сих пор лежат у меня в архиве.

Что случилось

В 2015 году Mozilla объявила deprecation и с Firefox 41 перестала собирать отдельные бинарники XULRunner: дескать, поддерживать дорого, использование низкое, ресурсы нужны на сам браузер. На деле приоритеты просто сместились — на мобильный Firefox и на войну с Chrome за рынок.

Дальше пришёл Firefox 57 Quantum (2017), и это был самый болезненный для совместимости момент в истории Mozilla. Они вырезали:

  • XUL как язык разметки для расширений. Старая система с <xul:window>, оверлеями, XBL — всё ушло. Расширения переписали на WebExtensions (то же API, что у Chrome).
  • XPCOM JavaScript-компоненты. Те самые .js-файлы в components/, регистрировавшиеся через nsIComponentRegistrar. Больше нельзя.
  • XBL. Заменили на Custom Elements — тут, кстати, обмен честный.
  • RDF. Целый слой данных, на котором держались Extensions.rdf, localstore.rdf, файлы помощи — вырезан.

К 2020 году UI самого Firefox переписали с XUL на HTML. К 2023 году тот же <xul:tree> (виртуализированный список — лучший в индустрии, к слову) остался только в виде осколка во внутреннем коде.

Что мы потеряли

История у Mozilla для десктопных веб-приложений была сильнее, чем у Electron, по двум причинам.

Во-первых, размер. XULRunner на пике весил около 10 MB, тогда как современные Electron-приложения тащат собственную копию Chromium и разрастаются до 200–300 MB: VS Code — 300, Slack — 200, Discord — 250. Это абсурдно, и все это знают.

Во-вторых, качество инженерии. Mozilla в нулевых писала Gecko аккуратно — отладчик, профайлер, инспектор работали с самого начала и интегрировались именно с приложением, а не только с браузером. Чтобы получить сегодня нормальные DevTools для Electron-приложения, приходится надеяться, что разработчик не выпилил --inspect.

К этому добавляется независимость Mozilla. Chrome принадлежит Google, и когда Google решит, что какой-то Web API больше не нужен (например, потому что мешает их рекламной модели), он этот API вырежет, а вместе с ним и все Electron-приложения. У Mozilla таких конфликтов интересов не было, и их движок развивался от потребностей веба, а не рекламной империи.

И всё это они закопали сами — не потому, что технология не работала, а потому что менеджмент посчитал приоритетом браузер, а всё остальное обузой. В итоге к 2025-му году Electron занял весь рынок «нативных приложений с веб-стеком», Tauri пытается отгрызть кусок, но использует системный WebView — Chromium на Windows (WebView2) и WebKit на macOS. Mozilla’овского движка не осталось нигде.

Что осталось

Когда я недавно вернулся к этой теме, обнаружилась забавная вещь. Формально Mozilla XULRunner убила — перестала собирать бинарники, выпилила из документации, убрала со страницы загрузки. Но сам код, который запускает Gecko в режиме «приложение, а не браузер», никуда не делся.

Запустите firefox.exe -app /path/to/application.ini --no-remote — и Firefox откроется не как браузер, а как хост для вашего приложения: подхватит chrome.manifest, прочитает prefs.js, откроет ваше окно. Флаг -app нет в --help и его нигде не документируют, но он работает — я проверил на Firefox 99 и на Firefox 115 ESR, везде заводится. По сути XULRunner на современном Gecko — только без XUL.

XUL мёртв окончательно: <xul:window>, <xul:tree>, <xul:hbox> либо удалены, либо превращены в CSS-шимы. Но если честно, это не критично — HTML с CSS Grid/Flexbox делают то же самое, только синтаксис лучше. Самая болезненная потеря — <xul:tree> (виртуализированный список/дерево), для которого нет хорошего HTML-аналога. А остальное живо:

  • JavaScript-движок — современный SpiderMonkey со всеми ES2024+ фичами.
  • Привилегированные chrome HTML-страницы — пишутся как обычный HTML с доступом к Services.*, ChromeUtils.*, IOUtils, nsIFilePicker и прочему.
  • Модули через ChromeUtils.importESModule — замена для XPCOM-компонентов на JS.
  • Custom Elements, Workers, WASM — всё, что есть в современном вебе, плюс полные системные права.

fx-shell

Решил собрать это в небольшой проектfx-shell. По сути это оболочка над установленным Firefox, позволяющая писать десктопные приложения как привилегированные HTML-страницы. Структура почти такая же, как у классического XULRunner-приложения:

1
2
3
4
5
6
7
8
9
10
11
12
my-app/
├── package.json ← конфиг
└── app/
├── application.ini ← метаданные (как в XULRunner)
├── chrome.manifest ← регистрация chrome://
├── defaults/preferences/
│ └── prefs.js ← toolkit.defaultChromeURI
└── chrome/
├── content/
│ └── main.xhtml ← привилегированный HTML
└── skin/
└── main.css

npm start берёт ваш установленный Firefox, запускает его в -app режиме и открывает ваше окно. npm run build скачивает Firefox с archive.mozilla.org, распаковывает, копирует, переименовывает firefox.exe в myapp.exe, патчит иконку и метаданные через rcedit и компилирует крошечный лаунчер на C#. На выходе самодостаточная папка 200 MB (80 MB в zip), которая запускается без установленного Firefox.

Это, конечно, не возрождение XULRunner — флаг -app Mozilla может убрать в любой версии. Но пока работает, и ощущается примерно так же, как в 2008-м: маленькое окно, HTML+JS, полные системные права, ничего лишнего.

Что дальше

Если флаг -app всё-таки выпилят, есть план B — собрать всё через autoconfig поверх переносимой копии Firefox, по той же схеме, что использует Zotero уже двадцать лет (у них это форк Firefox, собранный из исходников, с убранной браузерной частью). Есть и план C — переключиться на Pale Moon / UXP: форк Firefox эпохи 2017 года, в котором сообщество специально не дало умереть XUL, XPCOM и оверлеям, единственный живой XULRunner сегодня. Минус — они застряли на старом Gecko с backport-ами безопасных патчей, и веб-стандарты у них заметно отстают.

Но это всё на случай, если Mozilla доведёт дело до конца. А пока — firefox.exe -app application.ini, и можно немного побыть в 2008-м.


P. S. Если вам тоже жаль XULRunner — попробуйте fx-shell, интересно будет услышать, что вы об этом думаете.