Awesomium для C++

    Awesomium — это библиотека, для интеграции браузера на базе Chromium в своё приложение. Вся прелесть Awesomium состоит в том, что его можно интегрировать в приложение практически любого типа (есть примеры интеграции в 3D игры на базе Unity3D), он обладает широким набором возможностей для разработчика и, честно говоря, с ним просто приятно работать.

    Но, к сожалению, библиотека имеет довольно скромную документацию, практически отсутствуют примеры и комюнити (это касается разработки под C++, дотнетчикам повезло больше). Некоторые моменты мне приходилось по крупицам собирать с разных источников, а к другим понимание приходило после серии проб и ошибок.

    И вот накопив некоторые знания я решил ими поделится. Уверен, что я не первый и не последний, кто будет проходить этот путь.

    Сразу хочу предупредить: возможно местами я не правильно понял или недопонял, не стоит судить строго, просто наведите на правильный путь, буду очень признателен.

    Оконный и безоконный Awesomium



    Прежде всего стоит сказать, что приложения с использованием Awesomium могут быть 2 типов:

    • Offscreen — без отображения окна браузера (весь рендеринг и работа скриптов на странице происходит в фоне и не отображается пользователю) либо с отображением браузера, но его отрисовкой и обработкой сообщений занимается программист
    • Windowed — Awesomium «цепляется» к какому-то окну и отображается на нем. Отрисовка, обработка сообщений мыши и клавиатуры осуществляется средствами библиотеки


    Первый интересен тем, что позволяет работать с браузером без окна. Можно даже работать с консольного приложения, например, для создания скриншотов страниц. На этом типе мы останавливаться не будем, потому что по нему есть хорошие примеры и я с ним фактически не работал. Интересовал меня второй режим — Windowed.

    Windowed Awesomium используется когда вам нужен полноценный браузер в оконном приложении. В этом случае библиотека позаботится об основном — взаимодействии с пользователем. Но не стоит думать, что добавив пару строчек в программу вы получите полноценный браузер, это не так. Львиная часть работы лежит на плечах программистов, например: диалоги загрузки/заливки файлов (да в принципе все диалоги), всплывающие меню, выпадающие списки, создание новых вкладок и т.д.

    Структура библиотеки



    Структура библиотеки очень простая (правда сперва она была совсем не очевидная):

    Основой браузера выступает ядро (WebCore). Каждое приложение может иметь только одно ядро и это в свою очередь накладывает некоторые ограничения. Например, если вы хотите, чтобы какая-то вкладка имела отменный от других User Agent — то у вас ничего не получится, так как значение User Agent хранится в настройках ядра.

    Следующим в иерархии выступает WebView — это окно браузера. При создании каждого WebView — запускается отдельный процесс Chromium. Когда вам WebView больше не нужен — не забываем подчищать за ним, иначе процесс будет висеть до тех пор, пока мы не уничтожим WebCore.

    Каждый WebView может использовать сессию (WebSession). Сессия — это пользовательские данные (кукисы, кэшь, сертификаты, локальные БД и т.д.). Одна WebSession может подключена к разным WebView. Так же в WebSession хранятся важные настройки работы браузеров (тех, которые используют данную сессию), например прокси.

    Ну и последнее (из того, с чем мне пришлось работать) это Listeners — интерфейсы для взаимодействия с браузером. Listeners бывает несколько и каждый из них решает определенные задачи. Например в WebViewListener::Dialog содержится реализация методов для работы с диалоговыми окнами, а в WebViewListener::Download — методы информировании о ходе загрузки файлов.

    Рождение



    С теорией покончено, переходим к практике. Прежде всего рассмотрим процесс создания и инициализации ядра и вкладок браузера в Windowed режиме.

    Прежде всего необходимо инициализировать ядро:
    WebCore* core = 0;
    WebConfig config;
    core = WebCore::Initialize(config);
    


    Далее создаём сессию (и для примера, в настройках сессии прописываем используемый прокси)
    WebPreferences prefs;
    prefs.proxy_config = WSLit("198.1.99.26:3128");
    WebSession * session = core->CreateWebSession(WSLit(""), prefs);
    


    Ну и конечно же создаём объект WebView.
    WebView* view = core->CreateWebView(WIN_WIDTH, WIN_HEIGHT, session, kWebViewType_Window);
    view->set_parent_window(hwnd);
    


    На последнем шаге остановимся подробнее:

    • Прежде всего стоит заметить, что указывать начальную ширину и высоту окна браузера обязательно. Их позднее можно изменить (например при изменении размеров родительского окна)
    • Третьим параметром выступает сессия. Сессию в ходе работы вкладки менять нельзя, но доступ к ней остаётся, так что теоретически (я не проверял) можно менять настройки и они будут вступать в силу
    • Последний параметр метода создания вкладки браузера есть WebViewType. Он может быть либо kWebViewType_Offscreen (по умолчанию), либо kWebViewType_Window. Мы указываем второй, так как создаём окконый браузер
    • Обязательным шагом после создания WebView с типом kWebViewType_Window есть указание handle родительского окна. Handle не обязательно должен быть окна того же процесса, в котором используется Awesomium, у меня это к примеру handle панели в Delphi программе и всё прекрасно работает


    Жизнь



    В зависимости от того, для каких целей используется Awesomium — его «жизнь» может значительным образом отличатся. Я лишь остановлюсь на основных моментах при работе с Windowed типом вкладок.

    Во-первых, не смотря на то, что Windowed Awesomium частично живет своей жизнь, его всё же необходимо периодически подталкивать работать. Подталкиванием занимается метод ядра Update. Рекомендуется вызывать его в таймере, например так:

    // Create our WebCore Update timer, renders at a max of 66 FPS
    UINT myTimer = SetTimer ( hwnd, 0, 15, NULL );
    
    ...
    
    case WM_TIMER: 
    {
    	if (core)
    	{
    		core->Update();
    	}
    }
    
    ...


    Так же обновления ядра рекомендуется делать после уничтожения WebView.

    Во-вторых, если вы хотите, чтобы окно браузера меняло свои размеры вместе с родительским окном — необходимо обрабатывать сообщение WM_SIZE родительского окна и сообщать новые размеры браузеру. Это делается довольно просто:

    case WM_SIZE:
    {
    	if (view)
    	{
    		int nWidth = LOWORD(lParam);  // ширина рабочей области 
    		int nHeight = HIWORD(lParam); // высота рабочей области 
    		view->Resize(nWidth, nHeight);
    	}
    }


    Ну и в третьих, для полноценной работы браузера нужно реализовать интерфейсы Listeners и подключить их к каждому из WebView. С реализацией интерфейсов проблем быть не должно, они отлично документированы. Все необходимые подсказки можно найти в файле WebViewListener.h. Ну а подключать их совсем не сложно:

    DialogListener * dialog_lis = new DialogListener();
    view->set_dialog_listener(dialog_lis);


    Как я уже говорил, при работе в Windowed режиме о многом побеспокоится библиотека, это: отрисовка загруженной странице на родительском окне, обработка нажатия клавиш клавиатуры и мыши, изменения курсоров мышки и т.д.

    Ну и в завершение этой главы — небольшой пример открытия страницы и ожидания её загрузки:

    // Открываем страницу
    view->LoadURL(WebURL(WSLit("http://google.com/")));
    
    // Ожидаем завершения загрузки
    while(view->IsLoading())
    {
        Sleep(50);
        core->Update();  
    }
    
    //  Переводим фокус (иначе действия пользователя не будут обрабатываться)
    view->Focus();
    


    Смерть



    С уборкой после работы возникли наибольшие проблемы. Информации по этому поводу в официальной документации и в Google для работы в Windowed режиме чуть больше чем ноль. Методом проб и ошибок я дошел до следующего:

    1. Перед тем, как уничтожать WebView — сначала нужно уничтожить родительское окно (обратный порядок приведет к краху приложения).

    2. Если вы хотите, чтобы данные сессии сохранились (если это не in-memmory сессия) — нужно вызвать метод Release для экземпляра сессии.

    3. WebView уничтожается с помощью вызова метода Destroy (и никак иначе)

    4. Ядро уничтожается с помощью вызова метода Shutdown, который кстати умеет подчищать и за неверно уничтоженными WebView (но лучше всё таки контролировать самому этот процесс)

    Примерно, это выглядит следующим образом:

    // Где-то тут уничтожаем родительское окно
    ...
    
    // Останавливаем загрузку страницы (рекомендуется так же дождаться полной остановки, так как некоторые жалуются на ошибку, если этого не сделать)
    view->Stop();  
    
    // Сохраняем данные сессии на диск
    view->session()->Release();
    
    // Уничтожаем WebView (в этот момент процесс Chromium для этой вкладки изчезает из списка процессов)
    view->Destroy();
    
    // Обновляем ядро (не знаю на сколько обязательно это делать, но в документации есть такие рекомендации)
    core->Update();
    
    // Уничтожаем ядро
    core->Shutdown();
    


    Уничтожать ядро необходимо только перед завершением работы программы. Если вам нужно просто закрыть одну вкладку браузера, то и удалять нужно только её и WebView, который находился на этой вкладке.

    Полезные ссылки



    При изучении Awesomium мне очень сильно помогли комментарии в хидер файлах, которые находятся в папке \Awesomium SDK\1.7.1.0\include\Awesomium\ и следующие страницы интернета:

    wiki.awesomium.com/general-use — скромные примеры использования от создателей Awesomium. Там же есть описание того, как создавать преокт с использованием этой библиотеки и какие файлы необходимы для работы приложения

    awesomium.com/docs/1_7_0/cpp_api/index.html#intro_sec — официальная документация (по сути дублирует комментарии в подключаемых хидер файлах)

    gist.github.com/khrona — наработки пользователя khrona на github. Он очень сильно мне помог создать первое тестовое приложение, в частности этот пример Example of using Awesomium r148 API with GDI/WinForms/WinAPI. Правда он пытается сделать в Offscreen режиме подобие Windowed режима, из-за чего имеет скудный функционал (например не работает скрол и курсор мышки не меняется при наведении на ссылку), а так же много избыточного когда.

    answers.awesomium.com/index.html — ответы на вопросы пользователей. К сожалению далеко не все вопросы получают ответы, так что полезной информации там не очень много, но она есть
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 15

      0
      А про самое вкусное и не написано. Awesomium хорош прежде всего связыванием браузера и программы через JS функции.
        0
        Об этом и без меня достаточно написано. Я решил не писать «всё обо всем», а остановится на тонкостях работы именно с Windowed режимом, о котором очень мало информации в сети.
        0
        C++ API, mongoose, awesomium, html5/css3/js )
          +3
          Стиль в котором смешаны PascalCase, camelCase, wth_case просто офигенно хорошо читается. За слипы тоже атата. И вообще, чем оно лучше QtWebkt?
            +1
            Стиль в котором смешаны PascalCase, camelCase, wth_case просто офигенно хорошо читается.

            Это ко мне претензии или к разработчикам библиотеки?

            За слипы тоже атата.

            Действительно, лучше без слипов и каждый такт делать ненужное обновление ядра браузера. Зачем нам процессор, который простаивает? (для тех кто не понял, это была ирония, потому что слипы отлично подходят для использования в потоках и если вы так не считаете — это вопрос лишь вашего личного вероисповедания).

            И вообще, чем оно лучше QtWebkt?

            Обожаю такие вопросы. А что, он обязательно должен быть лучше, чтобы иметь право на жизнь? Да и если честно, таскать за собой Qt только лишь ради одного браузера считаю избыточным решением.
              0
              Вот и выглядит код библиотеки очень уж разляписто. Что мешает пустить себе это дело считаться в отдельном треде с низким приоритетом?

              И потом, чем оно вообще лучше обычного С++ апи, который и так есть в Вебките?
                0
                Вот и выглядит код библиотеки очень уж разляписто.

                Согласен, но честно говоря, до вашего комментария даже не обращал на это внимание.

                Что мешает пустить себе это дело считаться в отдельном треде с низким приоритетом?

                Абсолютно ничего, я же не агитирую за какой-то конкретный способ построения приложения, просто навел примеры кода, которые тем не менее нужно использовать с умом.
                  +1
                  1. Просто WebKit не имеет средств визуализации, создания окон, обработки событий и т.п. Тут корректнее сравнивать с QtWebKit, WebKitGTK+.
                  2. Chromium (Blink) = ~WebCore + V8, WebKit = WebCore + JavaScriptCore.
              0
              Поддерживается ли flash?
              0
              Раз уж упомянули Unity, есть же Coherent UI.
                0
                Будучи c#-ером я завидовал цэ++-серам так как у вас есть cef для работы с webkit-ом. А у авсомема лицензия корявая и апдейтится он нечасто (так было раньше).
                  0
                  А что вы завидовали есть же bitbucket.org/xilium/xilium.cefglue/wiki/Home, правда док там нема. Если опыта мало с C# (аки у меня), то сложновато конечно без док. Да и тот awesomium есть под C#, с лицензией там вроде все ясно, если заработал больше 100к. то будь добр заплати.
                    0
                    Дык в том и то и дело что cefglue или geckofx или другие обертки это всего лишь interop обертки, со всеми отсюда вытекающими последствиями, не говоря уже про отсутствие поддержки x64, хотя в нативном варианте она есть но в найтбилдах.
                  0
                  Напиши свою Opera с преферансом и барышнями.

                  Only users with full accounts can post comments. Log in, please.