diff --git a/cef.gyp b/cef.gyp index eba994d7..b6c926e7 100644 --- a/cef.gyp +++ b/cef.gyp @@ -46,7 +46,6 @@ 'tests/cefclient/scheme_test.h', 'tests/cefclient/string_util.cpp', 'tests/cefclient/string_util.h', - 'tests/cefclient/thread_util.h', 'tests/cefclient/uiplugin.cpp', 'tests/cefclient/uiplugin.h', 'tests/cefclient/uiplugin_test.cpp', @@ -208,6 +207,8 @@ 'libcef_dll/ctocpp/scheme_handler_ctocpp.h', 'libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc', 'libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h', + 'libcef_dll/ctocpp/task_ctocpp.cc', + 'libcef_dll/ctocpp/task_ctocpp.h', 'libcef_dll/ctocpp/v8handler_ctocpp.cc', 'libcef_dll/ctocpp/v8handler_ctocpp.h', 'libcef_dll/ctocpp/write_handler_ctocpp.cc', @@ -264,6 +265,8 @@ 'libcef_dll/ctocpp/stream_reader_ctocpp.h', 'libcef_dll/ctocpp/stream_writer_ctocpp.cc', 'libcef_dll/ctocpp/stream_writer_ctocpp.h', + 'libcef_dll/cpptoc/task_cpptoc.cc', + 'libcef_dll/cpptoc/task_cpptoc.h', 'libcef_dll/ctocpp/v8value_ctocpp.cc', 'libcef_dll/ctocpp/v8value_ctocpp.h', 'libcef_dll/transfer_util.cpp', @@ -353,11 +356,21 @@ 'libcef/browser_webview_delegate.cc', 'libcef/browser_webview_delegate.h', 'libcef/browser_webview_delegate_win.cc', + 'libcef/cef_context.cc', + 'libcef/cef_context.h', + 'libcef/cef_process.cc', + 'libcef/cef_process.h', + 'libcef/cef_process_io_thread.cc', + 'libcef/cef_process_io_thread.h', + 'libcef/cef_process_sub_thread.cc', + 'libcef/cef_process_sub_thread.h', + 'libcef/cef_process_ui_thread.cc', + 'libcef/cef_process_ui_thread.h', 'libcef/cef_string.c', 'libcef/cef_string_list.cc', 'libcef/cef_string_map.cc', - 'libcef/context.cc', - 'libcef/context.h', + 'libcef/cef_thread.cc', + 'libcef/cef_thread.h', 'libcef/printing/print_settings.cc', 'libcef/printing/print_settings.h', 'libcef/printing/win_printing_context.cc', diff --git a/include/cef.h b/include/cef.h index b2dbc16c..d1d7bda2 100644 --- a/include/cef.h +++ b/include/cef.h @@ -54,6 +54,7 @@ class CefSchemeHandler; class CefSchemeHandlerFactory; class CefStreamReader; class CefStreamWriter; +class CefTask; class CefV8Handler; class CefV8Value; @@ -153,6 +154,29 @@ bool CefRegisterScheme(const std::wstring& scheme_name, CefRefPtr factory); +typedef cef_thread_id_t CefThreadId; + +// CEF maintains multiple internal threads that are used for handling different +// types of tasks. The UI thread creates the browser window and is used for all +// interaction with the WebKit rendering engine and V8 JavaScript engine (The +// UI thread will be the same as the main application thread if CefInitialize() +// was called with a |multi_threaded_message_loop| value of false.) The IO +// thread is used for handling schema and network requests. The FILE thread is +// used for the application cache and other miscellaneous activities. This +// method will return true if called on the specified thread. +/*--cef()--*/ +bool CefCurrentlyOn(CefThreadId threadId); + +// Post a task for execution on the specified thread. +/*--cef()--*/ +bool CefPostTask(CefThreadId threadId, CefRefPtr task); + +// Post a task for delayed execution on the specified thread. +/*--cef()--*/ +bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr task, + long delay_ms); + + // Interface defining the the reference count implementation methods. All // framework classes must implement the CefBase class. class CefBase @@ -285,6 +309,16 @@ inline bool operator!=(const CefRect& a, const CefRect& b) return !(a == b); } +// Implement this interface for task execution. +/*--cef(source=client)--*/ +class CefTask : public CefBase +{ +public: + // Method that will be executed. |threadId| is the thread executing the call. + /*--cef()--*/ + virtual void Execute(CefThreadId threadId) =0; +}; + // Class used to represent a browser window. All methods exposed by this class // should be thread safe. diff --git a/include/cef_capi.h b/include/cef_capi.h index 5deb9ba6..371d5dd0 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -133,6 +133,24 @@ CEF_EXPORT int cef_register_extension(const wchar_t* extension_name, CEF_EXPORT int cef_register_scheme(const wchar_t* scheme_name, const wchar_t* host_name, struct _cef_scheme_handler_factory_t* factory); +// CEF maintains multiple internal threads that are used for handling different +// types of tasks. The UI thread creates the browser window and is used for all +// interaction with the WebKit rendering engine and V8 JavaScript engine (The UI +// thread will be the same as the main application thread if cef_initialize() +// was called with a |multi_threaded_message_loop| value of false (0).) The IO +// thread is used for handling schema and network requests. The FILE thread is +// used for the application cache and other miscellaneous activities. This +// function will return true (1) if called on the specified thread. +CEF_EXPORT int cef_currently_on(cef_thread_id_t threadId); + +// Post a task for execution on the specified thread. +CEF_EXPORT int cef_post_task(cef_thread_id_t threadId, + struct _cef_task_t* task); + +// Post a task for delayed execution on the specified thread. +CEF_EXPORT int cef_post_delayed_task(cef_thread_id_t threadId, + struct _cef_task_t* task, long delay_ms); + typedef struct _cef_base_t { // Size of the data structure. @@ -157,6 +175,19 @@ typedef struct _cef_base_t #define CEF_MEMBER_MISSING(s, f) (!CEF_MEMBER_EXISTS(s, f) || !((s)->f)) +// Implement this structure for task execution. +typedef struct _cef_task_t +{ + // Base structure. + cef_base_t base; + + // Method that will be executed. |threadId| is the thread executing the call. + void (CEF_CALLBACK *execute)(struct _cef_task_t* self, + cef_thread_id_t threadId); + +} cef_task_t; + + // Structure used to represent a browser window. All functions exposed by this // structure should be thread safe. typedef struct _cef_browser_t diff --git a/include/cef_types.h b/include/cef_types.h index f7ebf9cf..f7a39812 100644 --- a/include/cef_types.h +++ b/include/cef_types.h @@ -229,6 +229,14 @@ typedef struct _cef_rect_t int height; } cef_rect_t; +// Existing thread IDs. +enum cef_thread_id_t +{ + TID_UI = 0, + TID_IO = 1, + TID_FILE = 2, +}; + #ifdef __cplusplus } #endif diff --git a/libcef/browser_appcache_system.cc b/libcef/browser_appcache_system.cc index 8b5efbf0..7700ed7c 100644 --- a/libcef/browser_appcache_system.cc +++ b/libcef/browser_appcache_system.cc @@ -408,9 +408,6 @@ WebApplicationCacheHost* BrowserAppCacheSystem::CreateCacheHostForWebKit( DCHECK(is_ui_thread()); - // The IO thread needs to be running for this system to work. - BrowserResourceLoaderBridge::EnsureIOThread(); - if (!is_initialized()) return NULL; return new WebApplicationCacheHostImpl(client, backend_proxy_.get()); diff --git a/libcef/browser_impl.cc b/libcef/browser_impl.cc index 8ecb9aa3..966d040f 100644 --- a/libcef/browser_impl.cc +++ b/libcef/browser_impl.cc @@ -3,7 +3,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "context.h" +#include "cef_context.h" #include "browser_impl.h" #include "browser_webkit_glue.h" #include "request_impl.h" @@ -54,39 +54,38 @@ CefBrowserImpl::CefBrowserImpl(CefWindowInfo& windowInfo, bool popup, void CefBrowserImpl::GoBack() { - PostTask(FROM_HERE, NewRunnableMethod(this, - &CefBrowserImpl::UIT_HandleActionView, MENU_ID_NAV_BACK)); + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, + &CefBrowserImpl::UIT_HandleActionView, MENU_ID_NAV_BACK)); } void CefBrowserImpl::GoForward() { - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleActionView, MENU_ID_NAV_FORWARD)); } void CefBrowserImpl::Reload() { - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleActionView, MENU_ID_NAV_RELOAD)); } void CefBrowserImpl::StopLoad() { - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleActionView, MENU_ID_NAV_STOP)); } void CefBrowserImpl::SetFocus(bool enable) { - if (_Context->RunningOnUIThread()) + if (CefThread::CurrentlyOn(CefThread::UI)) { UIT_SetFocus(GetWebViewHost(), enable); } else { - PostTask(FROM_HERE, NewRunnableMethod(this, - &CefBrowserImpl::UIT_SetFocus, - GetWebViewHost(), enable)); + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, + &CefBrowserImpl::UIT_SetFocus, GetWebViewHost(), enable)); } } @@ -142,14 +141,14 @@ void CefBrowserImpl::Find(int identifier, const std::wstring& searchText, options.findNext = findNext; // Execute the request on the UI thread. - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_Find, identifier, searchText, options)); } void CefBrowserImpl::StopFinding(bool clearSelection) { // Execute the request on the UI thread. - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_StopFinding, clearSelection)); } @@ -209,49 +208,49 @@ WebFrame* CefBrowserImpl::GetWebFrame(CefRefPtr frame) void CefBrowserImpl::Undo(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_UNDO, frame.get())); } void CefBrowserImpl::Redo(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_REDO, frame.get())); } void CefBrowserImpl::Cut(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_CUT, frame.get())); } void CefBrowserImpl::Copy(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_COPY, frame.get())); } void CefBrowserImpl::Paste(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_PASTE, frame.get())); } void CefBrowserImpl::Delete(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_DELETE, frame.get())); } void CefBrowserImpl::SelectAll(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_SELECTALL, frame.get())); } @@ -259,14 +258,14 @@ void CefBrowserImpl::SelectAll(CefRefPtr frame) void CefBrowserImpl::Print(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_PRINT, frame.get())); } void CefBrowserImpl::ViewSource(CefRefPtr frame) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_HandleAction, MENU_ID_VIEWSOURCE, frame.get())); } @@ -276,7 +275,7 @@ void CefBrowserImpl::LoadRequest(CefRefPtr frame, DCHECK(request.get() != NULL); frame->AddRef(); request->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_LoadURLForRequestRef, frame.get(), request.get())); } @@ -284,7 +283,7 @@ void CefBrowserImpl::LoadURL(CefRefPtr frame, const std::wstring& url) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_LoadURL, frame.get(), url)); } @@ -293,7 +292,7 @@ void CefBrowserImpl::LoadString(CefRefPtr frame, const std::wstring& url) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_LoadHTML, frame.get(), string, url)); } @@ -304,7 +303,7 @@ void CefBrowserImpl::LoadStream(CefRefPtr frame, DCHECK(stream.get() != NULL); frame->AddRef(); stream->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_LoadHTMLForStreamRef, frame.get(), stream.get(), url)); } @@ -315,7 +314,7 @@ void CefBrowserImpl::ExecuteJavaScript(CefRefPtr frame, int startLine) { frame->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_ExecuteJavaScript, frame.get(), jsCode, scriptUrl, startLine)); } @@ -351,7 +350,7 @@ bool CefBrowser::CreateBrowser(CefWindowInfo& windowInfo, bool popup, CefRefPtr browser( new CefBrowserImpl(windowInfo, popup, handler)); - PostTask(FROM_HERE, NewRunnableMethod(browser.get(), + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(browser.get(), &CefBrowserImpl::UIT_CreateBrowser, newUrl)); return true; } @@ -361,7 +360,7 @@ CefRefPtr CefBrowser::CreateBrowserSync(CefWindowInfo& windowInfo, CefRefPtr handler, const std::wstring& url) { - if(!_Context.get() || !_Context->RunningOnUIThread()) + if(!_Context.get() || !CefThread::CurrentlyOn(CefThread::UI)) return NULL; std::wstring newUrl = url; diff --git a/libcef/browser_impl.h b/libcef/browser_impl.h index 19455b86..f68dbccb 100644 --- a/libcef/browser_impl.h +++ b/libcef/browser_impl.h @@ -7,11 +7,11 @@ #define _BROWSER_IMPL_H #include "include/cef.h" -#include "context.h" #include "webview_host.h" #include "browser_webview_delegate.h" #include "browser_navigation_controller.h" +#include "cef_thread.h" #if defined(OS_WIN) #include "printing/win_printing_context.h" #endif diff --git a/libcef/browser_impl_win.cc b/libcef/browser_impl_win.cc index 569c97a6..89b4fd77 100644 --- a/libcef/browser_impl_win.cc +++ b/libcef/browser_impl_win.cc @@ -3,7 +3,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "context.h" +#include "cef_context.h" #include "browser_impl.h" #include "browser_webkit_glue.h" #include "stream_impl.h" @@ -111,7 +111,7 @@ CefWindowHandle CefBrowserImpl::GetWindowHandle() std::wstring CefBrowserImpl::GetSource(CefRefPtr frame) { - if(!_Context->RunningOnUIThread()) + if(!CefThread::CurrentlyOn(CefThread::UI)) { // We need to send the request to the UI thread and wait for the result @@ -125,7 +125,7 @@ std::wstring CefBrowserImpl::GetSource(CefRefPtr frame) // Request the data from the UI thread frame->AddRef(); stream->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_GetDocumentStringNotify, frame.get(), stream.get(), hEvent)); @@ -150,7 +150,7 @@ std::wstring CefBrowserImpl::GetSource(CefRefPtr frame) std::wstring CefBrowserImpl::GetText(CefRefPtr frame) { - if(!_Context->RunningOnUIThread()) + if(!CefThread::CurrentlyOn(CefThread::UI)) { // We need to send the request to the UI thread and wait for the result @@ -164,7 +164,7 @@ std::wstring CefBrowserImpl::GetText(CefRefPtr frame) // Request the data from the UI thread frame->AddRef(); stream->AddRef(); - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_GetDocumentTextNotify, frame.get(), stream.get(), hEvent)); @@ -187,7 +187,7 @@ std::wstring CefBrowserImpl::GetText(CefRefPtr frame) bool CefBrowserImpl::CanGoBack() { - if(!_Context->RunningOnUIThread()) + if(!CefThread::CurrentlyOn(CefThread::UI)) { // We need to send the request to the UI thread and wait for the result @@ -199,7 +199,7 @@ bool CefBrowserImpl::CanGoBack() bool retVal = true; // Request the data from the UI thread - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_CanGoBackNotify, &retVal, hEvent)); // Wait for the UI thread callback to tell us that the data is available @@ -217,7 +217,7 @@ bool CefBrowserImpl::CanGoBack() bool CefBrowserImpl::CanGoForward() { - if(!_Context->RunningOnUIThread()) + if(!CefThread::CurrentlyOn(CefThread::UI)) { // We need to send the request to the UI thread and wait for the result @@ -229,7 +229,7 @@ bool CefBrowserImpl::CanGoForward() bool retVal = true; // Request the data from the UI thread - PostTask(FROM_HERE, NewRunnableMethod(this, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(this, &CefBrowserImpl::UIT_CanGoForwardNotify, &retVal, hEvent)); // Wait for the UI thread callback to tell us that the data is available @@ -254,7 +254,7 @@ void CefBrowserImpl::UIT_CreateBrowser(const std::wstring& url) window_info_.m_windowName, window_info_.m_dwStyle, window_info_.m_x, window_info_.m_y, window_info_.m_nWidth, window_info_.m_nHeight, window_info_.m_hWndParent, window_info_.m_hMenu, - _Context->GetInstanceHandle(), NULL); + ::GetModuleHandle(NULL), NULL); DCHECK(window_info_.m_hWnd != NULL); // Set window user data to this object for future reference from the window @@ -269,7 +269,7 @@ void CefBrowserImpl::UIT_CreateBrowser(const std::wstring& url) // Create the webview host object webviewhost_.reset( WebViewHost::Create(window_info_.m_hWnd, delegate_.get(), - *_Context->GetWebPreferences())); + *_Context->web_preferences())); delegate_->RegisterDragDrop(); // Size the web view window to the browser window diff --git a/libcef/browser_request_context.cc b/libcef/browser_request_context.cc index 25677a52..35ab38ee 100644 --- a/libcef/browser_request_context.cc +++ b/libcef/browser_request_context.cc @@ -73,6 +73,13 @@ BrowserRequestContext::~BrowserRequestContext() { delete static_cast(cookie_policy_); } +void BrowserRequestContext::SetAcceptAllCookies(bool accept_all_cookies) { + net::StaticCookiePolicy::Type policy_type = accept_all_cookies ? + net::StaticCookiePolicy::ALLOW_ALL_COOKIES : + net::StaticCookiePolicy::BLOCK_THIRD_PARTY_COOKIES; + static_cast(cookie_policy())->set_type(policy_type); +} + const std::string& BrowserRequestContext::GetUserAgent( const GURL& url) const { return webkit_glue::GetUserAgent(url); diff --git a/libcef/browser_request_context.h b/libcef/browser_request_context.h index e117fb65..3072f39d 100644 --- a/libcef/browser_request_context.h +++ b/libcef/browser_request_context.h @@ -16,6 +16,7 @@ class BrowserRequestContext : public URLRequestContext { public: // Use an in-memory cache BrowserRequestContext(); + ~BrowserRequestContext(); // Use an on-disk cache at the specified location. Optionally, use the cache // in playback or record mode. @@ -25,9 +26,9 @@ class BrowserRequestContext : public URLRequestContext { virtual const std::string& GetUserAgent(const GURL& url) const; - private: - ~BrowserRequestContext(); + void SetAcceptAllCookies(bool accept_all_cookies); + private: void Init(const FilePath& cache_path, net::HttpCache::Mode cache_mode, bool no_proxy); }; diff --git a/libcef/browser_resource_loader_bridge.cc b/libcef/browser_resource_loader_bridge.cc index 77dca540..a8e375b9 100644 --- a/libcef/browser_resource_loader_bridge.cc +++ b/libcef/browser_resource_loader_bridge.cc @@ -36,6 +36,9 @@ #include "browser_request_context.h" #include "browser_socket_stream_bridge.h" #include "browser_impl.h" +#include "cef_context.h" +#include "cef_process.h" +#include "cef_process_io_thread.h" #include "request_impl.h" #include "base/file_path.h" @@ -73,80 +76,6 @@ using net::StaticCookiePolicy; namespace { -struct BrowserRequestContextParams { - BrowserRequestContextParams( - const FilePath& in_cache_path, - net::HttpCache::Mode in_cache_mode, - bool in_no_proxy) - : cache_path(in_cache_path), - cache_mode(in_cache_mode), - no_proxy(in_no_proxy), - accept_all_cookies(false) {} - - FilePath cache_path; - net::HttpCache::Mode cache_mode; - bool no_proxy; - bool accept_all_cookies; -}; - -BrowserRequestContextParams* g_request_context_params = NULL; -URLRequestContext* g_request_context = NULL; -base::Thread* g_cache_thread = NULL; - -//----------------------------------------------------------------------------- - -class IOThread : public base::Thread { - public: - IOThread() : base::Thread("IOThread") { - } - - ~IOThread() { - // We cannot rely on our base class to stop the thread since we want our - // CleanUp function to run. - Stop(); - } - - virtual void Init() { - if (g_request_context_params) { - g_request_context = new BrowserRequestContext( - g_request_context_params->cache_path, - g_request_context_params->cache_mode, - g_request_context_params->no_proxy); - SetAcceptAllCookies(g_request_context_params->accept_all_cookies); - delete g_request_context_params; - g_request_context_params = NULL; - } else { - g_request_context = new BrowserRequestContext(); - SetAcceptAllCookies(false); - } - - g_request_context->AddRef(); - - BrowserAppCacheSystem::InitializeOnIOThread(g_request_context); - BrowserSocketStreamBridge::InitializeOnIOThread(g_request_context); - } - - virtual void CleanUp() { - BrowserSocketStreamBridge::Cleanup(); - if (g_request_context) { - g_request_context->Release(); - g_request_context = NULL; - } - } - - void SetAcceptAllCookies(bool accept_all_cookies) { - StaticCookiePolicy::Type policy_type = accept_all_cookies ? - StaticCookiePolicy::ALLOW_ALL_COOKIES : - StaticCookiePolicy::BLOCK_THIRD_PARTY_COOKIES; - static_cast(g_request_context->cookie_policy())-> - set_type(policy_type); - } -}; - -IOThread* g_io_thread = NULL; - -//----------------------------------------------------------------------------- - struct RequestParams { std::string method; GURL url; @@ -185,13 +114,13 @@ class RequestProxy : public URLRequest::Delegate, owner_loop_ = MessageLoop::current(); // proxy over to the io thread - g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( this, &RequestProxy::AsyncStart, params)); } void Cancel() { // proxy over to the io thread - g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( this, &RequestProxy::AsyncCancel)); } @@ -200,8 +129,7 @@ class RequestProxy : public URLRequest::Delegate, virtual ~RequestProxy() { // If we have a request, then we'd better be on the io thread! - DCHECK(!request_.get() || - MessageLoop::current() == g_io_thread->message_loop()); + DCHECK(!request_.get() || CefThread::CurrentlyOn(CefThread::IO)); } // -------------------------------------------------------------------------- @@ -216,7 +144,7 @@ class RequestProxy : public URLRequest::Delegate, if (peer_ && peer_->OnReceivedRedirect(new_url, info, &has_new_first_party_for_cookies, &new_first_party_for_cookies)) { - g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( this, &RequestProxy::AsyncFollowDeferredRedirect, has_new_first_party_for_cookies, new_first_party_for_cookies)); } else { @@ -245,7 +173,7 @@ class RequestProxy : public URLRequest::Delegate, // peer could generate new requests in reponse to the received data, which // when run on the io thread, could race against this function in doing // another InvokeLater. See bug 769249. - g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( this, &RequestProxy::AsyncReadData)); peer_->OnReceivedData(buf_copy.get(), bytes_read); @@ -349,7 +277,7 @@ class RequestProxy : public URLRequest::Delegate, request_->SetExtraRequestHeaders(headers); request_->set_load_flags(params->load_flags); request_->set_upload(params->upload.get()); - request_->set_context(g_request_context); + request_->set_context(_Context->request_context()); BrowserAppCacheSystem::SetExtraRequestInfo( request_.get(), params->appcache_host_id, params->request_type); @@ -657,7 +585,7 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { if (proxy_) { proxy_->DropPeer(); // Let the proxy die on the IO thread - g_io_thread->message_loop()->ReleaseSoon(FROM_HERE, proxy_); + CefThread::ReleaseSoon(CefThread::IO, FROM_HERE, proxy_); } } @@ -693,9 +621,6 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { virtual bool Start(Peer* peer) { DCHECK(!proxy_); - if (!BrowserResourceLoaderBridge::EnsureIOThread()) - return false; - proxy_ = new RequestProxy(browser_); proxy_->AddRef(); @@ -716,9 +641,6 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { virtual void SyncLoad(SyncLoadResponse* response) { DCHECK(!proxy_); - if (!BrowserResourceLoaderBridge::EnsureIOThread()) - return; - // this may change as the result of a redirect response->url = params_->url; @@ -746,8 +668,8 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { class CookieSetter : public base::RefCountedThreadSafe { public: void Set(const GURL& url, const std::string& cookie) { - DCHECK(MessageLoop::current() == g_io_thread->message_loop()); - g_request_context->cookie_store()->SetCookie(url, cookie); + REQUIRE_IOT(); + _Context->request_context()->cookie_store()->SetCookie(url, cookie); } private: @@ -762,7 +684,7 @@ class CookieGetter : public base::RefCountedThreadSafe { } void Get(const GURL& url) { - result_ = g_request_context->cookie_store()->GetCookies(url); + result_ = _Context->request_context()->cookie_store()->GetCookies(url); event_.Signal(); } @@ -798,11 +720,12 @@ ResourceLoaderBridge* ResourceLoaderBridge::Create( // Issue the proxy resolve request on the io thread, and wait // for the result. bool FindProxyForUrl(const GURL& url, std::string* proxy_list) { - DCHECK(g_request_context); + DCHECK(_Context->request_context()); scoped_refptr sync_proxy_service( - new net::SyncProxyServiceHelper(g_io_thread->message_loop(), - g_request_context->proxy_service())); + new net::SyncProxyServiceHelper( + _Context->process()->io_thread()->message_loop(), + _Context->request_context()->proxy_service())); net::ProxyInfo proxy_info; int rv = sync_proxy_service->ResolveProxy(url, &proxy_info, @@ -818,53 +741,14 @@ bool FindProxyForUrl(const GURL& url, std::string* proxy_list) { //----------------------------------------------------------------------------- -// static -void BrowserResourceLoaderBridge::Init( - const FilePath& cache_path, - net::HttpCache::Mode cache_mode, - bool no_proxy) { - // Make sure to stop any existing IO thread since it may be using the - // current request context. - Shutdown(); - - DCHECK(!g_request_context_params); - DCHECK(!g_request_context); - DCHECK(!g_io_thread); - - g_request_context_params = new BrowserRequestContextParams( - cache_path, cache_mode, no_proxy); -} - -// static -void BrowserResourceLoaderBridge::Shutdown() { - if (g_io_thread) { - delete g_io_thread; - g_io_thread = NULL; - - DCHECK(g_cache_thread); - delete g_cache_thread; - g_cache_thread = NULL; - - DCHECK(!g_request_context) << "should have been nulled by thread dtor"; - } else { - delete g_request_context_params; - g_request_context_params = NULL; - } -} - // static void BrowserResourceLoaderBridge::SetCookie(const GURL& url, const GURL& first_party_for_cookies, const std::string& cookie) { // Proxy to IO thread to synchronize w/ network loading. - if (!EnsureIOThread()) { - NOTREACHED(); - return; - } - scoped_refptr cookie_setter = new CookieSetter(); - g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( cookie_setter.get(), &CookieSetter::Set, url, cookie)); } @@ -873,61 +757,24 @@ std::string BrowserResourceLoaderBridge::GetCookies( const GURL& url, const GURL& first_party_for_cookies) { // Proxy to IO thread to synchronize w/ network loading - if (!EnsureIOThread()) { - NOTREACHED(); - return std::string(); - } - scoped_refptr getter = new CookieGetter(); - g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( getter.get(), &CookieGetter::Get, url)); return getter->GetResult(); } -// static -bool BrowserResourceLoaderBridge::EnsureIOThread() { - if (g_io_thread) - return true; - -#if defined(OS_WIN) - // Use NSS for SSL on Windows. TODO(wtc): this should eventually be hidden - // inside DefaultClientSocketFactory::CreateSSLClientSocket. - net::ClientSocketFactory::SetSSLClientSocketFactory( - net::SSLClientSocketNSSFactory); -#endif -#if defined(OS_MACOSX) || defined(OS_WIN) - // We want to be sure to init NSPR on the main thread. - base::EnsureNSPRInit(); -#endif - - // Create the cache thread. We want the cache thread to outlive the IO thread, - // so its lifetime is bonded to the IO thread lifetime. - DCHECK(!g_cache_thread); - g_cache_thread = new base::Thread("cache"); - CHECK(g_cache_thread->StartWithOptions( - base::Thread::Options(MessageLoop::TYPE_IO, 0))); - - g_io_thread = new IOThread(); - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - return g_io_thread->StartWithOptions(options); -} - // static void BrowserResourceLoaderBridge::SetAcceptAllCookies(bool accept_all_cookies) { - if (g_request_context_params) { - g_request_context_params->accept_all_cookies = accept_all_cookies; - DCHECK(!g_request_context); - DCHECK(!g_io_thread); - } else { - g_io_thread->SetAcceptAllCookies(accept_all_cookies); - } + // Proxy to IO thread to synchronize w/ network loading + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( + _Context->request_context().get(), + &BrowserRequestContext::SetAcceptAllCookies, accept_all_cookies)); } -// static +//static scoped_refptr BrowserResourceLoaderBridge::GetCacheThread() { - return g_cache_thread->message_loop_proxy(); + return CefThread::GetMessageLoopProxyForThread(CefThread::FILE); } diff --git a/libcef/browser_resource_loader_bridge.h b/libcef/browser_resource_loader_bridge.h index cdb0e75c..92a25686 100644 --- a/libcef/browser_resource_loader_bridge.h +++ b/libcef/browser_resource_loader_bridge.h @@ -15,29 +15,14 @@ class GURL; class BrowserResourceLoaderBridge { public: - // Call this function to initialize the simple resource loader bridge. - // It is safe to call this function multiple times. - // - // NOTE: If this function is not called, then a default request context will - // be initialized lazily. - // - static void Init(const FilePath& cache_path, - net::HttpCache::Mode cache_mode, - bool no_proxy); - - // Call this function to shutdown the simple resource loader bridge. - static void Shutdown(); - // May only be called after Init. static void SetCookie(const GURL& url, const GURL& first_party_for_cookies, const std::string& cookie); static std::string GetCookies(const GURL& url, const GURL& first_party_for_cookies); - static bool EnsureIOThread(); static void SetAcceptAllCookies(bool accept_all_cookies); - // This method should only be called after Init(), and before Shutdown(). static scoped_refptr GetCacheThread(); }; diff --git a/libcef/browser_webview_delegate.cc b/libcef/browser_webview_delegate.cc index 37ace4c2..16a7783c 100644 --- a/libcef/browser_webview_delegate.cc +++ b/libcef/browser_webview_delegate.cc @@ -11,7 +11,7 @@ #include "browser_appcache_system.h" #include "browser_impl.h" #include "browser_navigation_controller.h" -#include "context.h" +#include "cef_context.h" #include "request_impl.h" #include "v8_impl.h" @@ -126,13 +126,13 @@ int next_page_id_ = 1; // WebViewDelegate ----------------------------------------------------------- void BrowserWebViewDelegate::SetUserStyleSheetEnabled(bool is_enabled) { - WebPreferences* prefs = _Context->GetWebPreferences(); + WebPreferences* prefs = _Context->web_preferences(); prefs->user_style_sheet_enabled = is_enabled; prefs->Apply(browser_->GetWebView()); } void BrowserWebViewDelegate::SetUserStyleSheetLocation(const GURL& location) { - WebPreferences* prefs = _Context->GetWebPreferences(); + WebPreferences* prefs = _Context->web_preferences(); prefs->user_style_sheet_enabled = true; prefs->user_style_sheet_location = location; prefs->Apply(browser_->GetWebView()); diff --git a/libcef/browser_webview_delegate_win.cc b/libcef/browser_webview_delegate_win.cc index 9befc396..72157825 100644 --- a/libcef/browser_webview_delegate_win.cc +++ b/libcef/browser_webview_delegate_win.cc @@ -12,7 +12,7 @@ #include "browser_drop_delegate.h" #include "browser_navigation_controller.h" #include "browser_impl.h" -#include "context.h" +#include "cef_context.h" #include #include diff --git a/libcef/cef_context.cc b/libcef/cef_context.cc new file mode 100644 index 00000000..954b3ea5 --- /dev/null +++ b/libcef/cef_context.cc @@ -0,0 +1,322 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#include "cef_context.h" +#include "browser_impl.h" +#include "browser_webkit_glue.h" +#include "cef_thread.h" +#include "cef_process.h" +#include "../include/cef_nplugin.h" + +#include "base/file_util.h" +#if defined(OS_MACOSX) || defined(OS_WIN) +#include "base/nss_util.h" +#endif +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/webpreferences.h" + +// Global CefContext pointer +CefRefPtr _Context; + +bool CefInitialize(bool multi_threaded_message_loop, + const std::wstring& cache_path) +{ + // Return true if the context is already initialized + if(_Context.get()) + return true; + + // Create the new global context object + _Context = new CefContext(); + // Initialize the global context + return _Context->Initialize(multi_threaded_message_loop, cache_path); +} + +void CefShutdown() +{ + // Verify that the context is already initialized + if(!_Context.get()) + return; + + // Shut down the global context + _Context->Shutdown(); + // Delete the global context object + _Context = NULL; +} + +void CefDoMessageLoopWork() +{ + // Verify that the context is already initialized. + if(!_Context.get() || !_Context->process()) + return; + + if(!_Context->process()->CalledOnValidThread()) + return; + _Context->process()->DoMessageLoopIteration(); +} + +static void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info) +{ + REQUIRE_UIT(); + + NPAPI::PluginVersionInfo info; + + info.path = FilePath(plugin_info->unique_name); + info.product_name = plugin_info->display_name; + info.file_description = plugin_info->description; + info.file_version =plugin_info->version; + + for(size_t i = 0; i < plugin_info->mime_types.size(); ++i) { + if(i > 0) { + info.mime_types += L"|"; + info.file_extensions += L"|"; + info.type_descriptions += L"|"; + } + + info.mime_types += plugin_info->mime_types[i].mime_type; + info.type_descriptions += plugin_info->mime_types[i].description; + + for(size_t j = 0; + j < plugin_info->mime_types[i].file_extensions.size(); ++j) { + if(j > 0) { + info.file_extensions += L","; + } + info.file_extensions += plugin_info->mime_types[i].file_extensions[j]; + } + } + + info.entry_points.np_getentrypoints = plugin_info->np_getentrypoints; + info.entry_points.np_initialize = plugin_info->np_initialize; + info.entry_points.np_shutdown = plugin_info->np_shutdown; + + NPAPI::PluginList::Singleton()->RegisterInternalPlugin(info); + + delete plugin_info; +} + +bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info) +{ + if(!_Context.get()) + return false; + + CefPluginInfo* pPluginInfo = new CefPluginInfo; + *pPluginInfo = plugin_info; + + CefThread::PostTask(CefThread::UI, FROM_HERE, + NewRunnableFunction(UIT_RegisterPlugin, pPluginInfo)); + + return true; +} + +static int GetThreadId(CefThreadId threadId) +{ + switch(threadId) { + case TID_UI: return CefThread::UI; + case TID_IO: return CefThread::IO; + case TID_FILE: return CefThread::FILE; + }; + NOTREACHED(); + return -1; +} + +bool CefCurrentlyOn(CefThreadId threadId) +{ + int id = GetThreadId(threadId); + if(id < 0) + return false; + + return CefThread::CurrentlyOn(static_cast(id)); +} + +class CefTaskHelper : public base::RefCountedThreadSafe +{ +public: + CefTaskHelper(CefRefPtr task) : task_(task) {} + void Execute(CefThreadId threadId) { task_->Execute(threadId); } +private: + CefRefPtr task_; + DISALLOW_COPY_AND_ASSIGN(CefTaskHelper); +}; + +bool CefPostTask(CefThreadId threadId, CefRefPtr task) +{ + int id = GetThreadId(threadId); + if(id < 0) + return false; + + scoped_refptr helper(new CefTaskHelper(task)); + return CefThread::PostTask(static_cast(id), FROM_HERE, + NewRunnableMethod(helper.get(), &CefTaskHelper::Execute, threadId)); +} + +bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr task, + long delay_ms) +{ + int id = GetThreadId(threadId); + if(id < 0) + return false; + + scoped_refptr helper(new CefTaskHelper(task)); + return CefThread::PostDelayedTask(static_cast(id), FROM_HERE, + NewRunnableMethod(helper.get(), &CefTaskHelper::Execute, threadId), + delay_ms); +} + + +// CefContext + +CefContext::CefContext() : process_(NULL), webprefs_(NULL) +{ + +} + +CefContext::~CefContext() +{ + // Just in case CefShutdown() isn't called + Shutdown(); +} + +bool CefContext::Initialize(bool multi_threaded_message_loop, + const std::wstring& cache_path) +{ + cache_path_ = cache_path; + + // Initialize web preferences + webprefs_ = new WebPreferences; + *webprefs_ = WebPreferences(); + webprefs_->standard_font_family = L"Times"; + webprefs_->fixed_font_family = L"Courier"; + webprefs_->serif_font_family = L"Times"; + webprefs_->sans_serif_font_family = L"Helvetica"; + // These two fonts are picked from the intersection of + // Win XP font list and Vista font list : + // http://www.microsoft.com/typography/fonts/winxp.htm + // http://blogs.msdn.com/michkap/archive/2006/04/04/567881.aspx + // Some of them are installed only with CJK and complex script + // support enabled on Windows XP and are out of consideration here. + // (although we enabled both on our buildbots.) + // They (especially Impact for fantasy) are not typical cursive + // and fantasy fonts, but it should not matter for layout tests + // as long as they're available. +#if defined(OS_MACOSX) + webprefs_->cursive_font_family = L"Apple Chancery"; + webprefs_->fantasy_font_family = L"Papyrus"; +#else + webprefs_->cursive_font_family = L"Comic Sans MS"; + webprefs_->fantasy_font_family = L"Impact"; +#endif + webprefs_->default_encoding = "ISO-8859-1"; + webprefs_->default_font_size = 16; + webprefs_->default_fixed_font_size = 13; + webprefs_->minimum_font_size = 1; + webprefs_->minimum_logical_font_size = 9; + webprefs_->javascript_can_open_windows_automatically = true; + webprefs_->dom_paste_enabled = true; + webprefs_->developer_extras_enabled = true; + webprefs_->site_specific_quirks_enabled = true; + webprefs_->shrinks_standalone_images_to_fit = false; + webprefs_->uses_universal_detector = false; + webprefs_->text_areas_are_resizable = true; + webprefs_->java_enabled = true; + webprefs_->allow_scripts_to_close_windows = false; + webprefs_->xss_auditor_enabled = false; + webprefs_->remote_fonts_enabled = true; + webprefs_->local_storage_enabled = true; + webprefs_->application_cache_enabled = true; + webprefs_->databases_enabled = true; + webprefs_->allow_file_access_from_file_urls = true; + +#if defined(OS_MACOSX) || defined(OS_WIN) + // We want to be sure to init NSPR on the main thread. + base::EnsureNSPRInit(); +#endif + + process_ = new CefProcess(multi_threaded_message_loop); + process_->CreateChildThreads(); + + return true; +} + +void CefContext::Shutdown() +{ + // Remove all existing browsers. + //RemoveAllBrowsers(); + + // Deleting the process will destroy the child threads. + process_ = NULL; +} + + +bool CefContext::AddBrowser(CefRefPtr browser) +{ + bool found = false; + + Lock(); + + // check that the browser isn't already in the list before adding + BrowserList::const_iterator it = browserlist_.begin(); + for(; it != browserlist_.end(); ++it) { + if(it->get() == browser.get()) { + found = true; + break; + } + } + + if(!found) + { + browser->UIT_SetUniqueID(next_browser_id_++); + browserlist_.push_back(browser); + } + + Unlock(); + return !found; +} + +bool CefContext::RemoveBrowser(CefRefPtr browser) +{ + bool deleted = false; + bool empty = false; + + Lock(); + + BrowserList::iterator it = browserlist_.begin(); + for(; it != browserlist_.end(); ++it) { + if(it->get() == browser.get()) { + browserlist_.erase(it); + deleted = true; + break; + } + } + + if (browserlist_.empty()) { + next_browser_id_ = 1; + empty = true; + } + + Unlock(); + + if (empty) { + CefThread::PostTask(CefThread::UI, FROM_HERE, + NewRunnableFunction(webkit_glue::ClearCache)); + } + + return deleted; +} + +CefRefPtr CefContext::GetBrowserByID(int id) +{ + CefRefPtr browser; + Lock(); + + BrowserList::const_iterator it = browserlist_.begin(); + for(; it != browserlist_.end(); ++it) { + if(it->get()->UIT_GetUniqueID() == id) { + browser = it->get(); + break; + } + } + + Unlock(); + + return browser; +} diff --git a/libcef/cef_context.h b/libcef/cef_context.h new file mode 100644 index 00000000..eaf99aa9 --- /dev/null +++ b/libcef/cef_context.h @@ -0,0 +1,81 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#ifndef _CEF_CONTEXT_H +#define _CEF_CONTEXT_H + +#include "../include/cef.h" +#include "browser_request_context.h" +#include "cef_process.h" +#include "cef_thread.h" + +#include "base/at_exit.h" +#include "base/file_path.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include + +class BrowserRequestContext; +class CefBrowserImpl; +struct WebPreferences; + +class CefContext : public CefThreadSafeBase +{ +public: + typedef std::list > BrowserList; + + CefContext(); + ~CefContext(); + + // These methods will be called on the main application thread. + bool Initialize(bool multi_threaded_message_loop, + const std::wstring& cache_path); + void Shutdown(); + + scoped_refptr process() { return process_; } + + bool AddBrowser(CefRefPtr browser); + bool RemoveBrowser(CefRefPtr browser); + CefRefPtr GetBrowserByID(int id); + BrowserList* GetBrowserList() { return &browserlist_; } + + // Retrieve the path at which cache data will be stored on disk. If empty, + // cache data will be stored in-memory. + const std::wstring& cache_path() { return cache_path_; } + + WebPreferences* web_preferences() + { + REQUIRE_UIT(); + return webprefs_; + } + + // The BrowserRequestContext object is managed by CefProcessIOThread. + void set_request_context(BrowserRequestContext* request_context) + { request_context_ = request_context; } + scoped_refptr request_context() + { return request_context_; } + +private: + // Manages the various process threads. + scoped_refptr process_; + + // Initialize the AtExitManager on the main application thread to avoid + // asserts and possible memory leaks. + base::AtExitManager at_exit_manager_; + + std::wstring cache_path_; + WebPreferences* webprefs_; + scoped_refptr request_context_; + + // Map of browsers that currently exist. + BrowserList browserlist_; + + // Used for assigning unique IDs to browser instances. + int next_browser_id_; +}; + +// Global context object pointer +extern CefRefPtr _Context; + +#endif // _CONTEXT_H diff --git a/libcef/cef_process.cc b/libcef/cef_process.cc new file mode 100644 index 00000000..83f7e477 --- /dev/null +++ b/libcef/cef_process.cc @@ -0,0 +1,134 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cef_process.h" +#include "cef_process_io_thread.h" +#include "cef_process_sub_thread.h" +#include "cef_process_ui_thread.h" + +#include "base/thread.h" +#include "base/waitable_event.h" + +CefProcess* g_cef_process = NULL; + +// Class used to process events on the current message loop. +class CefMessageLoopForUI : public MessageLoopForUI +{ + typedef MessageLoopForUI inherited; + +public: + CefMessageLoopForUI() + { + } + + // Returns the MessageLoopForUI of the current thread. + static CefMessageLoopForUI* current() { + MessageLoop* loop = MessageLoop::current(); + DCHECK_EQ(MessageLoop::TYPE_UI, loop->type()); + return static_cast(loop); + } + + virtual bool DoIdleWork() { + bool valueToRet = inherited::DoIdleWork(); + pump_->Quit(); + return valueToRet; + } + + void DoMessageLoopIteration() { + Run(NULL); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CefMessageLoopForUI); +}; + +CefProcess::CefProcess(bool multi_threaded_message_loop) + : multi_threaded_message_loop_(multi_threaded_message_loop), + created_ui_thread_(false), + created_io_thread_(false), + created_file_thread_(false) { + g_cef_process = this; +} + +CefProcess::~CefProcess() { + // Terminate the IO thread. + io_thread_.reset(); + + // Terminate the FILE thread. + file_thread_.reset(); + + if(!multi_threaded_message_loop_) { + // Must explicitly clean up the UI thread. + ui_thread_->CleanUp(); + + // Terminate the UI thread. + ui_thread_.reset(); + + // Terminate the message loop. + ui_message_loop_.reset(); + } + + g_cef_process = NULL; +} + +void CefProcess::DoMessageLoopIteration() { + DCHECK(CalledOnValidThread() && ui_message_loop_.get() != NULL); + ui_message_loop_->DoMessageLoopIteration(); +} + +void CefProcess::CreateUIThread() { + DCHECK(!created_ui_thread_ && ui_thread_.get() == NULL); + created_ui_thread_ = true; + + scoped_ptr thread; + if(multi_threaded_message_loop_) { + // Create the message loop on a new thread. + thread.reset(new CefProcessUIThread()); + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_UI; + if (!thread->StartWithOptions(options)) + return; + } else { + // Create the message loop on the current (main application) thread. + ui_message_loop_.reset(new CefMessageLoopForUI()); + thread.reset( + new CefProcessUIThread(ui_message_loop_.get())); + + // Must explicitly initialize the UI thread. + thread->Init(); + } + + ui_thread_.swap(thread); +} + +void CefProcess::CreateIOThread() { + DCHECK(!created_io_thread_ && io_thread_.get() == NULL); + created_io_thread_ = true; + + scoped_ptr thread(new CefProcessIOThread()); + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + if (!thread->StartWithOptions(options)) + return; + io_thread_.swap(thread); +} + +void CefProcess::CreateFileThread() { + DCHECK(!created_file_thread_ && file_thread_.get() == NULL); + created_file_thread_ = true; + + scoped_ptr thread(new CefProcessSubThread(CefThread::FILE)); + base::Thread::Options options; +#if defined(OS_WIN) + // On Windows, the FILE thread needs to be have a UI message loop which pumps + // messages in such a way that Google Update can communicate back to us. + options.message_loop_type = MessageLoop::TYPE_UI; +#else + options.message_loop_type = MessageLoop::TYPE_IO; +#endif + if (!thread->StartWithOptions(options)) + return; + file_thread_.swap(thread); +} diff --git a/libcef/cef_process.h b/libcef/cef_process.h new file mode 100644 index 00000000..3c42c6cc --- /dev/null +++ b/libcef/cef_process.h @@ -0,0 +1,117 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This interface is for managing the global services of the application. Each +// service is lazily created when requested the first time. The service getters +// will return NULL if the service is not available, so callers must check for +// this condition. + +#ifndef _CEF_PROCESS_H +#define _CEF_PROCESS_H + +#include +#include + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/non_thread_safe.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "ipc/ipc_message.h" + +namespace base { +class Thread; +class WaitableEvent; +} + +class CefProcessIOThread; +class CefProcessUIThread; +class CefMessageLoopForUI; + +// NOT THREAD SAFE, call only from the main thread. +// These functions shouldn't return NULL unless otherwise noted. +class CefProcess : public base::RefCounted, public NonThreadSafe { + public: + CefProcess(bool multi_threaded_message_loop); + virtual ~CefProcess(); + + // Creates key child threads. We need to do this explicitly since + // CefThread::PostTask silently deletes a posted task if the target message + // loop isn't created. + void CreateChildThreads() { + ui_thread(); + // Create the FILE thread before the IO thread because IO thread + // initialization depends on existance of the FILE thread (for cache + // support, etc). + file_thread(); + io_thread(); + } + + CefProcessUIThread* ui_thread() { + DCHECK(CalledOnValidThread()); + if (!created_ui_thread_) + CreateUIThread(); + return ui_thread_.get(); + } + + // Necessary to perform work on the UI thread if started without a multi + // threaded message loop. + void DoMessageLoopIteration(); + + // Returns the thread that we perform I/O coordination on (network requests, + // communication with renderers, etc. + // NOTE: You should ONLY use this to pass to IPC or other objects which must + // need a MessageLoop*. If you just want to post a task, use + // CefThread::PostTask (or other variants) as they take care of checking + // that a thread is still alive, race conditions, lifetime differences etc. + // If you still must use this, need to check the return value for NULL. + CefProcessIOThread* io_thread() { + DCHECK(CalledOnValidThread()); + if (!created_io_thread_) + CreateIOThread(); + return io_thread_.get(); + } + + // Returns the thread that we perform random file operations on. For code + // that wants to do I/O operations (not network requests or even file: URL + // requests), this is the thread to use to avoid blocking the UI thread. + // It might be nicer to have a thread pool for this kind of thing. + base::Thread* file_thread() { + DCHECK(CalledOnValidThread()); + if (!created_file_thread_) + CreateFileThread(); + return file_thread_.get(); + } + +#if defined(IPC_MESSAGE_LOG_ENABLED) + // Enable or disable IPC logging for the browser, all processes + // derived from ChildProcess (plugin etc), and all + // renderers. + void SetIPCLoggingEnabled(bool enable); +#endif + + private: + void CreateUIThread(); + void CreateIOThread(); + void CreateFileThread(); + + bool multi_threaded_message_loop_; + + bool created_ui_thread_; + scoped_ptr ui_thread_; + scoped_ptr ui_message_loop_; + + bool created_io_thread_; + scoped_ptr io_thread_; + + bool created_file_thread_; + scoped_ptr file_thread_; + + DISALLOW_COPY_AND_ASSIGN(CefProcess); +}; + +extern CefProcess* g_cef_process; + +#endif // _CEF_PROCESS_H diff --git a/libcef/cef_process_io_thread.cc b/libcef/cef_process_io_thread.cc new file mode 100644 index 00000000..38f60319 --- /dev/null +++ b/libcef/cef_process_io_thread.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cef_process_io_thread.h" +#include "cef_context.h" +#include "browser_appcache_system.h" +#include "browser_resource_loader_bridge.h" +#include "browser_socket_stream_bridge.h" + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +CefProcessIOThread::CefProcessIOThread() + : CefThread(CefThread::IO), request_context_(NULL) {} + +CefProcessIOThread::CefProcessIOThread(MessageLoop* message_loop) + : CefThread(CefThread::IO, message_loop), request_context_(NULL) {} + +CefProcessIOThread::~CefProcessIOThread() { + // We cannot rely on our base class to stop the thread since we want our + // CleanUp function to run. + Stop(); +} + +void CefProcessIOThread::Init() { +#if defined(OS_WIN) + // Initializes the COM library on the current thread. + CoInitialize(NULL); +#endif + + request_context_ = new BrowserRequestContext(FilePath(_Context->cache_path()), + net::HttpCache::NORMAL, false); + _Context->set_request_context(request_context_); + + BrowserAppCacheSystem::InitializeOnIOThread(request_context_); + BrowserSocketStreamBridge::InitializeOnIOThread(request_context_); +} + +void CefProcessIOThread::CleanUp() { + // Flush any remaining messages. This ensures that any accumulated + // Task objects get destroyed before we exit, which avoids noise in + // purify leak-test results. + MessageLoop::current()->RunAllPending(); + + BrowserSocketStreamBridge::Cleanup(); + + _Context->set_request_context(NULL); + request_context_ = NULL; + +#if defined(OS_WIN) + // Closes the COM library on the current thread. CoInitialize must + // be balanced by a corresponding call to CoUninitialize. + CoUninitialize(); +#endif +} diff --git a/libcef/cef_process_io_thread.h b/libcef/cef_process_io_thread.h new file mode 100644 index 00000000..fa3057a1 --- /dev/null +++ b/libcef/cef_process_io_thread.h @@ -0,0 +1,40 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CEF_PROCESS_IO_THREAD +#define _CEF_PROCESS_IO_THREAD + +#include "base/basictypes.h" +#include "cef_thread.h" +#include "browser_request_context.h" + +// ---------------------------------------------------------------------------- +// CefProcessIOThread +// +// This simple thread object is used for the specialized threads that the +// CefProcess spins up. +// +// Applications must initialize the COM library before they can call +// COM library functions other than CoGetMalloc and memory allocation +// functions, so this class initializes COM for those users. +class CefProcessIOThread : public CefThread { + public: + explicit CefProcessIOThread(); + CefProcessIOThread(MessageLoop* message_loop); + virtual ~CefProcessIOThread(); + + virtual void Init(); + virtual void CleanUp(); + + scoped_refptr request_context() + { return request_context_; } + + private: + scoped_refptr request_context_; + + DISALLOW_COPY_AND_ASSIGN(CefProcessIOThread); +}; + +#endif // _CEF_PROCESS_UI_THREAD diff --git a/libcef/cef_process_sub_thread.cc b/libcef/cef_process_sub_thread.cc new file mode 100644 index 00000000..42e9f4f1 --- /dev/null +++ b/libcef/cef_process_sub_thread.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cef_process_sub_thread.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +CefProcessSubThread::CefProcessSubThread(CefThread::ID identifier) + : CefThread(identifier) {} + +CefProcessSubThread::CefProcessSubThread(CefThread::ID identifier, + MessageLoop* message_loop) + : CefThread(identifier, message_loop) {} + +CefProcessSubThread::~CefProcessSubThread() { + // We cannot rely on our base class to stop the thread since we want our + // CleanUp function to run. + Stop(); +} + +void CefProcessSubThread::Init() { +#if defined(OS_WIN) + // Initializes the COM library on the current thread. + CoInitialize(NULL); +#endif +} + +void CefProcessSubThread::CleanUp() { + // Flush any remaining messages. This ensures that any accumulated + // Task objects get destroyed before we exit, which avoids noise in + // purify leak-test results. + MessageLoop::current()->RunAllPending(); + +#if defined(OS_WIN) + // Closes the COM library on the current thread. CoInitialize must + // be balanced by a corresponding call to CoUninitialize. + CoUninitialize(); +#endif +} diff --git a/libcef/cef_process_sub_thread.h b/libcef/cef_process_sub_thread.h new file mode 100644 index 00000000..31a33bd6 --- /dev/null +++ b/libcef/cef_process_sub_thread.h @@ -0,0 +1,35 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CEF_PROCESS_SUB_THREAD +#define _CEF_PROCESS_SUB_THREAD + +#include "base/basictypes.h" +#include "cef_thread.h" + +// ---------------------------------------------------------------------------- +// CefProcessSubThread +// +// This simple thread object is used for the specialized threads that the +// CefProcess spins up. +// +// Applications must initialize the COM library before they can call +// COM library functions other than CoGetMalloc and memory allocation +// functions, so this class initializes COM for those users. +class CefProcessSubThread : public CefThread { + public: + explicit CefProcessSubThread(CefThread::ID identifier); + CefProcessSubThread(CefThread::ID identifier, MessageLoop* message_loop); + virtual ~CefProcessSubThread(); + + protected: + virtual void Init(); + virtual void CleanUp(); + + private: + DISALLOW_COPY_AND_ASSIGN(CefProcessSubThread); +}; + +#endif // _CEF_PROCESS_SUB_THREAD diff --git a/libcef/cef_process_ui_thread.cc b/libcef/cef_process_ui_thread.cc new file mode 100644 index 00000000..76f14d94 --- /dev/null +++ b/libcef/cef_process_ui_thread.cc @@ -0,0 +1,153 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cef_process_ui_thread.h" +#include "browser_impl.h" +#include "browser_resource_loader_bridge.h" +#include "browser_request_context.h" +#include "browser_webkit_glue.h" +#include "browser_webkit_init.h" +#include "cef_context.h" + +#include "base/command_line.h" +#include "base/i18n/icu_util.h" +#include "base/rand_util.h" +#include "base/stats_table.h" +#include "build/build_config.h" +#include "net/base/net_module.h" +#if defined(OS_WIN) +#include "net/socket/ssl_client_socket_nss_factory.h" +#endif +#include "webkit/extensions/v8/gc_extension.h" + +#if defined(OS_WIN) +#include +#include +#endif + +static const char* kStatsFilePrefix = "libcef_"; +static int kStatsFileThreads = 20; +static int kStatsFileCounters = 200; + +CefProcessUIThread::CefProcessUIThread() + : CefThread(CefThread::UI), statstable_(NULL), webkit_init_(NULL) {} + +CefProcessUIThread::CefProcessUIThread(MessageLoop* message_loop) + : CefThread(CefThread::UI, message_loop), statstable_(NULL), + webkit_init_(NULL) {} + +CefProcessUIThread::~CefProcessUIThread() { + // We cannot rely on our base class to stop the thread since we want our + // CleanUp function to run. + Stop(); +} + +void CefProcessUIThread::Init() { +#if defined(OS_WIN) + HRESULT res; + + // Initialize common controls + res = CoInitialize(NULL); + DCHECK(SUCCEEDED(res)); + INITCOMMONCONTROLSEX InitCtrlEx; + InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX); + InitCtrlEx.dwICC = ICC_STANDARD_CLASSES; + InitCommonControlsEx(&InitCtrlEx); + + // Start COM stuff + res = OleInitialize(NULL); + DCHECK(SUCCEEDED(res)); + + // Register the window class + WNDCLASSEX wcex = { + /* cbSize = */ sizeof(WNDCLASSEX), + /* style = */ CS_HREDRAW | CS_VREDRAW, + /* lpfnWndProc = */ CefBrowserImpl::WndProc, + /* cbClsExtra = */ 0, + /* cbWndExtra = */ 0, + /* hInstance = */ ::GetModuleHandle(NULL), + /* hIcon = */ NULL, + /* hCursor = */ LoadCursor(NULL, IDC_ARROW), + /* hbrBackground = */ 0, + /* lpszMenuName = */ NULL, + /* lpszClassName = */ CefBrowserImpl::GetWndClass(), + /* hIconSm = */ NULL, + }; + RegisterClassEx(&wcex); +#endif + +#ifndef _DEBUG + // Only log error messages and above in release build. + logging::SetMinLogLevel(logging::LOG_ERROR); +#endif + + // Initialize the global CommandLine object. + CommandLine::Init(0, NULL); + + // Initialize WebKit. + webkit_init_ = new BrowserWebKitInit(); + + // Initialize WebKit encodings + webkit_glue::InitializeTextEncoding(); + + // Load ICU data tables. + bool ret = icu_util::Initialize(); + if(!ret) { +#if defined(OS_WIN) + MessageBox(NULL, L"Failed to load the required icudt38 library", + L"CEF Initialization Error", MB_ICONERROR | MB_OK); +#endif + return; + } + + // Config the network module so it has access to a limited set of resources. + net::NetModule::SetResourceProvider(webkit_glue::NetResourceProvider); + + // Load and initialize the stats table. Attempt to construct a somewhat + // unique name to isolate separate instances from each other. + statstable_ = new StatsTable( + kStatsFilePrefix + Uint64ToString(base::RandUint64()), + kStatsFileThreads, + kStatsFileCounters); + StatsTable::set_current(statstable_); + + // CEF always exposes the GC. + webkit_glue::SetJavaScriptFlags(L"--expose-gc"); + // Expose GCController to JavaScript. + WebKit::WebScriptController::registerExtension( + extensions_v8::GCExtension::Get()); + +#if defined(OS_WIN) + // Use NSS for SSL on Windows. TODO(wtc): this should eventually be hidden + // inside DefaultClientSocketFactory::CreateSSLClientSocket. + net::ClientSocketFactory::SetSSLClientSocketFactory( + net::SSLClientSocketNSSFactory); +#endif +} + +void CefProcessUIThread::CleanUp() { + // Flush any remaining messages. This ensures that any accumulated + // Task objects get destroyed before we exit, which avoids noise in + // purify leak-test results. + MessageLoop::current()->RunAllPending(); + + // Tear down the shared StatsTable. + StatsTable::set_current(NULL); + delete statstable_; + statstable_ = NULL; + + // Shut down WebKit. + delete webkit_init_; + webkit_init_ = NULL; + +#if defined(OS_WIN) + // Uninitialize COM stuff + OleUninitialize(); + + // Closes the COM library on the current thread. CoInitialize must + // be balanced by a corresponding call to CoUninitialize. + CoUninitialize(); +#endif +} diff --git a/libcef/cef_process_ui_thread.h b/libcef/cef_process_ui_thread.h new file mode 100644 index 00000000..e7fd07ac --- /dev/null +++ b/libcef/cef_process_ui_thread.h @@ -0,0 +1,42 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CEF_PROCESS_UI_THREAD +#define _CEF_PROCESS_UI_THREAD + +#include "base/basictypes.h" +#include "cef_thread.h" + +class BrowserWebKitInit; +class StatsTable; + +// ---------------------------------------------------------------------------- +// CefProcessUIThread +// +// This simple thread object is used for the specialized threads that the +// CefProcess spins up. +// +// Applications must initialize the COM library before they can call +// COM library functions other than CoGetMalloc and memory allocation +// functions, so this class initializes COM for those users. +class CefProcessUIThread : public CefThread { + public: + explicit CefProcessUIThread(); + CefProcessUIThread(MessageLoop* message_loop); + virtual ~CefProcessUIThread(); + + virtual void Init(); + virtual void CleanUp(); + + private: + StatsTable* statstable_; + + // WebKit implementation class. + BrowserWebKitInit* webkit_init_; + + DISALLOW_COPY_AND_ASSIGN(CefProcessUIThread); +}; + +#endif // _CEF_PROCESS_UI_THREAD diff --git a/libcef/cef_thread.cc b/libcef/cef_thread.cc new file mode 100644 index 00000000..975d8ecc --- /dev/null +++ b/libcef/cef_thread.cc @@ -0,0 +1,210 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cef_thread.h" + +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" + +using base::MessageLoopProxy; + +// Friendly names for the well-known threads. +static const char* cef_thread_names[CefThread::ID_COUNT] = { + "Cef_UIThread", // UI + "Cef_FileThread", // FILE + "Cef_IOThread", // IO +}; + +// An implementation of MessageLoopProxy to be used in conjunction +// with CefThread. +class CefThreadMessageLoopProxy : public MessageLoopProxy { + public: + explicit CefThreadMessageLoopProxy(CefThread::ID identifier) + : id_(identifier) { + } + + // MessageLoopProxy implementation. + virtual bool PostTask(const tracked_objects::Location& from_here, + Task* task) { + return CefThread::PostTask(id_, from_here, task); + } + + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, + Task* task, int64 delay_ms) { + return CefThread::PostDelayedTask(id_, from_here, task, delay_ms); + } + + virtual bool PostNonNestableTask(const tracked_objects::Location& from_here, + Task* task) { + return CefThread::PostNonNestableTask(id_, from_here, task); + } + + virtual bool PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms) { + return CefThread::PostNonNestableDelayedTask(id_, from_here, task, + delay_ms); + } + virtual bool BelongsToCurrentThread() { + return CefThread::CurrentlyOn(id_); + } + + private: + CefThread::ID id_; + DISALLOW_COPY_AND_ASSIGN(CefThreadMessageLoopProxy); +}; + + +Lock CefThread::lock_; + +CefThread* CefThread::cef_threads_[ID_COUNT]; + +CefThread::CefThread(CefThread::ID identifier) + : Thread(cef_thread_names[identifier]), + identifier_(identifier) { + Initialize(); +} + +CefThread::CefThread(ID identifier, MessageLoop* message_loop) + : Thread(cef_thread_names[identifier]), + identifier_(identifier) { + message_loop->set_thread_name(cef_thread_names[identifier]); + set_message_loop(message_loop); + Initialize(); +} + +void CefThread::Initialize() { + AutoLock lock(lock_); + DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT); + DCHECK(cef_threads_[identifier_] == NULL); + cef_threads_[identifier_] = this; +} + +CefThread::~CefThread() { + // Stop the thread here, instead of the parent's class destructor. This is so + // that if there are pending tasks that run, code that checks that it's on the + // correct CefThread succeeds. + Stop(); + + AutoLock lock(lock_); + cef_threads_[identifier_] = NULL; +#ifndef NDEBUG + // Double check that the threads are ordererd correctly in the enumeration. + for (int i = identifier_ + 1; i < ID_COUNT; ++i) { + DCHECK(!cef_threads_[i]) << + "Threads must be listed in the reverse order that they die"; + } +#endif +} + +// static +bool CefThread::IsWellKnownThread(ID identifier) { + AutoLock lock(lock_); + return (identifier >= 0 && identifier < ID_COUNT && + cef_threads_[identifier]); +} + +// static +bool CefThread::CurrentlyOn(ID identifier) { + AutoLock lock(lock_); + DCHECK(identifier >= 0 && identifier < ID_COUNT); + return cef_threads_[identifier] && + cef_threads_[identifier]->message_loop() == MessageLoop::current(); +} + +// static +bool CefThread::PostTask(ID identifier, + const tracked_objects::Location& from_here, + Task* task) { + return PostTaskHelper(identifier, from_here, task, 0, true); +} + +// static +bool CefThread::PostDelayedTask(ID identifier, + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms) { + return PostTaskHelper(identifier, from_here, task, delay_ms, true); +} + +// static +bool CefThread::PostNonNestableTask( + ID identifier, + const tracked_objects::Location& from_here, + Task* task) { + return PostTaskHelper(identifier, from_here, task, 0, false); +} + +// static +bool CefThread::PostNonNestableDelayedTask( + ID identifier, + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms) { + return PostTaskHelper(identifier, from_here, task, delay_ms, false); +} + +// static +bool CefThread::GetCurrentThreadIdentifier(ID* identifier) { + MessageLoop* cur_message_loop = MessageLoop::current(); + for (int i = 0; i < ID_COUNT; ++i) { + if (cef_threads_[i] && + cef_threads_[i]->message_loop() == cur_message_loop) { + *identifier = cef_threads_[i]->identifier_; + return true; + } + } + + return false; +} + +// static +scoped_refptr CefThread::GetMessageLoopProxyForThread( + ID identifier) { + scoped_refptr proxy = + new CefThreadMessageLoopProxy(identifier); + return proxy; +} + +// static +bool CefThread::PostTaskHelper( + ID identifier, + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms, + bool nestable) { + DCHECK(identifier >= 0 && identifier < ID_COUNT); + // Optimization: to avoid unnecessary locks, we listed the ID enumeration in + // order of lifetime. So no need to lock if we know that the other thread + // outlives this one. + // Note: since the array is so small, ok to loop instead of creating a map, + // which would require a lock because std::map isn't thread safe, defeating + // the whole purpose of this optimization. + ID current_thread; + bool guaranteed_to_outlive_target_thread = + GetCurrentThreadIdentifier(¤t_thread) && + current_thread >= identifier; + + if (!guaranteed_to_outlive_target_thread) + lock_.Acquire(); + + MessageLoop* message_loop = cef_threads_[identifier] ? + cef_threads_[identifier]->message_loop() : NULL; + if (message_loop) { + if (nestable) { + message_loop->PostDelayedTask(from_here, task, delay_ms); + } else { + message_loop->PostNonNestableDelayedTask(from_here, task, delay_ms); + } + } else { + delete task; + } + + if (!guaranteed_to_outlive_target_thread) + lock_.Release(); + + return !!message_loop; +} diff --git a/libcef/cef_thread.h b/libcef/cef_thread.h new file mode 100644 index 00000000..ff655a4b --- /dev/null +++ b/libcef/cef_thread.h @@ -0,0 +1,185 @@ +// Copyright (c) 2010 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CEF_THREAD_H +#define _CEF_THREAD_H + +#include "base/lock.h" +#include "base/task.h" +#include "base/thread.h" + +namespace base { +class MessageLoopProxy; +} + +/////////////////////////////////////////////////////////////////////////////// +// CefThread +// +// This class represents a thread that is known by a browser-wide name. For +// example, there is one IO thread for the entire browser process, and various +// pieces of code find it useful to retrieve a pointer to the IO thread's +// Invoke a task by thread ID: +// +// CefThread::PostTask(CefThread::IO, FROM_HERE, task); +// +// The return value is false if the task couldn't be posted because the target +// thread doesn't exist. If this could lead to data loss, you need to check the +// result and restructure the code to ensure it doesn't occur. +// +// This class automatically handles the lifetime of different threads. +// It's always safe to call PostTask on any thread. If it's not yet created, +// the task is deleted. There are no race conditions. If the thread that the +// task is posted to is guaranteed to outlive the current thread, then no locks +// are used. You should never need to cache pointers to MessageLoops, since +// they're not thread safe. +class CefThread : public base::Thread { + public: + // An enumeration of the well-known threads. + // NOTE: threads must be listed in the order of their life-time, with each + // thread outliving every other thread below it. + enum ID { + // The main thread in the browser. + UI, + + // This is the thread that interacts with the file system. + FILE, + + // This is the thread that processes IPC and network messages. + IO, + + // This identifier does not represent a thread. Instead it counts the + // number of well-known threads. Insert new well-known threads before this + // identifier. + ID_COUNT + }; + + // Construct a CefThread with the supplied identifier. It is an error + // to construct a CefThread that already exists. + explicit CefThread(ID identifier); + + // Special constructor for the main (UI) thread and unittests. We use a dummy + // thread here since the main thread already exists. + CefThread(ID identifier, MessageLoop* message_loop); + + virtual ~CefThread(); + + // These are the same methods in message_loop.h, but are guaranteed to either + // get posted to the MessageLoop if it's still alive, or be deleted otherwise. + // They return true if the thread existed and the task was posted. Note that + // even if the task is posted, there's no guarantee that it will run, since + // the target thread may already have a Quit message in its queue. + static bool PostTask(ID identifier, + const tracked_objects::Location& from_here, + Task* task); + static bool PostDelayedTask(ID identifier, + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms); + static bool PostNonNestableTask(ID identifier, + const tracked_objects::Location& from_here, + Task* task); + static bool PostNonNestableDelayedTask( + ID identifier, + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms); + + template + static bool DeleteSoon(ID identifier, + const tracked_objects::Location& from_here, + T* object) { + return PostNonNestableTask( + identifier, from_here, new DeleteTask(object)); + } + + template + static bool ReleaseSoon(ID identifier, + const tracked_objects::Location& from_here, + T* object) { + return PostNonNestableTask( + identifier, from_here, new ReleaseTask(object)); + } + + // Callable on any thread. Returns whether the given ID corresponds to a well + // known thread. + static bool IsWellKnownThread(ID identifier); + + // Callable on any thread. Returns whether you're currently on a particular + // thread. + static bool CurrentlyOn(ID identifier); + + // If the current message loop is one of the known threads, returns true and + // sets identifier to its ID. Otherwise returns false. + static bool GetCurrentThreadIdentifier(ID* identifier); + + // Callers can hold on to a refcounted MessageLoopProxy beyond the lifetime + // of the thread. + static scoped_refptr GetMessageLoopProxyForThread( + ID identifier); + + // Use these templates in conjuction with RefCountedThreadSafe when you want + // to ensure that an object is deleted on a specific thread. This is needed + // when an object can hop between threads (i.e. IO -> FILE -> IO), and thread + // switching delays can mean that the final IO tasks executes before the FILE + // task's stack unwinds. This would lead to the object destructing on the + // FILE thread, which often is not what you want (i.e. to unregister from + // NotificationService, to notify other objects on the creating thread etc). + template + struct DeleteOnThread { + template + static void Destruct(T* x) { + if (CurrentlyOn(thread)) { + delete x; + } else { + DeleteSoon(thread, FROM_HERE, x); + } + } + }; + + // Sample usage: + // class Foo + // : public base::RefCountedThreadSafe< + // Foo, CefThread::DeleteOnIOThread> { + // + // ... + // private: + // friend class CefThread; + // friend class DeleteTask; + // + // ~Foo(); + struct DeleteOnUIThread : public DeleteOnThread { }; + struct DeleteOnIOThread : public DeleteOnThread { }; + struct DeleteOnFileThread : public DeleteOnThread { }; + + private: + // Common initialization code for the constructors. + void Initialize(); + + static bool PostTaskHelper( + ID identifier, + const tracked_objects::Location& from_here, + Task* task, + int64 delay_ms, + bool nestable); + + // The identifier of this thread. Only one thread can exist with a given + // identifier at a given time. + ID identifier_; + + // This lock protects |cef_threads_|. Do not read or modify that array + // without holding this lock. Do not block while holding this lock. + static Lock lock_; + + // An array of the CefThread objects. This array is protected by |lock_|. + // The threads are not owned by this array. Typically, the threads are owned + // on the UI thread by the g_browser_process object. CefThreads remove + // themselves from this array upon destruction. + static CefThread* cef_threads_[ID_COUNT]; +}; + +#define REQUIRE_UIT() DCHECK(CefThread::CurrentlyOn(CefThread::UI)) +#define REQUIRE_IOT() DCHECK(CefThread::CurrentlyOn(CefThread::IO)) + +#endif // _CEF_THREAD_H diff --git a/libcef/context.cc b/libcef/context.cc deleted file mode 100644 index d9dd3ef6..00000000 --- a/libcef/context.cc +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright (c) 2008-2009 The Chromium Embedded Framework Authors. -// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "context.h" -#include "browser_impl.h" -#include "browser_resource_loader_bridge.h" -#include "browser_request_context.h" -#include "browser_webkit_glue.h" -#include "browser_webkit_init.h" -#include "../include/cef_nplugin.h" - -#include "base/command_line.h" -#include "base/i18n/icu_util.h" -#include "base/path_service.h" -#include "base/rand_util.h" -#include "base/resource_util.h" -#include "base/stats_table.h" -#include "base/utf_string_conversions.h" -#include "net/base/net_module.h" -#include "third_party/WebKit/WebKit/chromium/public/WebScriptController.h" -#include "webkit/extensions/v8/gc_extension.h" -#include "webkit/glue/plugins/webplugin.h" -#include "webkit/glue/plugins/plugin_lib.h" -#include "webkit/glue/plugins/plugin_list.h" - -#include - - -// Global CefContext pointer -CefRefPtr _Context; - -static const char* kStatsFilePrefix = "libcef_"; -static int kStatsFileThreads = 20; -static int kStatsFileCounters = 200; - - -// 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, - const std::wstring& cache_path) -{ - // Return true if the context is already initialized - if(_Context.get()) - return true; - - // Create the new global context object - _Context = new CefContext(); - // Initialize the glboal context - return _Context->Initialize(multi_threaded_message_loop, cache_path); -} - -void CefShutdown() -{ - // Verify that the context is already initialized - if(!_Context.get()) - return; - - // Shut down the global context - _Context->Shutdown(); - // Delete the global context object - _Context = NULL; -} - -void CefDoMessageLoopWork() -{ - if(!_Context->RunningOnUIThread()) - return; - ((CefMessageLoopForUI*)CefMessageLoopForUI::current())->DoMessageLoopIteration(); -} - -bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info) -{ - if(!_Context.get()) - return false; - - CefPluginInfo* pPluginInfo = new CefPluginInfo; - *pPluginInfo = plugin_info; - - PostTask(FROM_HERE, NewRunnableMethod(_Context.get(), - &CefContext::UIT_RegisterPlugin, pPluginInfo)); - - return true; -} - -void CefContext::UIT_RegisterPlugin(struct CefPluginInfo* plugin_info) -{ - REQUIRE_UIT(); - - NPAPI::PluginVersionInfo info; - - info.path = FilePath(plugin_info->unique_name); - info.product_name = plugin_info->display_name; - info.file_description = plugin_info->description; - info.file_version =plugin_info->version; - - for(size_t i = 0; i < plugin_info->mime_types.size(); ++i) { - if(i > 0) { - info.mime_types += L"|"; - info.file_extensions += L"|"; - info.type_descriptions += L"|"; - } - - info.mime_types += plugin_info->mime_types[i].mime_type; - info.type_descriptions += plugin_info->mime_types[i].description; - - for(size_t j = 0; - j < plugin_info->mime_types[i].file_extensions.size(); ++j) { - if(j > 0) { - info.file_extensions += L","; - } - info.file_extensions += plugin_info->mime_types[i].file_extensions[j]; - } - } - - info.entry_points.np_getentrypoints = plugin_info->np_getentrypoints; - info.entry_points.np_initialize = plugin_info->np_initialize; - info.entry_points.np_shutdown = plugin_info->np_shutdown; - - NPAPI::PluginList::Singleton()->RegisterInternalPlugin(info); - - delete plugin_info; -} - -bool CefContext::DoInitialize() -{ - HRESULT res; - - // Initialize common controls - res = CoInitialize(NULL); - DCHECK(SUCCEEDED(res)); - INITCOMMONCONTROLSEX InitCtrlEx; - InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX); - InitCtrlEx.dwICC = ICC_STANDARD_CLASSES; - InitCommonControlsEx(&InitCtrlEx); - - // Start COM stuff - res = OleInitialize(NULL); - DCHECK(SUCCEEDED(res)); - - // Initialize the global CommandLine object. - CommandLine::Init(0, NULL); - - // Initialize WebKit. - webkit_init_ = new BrowserWebKitInit(); - - // Initialize WebKit encodings - webkit_glue::InitializeTextEncoding(); - - // Initializing with a default context, which means no on-disk cookie DB, - // and no support for directory listings. - //PathService::Get(base::DIR_EXE, &cache_path); - BrowserResourceLoaderBridge::Init(FilePath(cache_path_), net::HttpCache::NORMAL, - false); - - // Load ICU data tables. - bool ret = icu_util::Initialize(); - if(!ret) { - MessageBox(NULL, L"Failed to load the required icudt38 library", - L"CEF Initialization Error", MB_ICONERROR | MB_OK); - return false; - } - - // Config the network module so it has access to a limited set of resources. - net::NetModule::SetResourceProvider(webkit_glue::NetResourceProvider); - - // Load and initialize the stats table. Attempt to construct a somewhat - // unique name to isolate separate instances from each other. - statstable_ = new StatsTable( - kStatsFilePrefix + Uint64ToString(base::RandUint64()), - kStatsFileThreads, - kStatsFileCounters); - StatsTable::set_current(statstable_); - - // CEF always exposes the GC. - webkit_glue::SetJavaScriptFlags(L"--expose-gc"); - // Expose GCController to JavaScript. - WebKit::WebScriptController::registerExtension( - extensions_v8::GCExtension::Get()); - - 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. - MessageLoop::current()->RunAllPending(); - - BrowserResourceLoaderBridge::Shutdown(); - - // Tear down the shared StatsTable. - StatsTable::set_current(NULL); - delete statstable_; - statstable_ = NULL; - - // Shut down WebKit. - delete webkit_init_; - webkit_init_ = NULL; - - // Uninitialize COM stuff - OleUninitialize(); - - // Uninitialize common controls - CoUninitialize(); -} - -DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) -{ - CefContext *pContext = static_cast(lpParam); - - // AtExitManager is scoped to the UI thread. - base::AtExitManager at_exit_manager; - - 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; -} - -CefContext::CefContext() -{ - multi_threaded_message_loop_ = false; - hthreadui_ = NULL; - idthreadui_ = 0; - heventui_ = NULL; - messageloopui_ = NULL; - in_transition_ = false; - webprefs_ = NULL; - statstable_ = NULL; - at_exit_manager_ = NULL; - hinstance_ = ::GetModuleHandle(NULL); - next_browser_id_ = 1; - webkit_init_ = NULL; -} - -CefContext::~CefContext() -{ - // Just in case CefShutdown() isn't called - Shutdown(); - if(!multi_threaded_message_loop_) { - if(messageloopui_) - delete messageloopui_; - if(at_exit_manager_) - delete at_exit_manager_; - } -} - -bool CefContext::Initialize(bool multi_threaded_message_loop, - const std::wstring& cache_path) -{ - bool initialized = false, intransition = false; - - Lock(); - - // We only need to initialize if the UI thread is not currently running - if(!idthreadui_) { - // Only start the initialization if we're not currently in a transitional - // state - intransition = in_transition_; - if(!intransition) { - // We are now in a transitional state - in_transition_ = true; - - cache_path_ = cache_path; - - // Register the window class - WNDCLASSEX wcex = { - /* cbSize = */ sizeof(WNDCLASSEX), - /* style = */ CS_HREDRAW | CS_VREDRAW, - /* lpfnWndProc = */ CefBrowserImpl::WndProc, - /* cbClsExtra = */ 0, - /* cbWndExtra = */ 0, - /* hInstance = */ hinstance_, - /* hIcon = */ NULL, - /* hCursor = */ LoadCursor(NULL, IDC_ARROW), - /* hbrBackground = */ 0, - /* lpszMenuName = */ NULL, - /* lpszClassName = */ CefBrowserImpl::GetWndClass(), - /* hIconSm = */ NULL, - }; - RegisterClassEx(&wcex); - -#ifndef _DEBUG - // Only log error messages and above in release build. - logging::SetMinLogLevel(logging::LOG_ERROR); -#endif - - // Initialize web preferences - webprefs_ = new WebPreferences; - *webprefs_ = WebPreferences(); - webprefs_->standard_font_family = L"Times"; - webprefs_->fixed_font_family = L"Courier"; - webprefs_->serif_font_family = L"Times"; - webprefs_->sans_serif_font_family = L"Helvetica"; - // These two fonts are picked from the intersection of - // Win XP font list and Vista font list : - // http://www.microsoft.com/typography/fonts/winxp.htm - // http://blogs.msdn.com/michkap/archive/2006/04/04/567881.aspx - // Some of them are installed only with CJK and complex script - // support enabled on Windows XP and are out of consideration here. - // (although we enabled both on our buildbots.) - // They (especially Impact for fantasy) are not typical cursive - // and fantasy fonts, but it should not matter for layout tests - // as long as they're available. -#if defined(OS_MACOSX) - webprefs_->cursive_font_family = L"Apple Chancery"; - webprefs_->fantasy_font_family = L"Papyrus"; -#else - webprefs_->cursive_font_family = L"Comic Sans MS"; - webprefs_->fantasy_font_family = L"Impact"; -#endif - webprefs_->default_encoding = "ISO-8859-1"; - webprefs_->default_font_size = 16; - webprefs_->default_fixed_font_size = 13; - webprefs_->minimum_font_size = 1; - webprefs_->minimum_logical_font_size = 9; - webprefs_->javascript_can_open_windows_automatically = true; - webprefs_->dom_paste_enabled = true; - webprefs_->developer_extras_enabled = true; - webprefs_->site_specific_quirks_enabled = true; - webprefs_->shrinks_standalone_images_to_fit = false; - webprefs_->uses_universal_detector = false; - webprefs_->text_areas_are_resizable = true; - webprefs_->java_enabled = true; - webprefs_->allow_scripts_to_close_windows = false; - webprefs_->xss_auditor_enabled = false; - webprefs_->remote_fonts_enabled = true; - webprefs_->local_storage_enabled = true; - webprefs_->application_cache_enabled = true; - webprefs_->databases_enabled = true; - webprefs_->allow_file_access_from_file_urls = true; - - 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); - DCHECK(idthreadui_ != 0); - } else { - // AtExitManager is scoped to the context. - at_exit_manager_ = new base::AtExitManager; - if (!DoInitialize()) { - // TODO: Process initialization errors - } - // Message loop is scoped to the context. - SetMessageLoopForUI(new CefMessageLoopForUI()); - idthreadui_ = GetCurrentThreadId(); - DCHECK(idthreadui_ != 0); - } - - initialized = true; - } - } - - Unlock(); - - if(initialized) { - multi_threaded_message_loop_ = multi_threaded_message_loop; - - if (multi_threaded_message_loop) { - // Wait for initial UI thread setup to complete - WaitForSingleObject(heventui_, INFINITE); - } - - Lock(); - - // We have exited the transitional state - in_transition_ = false; - - Unlock(); - } - - return intransition ? false : true; -} - -void CefContext::Shutdown() -{ - bool shutdown = false, intransition = false; - BrowserList browserlist; - - Lock(); - - // We only need to shut down if the UI thread is currently running - if(idthreadui_) { - // Only start the shutdown if we're not currently in a transitional state - intransition = in_transition_; - if(!intransition) { - DCHECK(messageloopui_ != NULL); - - // We are now in a transitional state - in_transition_ = true; - - browserlist = browserlist_; - browserlist_.empty(); - - if(webprefs_) { - delete webprefs_; - webprefs_ = NULL; - } - - // Post the quit message to the UI message loop - messageloopui_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); - - shutdown = true; - } - } - - Unlock(); - - if(shutdown) { - if (browserlist.size() > 0) { - // Close any remaining browser windows - BrowserList::const_iterator it = browserlist.begin(); - for (; it != browserlist.end(); ++it) - PostMessage((*it)->GetWindowHandle(), WM_CLOSE, 0, 0); - browserlist.empty(); - } - - if (hthreadui_) { - // Wait for the UI thread to exit - WaitForSingleObject(hthreadui_, INFINITE); - - // Clean up thread and event handles - CloseHandle(hthreadui_); - CloseHandle(heventui_); - - hthreadui_ = NULL; - heventui_ = NULL; - } else { - DoUninitialize(); - } - - Lock(); - - // Unregister the window class - UnregisterClass(CefBrowserImpl::GetWndClass(), hinstance_); - - idthreadui_ = 0; - - // We have exited the transitional state - in_transition_ = false; - - Unlock(); - } -} - -bool CefContext::AddBrowser(CefRefPtr browser) -{ - bool found = false; - - Lock(); - - // check that the browser isn't already in the list before adding - BrowserList::const_iterator it = browserlist_.begin(); - for(; it != browserlist_.end(); ++it) { - if(it->get() == browser.get()) { - found = true; - break; - } - } - - if(!found) - { - browser->UIT_SetUniqueID(next_browser_id_++); - browserlist_.push_back(browser); - } - - Unlock(); - return !found; -} - -void CefContext::UIT_ClearCache() -{ - webkit_glue::ClearCache(); -} - -bool CefContext::RemoveBrowser(CefRefPtr browser) -{ - bool deleted = false; - bool empty = false; - - Lock(); - - BrowserList::iterator it = browserlist_.begin(); - for(; it != browserlist_.end(); ++it) { - if(it->get() == browser.get()) { - browserlist_.erase(it); - deleted = true; - break; - } - } - - if (browserlist_.empty()) { - next_browser_id_ = 1; - empty = true; - } - - Unlock(); - - if (empty) { - PostTask(FROM_HERE, NewRunnableMethod(_Context.get(), - &CefContext::UIT_ClearCache)); - } - - return deleted; -} - -CefRefPtr CefContext::GetBrowserByID(int id) -{ - CefRefPtr browser; - Lock(); - - BrowserList::const_iterator it = browserlist_.begin(); - for(; it != browserlist_.end(); ++it) { - if(it->get()->UIT_GetUniqueID() == id) { - browser = it->get(); - break; - } - } - - Unlock(); - - return browser; -} - -void CefContext::SetMessageLoopForUI(MessageLoopForUI* loop) -{ - Lock(); - messageloopui_ = loop; - Unlock(); -} - -void CefContext::NotifyEvent() -{ - Lock(); - // Set the event state to signaled so that the waiting thread will be - // released. - if(heventui_) - SetEvent(heventui_); - Unlock(); -} - -void PostTask(const tracked_objects::Location& from_here, Task* task) -{ - if(_Context.get()) { - _Context->Lock(); - MessageLoopForUI *pMsgLoop = _Context->GetMessageLoopForUI(); - if(pMsgLoop) - pMsgLoop->PostTask(from_here, task); - _Context->Unlock(); - } -} diff --git a/libcef/context.h b/libcef/context.h deleted file mode 100644 index 9a1e1e23..00000000 --- a/libcef/context.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2008 The Chromium Embedded Framework Authors. -// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef _CONTEXT_H -#define _CONTEXT_H - -#include "../include/cef.h" -#include "base/at_exit.h" -#include "base/message_loop.h" -#include "base/stats_table.h" -#include "gfx/native_widget_types.h" -#include "webkit/glue/webpreferences.h" - -class BrowserWebKitInit; -class CefBrowserImpl; - -class CefContext : public CefThreadSafeBase -{ -public: - typedef std::list > BrowserList; - - CefContext(); - ~CefContext(); - - bool Initialize(bool multi_threaded_message_loop, - const std::wstring& cache_path); - void Shutdown(); - - MessageLoopForUI* GetMessageLoopForUI() { return messageloopui_; } - - HMODULE GetInstanceHandle() { return hinstance_; } - HANDLE GetUIThreadHandle() { return hthreadui_; } - DWORD GetUIThreadId() { return idthreadui_; } - WebPreferences* GetWebPreferences() { return webprefs_; } - - // Retrieve the path at which cache data will be stored on disk. If empty, - // cache data will be stored in-memory. - std::wstring GetCachePath() { return cache_path_; } - - bool AddBrowser(CefRefPtr browser); - bool RemoveBrowser(CefRefPtr browser); - CefRefPtr GetBrowserByID(int id); - BrowserList* GetBrowserList() { return &browserlist_; } - - // Returns true if the calling thread is the same as the UI thread - bool RunningOnUIThread() { return (GetCurrentThreadId() == idthreadui_); } - - //////////////////////////////////////////////////////////// - // ALL UIT_* METHODS MUST ONLY BE CALLED ON THE UI THREAD // - //////////////////////////////////////////////////////////// - - void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info); - void UIT_UnregisterPlugin(struct CefPluginInfo* plugin_info); - void UIT_ClearCache(); - - bool DoWork(); - bool DoDelayedWork(); - bool DoIdleWork(); - - static bool ImplementsThreadSafeReferenceCounting() { return true; } - -private: - void SetMessageLoopForUI(MessageLoopForUI* loop); - void NotifyEvent(); - - bool DoInitialize(); - void DoUninitialize(); - -protected: - bool multi_threaded_message_loop_; - - HMODULE hinstance_; - DWORD idthreadui_; - HANDLE hthreadui_; - HANDLE heventui_; - bool in_transition_; - BrowserList browserlist_; - WebPreferences* webprefs_; - StatsTable* statstable_; - std::wstring cache_path_; - - // MessageLoopForUI will be scoped to the context when running in single - // threaded message loop mode. - MessageLoopForUI* messageloopui_; - - // AtExitManager will be scoped to the context when running in single threaded - // message loop mode. - base::AtExitManager* at_exit_manager_; - - // WebKit implementation class. - BrowserWebKitInit* webkit_init_; - - // Used for assigning unique IDs to browser instances. - int next_browser_id_; - - friend DWORD WINAPI ThreadHandlerUI(LPVOID lpParam); -}; - -// Post a task to the UI message loop -void PostTask(const tracked_objects::Location& from_here, Task* task); - -// Global context object pointer -extern CefRefPtr _Context; - -// Macro for requiring that a function be called on the UI thread -#define REQUIRE_UIT() DCHECK(_Context->RunningOnUIThread()) - -#endif // _CONTEXT_H diff --git a/libcef/scheme_impl.cc b/libcef/scheme_impl.cc index 3bf6d205..6af4f7a2 100644 --- a/libcef/scheme_impl.cc +++ b/libcef/scheme_impl.cc @@ -7,7 +7,6 @@ #include "base/logging.h" #include "base/message_loop.h" #include "base/utf_string_conversions.h" -#include "base/worker_pool.h" #include "googleurl/src/url_util.h" #include "net/base/completion_callback.h" #include "net/base/io_buffer.h" @@ -19,7 +18,7 @@ #include "include/cef.h" #include "tracker.h" -#include "context.h" +#include "cef_context.h" #include "request_impl.h" #include @@ -68,8 +67,8 @@ public: // Continue asynchronously. DCHECK(!async_resolver_); async_resolver_ = new AsyncResolver(this); - WorkerPool::PostTask(FROM_HERE, NewRunnableMethod( - async_resolver_.get(), &AsyncResolver::Resolve, url_), true); + CefThread::PostTask(CefThread::IO, FROM_HERE, NewRunnableMethod( + async_resolver_.get(), &AsyncResolver::Resolve, url_)); return; } @@ -411,7 +410,7 @@ bool CefRegisterScheme(const std::wstring& scheme_name, new SchemeRequestJobWrapper(WideToUTF8(scheme_name), WideToUTF8(host_name), factory)); - PostTask(FROM_HERE, NewRunnableMethod(wrapper.get(), + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(wrapper.get(), &SchemeRequestJobWrapper::RegisterScheme)); return true; diff --git a/libcef/v8_impl.cc b/libcef/v8_impl.cc index b4afa682..912c6be5 100644 --- a/libcef/v8_impl.cc +++ b/libcef/v8_impl.cc @@ -3,7 +3,7 @@ // can be found in the LICENSE file. #include "v8_impl.h" -#include "context.h" +#include "cef_context.h" #include "tracker.h" #include "base/lazy_instance.h" #include "base/utf_string_conversions.h" @@ -156,7 +156,7 @@ bool CefRegisterExtension(const std::wstring& extension_name, ExtensionWrapper* wrapper = new ExtensionWrapper(name->GetString(), code->GetString(), handler.get()); - PostTask(FROM_HERE, NewRunnableMethod(wrapper, + CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(wrapper, &ExtensionWrapper::UIT_RegisterExtension)); return true; } diff --git a/libcef_dll/cpptoc/task_cpptoc.cc b/libcef_dll/cpptoc/task_cpptoc.cc new file mode 100644 index 00000000..c8dd5ba3 --- /dev/null +++ b/libcef_dll/cpptoc/task_cpptoc.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// A portion of this file was generated by the CEF translator tool. When +// making changes by hand only do so within the body of existing function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// + +#include "libcef_dll/cpptoc/task_cpptoc.h" + + +// MEMBER FUNCTIONS - Body may be edited by hand. + +void CEF_CALLBACK task_execute(struct _cef_task_t* self, + cef_thread_id_t threadId) +{ + DCHECK(self); + if(!self) + return; + + CefTaskCppToC::Get(self)->Execute(threadId); +} + + +// CONSTRUCTOR - Do not edit by hand. + +CefTaskCppToC::CefTaskCppToC(CefTask* cls) + : CefCppToC(cls) +{ + struct_.struct_.execute = task_execute; +} + +#ifdef _DEBUG +long CefCppToC::DebugObjCt = 0; +#endif + diff --git a/libcef_dll/cpptoc/task_cpptoc.h b/libcef_dll/cpptoc/task_cpptoc.h new file mode 100644 index 00000000..40a42dec --- /dev/null +++ b/libcef_dll/cpptoc/task_cpptoc.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +#ifndef _TASK_CPPTOC_H +#define _TASK_CPPTOC_H + +#ifndef USING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed wrapper-side only") +#else // USING_CEF_SHARED + +#include "include/cef.h" +#include "include/cef_capi.h" +#include "libcef_dll/cpptoc/cpptoc.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed wrapper-side only. +class CefTaskCppToC + : public CefCppToC +{ +public: + CefTaskCppToC(CefTask* cls); + virtual ~CefTaskCppToC() {} +}; + +#endif // USING_CEF_SHARED +#endif // _TASK_CPPTOC_H + diff --git a/libcef_dll/ctocpp/task_ctocpp.cc b/libcef_dll/ctocpp/task_ctocpp.cc new file mode 100644 index 00000000..1597a252 --- /dev/null +++ b/libcef_dll/ctocpp/task_ctocpp.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// A portion of this file was generated by the CEF translator tool. When +// making changes by hand only do so within the body of existing static and +// virtual method implementations. See the translator.README.txt file in the +// tools directory for more information. +// + +#include "libcef_dll/ctocpp/task_ctocpp.h" + + +// VIRTUAL METHODS - Body may be edited by hand. + +void CefTaskCToCpp::Execute(CefThreadId threadId) +{ + if(CEF_MEMBER_MISSING(struct_, execute)) + return; + + struct_->execute(struct_, threadId); +} + + +#ifdef _DEBUG +long CefCToCpp::DebugObjCt = 0; +#endif + diff --git a/libcef_dll/ctocpp/task_ctocpp.h b/libcef_dll/ctocpp/task_ctocpp.h new file mode 100644 index 00000000..24f0c2ee --- /dev/null +++ b/libcef_dll/ctocpp/task_ctocpp.h @@ -0,0 +1,39 @@ +// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// ------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// + +#ifndef _TASK_CTOCPP_H +#define _TASK_CTOCPP_H + +#ifndef BUILDING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed DLL-side only") +#else // BUILDING_CEF_SHARED + +#include "include/cef.h" +#include "include/cef_capi.h" +#include "libcef_dll/ctocpp/ctocpp.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed DLL-side only. +class CefTaskCToCpp + : public CefCToCpp +{ +public: + CefTaskCToCpp(cef_task_t* str) + : CefCToCpp(str) {} + virtual ~CefTaskCToCpp() {} + + // CefTask methods + virtual void Execute(CefThreadId threadId); +}; + +#endif // BUILDING_CEF_SHARED +#endif // _TASK_CTOCPP_H + diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index d0bacf7e..4b034163 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -18,6 +18,7 @@ #include "ctocpp/read_handler_ctocpp.h" #include "ctocpp/scheme_handler_ctocpp.h" #include "ctocpp/scheme_handler_factory_ctocpp.h" +#include "ctocpp/task_ctocpp.h" #include "ctocpp/v8handler_ctocpp.h" #include "ctocpp/write_handler_ctocpp.h" #include "base/string_util.h" @@ -133,3 +134,28 @@ CEF_EXPORT int cef_register_scheme(const wchar_t* scheme_name, return CefRegisterScheme(nameStr, codeStr, CefSchemeHandlerFactoryCToCpp::Wrap(factory)); } + +CEF_EXPORT int cef_currently_on(cef_thread_id_t threadId) +{ + return CefCurrentlyOn(threadId); +} + +CEF_EXPORT int cef_post_task(cef_thread_id_t threadId, + struct _cef_task_t* task) +{ + DCHECK(task); + if(!task) + return 0; + + return CefPostTask(threadId, CefTaskCToCpp::Wrap(task)); +} + +CEF_EXPORT int cef_post_delayed_task(cef_thread_id_t threadId, + struct _cef_task_t* task, long delay_ms) +{ + DCHECK(task); + if(!task) + return 0; + + return CefPostDelayedTask(threadId, CefTaskCToCpp::Wrap(task), delay_ms); +} \ No newline at end of file diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index a473fe20..3f42948e 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -10,6 +10,7 @@ #include "libcef_dll/cpptoc/read_handler_cpptoc.h" #include "libcef_dll/cpptoc/scheme_handler_cpptoc.h" #include "libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h" +#include "libcef_dll/cpptoc/task_cpptoc.h" #include "libcef_dll/cpptoc/v8handler_cpptoc.h" #include "libcef_dll/cpptoc/write_handler_cpptoc.h" #include "libcef_dll/ctocpp/browser_ctocpp.h" @@ -111,3 +112,20 @@ bool CefRegisterScheme(const std::wstring& scheme_name, return cef_register_scheme(scheme_name.c_str(), host_name.c_str(), CefSchemeHandlerFactoryCppToC::Wrap(factory))?true:false; } + +bool CefCurrentlyOn(CefThreadId threadId) +{ + return cef_currently_on(threadId)?true:false; +} + +bool CefPostTask(CefThreadId threadId, CefRefPtr task) +{ + return cef_post_task(threadId, CefTaskCppToC::Wrap(task))?true:false; +} + +bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr task, + long delay_ms) +{ + return cef_post_delayed_task(threadId, CefTaskCppToC::Wrap(task), delay_ms)? + true:false; +} diff --git a/tests/cefclient/cefclient.cpp b/tests/cefclient/cefclient.cpp index 4c8164d1..9a401ce2 100644 --- a/tests/cefclient/cefclient.cpp +++ b/tests/cefclient/cefclient.cpp @@ -9,7 +9,6 @@ #include "resource_util.h" #include "scheme_test.h" #include "string_util.h" -#include "thread_util.h" #include "uiplugin_test.h" #include #include @@ -376,12 +375,12 @@ public: new ClientReadHandler(pBytes, dwSize)); mimeType = L"text/html"; } - } else if(wcsstr(url.c_str(), L"/logo.gif") != NULL) { + } else if(wcsstr(url.c_str(), L"/logo1w.png") != NULL) { // Any time we find "logo.gif" in the URL substitute in our own image if(LoadBinaryResource(IDS_LOGO, dwSize, pBytes)) { resourceStream = CefStreamReader::CreateForHandler( new ClientReadHandler(pBytes, dwSize)); - mimeType = L"image/jpg"; + mimeType = L"image/png"; } } else if(wcsstr(url.c_str(), L"/logoball.png") != NULL) { // Load the "logoball.png" image resource. @@ -563,7 +562,7 @@ public: fclose(file); if(first_message) { - // Show the message box on the UI thread. + // Show the message box on the main application thread. PostMessage(m_MainHwnd, WM_COMMAND, ID_WARN_CONSOLEMESSAGE, 0); } } @@ -945,23 +944,21 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) #ifdef TEST_SINGLE_THREADED_MESSAGE_LOOP RunGetSourceTest(browser->GetMainFrame()); #else // !TEST_SINGLE_THREADED_MESSAGE_LOOP - // Execute the GetSource() call on a new worker thread to avoid - // blocking the UI thread when using a multi-threaded message loop + // Execute the GetSource() call on the FILE thread to avoid blocking + // the UI thread when using a multi-threaded message loop // (issue #79). - class ExecHandler : public Thread::Handler + class ExecTask : public CefThreadSafeBase { public: - ExecHandler(CefRefPtr frame) : m_Frame(frame) {} - virtual DWORD Run() + ExecTask(CefRefPtr frame) : m_Frame(frame) {} + virtual void Execute(CefThreadId threadId) { RunGetSourceTest(m_Frame); - return 0; } - virtual void Destroy() { delete this; } private: CefRefPtr m_Frame; }; - Thread::Execute(new ExecHandler(browser->GetMainFrame())); + CefPostTask(TID_FILE, new ExecTask(browser->GetMainFrame())); #endif // !TEST_SINGLE_THREADED_MESSAGE_LOOP } return 0; @@ -970,23 +967,21 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) #ifdef TEST_SINGLE_THREADED_MESSAGE_LOOP RunGetTextTest(browser->GetMainFrame()); #else // !TEST_SINGLE_THREADED_MESSAGE_LOOP - // Execute the GetText() call on a new worker thread to avoid - // blocking the UI thread when using a multi-threaded message loop + // Execute the GetText() call on the FILE thread to avoid blocking + // the UI thread when using a multi-threaded message loop // (issue #79). - class ExecHandler : public Thread::Handler + class ExecTask : public CefThreadSafeBase { public: - ExecHandler(CefRefPtr frame) : m_Frame(frame) {} - virtual DWORD Run() + ExecTask(CefRefPtr frame) : m_Frame(frame) {} + virtual void Execute(CefThreadId threadId) { RunGetTextTest(m_Frame); - return 0; } - virtual void Destroy() { delete this; } private: CefRefPtr m_Frame; }; - Thread::Execute(new ExecHandler(browser->GetMainFrame())); + CefPostTask(TID_FILE, new ExecTask(browser->GetMainFrame())); #endif // !TEST_SINGLE_THREADED_MESSAGE_LOOP } return 0; diff --git a/tests/cefclient/cefclient.rc b/tests/cefclient/cefclient.rc index cb6a5196..57a7aa9b 100644 --- a/tests/cefclient/cefclient.rc +++ b/tests/cefclient/cefclient.rc @@ -28,7 +28,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // Binary // -IDS_LOGO BINARY "res\logo.jpg" +IDS_LOGO BINARY "res\logo.png" IDS_UIPLUGIN BINARY "res\uiplugin.html" IDS_LOGOBALL BINARY "res\logoball.png" diff --git a/tests/cefclient/res/logo.jpg b/tests/cefclient/res/logo.jpg deleted file mode 100644 index 1050d1b8..00000000 Binary files a/tests/cefclient/res/logo.jpg and /dev/null differ diff --git a/tests/cefclient/res/logo.png b/tests/cefclient/res/logo.png new file mode 100644 index 00000000..41dd728d Binary files /dev/null and b/tests/cefclient/res/logo.png differ diff --git a/tests/cefclient/thread_util.h b/tests/cefclient/thread_util.h deleted file mode 100644 index 2e89793c..00000000 --- a/tests/cefclient/thread_util.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2010 The Chromium Embedded Framework Authors. All rights -// reserved. Use of this source code is governed by a BSD-style license that -// can be found in the LICENSE file. - -#pragma once -#include - -class Thread -{ -public: - // Interface for thread execution. - class Handler - { - public: - virtual DWORD Run() =0; - virtual void Destroy() =0; - }; - - // Create and execute a new thread for the specified handler. - static HANDLE Execute(Handler* pHandler) - { - if (!pHandler) - return 0; - Thread* pThread = new Thread(pHandler); - return pThread->Execute(); - } - -private: - Thread(Handler* pHandler) : m_hThread(NULL), m_pHandler(pHandler) {} - ~Thread() - { - if (m_hThread) - CloseHandle(m_hThread); - m_pHandler->Destroy(); - } - - HANDLE Execute() - { - m_hThread = CreateThread(NULL, 4096, ThreadHandler, - reinterpret_cast(this), 0, NULL); - if (m_hThread == NULL) { - delete this; - return NULL; - } - return m_hThread; - } - - static DWORD WINAPI ThreadHandler(LPVOID lpThreadParameter) - { - Thread* pThread = reinterpret_cast(lpThreadParameter); - DWORD ret = pThread->m_pHandler->Run(); - delete pThread; - return ret; - } - - Handler* m_pHandler; - HANDLE m_hThread; -}; diff --git a/tools/cef_parser.py b/tools/cef_parser.py index ab971876..3f605631 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -1072,6 +1072,7 @@ class obj_analysis: 'bool' : 'int', 'CefWindowHandle' : 'cef_window_handle_t', 'CefRect' : 'cef_rect_t', + 'CefThreadId' : 'cef_thread_id_t', } if value in simpletypes.keys(): return {