Интеграция HTML движка в нативное Windows приложение – выбор и архитектура

    Как мы перевели работу с HTML в 1С:Предприятии с Internet Explorer на WebKit

    Возможность отображать HTML в формах 1С появилась в платформе 1С:Предприятие в 2003 г. версии 8.0. Для работы с HTML в платформе использовался движок браузера Internet Explorer (1С:Предприятие на тот момент работало только под Windows). Движок браузера использовался платформой для утилитарных целей. Например, писать с нуля полноценный элемент для редактирования текста а-ля Word – с возможностью различных цветовых и шрифтовых решений, вставки картинок и т.д. – весьма непростая задача. А если задействовать для этих целей HTML и использовать в качестве средства отображения движок интернет-браузера, то задача сильно упрощается. Также при помощи движка был реализован ряд других механизмов (например, отображение справочной информации) и элементов (например, Планировщик).

    Ну а возможность для разработчиков прикладных решений отображать с помощью HTML нестандартный, по меркам мира учетных систем, дизайн позволяла иногда привносить разнообразные приятные изюминки в интерфейс бизнес-приложений.

    Время шло, платформа стала поддерживать сначала Linux, а потом и macOS. Для работы с HTML в этих ОС Internet Explorer не подходил по понятным причинам; в Linux нами был задействован WebKitGTK+, а в macOs — библиотека на основе Cocoa. Таким образом, единство кодовой базы для разных ОС (которое мы стараемся поддерживать для клиентского кода на уровне 95%) в этой области было нарушено. Ну и движок IE к этому времени стал источником ряда проблем.
    image

    Проблемы:

    • У движка IE закрытый исходный код – значит:
      • Невозможна гибкая настройка движка под нужды платформы
      • Невозможны отладка и понимание процессов, происходящих внутри
      • Невозможно устранение багов и ошибок путем обновления версии движка
    • Движок не подходит для реализации современных задач веб-программирования
    • Проблемы производительности на слабых машинах

    Итак, напрашивался перевод работы с HTML в версии 1С:Предприятия для Windows с движка IE на что-то другое. Что же выбрать?

    Для начала мы сформулировали требования к движку:

    • Поддержка современных технологий веб-программирования
    • Открытый исходный код для возможности гибкой настройки движка и понимания логики его работы
    • Высокая производительность на слабых компьютерах
    • Желательно, чтобы движок требовал для работы небольшое количество сторонних библиотек

    Выбор движка


    Из чего выбирать? Начали мы, естественно, с WebKit, с которым уже работали в версиях платформы для Linux и macOS.

    WebKit был разработан в Apple в начале 2000-х на основе движков с открытым исходным кодом KHTML и KJS. На основе WebKit был создан браузер Safari, позже – Chrome (еще позже Chrome перешел с WebKit на Blink, который опять-таки создан на основе кода WebCore из WebKit).

    Исходный код WebKit – открытый, под лицензией LGPL. WebKit написан под macOS, под Windows есть несколько портов:

    WebKit AppleWin


    Это порт, который разработчики WebKit предлагают собирать под Windows по умолчанию. Был сделан сотрудниками Apple в середине-конце нулевых. Использует графическую библиотеку CoreGraphics, которая является упрощенной версией библиотеки под macOS, портированной на Windows. Для выполнения JavaScript порт использует библиотеку JavaScriptCore c тем же API, что используется в реализации платформы под Linux. Это делает его основным кандидатом для использования.

    WebKit WinCairo


    Этот порт использует для графики библиотеку Cairo. Этот порт Apple какое-то время активно развивала как аналог основному порту AppleWin. Преимущество этого порта в том, что он менее зависим от macOS-специфичных библиотек, которые необходимы для работы CoreGraphics. Помимо этого порт использует ту же библиотеку для графики (Cairo), что и движок WebKitGTK+, который используется нами в Linux-реализации платформы, а это хорошо для стандартизации поведения нашего кода.

    QtWebKit


    Ещё одна реализация движка WebKit под Windows, теперь уже независимая от разработчиков самого движка. Qt – популярная кроссплатформенная библиотека с собственной графической библиотекой QtGui. Этот порт также использует библиотеку JavaScriptCore для обработки JavaScript, однако у него есть минусы:

    • Сильная зависимость от основных компонент Qt, которые в случае использования в стороннем программном обеспечении нужно будет поставлять вместе с ним
    • Отличающийся набор интерфейсов для работы с компонентами для отрисовки HTML по сравнению с WebKitGTK и собственная логика работы с ними.

    WebKitGtk+ for Windows


    Мы уже использовали WebKitGtk+ в Linux-версии платформы. Но вариант задействовать его в Windows был исключен из-за сложности сборки, плохой документированности этого процесса и отсутствия постоянной поддержки этого направления развития от разработчиков WebKitGTK+.

    Chromium(Blink)


    Первый и единственный не WebKit-подобный движок, который рассматривался как кандидат для решения задачи. Был отвергнут из-за больших различий в логике работы компонент для отрисовки HTML по сравнению с WebKitGTK+ и другой библиотеки для работы с JavaScript (V8).

    Что выбрать?


    После исследований в финал вышли AppleWin и WinCairo.
    Чтобы сделать окончательный выбор, мы изучили, как работает WebKit.

    Структура движка WebKit


    Обычно разные порты WebKit отличаются двумя вещами. Первая – это непосредственно реализация для конкретной ОС с задействованием ОС-специфичных компонент. Вторая – это графическая библиотека. На рисунке ниже описаны различия в этом смысле между портами WebKit. Для Windows разработчиками WebKit были написаны порты на адаптированной CoreGraphics и библиотеке Cairo.

    image

    Упрощенная модель работы движка: три традиционных механизма форматирования веб-страницы — HTML, JavaScript и CSS — подаются на вход движку, а он из них формирует и отображает страницу:

    image

    Сам движок состоит из нескольких компонент:

    • WTF (Web Template Framework, а не то, что вы, возможно, подумали): здесь находятся собственные реализации структур данных для функционирования движка, а также осуществляется работа с потоками
    • JavaScriptCore: компонента, как ясно из названия, для работы с языком JavaScript
    • WebCore: здесь написана вся работа с DOM, стилями, парсингом HTML и XML. Здесь делается вся основная «магия» движка
    • Platform: выполняет технические действия для взаимодействия с сетью, помещение данных в БД, декодирование изображений, работа с медиа
    • WebKit и WebKit2 API – связывание всех компонент и предоставление доступа к ним

    image
    Взаимосвязь компонент WebKit и ОС-специфичных особенностей — на рисунке ниже. Как видно, есть довольно много специфичных моментов, которые нужно реализовывать для каждой ОС отдельно. Хотя JavaScriptCore позволяет использовать себя в каждом порте без отдельных реализаций.

    image

    Как формируется веб-страница


    Из сети приходит ответ на запрос к серверу с данными для загрузки. Загрузчик передает данные в парсер, который, взаимодействуя с компонентой для JavaScript, формирует DOM и таблицу стилей. Далее сформированные данные передаются в дерево рендеринга и отображаются графическим контекстом.
    image
    Сама страница состоит также из отдельных компонент. В компоненте WebCore реализован класс Page, который позволяет получить доступ ко всей странице. У Page есть главная рамка – MainFrame, в рамке всегда есть документ. В главной рамке может быть любое количество других рамок, также с документами внутри. Для каждой рамки отдельно формируются некоторые события, а также специфичные графический и JavaScript контексты.

    image

    Упрощенно HTML-парсер работает так. Из набора полученных от сервера байт декодер формирует набор символов для разбора. Символы преобразуются в лексемы или токены, которые обычно представляют собой элементарные части кода с мета-информацией о том, что это за текст, является ли он частью синтаксиса языка или контентом. Затем из токенов формируются узлы для построения DOM-дерева. Строитель дерева из набора узлов формирует полноценную объектную модель документа веб-страницы.

    image

    Окончательный выбор


    • AppleWin
      • Плюсы:
        • Реализован на графической библиотеке, которая работает в macOS – главной целевой платформе для разработчиков WebKit
      • Минусы:
        • Отсутствие реализации механизма печати
        • Большое количество зависимостей
    • WinCairo
      • Плюсы:
        • Та же графическая библиотека (Cairo), что и в используемом Linux-порте платформы 1С
      • Минусы:
        • Существенных для наших задач не обнаружено

    Победил WinCairo. Для разработки взята последняя доступная на тот момент версия WebKit – 605.1.11.

    Реализация


    Хотя движок довольно неплохо покрыт юнит-тестами (около 30 000 на все компоненты движка, написаны авторами движка), в реализациях для “непрофильных” ОС (т.е. для всего, что не macOS), существуют ошибки и недоработки. Эти пробелы реализации постепенно обнаруживались по мере разработки и тестирования работы движка в составе платформы 1С:Предприятие.

    Загрузка HTML-кода через Drag&Drop


    При перетаскивании текста в окно обнаружилось, что если перетаскиваемый текст содержит не-ASCII символы, то в финальный документ вставляются иероглифы. Ошибка проявлялась только в Windows-реализации движка, потому что она работала со ОС-специфичным механизмом перетаскивания элементов. Оказывается, текст не декодировался из UNICODE в UTF-16 перед передачей в обработчик события вставки.

    Изменение поведения по комбинации клавиш Shift+Enter


    В большинстве текстовых редакторов (включая Microsoft Word) эта комбинация вставляет перенос строки. Стандартное же поведение WebKit – вставка нового параграфа (как при простом нажатии на Enter). Мы изменили движок, сделав поведение более привычным для пользователей.

    Организация механизма Undo&Redo.


    WebKit предоставляет API для реализации собственного механизма отмены и повторения действия пользователя. Его схема такова: когда пользователь совершает некое дискретное с точки зрения движка действие (например, переход к новому параграфу, форматирование курсивом, вставка), то WebKit сообщает об этом разработчику средствами API, чтобы он мог зарегистрировать это действие.

    В процессе тестирования реализованного механизма выяснилась неприятная вещь: движок не сообщает об изменениях в структуре таблиц. Были добавлены команды добавления и удаления ячеек и изменения атрибута colSpan, которые стали частями составных действий, таких как, например, добавление/удаление столбца или строки таблицы. Такие составные команды регистрируются в тот же стек undo&redo, и вместе с командами из движка обеспечивают корректную работу механизма.

    Вставка из Excel


    Те, кто работал с буфером обмена Windows и Excel, возможно, знают, что, во-первых, при копировании из Excel в HTML формат буфера обмена в копируемый фрагмент помещаются только теги ячеек и рядов, но не тег самой таблицы, во-вторых, в ячейки не передаются стили из Excel документа. Из-за этого, вставка, например, в редактируемый элемент в Chrome цветной таблицы выглядит так:

    Оригинал:

    image

    В Chrome:

    image

    Оба этих фактора не учли и разработчики WebKit. Открытость кода движка позволила нам доработать механизм вставки, и теперь вставленный в ПолеHTMLДокумента фрагмент таблицы близок к оригинальному:

    image

    Генерация курсивных шрифтов


    Если в Windows отсутствует italic-версия нестандартного шрифта, большинство текстовых редакторов умеет генерировать такой шрифт по его regular-версии. Однако WebKit такого не умел и пару раз ввёл разработчиков в заблуждение: как же так, в коде HTML документа мы помещаем текст в тег <i>, но несмотря на это текст оставался прямым. Причина в алгоритме подбора движком WebKit нужного шрифта в используемом нами порте WinCairo – в случае, если курсивной версии нет, движок использует regular-версию. Это поведение было заменено на использование генерируемого графической библиотекой Cairo курсивного шрифта.

    Ошибки при декодировании изображений и анимации


    Были обнаружены ошибки поведения движка при работе с графическими элементами. При загрузке некоторых картинок в формате PNG наблюдалось искажение изображения, а иногда и вовсе его отсутствие. Причину такого поведения выяснить не удалось, так как ошибка происходит при декодировании изображений в недрах библиотеки libpng.

    Опытным путём было выяснено, что при линковке библиотеки libpng динамическим способом вместо статического, проблема устраняется. Кстати, в актуальной версии движка линковка выполнена именно этим способом. Было решено поступить так же.

    Еще одной проблемой оказалась работа движка при загрузке анимаций в формате GIF. Ошибка периодически воспроизводилась при загрузке страницы с такими анимациями и приводила к аварийному завершению работы программы. Ошибка была вызвана отсутствием синхронизации при работе с буфером, в который помещается очередной кадр анимации. Проблема была устранена использованием внутренних средств синхронизации WebKit.

    Поддержка правописания


    В сборке с Internet Explorer, в Windows версии 8 и новее у редактируемого поля HTML можно было включить проверку правописания. Для этого достаточно было атрибут «spellcheck» сделать равным «true». У WebKit для разных портов были свои решения: в Linux это библиотека Enchant, в macOS — собственный механизм, знакомый всем пользователям продукции Apple. А вот для Windows реализации нет, но предоставляется API для собственного решения. Мы использовали Windows Spell Checking API, доступный начиная с Windows 8, и реализовали механизм по аналогии со сборкой с Internet Explorer. Кстати, теперь в форматированном документе в нативных клиентах 1С:Предприятия тоже работает эта функциональность. До версии 8.3.14 она была отключена из-за низкой производительности Internet Explorer.

    Поддержка Windows XP


    Часть наших клиентов до сих пор работает на Windows XP, и в ближайшее время апгрейдить ОС не собирается. Печально для нас как разработчиков, но факт. А значит — нам надо их поддерживать. И тут нас ждал неприятный сюрприз: разработчики WebKit уже около года не поддерживали работу движка в WinXP. Попытка собрать версию движка с набором средств сборки для WinXP не привела к успеху — разработчики WebKit используют библиотеки, доступные только с версии Windows Vista и более поздних.

    Что делать? Варианты были такие:

    1. Оставить в WinXP реализацию с движком Internet Explorer, а в старших версиях Windows использовать WebKit
    2. Взять для разработки более раннюю версию движка WebKit, работоспособную в WinXP, и использовать эту версию во всех ОС
    3. Использовать в WinXP подходящую версию WebKit, а в старших версиях Windows использовать свежий движок
    4. Портировать актуальную версию движка на WinXP самостоятельно и использовать её везде

    Вопрос стоял непростой. Первый вариант позволял использовать самую свежую версию движка WebKit, но вынудил бы вернуть старую реализацию с Internet Explorer. В таком решении было бы тяжело обеспечивать безошибочную работу программы, а также сам код сильно бы усложнился. Второй вариант обеспечивал одинаковое поведение на всех ОС Windows, однако не оставлял бы нам возможности для развития – обновления движка для исправления ошибок и получения новых возможностей от разработчиков движка в более поздних версиях. Третий вариант позволял задействовать актуальную версию движка в старших версиях Windows, но сильно усложнял логику установки и обеспечение одинакового поведения версий во всех ОС. Четвёртый вариант выглядел предпочтительнее всех остальных, однако было невозможно спрогнозировать сложность и вообще возможность такого решения.

    Решили все же рискнуть и реализовать четвертый вариант, самый правильный с архитектурной точки зрения (использование единого исходного кода движка на всех версиях Windows). Портированная версия WebKit по-разному работает в WinXP и более новых версиях Windows:

    • Пришлось отказаться от средств нового DirectX (d3d11) в пользу старого DirectX9 (d3d9) и адаптировать его заголовочные файлы к младшей версии SDK.
    • Функции из нового SDK при выполнении на новых версиях Windows вызываются по адресу (получаемому через GetProcAddress).
    • Для передачи в движке данных между потоками в WinXP используется Thread local storage, в новых версиях — Fiber local storage.

    Итог


    Итак, теперь у нас в платформе 1С:Предприятие начиная с версии 8.3.14 (релиз — конец 2018 г.) HTML будет поддерживаться по высшему разряду – HTML5, OpenGL и т.д. И количество, и качество изюминок, которые можно привнести в решения на нашей платформе, ограничиваются только фантазией разработчика. И еще, конечно, операционной системой клиента – на WinXP многие вкусные плюшки из HTML5 работать, по понятным причинам, не смогут.

    Теперь на Windows приложения на платформе 1С:Предприятие смогут показывать вот такое:



    Но, используя «вкусности» HTML в прикладных решениях, не стоит забывать и здравый смысл. Использование HTML целесообразно и рекомендуется для специализированных задач (отображения справки, методик, описаний товаров, …), но не для реализации задач бизнес-логики (ввода/вывода структурированной информации). Для этого нужно использовать штатные механизмы интерфейса 1С:Предприятия, обеспечивающие автоматическую поддержку прав доступа, управления функциональностью, адаптации к форм-фактору устройства, поддержку пользовательских настроек и работу многих других механизмов, без которых полноценная работа бизнес-приложения становится практически невозможной.
    58,00
    Мы делаем средства разработки бизнес-приложений
    Поделиться публикацией

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

      0
      С какой версии 1С этот новый движек доступен?
        0
        8.3.14.
        Спасибо за вопрос по делу, сейчас добавлю в статью.
        0
        Del
          0
          Когда планируется поддержка таблиц в форматированном документе?
            0
            Примечание: Если у кого-нибудь в конфигурации использовался, например, разбор html-страницы c помощью объектной модели IE, то при переходе на новую версию платформы, такую конфигурацию надо будет доработать (перевести на модель WebKit).
            Возможно, таких конфигураций немного, но всё же…
              +4
              Один вопрос — вы все эти патчи при себе держите или хоть что-то в апстрим отправляете? Те же баги со вставкой под виндой или курсивом думаю у многих встречаются.
                0
                Пока держим при себе.
                Согласен, влить было бы хорошо, но пока руки не доходят.
                0
                Добрый день. Планируются ли какие-либо улучшения поля HTML в веб-клиенте? Например, программная работа с буфером обмена.
                0
                В форматированном документе, если работать с ним через веб-клиент, есть очень неприятная вещь — он теряет часть содержимого. Воспроизвести можно примерно следующим образом:

                1) Добавить на управляемую форму (скажем, справочника) группу страниц.
                2) Разнести элементы формы по разным страницам. При этом форматированный документ оставить на второй странице.
                3) В режиме «Предприятие» в веб-клиенте наполнить форматированный документ.
                4) Мышью переключиться на другую страницу и нажать CTRL+S.
                5) После записи объекта перейти на вкладку с форматированным документом. Текст или его часть будет отсутствовать.

                Опытным путем пришли к решению, что изменения содержимого поля форматированного документа помещаются в реквизит формы при смене фокуса на другой элемент. А в приведенном выше примере этого не происходит, соответственно, обновления данных в реквизите тоже.

                Пока проблему решили принудительной установкой фокуса на другой элемент формы при смене страницы.

                P.S. Платформа 8.3.11. Если не получится воспроизвести, могу скинуть видео.
                  0
                  Спасибо!
                  Если не получится воспроизвести — запрошу у вас видео.
                  0
                  Спасибо, хорошие новости.
                  Скажите а когда можно будет реализовать событие ПриНажатии в HTML поле в мобильном приложении?
                    0
                    Тоже с ним проблемы? Оно вроде как есть, но у нас не работало. Хотя надо бы на последних версиях проверить, может починили.
                      0
                      Да мы перешли на последнюю ознакомительную мобильную 8.3.13, но проблема не решена.
                        0
                        Насколько помню, в мобильной платформе событие ПриНажатии происходит только если нажатие приводит к переходу на другую страницу. У вас нажатие приводит к переходу на другую страницу?
                          0
                          Спасибо попробуем!

                          Пока что мы сильно не пользовались полем HTML в мобильной версии, т.к. при клике не срабатывало ПриНажатии.
                            0
                            Если что — пишите. Не работает — постараемся починить.
                              0
                              Уже попробовали, действительно работает.
                              Спасибо, действительно чувствуется ускорение в решении проблем, что не может не радовать.
                    0
                    А можно, пожалуйста, исправленный код поддержки вставки пулл-реквестом в вебкит и, по возможности, блинк? Спасибо!
                      0
                      Как дойдут у разработчиков руки — вольем. Но обычно разработчики у нас сильно загружены :(
                        0
                        А исходники этой доработанной версии WebKit где-то доступны?
                          0
                          Пока нет.
                          К выпуску версии 8.3.14 (ориентировочно — конец 2018 г.) планируем выложить исходники WebKit с нашими доработками здесь: github.com/1C-Company-third-party
                      0
                      с версии 8.3.14 (релиз — конец 2018 г.)

                      Ничего не перепутали? Конец 2018 года это же релиз 8.3.13 или же все таки к концу года и 8.3.14 ждать еще?
                        0
                        8.3.13 уже опубликована
                          0
                          Увы мечтам не было суждено сбыться :D
                        0
                        Есть еще пятый вариант: Gecko
                          0
                          Где можно найти инструкцию по использованию Gecko?
                            0
                            Совершенно верно. Я даже его на картинке нарисовал :)
                            Но Gecko у нас отвалился на ранней стадии рассмотрения, причину уже и не вспомню.
                            А у вас есть пример удачного использования Gecko? Кроме Firefox.
                            Будет здорово, если поделитесь.
                            0
                            А простые смертные (те, которые не могут себе позволить штат разработчиков, которые поставят ActivePerl, Php, Python, и кучу всего, разберутся как каждый компонент настроить, разберутся с такими советами, как отключение антивирусов, не обновлять WinCairo (!), «You may have to build twice before it works.» и т.д.) могут где-то получить замечательный WinCairo, чтоб вот как «этот уродский IE» взять и подключить к проекту одной строкой/dll'кой?
                              0
                              Даже не знаю. Нам пришлось пройти длинным путем.
                              0
                              Расскажите пожалуйста, PeterG, чуть подробнее про решение НЕ в пользу
                              Chromium(Blink)

                              Первый и единственный не WebKit-подобный движок, который рассматривался как кандидат для решения задачи. Был отвергнут из-за больших различий в логике работы компонент для отрисовки HTML по сравнению с WebKitGTK+ и другой библиотеки для работы с JavaScript (V8).

                              Вроде бы считается, что движок V8 является одним из самых производительных для Javascript, а связка V8 + libuv позволяет реализовывать полноценный обмен с вешним миром, NODE.JS тому пример.
                              Что имеется в виду под «различиями в отрисовки HTML», которые оказались критичны для 1С, но с другой стороны создают конкурентное преимущество для Хрома?

                              И почему вы его называете не WebKit-подобным, хотя он основан на WebCore из WebKit?
                                0
                                Главная причина отказа от Chromium в пользу WebKit была в том, что у WebKit для исполнения JavaScript используется библиотека JavaScriptCore, на основе которой у нас уже была реализована работа с DOM для Linux. Эту реализацию мы использовали в готовом виде для Windows и macOS, т.к. API у этой библиотеки для всех ОС одинаковый.
                                В случае с Blink, хотя за основу и была взята библиотека WebCore, далее движок развивался независимо. Поэтому WebKit выглядит предпочтительнее в смысле унификации поведения нашего клиента в разных ОС.
                                0
                                Что будет с конфигурациями, которые будут запущены на платформе 8.3.14, которые на текущий момент используют ActiveX?
                                  0
                                  Если речь идет о внешних ActiveX-компонентах, которые использовались в ПолеHTMLДокумента в нативном клиенте, то их функционирование в 8.3.14 прекратится, WebKit эту технологию не поддерживает.
                                  0
                                  Портировать актуальную версию движка на WinXP самостоятельно и использовать её везде
                                  Не означает ли это, что вы надолго/навсегда застрянете на текущей версии WebKit, которая через какое-то время всё равно устареет?
                                    0
                                    Нет, мы планируем периодически обновляться. Естественно, с учетом возможностей той же WinXP; думаю, не все обновления смогут быть применены на WinXP по объективным причинам.

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

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