下面是cefsimple的入口代碼,主要分成兩個部分
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Enable High-DPI support on Windows 7 or newer.
CefEnableHighDPISupport();
void* sandbox_info = NULL;
#if defined(CEF_USE_SANDBOX)
// Manage the life span of the sandbox information object. This is necessary
// for sandbox support on Windows. See cef_sandbox_win.h for complete details.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, NULL, sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
// Specify CEF global settings here.
CefSettings settings;
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
// SimpleApp implements application-level callbacks for the browser process.
// It will create the first browser instance in OnContextInitialized() after
// CEF has initialized.
CefRefPtr<SimpleApp> app(new SimpleApp);
// Initialize CEF.
CefInitialize(main_args, settings, app.get(), sandbox_info);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
進程判斷
首先是初始化進程的代碼,cef的進程結構和chromium類似,都是多進程共用代碼。所以cef提供了一些函數來檢測主進程(即browser進程)的流程和子進程的流程,以分別執行適合當前執行進程的邏輯。這段代碼如下所示。
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, NULL, sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
其中main_args用於獲取當前進程的命令行參數,因為在chromium中,進程的區分就是靠命令行參數中的--type
,如果是browser進程,則沒有--type參數,其他進程該參數的值為renderer
,gpu
等
int CefExecuteProcess(const CefMainArgs& args,
CefRefPtr<CefApp> application,
void* windows_sandbox_info) {
#if defined(OS_WIN)
#if defined(ARCH_CPU_X86_64)
DisableFMA3();
#endif
InitInstallDetails();
InitCrashReporter();
#endif
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
#if defined(OS_WIN)
command_line.ParseFromString(::GetCommandLineW());
#else
command_line.InitFromArgv(args.argc, args.argv);
#endif
// Wait for the debugger as early in process initialization as possible.
if (command_line.HasSwitch(switches::kWaitForDebugger))
base::debug::WaitForDebugger(60, true);
// If no process type is specified then it represents the browser process and
// we do nothing.
std::string process_type =
command_line.GetSwitchValueASCII(switches::kProcessType);
if (process_type.empty())
return -1;
#if defined(OS_MACOSX) || defined(OS_WIN)
if (process_type == crash_reporter::switches::kCrashpadHandler)
return RunAsCrashpadHandler(command_line);
#endif
CefMainDelegate main_delegate(application);
// Execute the secondary process.
#if defined(OS_WIN)
sandbox::SandboxInterfaceInfo sandbox_info = {0};
if (windows_sandbox_info == NULL) {
content::InitializeSandboxInfo(&sandbox_info);
windows_sandbox_info = &sandbox_info;
}
content::ContentMainParams params(&main_delegate);
params.instance = args.instance;
params.sandbox_info =
static_cast<sandbox::SandboxInterfaceInfo*>(windows_sandbox_info);
return content::ContentMain(params);
#else
content::ContentMainParams params(&main_delegate);
params.argc = args.argc;
params.argv = const_cast<const char**>(args.argv);
return content::ContentMain(params);
#endif
}
只有當非browser進程時才會執行ContentMain
函數,這也符合chromium的邏輯。ContentMain
函數是其他子進程的入口點。
而browser進程則會返回執行CefInitialize
和CefRunMessageLoop
進入browser進程的主循環。當進程需要退出時,CefRunMessageLoop
主循環退出,執行CefShutdown
進程結束。
cef的browser進程啟動
第二個部分就是browser進程的主流程,下面是拆分出來的代碼。
// Initialize CEF.
CefInitialize(main_args, settings, app.get(), sandbox_info);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
首先看CefInitialize
初始化函數,其中app.get()
即CefApp
對象指針的參數尤為重要,在文件libcef/browser/context.cc
中,提供了不少類似CefInitialize
的cef控制函數,初始化,關閉,開啟和退出消息循環等
bool CefInitialize(const CefMainArgs& args,
const CefSettings& settings,
CefRefPtr<CefApp> application,
void* windows_sandbox_info) {
#if defined(OS_WIN)
#if defined(ARCH_CPU_X86_64)
DisableFMA3();
#endif
InitInstallDetails();
InitCrashReporter();
#endif
// Return true if the global context already exists.
if (g_context)
return true;
if (settings.size != sizeof(cef_settings_t)) {
NOTREACHED() << "invalid CefSettings structure size";
return false;
}
g_browser_process = new ChromeBrowserProcessStub();
// Create the new global context object.
g_context = new CefContext();
// Initialize the global context.
return g_context->Initialize(args, settings, application,
windows_sandbox_info);
}
為的是創建一個全局的CefContext
對象,只有當CefContext
對象被創建完成后,才能進行CreateBrowser
操作,在下面的代碼中
return g_context->Initialize(args, settings, application,
windows_sandbox_info);
Initialize
的主要作用是根據content api開始做瀏覽器進程啟動准備。因為content api要涉及chromium代碼,這里就不繼續往下追溯,主要看cef的流程
bool CefContext::Initialize(const CefMainArgs& args,
const CefSettings& settings,
CefRefPtr<CefApp> application,
void* windows_sandbox_info) {
...
main_delegate_.reset(new CefMainDelegate(application));
...
if (CEF_CURRENTLY_ON_UIT()) {
OnContextInitialized();
} else {
// Continue initialization on the UI thread.
CEF_POST_TASK(CEF_UIT, base::Bind(&CefContext::OnContextInitialized,
base::Unretained(this)));
}
...
CefApp的引用會一直傳遞到這里,當初始化的流程結束的時候(OnContextInitialized
),就到了CefApp發揮作用的時候了。
其中main_delegate_
作為一個代理,主要代理總體管理的相關功能,包括資源初始化,瀏覽器關閉等。那么CefApp對它有什么用呢?主要是為了創建一個全局的CefContentClient
對象。
CefMainDelegate::CefMainDelegate(CefRefPtr<CefApp> application)
: content_client_(application) {
然后當CefContext
初始化完成后,就會從這個全局的CefContentClient
對象中獲取CefApp
的引用。而獲得引用之后是為了獲得handler,以完成對應的回調。
void CefContext::OnContextInitialized() {
CEF_REQUIRE_UIT();
static_cast<ChromeBrowserProcessStub*>(g_browser_process)
->OnContextInitialized();
#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
CefWidevineLoader::GetInstance()->OnContextInitialized();
#endif
// Notify the handler.
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
if (app.get()) {
CefRefPtr<CefBrowserProcessHandler> handler =
app->GetBrowserProcessHandler();
if (handler.get())
handler->OnContextInitialized();
}
}
此處主要獲取的是browser process handler,這個handler的主要目的是監控並觸發browser進程生命周期內各個關鍵時機的回調。
如果多少用過cef,會知道browser進程中很重要的一個函數就是CreateBrowser
,cefsimple這個demo中,使用在了OnContextInitialized
函數中,這說明,當cef上下文初始化完成之后就可以創建瀏覽功能了。
void SimpleApp::OnContextInitialized() {
CEF_REQUIRE_UI_THREAD();
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::GetGlobalCommandLine();
#if defined(OS_WIN) || defined(OS_LINUX)
// Create the browser using the Views framework if "--use-views" is specified
// via the command-line. Otherwise, create the browser using the native
// platform framework. The Views framework is currently only supported on
// Windows and Linux.
const bool use_views = command_line->HasSwitch("use-views");
#else
const bool use_views = false;
#endif
// SimpleHandler implements browser-level callbacks.
CefRefPtr<SimpleHandler> handler(new SimpleHandler(use_views));
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
std::string url;
// Check if a "--url=" value was provided via the command-line. If so, use
// that instead of the default URL.
url = command_line->GetSwitchValue("url");
if (url.empty())
url = "http://www.google.com";
if (use_views) {
// Create the BrowserView.
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
handler, url, browser_settings, NULL, NULL, NULL);
// Create the Window. It will show itself after creation.
CefWindow::CreateTopLevelWindow(new SimpleWindowDelegate(browser_view));
} else {
// Information used when creating the native window.
CefWindowInfo window_info;
#if defined(OS_WIN)
// On Windows we need to specify certain flags that will be passed to
// CreateWindowEx().
window_info.SetAsPopup(NULL, "cefsimple");
#endif
// Create the first browser window.
CefBrowserHost::CreateBrowser(window_info, handler, url, browser_settings,
NULL, NULL);
}
}
而對應的ShutdownBrowser
則是在CefContext::Shutdown()
中被調用。並不需要開發者過多操作。