- Allow execution of CEF using the current application's message loop.
tests/cefclient:
- Support running of the cefclient application using a single message loop via the TEST_SINGLE_THREADED_MESSAGE_LOOP define.

Issue #1, Suggested implementation by: vridosh

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@13 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2009-01-29 18:53:17 +00:00
parent 9d3cd36b4b
commit 15f6c270fa
4 changed files with 150 additions and 63 deletions

View File

@ -39,14 +39,21 @@
// This function should only be called once when the application is started. // This function should only be called once when the application is started.
// Create the thread to host the UI message loop. A return value of true // Create the thread to host the UI message loop. A return value of true
// indicates that it succeeded and false indicates that it failed. // indicates that it succeeded and false indicates that it failed. Set
bool CefInitialize(); // |multi_threaded_message_loop| to true to have the message loop run in
// a separate thread. If |multi_threaded_message_loop| is false than
// the CefDoMessageLoopWork() function must be called from your message loop.
bool CefInitialize(bool multi_threaded_message_loop);
// This function should only be called once before the application exits. // This function should only be called once before the application exits.
// Shut down the thread hosting the UI message loop and destroy any created // Shut down the thread hosting the UI message loop and destroy any created
// windows. // windows.
void CefShutdown(); void CefShutdown();
// Perform message loop processing. Has no affect if the browser UI loop is
// running in a separate thread.
void CefDoMessageLoopWork();
// Interface defining the the reference count implementation methods. All // Interface defining the the reference count implementation methods. All
// framework classes must implement the CefBase class. // framework classes must implement the CefBase class.

View File

@ -10,7 +10,6 @@
#include "browser_request_context.h" #include "browser_request_context.h"
#include "../include/cef_nplugin.h" #include "../include/cef_nplugin.h"
#include "base/at_exit.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/icu_util.h" #include "base/icu_util.h"
#include "base/path_service.h" #include "base/path_service.h"
@ -33,7 +32,30 @@ static const char* kStatsFilePrefix = "libcef_";
static int kStatsFileThreads = 20; static int kStatsFileThreads = 20;
static int kStatsFileCounters = 200; static int kStatsFileCounters = 200;
bool CefInitialize()
// Class used to process events on the current message loop.
class CefMessageLoopForUI : public MessageLoopForUI
{
typedef MessageLoopForUI inherited;
public:
CefMessageLoopForUI()
{
}
virtual bool DoIdleWork() {
bool valueToRet = inherited::DoIdleWork();
pump_->Quit();
return valueToRet;
}
void DoMessageLoopIteration() {
Run(NULL);
}
};
bool CefInitialize(bool multi_threaded_message_loop)
{ {
// Return true if the context is already initialized // Return true if the context is already initialized
if(_Context.get()) if(_Context.get())
@ -42,7 +64,7 @@ bool CefInitialize()
// Create the new global context object // Create the new global context object
_Context = new CefContext(); _Context = new CefContext();
// Initialize the glboal context // Initialize the glboal context
return _Context->Initialize(); return _Context->Initialize(multi_threaded_message_loop);
} }
void CefShutdown() void CefShutdown()
@ -57,6 +79,13 @@ void CefShutdown()
_Context = NULL; _Context = NULL;
} }
void CefDoMessageLoopWork()
{
if(!_Context->RunningOnUIThread())
return;
((CefMessageLoopForUI*)CefMessageLoopForUI::current())->DoMessageLoopIteration();
}
bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info) bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info)
{ {
if(!_Context.get()) if(!_Context.get())
@ -124,10 +153,8 @@ StringPiece NetResourceProvider(int key) {
return GetRawDataResource(::GetModuleHandle(NULL), key); return GetRawDataResource(::GetModuleHandle(NULL), key);
} }
DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) bool CefContext::DoInitialize()
{ {
CefContext *pContext = static_cast<CefContext*>(lpParam);
HRESULT res; HRESULT res;
// Initialize common controls // Initialize common controls
@ -142,16 +169,9 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam)
res = OleInitialize(NULL); res = OleInitialize(NULL);
DCHECK(SUCCEEDED(res)); DCHECK(SUCCEEDED(res));
// Instantiate the AtExitManager to avoid asserts and possible memory leaks.
base::AtExitManager at_exit_manager;
// Initialize the global CommandLine object. // Initialize the global CommandLine object.
CommandLine::Init(0, NULL); CommandLine::Init(0, NULL);
// Instantiate the message loop for this thread.
MessageLoopForUI main_message_loop;
pContext->SetMessageLoopForUI(&main_message_loop);
// Initializing with a default context, which means no on-disk cookie DB, // Initializing with a default context, which means no on-disk cookie DB,
// and no support for directory listings. // and no support for directory listings.
// TODO(cef): Either disable caching or send the cache files to a reasonable // TODO(cef): Either disable caching or send the cache files to a reasonable
@ -166,7 +186,7 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam)
if(!ret) { if(!ret) {
MessageBox(NULL, L"Failed to load the required icudt38 library", MessageBox(NULL, L"Failed to load the required icudt38 library",
L"CEF Initialization Error", MB_ICONERROR | MB_OK); L"CEF Initialization Error", MB_ICONERROR | MB_OK);
return 1; return false;
} }
// Config the network module so it has access to a limited set of resources. // Config the network module so it has access to a limited set of resources.
@ -174,19 +194,17 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam)
// Load and initialize the stats table. Attempt to construct a somewhat // Load and initialize the stats table. Attempt to construct a somewhat
// unique name to isolate separate instances from each other. // unique name to isolate separate instances from each other.
StatsTable *table = new StatsTable( statstable_ = new StatsTable(
kStatsFilePrefix + Uint64ToString(base::RandUint64()), kStatsFilePrefix + Uint64ToString(base::RandUint64()),
kStatsFileThreads, kStatsFileThreads,
kStatsFileCounters); kStatsFileCounters);
StatsTable::set_current(table); StatsTable::set_current(statstable_);
// Notify the context that initialization is complete so that the return true;
// Initialize() function can return. }
pContext->NotifyEvent();
// Execute the message loop that will run until a quit task is received.
MessageLoop::current()->Run();
void CefContext::DoUninitialize()
{
// Flush any remaining messages. This ensures that any accumulated // Flush any remaining messages. This ensures that any accumulated
// Task objects get destroyed before we exit, which avoids noise in // Task objects get destroyed before we exit, which avoids noise in
// purify leak-test results. // purify leak-test results.
@ -196,13 +214,35 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam)
// Tear down shared StatsTable; prevents unit_tests from leaking it. // Tear down shared StatsTable; prevents unit_tests from leaking it.
StatsTable::set_current(NULL); StatsTable::set_current(NULL);
delete table; delete statstable_;
statstable_ = NULL;
// Uninitialize COM stuff // Uninitialize COM stuff
OleUninitialize(); OleUninitialize();
// Uninitialize common controls // Uninitialize common controls
CoUninitialize(); CoUninitialize();
}
DWORD WINAPI ThreadHandlerUI(LPVOID lpParam)
{
CefContext *pContext = static_cast<CefContext*>(lpParam);
if (!pContext->DoInitialize())
return 1;
// Instantiate the message loop for this thread.
MessageLoopForUI main_message_loop;
pContext->SetMessageLoopForUI(&main_message_loop);
// Notify the context that initialization is complete so that the
// Initialize() function can return.
pContext->NotifyEvent();
// Execute the message loop that will run until a quit task is received.
MessageLoop::current()->Run();
pContext->DoUninitialize();
return 0; return 0;
} }
@ -222,9 +262,11 @@ CefContext::~CefContext()
{ {
// Just in case CefShutdown() isn't called // Just in case CefShutdown() isn't called
Shutdown(); Shutdown();
DoUninitialize();
} }
bool CefContext::Initialize() bool CefContext::Initialize(bool multi_threaded_message_loop)
{ {
bool initialized = false, intransition = false; bool initialized = false, intransition = false;
@ -294,15 +336,24 @@ bool CefContext::Initialize()
webprefs_->java_enabled = true; webprefs_->java_enabled = true;
webprefs_->allow_scripts_to_close_windows = false; webprefs_->allow_scripts_to_close_windows = false;
// Event that will be used to signal thread setup completion. Start if (multi_threaded_message_loop) {
// in non-signaled mode so that the event will block. // Event that will be used to signal thread setup completion. Start
heventui_ = CreateEvent(NULL, TRUE, FALSE, NULL); // in non-signaled mode so that the event will block.
DCHECK(heventui_ != NULL); heventui_ = CreateEvent(NULL, TRUE, FALSE, NULL);
DCHECK(heventui_ != NULL);
// Thread that hosts the UI loop
hthreadui_ = CreateThread( // Thread that hosts the UI loop
NULL, 0, ThreadHandlerUI, this, 0, &idthreadui_); hthreadui_ = CreateThread(
DCHECK(hthreadui_ != NULL); NULL, 0, ThreadHandlerUI, this, 0, &idthreadui_);
DCHECK(hthreadui_ != NULL);
} else {
if (!DoInitialize()) {
// TODO: Process initialization errors
}
// Create our own message loop there
SetMessageLoopForUI(new CefMessageLoopForUI());
idthreadui_ = GetCurrentThreadId();
}
initialized = true; initialized = true;
} }
@ -311,8 +362,10 @@ bool CefContext::Initialize()
Unlock(); Unlock();
if(initialized) { if(initialized) {
// Wait for initial UI thread setup to complete if (multi_threaded_message_loop) {
WaitForSingleObject(heventui_, INFINITE); // Wait for initial UI thread setup to complete
WaitForSingleObject(heventui_, INFINITE);
}
Lock(); Lock();

View File

@ -7,6 +7,7 @@
#define _CONTEXT_H #define _CONTEXT_H
#include "../include/cef.h" #include "../include/cef.h"
#include "base/at_exit.h"
#include "base/message_loop.h" #include "base/message_loop.h"
#include "base/gfx/native_widget_types.h" #include "base/gfx/native_widget_types.h"
#include "webkit/glue/webpreferences.h" #include "webkit/glue/webpreferences.h"
@ -21,7 +22,7 @@ public:
CefContext(); CefContext();
~CefContext(); ~CefContext();
bool Initialize(); bool Initialize(bool multi_threaded_message_loop);
void Shutdown(); void Shutdown();
MessageLoopForUI* GetMessageLoopForUI() { return messageloopui_; } MessageLoopForUI* GetMessageLoopForUI() { return messageloopui_; }
@ -45,10 +46,17 @@ public:
void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info); void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info);
void UIT_UnregisterPlugin(struct CefPluginInfo* plugin_info); void UIT_UnregisterPlugin(struct CefPluginInfo* plugin_info);
bool DoWork();
bool DoDelayedWork();
bool DoIdleWork();
private: private:
void SetMessageLoopForUI(MessageLoopForUI* loop); void SetMessageLoopForUI(MessageLoopForUI* loop);
void NotifyEvent(); void NotifyEvent();
bool DoInitialize();
void DoUninitialize();
protected: protected:
HMODULE hinstance_; HMODULE hinstance_;
DWORD idthreadui_; DWORD idthreadui_;
@ -58,6 +66,10 @@ protected:
bool in_transition_; bool in_transition_;
BrowserList browserlist_; BrowserList browserlist_;
WebPreferences* webprefs_; WebPreferences* webprefs_;
StatsTable* statstable_;
// Instantiate the AtExitManager to avoid asserts and possible memory leaks.
base::AtExitManager at_exit_manager_;
friend DWORD WINAPI ThreadHandlerUI(LPVOID lpParam); friend DWORD WINAPI ThreadHandlerUI(LPVOID lpParam);
}; };

View File

@ -15,6 +15,10 @@
#define BUTTON_WIDTH 72 #define BUTTON_WIDTH 72
#define URLBAR_HEIGHT 24 #define URLBAR_HEIGHT 24
// Define this value to run CEF with messages processed using the current
// application's message loop.
//#define TEST_SINGLE_THREADED_MESSAGE_LOOP
// Global Variables: // Global Variables:
HINSTANCE hInst; // current instance HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
@ -31,11 +35,17 @@ int APIENTRY _tWinMain(HINSTANCE hInstance,
LPTSTR lpCmdLine, LPTSTR lpCmdLine,
int nCmdShow) int nCmdShow)
{ {
UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(lpCmdLine);
// Initialize the CEF #ifdef TEST_SINGLE_THREADED_MESSAGE_LOOP
CefInitialize(); // Initialize the CEF with messages processed using the current application's
// message loop.
CefInitialize(false);
#else
// Initialize the CEF with messages processed using a separate UI thread.
CefInitialize(true);
#endif
// Structure providing information about the client plugin. // Structure providing information about the client plugin.
CefPluginInfo plugin_info; CefPluginInfo plugin_info;
@ -56,31 +66,36 @@ int APIENTRY _tWinMain(HINSTANCE hInstance,
// Register the internal client plugin // Register the internal client plugin
CefRegisterPlugin(plugin_info); CefRegisterPlugin(plugin_info);
MSG msg; MSG msg;
HACCEL hAccelTable; HACCEL hAccelTable;
// Initialize global strings // Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING); LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); MyRegisterClass(hInstance);
// Perform application initialization // Perform application initialization
if (!InitInstance (hInstance, nCmdShow)) if (!InitInstance (hInstance, nCmdShow))
{ {
return FALSE; return FALSE;
} }
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT)); hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT));
// Main message loop // Main message loop
while (GetMessage(&msg, NULL, 0, 0)) while (GetMessage(&msg, NULL, 0, 0))
{ {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) #ifdef TEST_SINGLE_THREADED_MESSAGE_LOOP
{ // Allow the CEF to do its message loop processing.
TranslateMessage(&msg); CefDoMessageLoopWork();
DispatchMessage(&msg); #endif
}
} if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Shut down the CEF // Shut down the CEF
CefShutdown(); CefShutdown();