From 15f6c270fa62a4b2582f26bc75cee5c2fbb9a710 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Thu, 29 Jan 2009 18:53:17 +0000 Subject: [PATCH] libcef: - 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 --- include/cef.h | 11 ++- libcef/context.cc | 123 ++++++++++++++++++++++++---------- libcef/context.h | 14 +++- tests/cefclient/cefclient.cpp | 65 +++++++++++------- 4 files changed, 150 insertions(+), 63 deletions(-) diff --git a/include/cef.h b/include/cef.h index f23607f58..f702dca60 100644 --- a/include/cef.h +++ b/include/cef.h @@ -39,14 +39,21 @@ // 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 -// indicates that it succeeded and false indicates that it failed. -bool CefInitialize(); +// indicates that it succeeded and false indicates that it failed. Set +// |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. // Shut down the thread hosting the UI message loop and destroy any created // windows. 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 // framework classes must implement the CefBase class. diff --git a/libcef/context.cc b/libcef/context.cc index c4704038b..a65acaf77 100644 --- a/libcef/context.cc +++ b/libcef/context.cc @@ -10,7 +10,6 @@ #include "browser_request_context.h" #include "../include/cef_nplugin.h" -#include "base/at_exit.h" #include "base/command_line.h" #include "base/icu_util.h" #include "base/path_service.h" @@ -33,7 +32,30 @@ static const char* kStatsFilePrefix = "libcef_"; static int kStatsFileThreads = 20; 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 if(_Context.get()) @@ -42,7 +64,7 @@ bool CefInitialize() // Create the new global context object _Context = new CefContext(); // Initialize the glboal context - return _Context->Initialize(); + return _Context->Initialize(multi_threaded_message_loop); } void CefShutdown() @@ -57,6 +79,13 @@ void CefShutdown() _Context = NULL; } +void CefDoMessageLoopWork() +{ + if(!_Context->RunningOnUIThread()) + return; + ((CefMessageLoopForUI*)CefMessageLoopForUI::current())->DoMessageLoopIteration(); +} + bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info) { if(!_Context.get()) @@ -124,10 +153,8 @@ StringPiece NetResourceProvider(int key) { return GetRawDataResource(::GetModuleHandle(NULL), key); } -DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) +bool CefContext::DoInitialize() { - CefContext *pContext = static_cast(lpParam); - HRESULT res; // Initialize common controls @@ -142,16 +169,9 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) res = OleInitialize(NULL); DCHECK(SUCCEEDED(res)); - // Instantiate the AtExitManager to avoid asserts and possible memory leaks. - base::AtExitManager at_exit_manager; - // Initialize the global CommandLine object. 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, // and no support for directory listings. // TODO(cef): Either disable caching or send the cache files to a reasonable @@ -166,7 +186,7 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) if(!ret) { MessageBox(NULL, L"Failed to load the required icudt38 library", 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. @@ -174,19 +194,17 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) // Load and initialize the stats table. Attempt to construct a somewhat // unique name to isolate separate instances from each other. - StatsTable *table = new StatsTable( + statstable_ = new StatsTable( kStatsFilePrefix + Uint64ToString(base::RandUint64()), kStatsFileThreads, kStatsFileCounters); - StatsTable::set_current(table); + StatsTable::set_current(statstable_); - // 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(); + return true; +} +void CefContext::DoUninitialize() +{ // Flush any remaining messages. This ensures that any accumulated // Task objects get destroyed before we exit, which avoids noise in // purify leak-test results. @@ -196,13 +214,35 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) // Tear down shared StatsTable; prevents unit_tests from leaking it. StatsTable::set_current(NULL); - delete table; + delete statstable_; + statstable_ = NULL; // Uninitialize COM stuff OleUninitialize(); // Uninitialize common controls CoUninitialize(); +} + +DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) +{ + CefContext *pContext = static_cast(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; } @@ -222,9 +262,11 @@ CefContext::~CefContext() { // Just in case CefShutdown() isn't called Shutdown(); + + DoUninitialize(); } -bool CefContext::Initialize() +bool CefContext::Initialize(bool multi_threaded_message_loop) { bool initialized = false, intransition = false; @@ -294,15 +336,24 @@ bool CefContext::Initialize() webprefs_->java_enabled = true; webprefs_->allow_scripts_to_close_windows = false; - // Event that will be used to signal thread setup completion. Start - // in non-signaled mode so that the event will block. - heventui_ = CreateEvent(NULL, TRUE, FALSE, NULL); - DCHECK(heventui_ != NULL); - - // Thread that hosts the UI loop - hthreadui_ = CreateThread( - NULL, 0, ThreadHandlerUI, this, 0, &idthreadui_); - DCHECK(hthreadui_ != NULL); + if (multi_threaded_message_loop) { + // Event that will be used to signal thread setup completion. Start + // in non-signaled mode so that the event will block. + heventui_ = CreateEvent(NULL, TRUE, FALSE, NULL); + DCHECK(heventui_ != NULL); + + // Thread that hosts the UI loop + hthreadui_ = CreateThread( + 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; } @@ -311,8 +362,10 @@ bool CefContext::Initialize() Unlock(); if(initialized) { - // Wait for initial UI thread setup to complete - WaitForSingleObject(heventui_, INFINITE); + if (multi_threaded_message_loop) { + // Wait for initial UI thread setup to complete + WaitForSingleObject(heventui_, INFINITE); + } Lock(); diff --git a/libcef/context.h b/libcef/context.h index f4373d9e5..cf1f30ac4 100644 --- a/libcef/context.h +++ b/libcef/context.h @@ -7,6 +7,7 @@ #define _CONTEXT_H #include "../include/cef.h" +#include "base/at_exit.h" #include "base/message_loop.h" #include "base/gfx/native_widget_types.h" #include "webkit/glue/webpreferences.h" @@ -21,7 +22,7 @@ public: CefContext(); ~CefContext(); - bool Initialize(); + bool Initialize(bool multi_threaded_message_loop); void Shutdown(); MessageLoopForUI* GetMessageLoopForUI() { return messageloopui_; } @@ -45,10 +46,17 @@ public: void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info); void UIT_UnregisterPlugin(struct CefPluginInfo* plugin_info); + bool DoWork(); + bool DoDelayedWork(); + bool DoIdleWork(); + private: void SetMessageLoopForUI(MessageLoopForUI* loop); void NotifyEvent(); + bool DoInitialize(); + void DoUninitialize(); + protected: HMODULE hinstance_; DWORD idthreadui_; @@ -58,6 +66,10 @@ protected: bool in_transition_; BrowserList browserlist_; 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); }; diff --git a/tests/cefclient/cefclient.cpp b/tests/cefclient/cefclient.cpp index 06b9a4137..6b2d5b89f 100644 --- a/tests/cefclient/cefclient.cpp +++ b/tests/cefclient/cefclient.cpp @@ -15,6 +15,10 @@ #define BUTTON_WIDTH 72 #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: HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text @@ -31,11 +35,17 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, LPTSTR lpCmdLine, int nCmdShow) { - UNREFERENCED_PARAMETER(hPrevInstance); - UNREFERENCED_PARAMETER(lpCmdLine); + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); - // Initialize the CEF - CefInitialize(); +#ifdef TEST_SINGLE_THREADED_MESSAGE_LOOP + // 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. CefPluginInfo plugin_info; @@ -56,31 +66,36 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, // Register the internal client plugin CefRegisterPlugin(plugin_info); - MSG msg; - HACCEL hAccelTable; + MSG msg; + HACCEL hAccelTable; - // Initialize global strings - LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); - LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING); - MyRegisterClass(hInstance); + // Initialize global strings + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); - // Perform application initialization - if (!InitInstance (hInstance, nCmdShow)) - { - return FALSE; - } + // Perform application initialization + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } - hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT)); + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT)); - // Main message loop - while (GetMessage(&msg, NULL, 0, 0)) - { - if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } + // Main message loop + while (GetMessage(&msg, NULL, 0, 0)) + { +#ifdef TEST_SINGLE_THREADED_MESSAGE_LOOP + // Allow the CEF to do its message loop processing. + CefDoMessageLoopWork(); +#endif + + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } // Shut down the CEF CefShutdown();