Другое

Полное руководство: Отключение браузерных функций в CEF для игрового лаунчера

Узнайте, как полностью отключить браузерные функции в CEF для кастомного игрового лаунчера. Блокировка DevTools, всплывающих окон, контекстных меню и многого другого в нашем подробном руководстве.

Как полностью отключить браузерные возможности в кастомном игровом лаунчере на основе CEF?

Я разрабатываю кастомный игровой лаунчер с использованием CEF (Chromium Embedded Framework) в Windows с целью использования CEF исключительно как рендерера UI, а не как веб-браузера.

Текущие проблемы:
Несмотря на отключение большинства браузерных опций, пользователи все еще могут вызывать браузерное поведение:

  • Ctrl + Shift + I → открытие DevTools
  • window.open() → новые пустые всплывающие окна
  • Правый клик → стандартное контекстное меню
  • Наведение на ссылки → отображение URL в строке состояния
  • Выделяемый текст, доступ к буферу обмена и т.д.

Что я уже пробовал:

Настройки командной строки:

cpp
settings.windowless_rendering_enabled = true;
settings.command_line_args_disabled = true;
cmd->AppendSwitch("disable-dev-tools");
cmd->AppendSwitch("disable-extensions");
cmd->AppendSwitch("no-sandbox");
cmd->AppendSwitch("disable-popup-blocking");

Обработчики подкласса CefClient (MiniClient):

  • OnBeforePopup → возврат true
  • OnOpenURLFromTab → блокировка неактивных вкладок
  • OnBeforeBrowse → блокировка chrome://, devtools://
  • OnBeforeDevToolsPopup → установка *use_default_window = false
  • OnChromeCommand(IDC_DEV_TOOLS) → возврат true

Дополнительные попытки:

  • Переопределение всех обработчиков (CefLifeSpanHandler, CefRequestHandler, CefContextMenuHandler и т.д.)
  • Возврат true в OnBeforePopup
  • Использование windowInfo.SetAsWindowless(nullptr)
  • Отключение всего через OnBeforeCommandLineProcessing

Несмотря на эти усилия, новые всплывающие окна все еще появляются время от времени (пустое окно), а DevTools иногда обходит обработчик.

Что я хочу:
Я хочу, чтобы мой экземпляр CEF вел себя как встроенный UI-движок (для моего игрового лаунчера), а не как браузер вообще — без DevTools, без контекстных меню, без новых окон, без правого клика, без горячих клавиш и т.д. По сути, CEF должен вести себя как изолированный WebView — полностью неинтерактивный вне логики UI моего приложения.

Конкретные вопросы:

  1. Существует ли надежный способ отключить все браузерные поведения (всплывающие окна, DevTools, контекстные меню и т.д.) в CEF 141?
  2. Можно ли как-то включить или использовать chrome_runtime для получения более строгого контроля над созданием всплывающих окон/DevTools?
  3. Возможно ли скомпилировать CEF в истинном режиме “только для UI” (без chrome://, без внутренних браузерных возможностей)?

Любые рекомендации или минимальный пример, показывающий, как превратить CEF в чистый рендерер, будут очень полезны.

Чтобы полностью отключить браузерные функции в кастомном игровом лаунчере на основе CEF, необходимо реализовать многоуровневый подход, сочетающий параметры командной строки, переопределение обработчиков и правильную конфигурацию Chrome Runtime. Ключевая задача - заблокировать всю браузерную функциональность как на уровне инициализации, так и во время выполнения, сохранив возможности рендеринга пользовательского интерфейса.

Содержание


Полная стратегия реализации

Чтобы преобразовать CEF в чистый рендерер UI без браузерных возможностей, необходим комплексный подход, учитывающий все способы, которыми пользователи могут запустить браузерное поведение. На основе результатов исследований наиболее надежный метод сочетает несколько техник:

  1. Параметры командной строки - Отключают функции на уровне инициализации
  2. Переопределение обработчиков - Блокируют события во время выполнения
  3. Конфигурация Chrome Runtime - Использует более строгий режим браузера
  4. Фильтрация событий - Перехватывают и блокируют все браузерные события

Исследования показывают, что простое возвращение true в различных обработчиках не всегда достаточно, так как некоторые браузерные функции могут обойти эти проверки. Необходимо более агрессивно блокировать функциональность.


Конфигурация командной строки

Начните с комплексного набора параметров командной строки, которые отключают браузерные функции на уровне инициализации CEF:

cpp
CefSettings settings;
settings.command_line_args_disabled = false; // Разрешить пользовательские параметры
settings.chrome_runtime = false; // Использовать устаревший режим для большего контроля

CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->AppendSwitch("disable-devtools");
command_line->AppendSwitch("disable-extensions");
command_line->AppendSwitch("disable-popup-blocking");
command_line->AppendSwitch("disable-infobars");
command_line->AppendSwitch("disable-features");
command_line->AppendSwitchValue("disable-features", "TranslateUI,MediaRouter,Extensions");
command_line->AppendSwitch("no-sandbox");
command_line->AppendSwitch("disable-gpu");
command_line->AppendSwitch("disable-background-timer-throttling");
command_line->AppendSwitch("disable-renderer-backgrounding");
command_line->AppendSwitch("disable-ipc-flooding-protection");
command_line->AppendSwitch("disable-restore-session-state");
command_line->AppendSwitch("disable-session-crashed-bubble");
command_line->AppendSwitch("disable-sync");
command_line->AppendSwitch("disable-web-security");
command_line->AppendSwitch("allow-file-access");
command_line->AppendSwitch("allow-file-access-from-files");
command_line->AppendSwitch("disable-ipc-flooding-protection");

Согласно документации Chromium Command Line Switches, эти параметры эффективно отключают большинство браузерных функций, сохраняя возможности рендеринга.


Переопределение обработчиков и блокировка событий

Создайте комплексные реализации обработчиков, которые блокируют все браузерное поведение:

Обработчик контекстного меню

cpp
class MyContextMenuHandler : public CefContextMenuHandler {
public:
    MyContextMenuHandler() {}
    ~MyContextMenuHandler() override {}

    void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame,
                            CefRefPtr<CefContextMenuParams> params,
                            CefRefPtr<CefMenuModel> model) override {
        // Очистить все элементы контекстного меню по умолчанию
        model->Clear();
        
        // Добавить только базовые функции редактирования текста, если необходимо
        if (params->GetEditStateFlags() & CM_EDITFLAG_CAN_CUT) {
            model->AddItem(CefContextMenuHandler::IDM_CUT, L"Вырезать");
        }
        if (params->GetEditStateFlags() & CM_EDITFLAG_CAN_COPY) {
            model->AddItem(CefContextMenuHandler::IDM_COPY, L"Копировать");
        }
        if (params->GetEditStateFlags() & CM_EDITFLAG_CAN_PASTE) {
            model->AddItem(CefContextMenuHandler::IDM_PASTE, L"Вставить");
        }
    }
};

Как показано в обсуждении на форуме CEF, очистка модели контекстного меню эффективно отключает контекстное меню браузера при правом клике.

Обработчик жизненного цикла

cpp
class MyLifeSpanHandler : public CefLifeSpanHandler {
public:
    MyLifeSpanHandler() {}
    ~MyLifeSpanHandler() override {}

    bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
                       CefRefPtr<CefFrame> frame,
                       const CefString& target_url,
                       const CefString& target_frame_name,
                       CefLifeSpanHandler::WindowOpenDisposition target_disposition,
                       bool user_gesture,
                       const CefPopupFeatures& popupFeatures,
                       CefWindowInfo& windowInfo,
                       CefRefPtr<CefClient>& client,
                       CefBrowserSettings& settings,
                       CefRefPtr<CefDictionaryValue>& extra_info,
                       bool* no_javascript_access) override {
        // Полностью заблокировать все всплывающие окна
        return true;
    }
    
    void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
        // Убедиться, что окна DevTools не создаются
        if (browser->GetHost()->HasDevTools()) {
            browser->GetHost()->CloseDevTools();
        }
    }
};

Обработчик команд

cpp
class MyCommandHandler : public CefCommandHandler {
public:
    MyCommandHandler() {}
    ~MyCommandHandler() override {}

    bool OnChromeCommand(CefRefPtr<CefBrowser> browser,
                        int command_id) override {
        // Блокировать DevTools и другие команды Chrome
        if (command_id == IDC_DEV_TOOLS ||
            command_id == IDC_TASK_MANAGER ||
            command_id == IDC_CONTENT_CONTEXT_INSPECTELEMENT) {
            return true; // Обработано (заблокировано)
        }
        return false; // Не обработано
    }
};

Документация CEF подтверждает, что возвращение true из OnChromeCommand для IDC_DEV_TOOLS блокирует всплывающие окна DevTools.


Режим Chrome Runtime

Для более строгого контроля рассмотрите использование режима Chrome Runtime:

cpp
CefSettings settings;
settings.chrome_runtime = false; // Установите в false для большего контроля

Как отмечено в обсуждении на форуме CEF, устаревший режим (chrome_runtime = false) обеспечивает более детальный контроль над браузерными функциями по сравнению с режимом Chrome Runtime.

Однако, если вы хотите попробовать режим Chrome Runtime для потенциально лучшей блокировки всплывающих окон:

cpp
CefSettings settings;
settings.chrome_runtime = true;

// Дополнительные параметры для Chrome Runtime
command_line->AppendSwitch("disable-features");
command_line->AppendSwitchValue("disable-features", "TranslateUI,MediaRouter,Extensions,ChimeSdkMeetings");
command_line->AppendSwitch("disable-background-sync");
command_line->AppendSwitch("disable-background-timer-throttling");
command_line->AppendSwitch("disable-backgrounding-occluded-windows");
command_line->AppendSwitch("disable-client-side-phishing-detection");
command_line->AppendSwitch("disable-component-update");
command_line->AppendSwitch("disable-default-apps");
command_line->AppendSwitch("disable-domain-reliability");
command_line->AppendSwitch("disable-extensions");
command_line->AppendSwitch("disable-features");
command_line->AppendSwitch("disable-hang-monitor");
command_line->AppendSwitch("disable-ipc-flooding-protection");
command_line->AppendSwitch("disable-popup-blocking");
command_line->AppendSwitch("disable-prompt-on-repost");
command_line->AppendSwitch("disable-renderer-backgrounding");
command_line->AppendSwitch("disable-sync");
command_line->AppendSwitch("disable-web-security");

Настройка на этапе сборки

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

  1. Редактировать файлы *_switches.cc:

    • base/base_switches.cc
    • cef/libcef/common/cef_switches.cc
    • chrome/common/chrome_switches.cc
    • content/public/common/content_switches.cc
  2. Компилировать с минимальным набором функций, изменив конфигурацию сборки для исключения:

    • Функциональности печати
    • Медиа возможностей
    • DevTools
    • Специфичных для Chrome функций

Обсуждение на Stack Overflow упоминает, что этот подход может снизить использование памяти примерно на 100 МБ и полностью устранить нежелательные функции.


Расширенные техники блокировки

Обработчик запросов для фильтрации URL

cpp
class MyRequestHandler : public CefRequestHandler {
public:
    MyRequestHandler() {}
    ~MyRequestHandler() override {}

    bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
                       CefRefPtr<CefFrame> frame,
                       CefRefPtr<CefRequest> request,
                       bool user_gesture,
                       bool is_redirect) override {
        CefString url = request->GetURL();
        // Блокировать внутренние URL браузера
        if (url.find("chrome://") != CefString::npos ||
            url.find("devtools://") != CefString::npos ||
            url.find("edge://") != CefString::npos) {
            return true; // Отменить навигацию
        }
        return false;
    }
};

Обработчик отображения для элементов UI

cpp
class MyDisplayHandler : public CefDisplayHandler {
public:
    MyDisplayHandler() {}
    ~MyDisplayHandler() override {}

    void OnStatusMessage(CefRefPtr<CefBrowser> browser,
                        const CefString& value) override {
        // Блокировать текст в строке состояния (URL при наведении)
    }
    
    void OnTooltip(CefRefPtr<CefBrowser> browser,
                  const CefString& text) override {
        // Блокировать всплывающие подсказки
    }
};

Обработчик фокуса для горячих клавиш

cpp
class MyFocusHandler : public CefFocusHandler {
public:
    MyFocusHandler() {}
    ~MyFocusHandler() override {}

    bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
                                 CefProcessId source_process,
                                 CefRefPtr<CefProcessMessage> message) override {
        // Блокировать горячие клавиши DevTools (Ctrl+Shift+I)
        if (message->GetName() == "DevToolsOpener") {
            return true; // Заблокировать
        }
        return false;
    }
};

Минимальный рабочий пример

Вот полный минимальный пример, который сочетает все техники:

cpp
#include "include/cef_app.h"
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
#include "include/cef_frame.h"
#include "include/cef_parser.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"

class MyApp : public CefApp, public CefBrowserProcessHandler {
public:
    MyApp() {}
    ~MyApp() override {}

    CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override {
        return this;
    }

    void OnContextInitialized() override {
        CefRefPtr<CefCommandLine> command_line =
            CefCommandLine::CreateCommandLine();

        // Комплексные параметры командной строки
        command_line->AppendSwitch("disable-devtools");
        command_line->AppendSwitch("disable-extensions");
        command_line->AppendSwitch("disable-popup-blocking");
        command_line->AppendSwitch("disable-infobars");
        command_line->AppendSwitch("disable-features");
        command_line->AppendSwitchValue("disable-features", "TranslateUI,MediaRouter,Extensions");
        command_line->AppendSwitch("no-sandbox");
        command_line->AppendSwitch("disable-gpu");
        command_line->AppendSwitch("disable-background-timer-throttling");
        command_line->AppendSwitch("disable-renderer-backgrounding");
        command_line->AppendSwitch("disable-ipc-flooding-protection");
        command_line->AppendSwitch("disable-restore-session-state");
        command_line->AppendSwitch("disable-session-crashed-bubble");

        CefBrowserSettings browser_settings;
        browser_settings.windowless_rendering_enabled = false;
        browser_settings.web_security = STATE_DISABLED;

        CefWindowInfo window_info;
        window_info.SetAsPopup(nullptr, "MyGameLauncher");

        CefRefPtr<MyClient> client = new MyClient();
        CefBrowserHost::CreateBrowser(window_info, client,
                                    "about:blank", browser_settings,
                                    nullptr, command_line);
    }

private:
    IMPLEMENT_REFCOUNTING(MyApp);
};

class MyClient : public CefClient,
                 public CefLifeSpanHandler,
                 public CefContextMenuHandler,
                 public CefCommandHandler,
                 public CefRequestHandler,
                 public CefDisplayHandler {
public:
    MyClient() {}
    ~MyClient() override {}

    // Методы CefClient
    CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
    CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override { return this; }
    CefRefPtr<CefCommandHandler> GetCommandHandler() override { return this; }
    CefRefPtr<CefRequestHandler> GetRequestHandler() override { return this; }
    CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }

    // Методы CefLifeSpanHandler
    bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
                       CefRefPtr<CefFrame> frame,
                       const CefString& target_url,
                       const CefString& target_frame_name,
                       CefLifeSpanHandler::WindowOpenDisposition target_disposition,
                       bool user_gesture,
                       const CefPopupFeatures& popupFeatures,
                       CefWindowInfo& windowInfo,
                       CefRefPtr<CefClient>& client,
                       CefBrowserSettings& settings,
                       CefRefPtr<CefDictionaryValue>& extra_info,
                       bool* no_javascript_access) override {
        return true; // Заблокировать все всплывающие окна
    }

    // Методы CefContextMenuHandler
    void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame,
                            CefRefPtr<CefContextMenuParams> params,
                            CefRefPtr<CefMenuModel> model) override {
        model->Clear(); // Удалить все элементы контекстного меню
    }

    // Методы CefCommandHandler
    bool OnChromeCommand(CefRefPtr<CefBrowser> browser,
                        int command_id) override {
        // Блокировать DevTools и другие команды Chrome
        return (command_id == IDC_DEV_TOOLS ||
                command_id == IDC_TASK_MANAGER ||
                command_id == IDC_CONTENT_CONTEXT_INSPECTELEMENT);
    }

    // Методы CefRequestHandler
    bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
                       CefRefPtr<CefFrame> frame,
                       CefRefPtr<CefRequest> request,
                       bool user_gesture,
                       bool is_redirect) override {
        CefString url = request->GetURL();
        // Блокировать внутренние URL браузера
        return (url.find("chrome://") != CefString::npos ||
                url.find("devtools://") != CefString::npos);
    }

    // Методы CefDisplayHandler
    void OnStatusMessage(CefRefPtr<CefBrowser> browser,
                        const CefString& value) override {
        // Блокировать текст в строке состояния
    }

private:
    IMPLEMENT_REFCOUNTING(MyClient);
};

int main() {
    CefMainArgs main_args;
    CefRefPtr<MyApp> app(new MyApp());
    
    // Инициализировать CEF с настройками
    CefSettings settings;
    settings.command_line_args_disabled = false;
    settings.chrome_runtime = false;
    settings.windowless_rendering_enabled = false;
    
    CefInitialize(main_args, settings, app.get());
    CefRunMessageLoop();
    CefShutdown();
    
    return 0;
}

Эта комплексная реализация решает все упомянутые в вашем вопросе проблемы путем:

  1. Блокировки всплывающих окон через возврат true в OnBeforePopup
  2. Отключения DevTools через параметры командной строки и OnChromeCommand
  3. Удаления контекстных меню путем очистки модели меню
  4. Блокировки сообщений в строке состояния через обработчик отображения
  5. Фильтрации внутренних URL браузера через обработчик запросов

Заключение

На основе результатов исследований, полное отключение браузерных функций в CEF требует многоуровневого подхода. Вот ключевые выводы:

  1. Используйте комплексные параметры командной строки для отключения функций на уровне инициализации, включая disable-devtools, disable-extensions и различные параметры disable-features.

  2. Реализуйте все соответствующие обработчики и переопределите их методы для блокировки нежелательного поведения - особенно OnBeforePopup, OnBeforeContextMenu и OnChromeCommand.

  3. Рассмотрите настройки режима Chrome Runtime (chrome_runtime = false) для более детального контроля, хотя устаревший режим может обеспечивать лучшие возможности блокировки.

  4. Для максимального контроля рассмотрите возможность настройки сборки CEF для полного удаления нежелательных функций, хотя это требует больших усилий по разработке.

  5. Сочетайте несколько техник - ни один单一 подход не заблокирует все браузерное поведение, но комплексная реализация с использованием параметров командной строки, переопределения обработчиков и блокировки во время выполнения должна достичь вашей цели по превращению CEF в чистый рендерер UI.

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

Источники

  1. Как полностью отключить браузерные функции в кастомном игровом лаунчере на основе CEF?
  2. Chromium Embedded Framework (CEF): Справочник класса CefLifeSpanHandler
  3. Отключить контекстное меню в Chromium Embedded 3 (DCEF3)
  4. Форум CEF • Отключить контекстное меню CEF и разрешить контекстное меню приложения
  5. Параметры командной строки Chromium
  6. Windows - Настроить сборку CEF и удалить неиспользуемые функции Chromium
  7. Chromium Embedded Framework (CEF): Справочник структуры cef_settings_t
  8. Форум CEF • Полностью блокировать окна UI Chrome в режиме Chrome Runtime?
Авторы
Проверено модерацией
Модерация