Lazarus fpCEF3 - объединяем браузер и десктопное приложение
Можно объединить возможности десктопного и браузерного приложений,
подключив настоящий современный браузер.
Веб-интерфейс вне конкуренции, плюс доступ к локальным ресурсам и данным,
плюс скриптовые сетевые приложения с логикой и данными.
Этот раздел сайта посвящён кросс-платформенным и бесплатным: IDE Lazarus и веб-движке Chromium.
Для полноценной работы потребуется подключить Chromium Embedded Framework (CEF)
и организовать управление веб-страницами и обмен данными с JavaScript.
Как подключить веб-браузер (fpCEF3 + runtime client)
Считаю необходимым подключение Synapse ко многим проектам, чтобы иметь возможность корректно работать с URL (функции EncodeURL() и DecodeURL() модуля synacode), плюс работать с интернет-серверами напрямую, без браузера. Как подключать Synapse.
Функции Synapse работают в синхронном режиме, а браузер работает в асихронном, поэтому предпочтительней использовать асинхронный JavaScript-код, но отказываться от возможностей Synapse было-бы неразумно.
Для навигации по всем страницам этого раздела сайта используйте иконку меню или ссылку Навигация в строке навигации.
Примеры использования Lazarus + fpCEF3 сделаны на следующей конфигурации ПО:
Lazarus IDE v1.6.4, fpc 3.0.2, fpCEF3 v3.2924 (v3.2840, v3.2704, v3.2526), win7 64
(первые примеры сначала делались на Lazarus IDE v1.4.4, fpc 2.6.4, fpCEF3 v3.2454 - результат одинаков).
При необходимости, можете прочитать ниже, как подключить веб-браузер и проверить его работу: тест обмена данными с JavaScript
project1 - первый запуск - типа hello world.
project2 - один экземпляр браузера и контекстное меню - некоторые возможности по управлению браузером.
project3 - веб-интерфейс для десктопного приложения - уже вполне близкий к реальным задачам проект.
Справочная информация по fpCEF3
TChromium Options - опции в инспекторе объектов - настройка работы Chromium через значения Options компонента TChromium.
TChromium Events - обработка событий - описание работы с событиями Chromium.
Подключение веб-браузера в Lazarus
Нужны исходники fpCEF3 и runtime-файлы веб-движка Chromium.
1. Source code
Берём fpCEF3 Source code отсюда: https://github.com/dliw/fpCEF3/releases
На август 2017 последняя версия v3.3029 : fpCEF3 v3.3029 Source code (zip)
Распаковываем, например в папку рядом с папками проектов (потом удалять оттуда нельзя - будет ошибка при сборке проектов).
В Lazarus IDE выбираем пункт меню «Пакет - Открыть файл пакета (.lpk)» и выбираем cef3.lpk.
Появится окно установки пакета. В нем нажать сначала кнопку «Компилировать», затем кнопку «Использовать - Установить».
Потом подтвердить пересборку Lazarus, выбрав «Да» в появившемся диалоговом окне.
После этого происходит процесс пересборки и на палитре компонентов должна появиться вкладка Chromium.
В версиях fpCEF3 до 2704 на вкладке 2 компонента: TChromium (cef3lcl.pas) и TChromiumOSR (cef3osr.pas).
Начиная с версии fpCEF3 2704 на вкладке уже 3 компонента: TChromium (cef3lcl), TChromiumOSR (cef3osr), TChromiumContext (cef3context).
Обновить версию Chromium можно так: открываем файл пакета CEF3 через меню «Пакет - Открыть загруженный пакет...» или «Пакет - Открыть недавний пакет...», далее выбираем «Использовать - Удалить», подтверждаем. Затем делаем установку новой версии пакета.
Перекомпилируем проекты - собранные exe-файлы всегда работают только с одной (своей) версией Runtime-файлов Chromium.
2. Runtime
Скачиваем runtime - файлы CEF3 (32bit или 64bit - зависит от версии Lazarus и ОС),
можно скачать отсюда: http://opensource.spotify.com/cefbuilds/index.html
На август 2017 актуально - Branch 3029 - Chromium 58.0.3029.81 (от 12 мая 2017 г) :
- Windows 32bit - файл под именем cef_binary_3.3029.1619.geeeb5d7_windows32_client.tar.bz2
- Windows 64bit - файл под именем cef_binary_3.3029.1619.geeeb5d7_windows64_client.tar.bz2
При работе скомпилированной программы, когда происходит работа с TChromium - должны быть доступны runtime - файлы браузера, иначе будет возникать ошибка. Содержимое папки «Release» необходимо поместить в такое место, чтобы runtime - файлы браузера были доступны нашему приложению (например рядом с exe-шником скомпилированной программы), короче несколько dll и других файлов должны быть доступны программе при старте. Это и есть наш браузер, его можно отдельно запускать (cefclient.exe).
Все ресурсы с относительным адресом, на которые ссылается любая веб-страница (изображения, стили и т.д.), должны быть доступны относительно адреса самой веб-страницы, вне зависимости от местонахождения браузера или нашего десктопного приложения.
В 2017 году различия между новыми версиями Chromium незначительны, последнее важное отличие - это включение поддержки CSS-variables, мы можем использовать такие переменные начиная с fpCEF3 2704 (Chromium 51). Подробности и сравнение браузеров см. caniuse.com
Начиная с версии 2704, Chromium уже не работает с Windows XP, соответсвенно браузер Chrome больше не будет обновляться для XP
Последняя версия fpCEF3 для работы под WinXP: v3.2526 (Chromium 47.0.2526.80) - отсутствует поддержка CSS-Variables.
У меня есть оригинальные копии runtime - файлов этой сборки под 32 бит (cef_binary_3.2526.1373.gb660893_windows32_client.7z) и 64 бит (cef_binary_3.2526.1366.g8617e7c_windows64_client.7z), но их размер 24 и 30 Мбайт соответственно, поэтому могу поделиться с ними только через запрос в форме обратной связи, так как прямые ссылки на скачивание делать не хочу, чтобы спам-боты не забивали канал.
Подключение Synapse
Последняя версия Synapse (2012-04-23 - release no. 40 synapse.zip (898.65 KB).
Я распаковал файлы Synapse в папку рядом с папками проектов.
В Lazarus IDE выбрал пункт меню «Пакет - Открыть файл пакета (.lpk)» и выбрал laz_synapse.lpk, затем выбрал «Компилировать».
Для новых проектов я вручную добавляю модули Synapse, через «Использовать - Добавить к проекту» окна установки пакета.
Тестовое приложение - обмен данными с JavaScript
Сразу нужно сказать об обязательной поддержке кодировки UTF-8 - все файлы нужно делать именно в ней.
Если по каким-либо причинам IDE не работает с UTF-8, нужно обновиться на новую версию.
У компонента TChromium есть возможность выполнить произвольный JavaScript на веб-странице / во фрейме через ExecuteJavaScript(), а так-же есть событие OnConsoleMessage, обработчик которого вызывается каждый раз при выводе текста в консоль браузера.
Можно принимать данные из JavaScript-кода через консоль браузера, выводя информацию и данные через стандартный JS-метод console.log().
Данные текстовые, но можно любой JS-объект завернуть в JSON-текст JS-методом str = JSON.stringify(obj) и распарсить JS-методом obj = JSON.parse(str);
Перед передачей UTF8-строк в ExecuteJavaScript(), нужно использовать Utf8Decode(), при получении UTF8-строк из консоли нужно использовать UTF8Encode().
Итак, создадим в notepad++ HTML-файл 1.html в кодировке UTF-8 (без BOM):
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta http-equiv = "Content-Type" content = "text/html; charset=utf-8">
<style>body { border: solid 1px #808080; background: #ffffff; } </style>
<script>
function test_cef(str) {
// alert(str);
console.log("test_cef->" + str); // "test_cef->" - это текстовый маркер функции, чтобы знать, откуда пришло сообщение
}
</script>
</head>
<body>text text text</body>
</html>
В Lazarus создаём тестовый проект, например, project1
Добавляем компонент TChromium на форму, устанавливаем свойство Anchors [akTop,akLeft,akRight,akBottom], привязываемся к событию OnConsoleMessage, добавляем кнопку Button1 для выполнения тестового JavaScript-кода.
В итоге должны получиться примерно такие файлы:
project1.lpr
program project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, Unit1
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource := True;
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
MainForm.Chromium1.Load( 'file://localhost/.../Lazarus/1.html' );
Application.Run;
end.
unit1.pas
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
cef3lcl, cef3types, cef3intf;
type
{ TMainForm }
TMainForm = class(TForm)
Button1: TButton;
Chromium1: TChromium;
procedure Button1Click(Sender: TObject);
procedure Chromium1ConsoleMessage(Sender: TObject;
const Browser: ICefBrowser; const message, Source: ustring;
line: Integer; out Result: Boolean);
private
{ private declarations }
public
{ public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
{ TMainForm }
procedure TMainForm.Button1Click(Sender: TObject);
var i: integer;
begin
if (Chromium1.Browser=nil) then Exit;
if (Chromium1.Browser.MainFrame=nil) then Exit;
for i:=1 to 2 do
begin
Chromium1.Browser.MainFrame.ExecuteJavaScript( Utf8Decode('test_cef(''Привет ''+'+IntToStr(i)+')'), 'about:blank', 0);
end;
end;
procedure TMainForm.Chromium1ConsoleMessage(Sender: TObject;
const Browser: ICefBrowser; const message, Source: ustring; line: Integer;
out Result: Boolean);
begin
ShowMessage(UTF8Encode(message));
Result:=true;
end;
end.