nim_duilib/ui_components/cef_control/manager/cef_manager.cpp
jiajia_deng ad9a6b3edc Modified the namespace of the UI component
Signed-off-by: jiajia_deng <2894220@gmail.com>
2019-09-22 11:08:20 +08:00

222 lines
6.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "stdafx.h"
#include "cef_manager.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/base/cef_bind.h"
#include "cef_control/app/client_app.h"
#include "cef_control/handler/browser_handler.h"
namespace nim_comp
{
BOOL CefMessageLoopDispatcher::IsIdleMessage(const MSG* pMsg)
{
switch (pMsg->message)
{
case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
case WM_PAINT:
return FALSE;
}
return TRUE;
}
bool CefMessageLoopDispatcher::Dispatch(const MSG &msg)
{
static BOOL bDoIdle = TRUE;
TranslateMessage(&msg);
DispatchMessage(&msg);
if (IsIdleMessage(&msg))
{
bDoIdle = TRUE;
}
while (bDoIdle && !::PeekMessage(const_cast<MSG*>(&msg), NULL, 0, 0, PM_NOREMOVE))
{
CefDoMessageLoopWork();
bDoIdle = FALSE;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////
// 发现一个非常奇葩的bug离屏渲染+多线程消息循环模式下,在浏览器对象上右击弹出菜单,是无法正常关闭的
// 翻cef源码后发现菜单是用TrackPopupMenu函数创建的在MSDN资料上查看后发现调用TrackPopupMenu前
// 需要给其父窗口调用SetForegroundWindow。但是在cef源码中没有调用
// 最终翻cef源码后得到的解决方法是在cef的UI线程创建一个窗口这个窗体的父窗口必须是在主程序UI线程创建的
// 这样操作之后就不会出现菜单无法关闭的bug了虽然不知道为什么但是bug解决了
void FixContextMenuBug(HWND hwnd)
{
CreateWindow(L"Static", L"", WS_CHILD, 0, 0, 0, 0, hwnd, NULL, NULL, NULL);
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////////
CefManager::CefManager()
{
browser_count_ = 0;
is_enable_offset_render_ = true;
}
void CefManager::AddCefDllToPath()
{
#if !defined(SUPPORT_CEF)
return;
#endif
TCHAR path_envirom[4096] = { 0 };
GetEnvironmentVariable(L"path", path_envirom, 4096);
std::wstring cef_path = nbase::win32::GetCurrentModuleDirectory();
#ifdef _DEBUG
//cef_path += L"cef_debug"; // 现在即使在debug模式下也使用cef release版本的dll为了屏蔽掉cef退出时的中断如果不需要调试cef的功能不需要使用debug版本的dll
cef_path += L"cef";
#else
cef_path += L"cef";
#endif
if (!nbase::FilePathIsExist(cef_path, true))
{
MessageBox(NULL, L"请解压Cef.rar压缩包", L"提示", MB_OK);
exit(0);
}
std::wstring new_envirom(cef_path);
new_envirom.append(L";").append(path_envirom);
SetEnvironmentVariable(L"path", new_envirom.c_str());
// 解决播放flash弹出黑框的问题
// https://blog.csdn.net/zhuhongshu/article/details/77482985
std::wstring cmd_path = cef_path + L"\\dummy_cmd.exe";
SetEnvironmentVariable(L"ComSpec", cmd_path.c_str());
}
// Cef的初始化接口同时备注了使用各个版本的Cef时遇到的各种坑
// Cef1916版本较稳定各个功能使用正常但是某些在debug模式网页打开时会出中断警告但并不是错误可能是因为对新html标准支持不够但是在release模式下正常使用
// Cef2357版本无法使用当程序处理重定向信息并且重新加载页面后渲染进程会崩掉
// Cef2526、2623版本对各种新页面都支持唯一的坑就是debug模式在多线程消息循环开启下程序退出时会中断但是release模式正常。
// (PS:如果开发者不使用负责Cef功能的开发可以切换到release模式的cef dll文件这样即使在deubg下也不会报错修改AddCefDllToPath代码可以切换到release目录)
bool CefManager::Initialize(const std::wstring& app_data_dir, CefSettings &settings, bool is_enable_offset_render /*= true*/)
{
#if !defined(SUPPORT_CEF)
return true;
#endif
is_enable_offset_render_ = is_enable_offset_render;
CefMainArgs main_args(GetModuleHandle(NULL));
CefRefPtr<ClientApp> app(new ClientApp);
// 如果是在子进程中调用会堵塞直到子进程退出并且exit_code返回大于等于0
// 如果在Browser进程中调用则立即返回-1
int exit_code = CefExecuteProcess(main_args, app.get(), NULL);
if (exit_code >= 0)
return false;
GetCefSetting(app_data_dir, settings);
bool bRet = CefInitialize(main_args, settings, app.get(), NULL);
if (is_enable_offset_render_)
{
HWND hwnd = CreateWindow(L"Static", L"", WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
CefPostTask(TID_UI, base::Bind(&FixContextMenuBug, hwnd));
}
return bRet;
}
void CefManager::UnInitialize()
{
#if !defined(SUPPORT_CEF)
return;
#endif
CefShutdown();
}
bool CefManager::IsEnableOffsetRender() const
{
return is_enable_offset_render_;
}
nbase::Dispatcher* CefManager::GetMessageDispatcher()
{
return &message_dispatcher_;
}
void CefManager::AddBrowserCount()
{
browser_count_++;
}
void CefManager::SubBrowserCount()
{
browser_count_--;
ASSERT(browser_count_ >= 0);
}
int CefManager::GetBrowserCount()
{
return browser_count_;
}
void CefManager::PostQuitMessage(int nExitCode)
{
#if !defined(SUPPORT_CEF)
::PostQuitMessage(nExitCode);
return;
#endif
// 当我们需要结束进程时,千万不要直接调用::PostQuitMessage这是可能还有浏览器对象没有销毁
// 应该等所有浏览器对象都销毁后再调用::PostQuitMessage
if (browser_count_ == 0)
{
nbase::ThreadManager::PostTask(kThreadUI, [nExitCode]()
{
::PostQuitMessage(nExitCode);
});
}
else
{
auto cb = [nExitCode]()
{
CefManager::GetInstance()->PostQuitMessage(nExitCode);
};
nbase::ThreadManager::PostDelayedTask(kThreadUI, cb, nbase::TimeDelta::FromMilliseconds(500));
}
}
void CefManager::GetCefSetting(const std::wstring& app_data_dir, CefSettings &settings)
{
if (false == nbase::FilePathIsExist(app_data_dir, true))
nbase::CreateDirectory(app_data_dir);
settings.no_sandbox = true;
// 设置localstorage不要在路径末尾加"\\",否则运行时会报错
CefString(&settings.cache_path) = app_data_dir + L"CefLocalStorage";
// 设置debug log文件位置
CefString(&settings.log_file) = app_data_dir + L"cef.log";
// 调试模型下使用单进程但是千万不要在release发布版本中使用官方已经不推荐使用单进程模式
// cef1916版本debug模式:在单进程模式下程序退出时会触发中断
#ifdef _DEBUG
settings.single_process = true;
#else
settings.single_process = false;
#endif
// cef2623、2526版本debug模式:在使用multi_threaded_message_loop时退出程序会触发中断
// 加入disable-extensions参数可以修复这个问题但是会导致一些页面打开时报错
// 开启Cef多线程消息循环兼容nbase库消息循环
settings.multi_threaded_message_loop = true;
// 开启离屏渲染
settings.windowless_rendering_enabled = is_enable_offset_render_;
}
}