Сегодня мы только что выпустили новую версию приложения Slack Desktop для macOS. Мы создали его с помощью Electron, и, как следствие, он быстрее, имеет безрамочный вид и содержит ряд закулисных улучшений, которые делают работу со Slack намного лучше.

Конечно, есть разные способы создания настольных приложений с использованием веб-технологий. В отличие от подхода «100% входящие», который используют некоторые другие приложения, Slack использует гибридный подход, при котором мы отправляем некоторые активы как часть приложения, но большая часть ресурсов и кода загружается удаленно. Поскольку информации о том, как это сделать с помощью Electron, не так много, мы хотели более подробно остановиться на том, как работает наше гибридное приложение.

Во-первых, немного истории

Первоначально настольное приложение Slack было написано с использованием фреймворка MacGap v1, который внутренне использовал WebView для размещения веб-контента внутри собственного фрейма приложения. Хотя это хорошо служило нам в течение долгого времени (включая модернизацию поддержки нескольких команд), эта архитектура начала показывать свой возраст. Новые функции, такие как HTTP / 2, появятся только в новом представлении Apple WKWebView, и переход к нему фактически потребует полного переписывания приложения. Кроме того, WebView был привязан к версии Safari для операционной системы, а это означало, что у нас не было много вариантов, когда в более старых версиях macOS возникала проблема в Safari, которая затрагивала наше приложение.

Отдельно, когда мы создавали приложение Slack Windows, мы не могли использовать существующую кодовую базу, поэтому мы решили сделать ставку на совершенно новую платформу под названием Electron.

Мы уже писали об Electron раньше, но, вкратце, Electron - это платформа, которая объединяет движок рендеринга из Chromium, среду выполнения и модульную систему Node.js.

С самого начала разработки приложения Slack Electron у нас была рабочая версия для macOS (хотя и со многими отсутствующими функциями). Для нас было полезно иметь возможность поделиться нашим приложением с коллегами, использующими macOS, для таких вещей, как отзывы о дизайне. Итак, когда мы рассмотрели, как модернизировать приложение для Mac, переход на единую кодовую базу для Mac, Windows и Linux был легким выбором.

Стек технологий

Несмотря на то, что это первое производственное приложение Electron за пределами Atom, приложение Slack Desktop постоянно обновлялось с точки зрения веб-технологий. Наше приложение перешло из приложения CoffeeScript, написанного с использованием ванильных API-интерфейсов DOM, в современное приложение ES6 + async / await React, и в настоящее время мы постепенно переводим наше приложение на TypeScript.

Многопроцессорная модель Chromium

Electron наследует многопроцессорную модель Chromium - основное приложение, а также каждую команду Slack, в которую вы вошли, работают в отдельном процессе с собственным пространством памяти. Для нас это означает, что мы можем перезапускать отдельные команды, которые терпят сбой или имеют другие проблемы, не затрагивая остальную часть приложения, а также защиту от проблем с драйверами графического процессора с помощью отдельного процесса графического процессора.

В macOS эти процессы отрисовки помечены как Slack Helper; вы увидите по одному для каждой команды, плюс три дополнительных для отчетов о сбоях, графического процессора и процесса, в котором находится переключатель команд.

Тег WebView

Хотя мы обычно доверяем запуску локального приложения Slack с полным доступом к рабочему столу и Node.js, что позволяет удаленному контенту напрямую обращаться к функциям рабочего стола, а Node.js небезопасен - если бы кто-то был в Slack Man-In-The-Middle, они будет иметь полный контроль над пользовательскими компьютерами! Чтобы предотвратить это, мы используем функцию Electron, перенесенную из приложений Chrome, под названием элемент WebView (не имеющий отношения к представлению Apple WebView, упомянутому выше). Концептуально этот HTML-элемент похож на iframe, поскольку он включает в себя другой сайт, встроенный в качестве блочного элемента. Однако на самом деле он создает отдельный процесс рендеринга Chromium и делегирует рендеринг контента своему рендереру хоста, аналогично тому, как работает фреймворк хоста плагина Flash.

Прежде чем произойдет какая-либо навигация, у нас есть возможность запустить собственный код с включенной интеграцией Node.js, называемый «сценарием предварительной загрузки». Этот скрипт запускается до создания DOM и до того, как страница имеет источник, но дает нам доступ к API-интерфейсам Electron и Node.js.

Одна вещь, которую мы можем сделать в нашем сценарии предварительной загрузки, - это установить ключ для объекта окна. Это позволяет нам предоставлять API на стороне веб-приложений Slack. Поскольку мы определяем этот API, мы можем установить границу безопасности, которая предоставляет веб-приложению только определенные методы.

Чтобы этот подход был безопасным, вы должны сделать несколько вещей:

  1. Вы должны убедиться, что у вас нет утечек модулей Node.js на поверхность вашего API.
  2. Вы должны внимательно относиться к своим API, особенно к тем, которые связаны с путями к файлам. Убедитесь, что злоумышленник, вызывающий ваш API, не может получить доступ к данным в файловой системе пользователя.
  3. Вам нужно только беспокоиться о доступе к объектам JS через сам JavaScript, возможность видеть объекты Node.js через вкладку консоли DevTools, как правило, безопасно. DevTools имеет доступ к скрытым методам V8, которых нет в JavaScript, поэтому возможность доступа к объектам Node.js, например, с помощью псевдопеременной «закрытие» не вызывает беспокойства.

Связь между процессами

Связь между всеми этими различными процессами - сложный бизнес. На вершине низкоуровневого модуля IPC Chromium, который позволяет отправлять сообщения между процессами, мы создали библиотеку под названием electronic-remote.

electron-remote - это урезанная, более быстрая версия модуля Electron remote, использующая прокси-объекты ES6. Используя прокси-серверы, мы создаем объект, который представляет window на удаленном рендерере, и все вызовы методов отправляются этому удаленному в виде сообщений. Это позволяет выполнять те же действия, что и традиционный модуль remote, но без подводных камней удаленных обработчиков событий и синхронного IPC.

Сначала настройте API, который вы хотите создать, в главном окне. Чтобы упростить понимание нашего примера, мы будем использовать глобальную переменную:

import {remote} from ‘electron’;
class DockHelper {
 bounceDock() {
   remote.app.dock.bounce(‘informational’);
 }
}
window.dockHelper = new DockHelper();

Затем в нашем скрипте предварительной загрузки мы подключим его:

import {remote} from ‘electron’;
import {createProxyForRemote} from ‘electron-remote’;
let mainWindow = createProxyForRemote(remote.getCurrentWindow());
window.desktopIntegration = {
   bounceDock: () => {
    // NB: bounceDock returns a Promise here because we’re
    // accessing it Remotely — if app.dock.bounce throws, 
    // we’ll see it show up here as a rejected Promise.
    return mainWindow.dockHelper.bounceDock();
  }
 }
}

Теперь ваше веб-приложение имеет доступ к новому объекту desktopIntegration, у которого есть метод bounceDock:

window.desktopIntegration.bounceDock
 .catch((e) => console.log(`Failed to bounce dock: ${e.message}`));

Возможность эффективного доступа к удаленным объектам значительно упрощает реализацию API вашего веб-приложения. В нашем случае это позволяет нам легко отправлять Действия Redux App для обновления состояния нашего приложения и через прокси пользовательский интерфейс, который зависит от этого состояния, отображать обновления значков на значке Dock или обновлять непрочитанное состояние на элементы переключателя команды.

Вы должны быть осторожны при использовании electronic-remote для аудита ваших удаленных объектов так же, как вы проверяете другие объекты предварительной загрузки - возможность попросить другой процесс сделать что-то вредоносное так же плохо, как и сделать это внутри процесса!

Библиотеки с открытым исходным кодом

В рамках написания приложения Slack Desktop мы разработали ряд библиотек и инструментов с открытым исходным кодом:

Мы также потратили некоторое время на участие в самом проекте Electron, чтобы помочь улучшить структуру для разработчиков.

Как видите, новое приложение Slack Desktop помогает нашей команде разработчиков использовать лучшее из обоих миров - быструю итерацию и экосистему веб-разработки, а также возможность (с небольшим количеством C ++ и смазкой локтя!) Получить доступ к базовой операционной системе Mac. системы способами, недоступными для веб-сайтов. Мы с нетерпением ждем будущего наших настольных приложений, особенно того, что мы можем сделать, чтобы объединить работу вашей команды.