Node-Webkit без вебкита

    Как-то мы обсуждали десктопные приложения nw.js. Всё хорошо, но необходимость распространять весь движок браузера (который с течением времени и добавлением новых фич меньше не становится и сейчас весит сжатый 30МБ) — удручает.
    А что если сделать модуль для node.js, умеющий показывать UI в системном браузере?
    Под катом расскажу о попытке — как оно, стоит ли усилий, можно ли использовать, и что в результате получилось.

    Dicslaimer


    В основном проект был сделан скорее как proof-of-concept: поиграться, посмотреть, проверить, как оно. Сделал на нём 3-4 заказа в стиле «хочу маленькое приложение со встроенным webview» или «хочу обёртку для сайта в окне десктоп-приложения». Может быть, кому-то мой опыт окажется полезен, поэтому выложил в паблик. Код на гитхабе. Подробно описывать нюансы кода не буду, цель — не описать код, а скорее подумать над пригодностью результата.

    Как это работает


    Приложение статически собирается с node.js со своей точкой входа. При запуске в ноду добавляется нативный модуль с логикой отображения окон, и управление передаётся _thirdPartyMain.js (в ноде есть такая возможность). Оттуда загружается пользовательский main.js, где подключается модуль ui. Модуль стартует http-сервер, открывает окно с заданным конфигом и направляет webview на адрес встроенного сервера, который отдаёт пользовательский контент. Окно генерирует события (перемещение, сворачивание, ...) и умеет выполнять методы (например, закрыться).

    WebView


    Под винды вставляется ActiveX-ный WebBrowser (это тот же MSIE, но без тубларов, адресных строк итд). По умолчанию он показывает много ненужных диалогов (которые отключаются странным образом), но в целом работает как хочется. IE, обновляется неважно, а поддержать XP надо было, поэтому для старых версий Windows предусмотрена возможность скачать вебкит (chrome embedded framework). При необходимости, если пользователь не против, приложение скачивает библиотеки, распаковывает их и работает уже с вебкитом.
    Под маком всё более прозрачно, WebKit WebView по сравнению с IE в интеграции намного проще и понянтее; под линуксом есть webkit-gtk.

    Упаковка приложений


    Нужно было, чтобы приложения паковались в один EXE. Самым подходящим форматом оказался ZIP: он читается с конца, позволяя дописывать в исполняемый файл что угодно, и распаковывать его из себя; по этому принципу и работают SFX. Для использование из node.js, можно повесить хук на обращения к fs и перенаправить запросы к файлам, которые есть в архиве, к виртуальной файловой системе вместо чтения с диска.

    Взаимодействие с браузером


    IE и WebKit предлагают способы взаимодействия как из C++ в JS, так и наоборот. В IE javascript-овый объект, в том числе и функция, представляет собой объект IDispatch. Если проверить typeof такого объекта, javascript-движок отдаст unknown, это так называемые host objects (использование таких результатов оператора typeof в стандарте ES6 уже не рекомендуется, но раньше про нестандартные типы ничего не было сказано). Что-то вызвать из C++ можно, обратившись к объекту window.external, при условии, что хост реализовал этот метод.
    В вебките аналогично, можно добавить свойство window как объект с методми, доступными для вызова.

    Взаимодействие с node.js


    В ноде процесс создания плагинов прекрасно документирован. Аналогично создаются и встроенные аддоны, но регистрируются другим макросом. После создания класса аддона, он наследуются от EventEmitter-а и умеет генерировать события, получив ссылку на метод emit.
    Node.js намеренно живёт в отдельном треде, чтобы длительные операции не блокировали UI, поэтому для отправки сообщения в ноду и синхронизации используется библиотека UV.

    Что получилось хорошо


    Размер приложения


    Он составляет 2-3 МБ (незапакованного 7). В принципе можно сделать сборку и меньшего размера, если надо только открыть страничку в приложении, без node.js (вобщем-то ради размера всё и делалось).

    Время запуска


    Т.к. в приложении не используются дополнительные фреймворки, запускается оно быстро в среднем 1 секунду (про время первого запуска см. ниже); добавив профилирование, можно убедиться, что основное время уходит на инициализацию браузера.

    Проблемы


    Первый запуск


    Windows оптимизирует запуск приложений и компонентов, которые вы используете чаще. Если IE пользуются редко, время первого запуска приложения составляет где-то 3..10 секунд, в зависимости от операционки и компьютера.

    Версии браузеров


    Очень часто IE используют как инструмент для скачивания браузера и не обновляют вообще никогда (хотя с последними версиями Windows ситуация улучшается). IE нельзя обновить принудительно (показать диалог обновите IE из приложения равносильно самоубийству: обновление IE сложное, долгое, часто требует перезагрузки — нельзя так издеваться над пользователем, он просто найдёт другое приложение).
    Версии браузеров привязаны к операционке: для Windows XP нет IE10, для OS X Lion нет Safari 8.

    Контексты javascript


    Объекты node.js нельзя использовать в браузере и наоборот, взаимодействие происходит или как в классическом клиент-серверном приложении (xhr), или способом, предоставленным программой (postMessage в окне или ноде) — т.е. как в web worker, передать можно только простые данные. Можно было сделать context bridge, но это во-первых сложно и бажно, во-вторых недальновидно, потому что ES6 уже почти тут, а что делать, например, с объектом-прокси в IE10 — неизвестно.

    Что есть


    Есть сборка под win/mac/linux, которую в принципе можно использовать, кастомизировать итд. Я и использую её для создания кастомизированных «приложений-браузеров», когда это бывает вдруг кому-то надо. Она относительно стабильна, но до ума и релизного качества я её не доводил, потому что…

    Выводы


    Приложения скачиваются редко, интернет сейчас достаточно быстрый (и в случае установки приложений, EDGE действительно можно пренебречь, т.к. приложение — это не сайт, его не будут устаналивать в дороге), нерешаемые проблемы в использовании системного браузера присутствуют. В большинстве случаев сейчас игра не стоит свеч и использовать вебкит кажется более правильным. Если цель — создать маленькое приложение в основном под новые операционки — а может быть, можно и так.

    Ссылки


    Комментарии 16

      0
      Приложение запаковано UPX — некоторые антивирусы могут ругаться на него. Можно не паковать приложение и получить размер 6-7МБ.
      Ахаха, убили на прочь, а на хрена в 2015 году использовать UPX или любой другой упаковщик исполняемых файлов? В чем этот магический профит? Пробовали бенчмарить без него?

      В целом — круто, спасибо, ждал когда кто-то что-то подобное реализует рано или поздно, тут действительно большой простор для фантазий.
        0
        > в 2015 году UPX
        Согласен, тем более что приложения в виде «голого» exe сейчас никто не распространяет, всегда или паковщик из инсталлятора, или архив. Заказчик хотел upx, так он там и остался, как-то не подумал удалить его, спасибо что напомнили, выпилю, профита 0. Бенчмаркил и без него, и с ним, конечно. upx и другие пакеры замедляют выполнение на 200-500 мс.
        0
        Как-то раз пробовал писать на nw.js там была проблема которую я на тот момент не смог решить и отказался от данного решения.
        Проблема заключалась в том что мне нужно было в контенте приложения выводить гиперссылки которые должны открываться дефолтным браузером ОС, но так как там все приложение по сути и есть браузером с веб приложением то все гиперссылки открывались в том же окне…
        Как у Вас обстоят дела с таким явлением, можно ли заставить приложение открывать ссылки в дефолтном браузере которым пользуется текущий пользователь системы?
          0
          Можно. Вроде как, и в nw.js тоже можно. На ссылку повесить обработчик, в обработчике вызвать нодовский spawn.
            +3
            если использовать
            var g = require('nw.gui');
            то потом вот так легко открываем в дефолтном браузере ссылку
            g.Shell.openExternal('https://google.com');
              0
              Спасибо!
            +1
            А как решали проблему c настройками IE? Отключенный показ картинок и отключенный JS?

            Я делал подобное только под Windows и использовал встроенный JavaScript из WSH, он работал в потоке приложения как основная логика, а с браузером взаимодействовал через COM-объекты проброшенные в браузер. Ноду не использовал, у меня была своя библиотека объектов доступная из скрипта для работы с сетью, zip-ами, xml, графикой и т.п. с файловой системой работал через объекты WSH. Даже приложение написал рабочее pingxpert.com Но у Вас решение более универсальное.
              0
              IE позволяет приложению задать путь к настройкам реестра через GetOptionKeyPath (и GetOverrideKeyPath). Если реализовать его, контрол будет с дефолтовыми настройками. Там всякие настройки запрета показа отладчиков итд. (а если честно отключённые js не проверяли, посмотрю, может и нужен более хитрый метод со своим security manager-ом).
                0
                Да я использовал этот метод и security manager, но пользователи всё-равно частенько жалуются на неработоспособность программы, может тоже где-то недотестил и сам накосячил. А ещё большая проблема с отображением контента, если старые версии IE, то Bootstrap уже нельзя использовать. Поэтому WebKit мне в этом плане надёжнее кажется. А в передаче контекста в IE мне конечно проще было значительно, я просто отдавал объекты JavaScript-а основной логики прямо в браузер через SetExternalDispatch и получал в браузере весь нужный функционал.
                  0
                  Вот примерно к такому выводу я и пришёл, поигравшись со встроенными браузерами: в большинстве случаев вебкит показалось использовать более целесообразным.
              0
              Экономия в 30 мб, в десктопном приложении действительно стоит таких финтов ушами?
                +1
                Не думаю (о чём и написал в «проблемах» и «выводах»). Скажу даже больше: если бы работа над проектом не была оплачена, я бы не стал это делать. А так, мне давно было интересно, а что же с таким подходом получится — вот довелось попробовать.
                  +1
                  Я бы сформулировал вопрос по-другому: использования подобных технологий в десктопном приложении действительно стоит таких финтов ушами?
                  0
                  Объясните мне пожалуйста принцип работы. Это работает с обычного браузера? И запускается исполняемый файл? Или как? Спасибо.
                    0
                    Исполняемый файл, который показывает окно, внутри которого html+css+js.
                    +1
                    Ух ты! Cordova!

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое