Fix a crash when calling CefShutdown() before destroying all browser windows (issue #159).
git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@157 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
parent
650cb1d41d
commit
c7959e2106
|
@ -483,8 +483,11 @@ bool CefBrowser::CreateBrowser(CefWindowInfo& windowInfo, bool popup,
|
||||||
CefRefPtr<CefHandler> handler,
|
CefRefPtr<CefHandler> handler,
|
||||||
const CefString& url)
|
const CefString& url)
|
||||||
{
|
{
|
||||||
if(!_Context.get())
|
// Verify that the context is in a valid state.
|
||||||
|
if (!CONTEXT_STATE_VALID()) {
|
||||||
|
NOTREACHED();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CefString newUrl = url;
|
CefString newUrl = url;
|
||||||
CefBrowserSettings settings(_Context->browser_defaults());
|
CefBrowserSettings settings(_Context->browser_defaults());
|
||||||
|
@ -510,8 +513,17 @@ bool CefBrowser::CreateBrowser(CefWindowInfo& windowInfo, bool popup,
|
||||||
CefRefPtr<CefBrowser> CefBrowser::CreateBrowserSync(CefWindowInfo& windowInfo,
|
CefRefPtr<CefBrowser> CefBrowser::CreateBrowserSync(CefWindowInfo& windowInfo,
|
||||||
bool popup, CefRefPtr<CefHandler> handler, const CefString& url)
|
bool popup, CefRefPtr<CefHandler> handler, const CefString& url)
|
||||||
{
|
{
|
||||||
if(!_Context.get() || !CefThread::CurrentlyOn(CefThread::UI))
|
// Verify that the context is in a valid state.
|
||||||
|
if (!CONTEXT_STATE_VALID()) {
|
||||||
|
NOTREACHED();
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that this method is being called on the UI thread.
|
||||||
|
if (!CefThread::CurrentlyOn(CefThread::UI)) {
|
||||||
|
NOTREACHED();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
CefString newUrl = url;
|
CefString newUrl = url;
|
||||||
CefRefPtr<CefBrowser> alternateBrowser;
|
CefRefPtr<CefBrowser> alternateBrowser;
|
||||||
|
|
|
@ -21,7 +21,7 @@ CefRefPtr<CefContext> _Context;
|
||||||
bool CefInitialize(const CefSettings& settings,
|
bool CefInitialize(const CefSettings& settings,
|
||||||
const CefBrowserSettings& browser_defaults)
|
const CefBrowserSettings& browser_defaults)
|
||||||
{
|
{
|
||||||
// Return true if the context is already initialized
|
// Return true if the global context already exists.
|
||||||
if(_Context.get())
|
if(_Context.get())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -31,33 +31,48 @@ bool CefInitialize(const CefSettings& settings,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the new global context object
|
// Create the new global context object.
|
||||||
_Context = new CefContext();
|
_Context = new CefContext();
|
||||||
|
|
||||||
// Initialize the global context
|
// Initialize the global context.
|
||||||
return _Context->Initialize(settings, browser_defaults);
|
return _Context->Initialize(settings, browser_defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CefShutdown()
|
void CefShutdown()
|
||||||
{
|
{
|
||||||
// Verify that the context is already initialized
|
// Verify that the context is in a valid state.
|
||||||
if(!_Context.get())
|
if (!CONTEXT_STATE_VALID()) {
|
||||||
|
NOTREACHED();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Shut down the global context
|
// Must always be called on the same thread as Initialize.
|
||||||
|
if(!_Context->process()->CalledOnValidThread()) {
|
||||||
|
NOTREACHED();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shut down the global context. This will block until shutdown is complete.
|
||||||
_Context->Shutdown();
|
_Context->Shutdown();
|
||||||
// Delete the global context object
|
|
||||||
|
// Delete the global context object.
|
||||||
_Context = NULL;
|
_Context = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CefDoMessageLoopWork()
|
void CefDoMessageLoopWork()
|
||||||
{
|
{
|
||||||
// Verify that the context is already initialized.
|
// Verify that the context is in a valid state.
|
||||||
if(!_Context.get() || !_Context->process())
|
if (!CONTEXT_STATE_VALID()) {
|
||||||
|
NOTREACHED();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!_Context->process()->CalledOnValidThread())
|
// Must always be called on the same thread as Initialize.
|
||||||
|
if(!_Context->process()->CalledOnValidThread()) {
|
||||||
|
NOTREACHED();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_Context->process()->DoMessageLoopIteration();
|
_Context->process()->DoMessageLoopIteration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +119,11 @@ static void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info)
|
||||||
|
|
||||||
bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info)
|
bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info)
|
||||||
{
|
{
|
||||||
if(!_Context.get())
|
// Verify that the context is in a valid state.
|
||||||
|
if (!CONTEXT_STATE_VALID()) {
|
||||||
|
NOTREACHED();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CefPluginInfo* pPluginInfo = new CefPluginInfo;
|
CefPluginInfo* pPluginInfo = new CefPluginInfo;
|
||||||
*pPluginInfo = plugin_info;
|
*pPluginInfo = plugin_info;
|
||||||
|
@ -173,15 +191,15 @@ bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr<CefTask> task,
|
||||||
|
|
||||||
// CefContext
|
// CefContext
|
||||||
|
|
||||||
CefContext::CefContext() : process_(NULL)
|
CefContext::CefContext() : initialized_(false), shutting_down_(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CefContext::~CefContext()
|
CefContext::~CefContext()
|
||||||
{
|
{
|
||||||
// Just in case CefShutdown() isn't called
|
if(!shutting_down_)
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CefContext::Initialize(const CefSettings& settings,
|
bool CefContext::Initialize(const CefSettings& settings,
|
||||||
|
@ -200,19 +218,38 @@ bool CefContext::Initialize(const CefSettings& settings,
|
||||||
process_ = new CefProcess(settings_.multi_threaded_message_loop);
|
process_ = new CefProcess(settings_.multi_threaded_message_loop);
|
||||||
process_->CreateChildThreads();
|
process_->CreateChildThreads();
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CefContext::Shutdown()
|
void CefContext::Shutdown()
|
||||||
{
|
{
|
||||||
// Remove all existing browsers.
|
// Must always be called on the same thread as Initialize.
|
||||||
//RemoveAllBrowsers();
|
DCHECK(process_->CalledOnValidThread());
|
||||||
|
|
||||||
// Deleting the process will destroy the child threads.
|
shutting_down_ = true;
|
||||||
|
|
||||||
|
if(settings_.multi_threaded_message_loop) {
|
||||||
|
// Event that will be used to signal when shutdown is complete. Start in
|
||||||
|
// non-signaled mode so that the event will block.
|
||||||
|
base::WaitableEvent event(false, false);
|
||||||
|
|
||||||
|
// Finish shutdown on the UI thread.
|
||||||
|
CefThread::PostTask(CefThread::UI, FROM_HERE,
|
||||||
|
NewRunnableMethod(this, &CefContext::UIT_FinishShutdown, &event));
|
||||||
|
|
||||||
|
// Block until shutdown is complete.
|
||||||
|
event.Wait();
|
||||||
|
} else {
|
||||||
|
// Finish shutdown on the current thread, which should be the UI thread.
|
||||||
|
UIT_FinishShutdown(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the process to destroy the child threads.
|
||||||
process_ = NULL;
|
process_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CefContext::AddBrowser(CefRefPtr<CefBrowserImpl> browser)
|
bool CefContext::AddBrowser(CefRefPtr<CefBrowserImpl> browser)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -286,3 +323,27 @@ CefRefPtr<CefBrowserImpl> CefContext::GetBrowserByID(int id)
|
||||||
|
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CefContext::UIT_FinishShutdown(base::WaitableEvent* event)
|
||||||
|
{
|
||||||
|
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
|
||||||
|
|
||||||
|
BrowserList list;
|
||||||
|
|
||||||
|
Lock();
|
||||||
|
if (!browserlist_.empty()) {
|
||||||
|
list = browserlist_;
|
||||||
|
browserlist_.clear();
|
||||||
|
}
|
||||||
|
Unlock();
|
||||||
|
|
||||||
|
// Destroy any remaining browser windows.
|
||||||
|
if (!list.empty()) {
|
||||||
|
BrowserList::iterator it = list.begin();
|
||||||
|
for(; it != list.end(); ++it)
|
||||||
|
(*it)->UIT_DestroyBrowser();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event)
|
||||||
|
event->Signal();
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,12 @@ public:
|
||||||
const CefBrowserSettings& browser_defaults);
|
const CefBrowserSettings& browser_defaults);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
// Returns true if the context is initialized.
|
||||||
|
bool initialized() { return initialized_; }
|
||||||
|
|
||||||
|
// Returns true if the context is shutting down.
|
||||||
|
bool shutting_down() { return shutting_down_; }
|
||||||
|
|
||||||
scoped_refptr<CefProcess> process() { return process_; }
|
scoped_refptr<CefProcess> process() { return process_; }
|
||||||
|
|
||||||
bool AddBrowser(CefRefPtr<CefBrowserImpl> browser);
|
bool AddBrowser(CefRefPtr<CefBrowserImpl> browser);
|
||||||
|
@ -59,7 +65,17 @@ public:
|
||||||
{ storage_context_.reset(storage_context); }
|
{ storage_context_.reset(storage_context); }
|
||||||
DOMStorageContext* storage_context() { return storage_context_.get(); }
|
DOMStorageContext* storage_context() { return storage_context_.get(); }
|
||||||
|
|
||||||
|
static bool ImplementsThreadSafeReferenceCounting() { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Performs shutdown actions that need to occur on the UI thread before any
|
||||||
|
// threads are destroyed.
|
||||||
|
void UIT_FinishShutdown(base::WaitableEvent* event);
|
||||||
|
|
||||||
|
// Track context state.
|
||||||
|
bool initialized_;
|
||||||
|
bool shutting_down_;
|
||||||
|
|
||||||
// Manages the various process threads.
|
// Manages the various process threads.
|
||||||
scoped_refptr<CefProcess> process_;
|
scoped_refptr<CefProcess> process_;
|
||||||
|
|
||||||
|
@ -80,7 +96,11 @@ private:
|
||||||
int next_browser_id_;
|
int next_browser_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global context object pointer
|
// Global context object pointer.
|
||||||
extern CefRefPtr<CefContext> _Context;
|
extern CefRefPtr<CefContext> _Context;
|
||||||
|
|
||||||
|
// Helper macro that returns true if the global context is in a valid state.
|
||||||
|
#define CONTEXT_STATE_VALID() \
|
||||||
|
(_Context.get() && _Context->initialized() && !_Context->shutting_down())
|
||||||
|
|
||||||
#endif // _CEF_CONTEXT_H
|
#endif // _CEF_CONTEXT_H
|
||||||
|
|
|
@ -393,9 +393,11 @@ bool CefRegisterScheme(const CefString& scheme_name,
|
||||||
const CefString& host_name,
|
const CefString& host_name,
|
||||||
CefRefPtr<CefSchemeHandlerFactory> factory)
|
CefRefPtr<CefSchemeHandlerFactory> factory)
|
||||||
{
|
{
|
||||||
// Verify that the context is already initialized
|
// Verify that the context is in a valid state.
|
||||||
if(!_Context.get())
|
if (!CONTEXT_STATE_VALID()) {
|
||||||
|
NOTREACHED();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Use a smart pointer for the wrapper object because
|
// Use a smart pointer for the wrapper object because
|
||||||
// RunnableMethodTraits::RetainCallee() (originating from NewRunnableMethod)
|
// RunnableMethodTraits::RetainCallee() (originating from NewRunnableMethod)
|
||||||
|
|
|
@ -143,12 +143,16 @@ bool CefRegisterExtension(const CefString& extension_name,
|
||||||
const CefString& javascript_code,
|
const CefString& javascript_code,
|
||||||
CefRefPtr<CefV8Handler> handler)
|
CefRefPtr<CefV8Handler> handler)
|
||||||
{
|
{
|
||||||
// Verify that the context is already initialized
|
// Verify that the context is in a valid state.
|
||||||
if(!_Context.get())
|
if (!CONTEXT_STATE_VALID()) {
|
||||||
|
NOTREACHED();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!handler.get())
|
if(!handler.get()) {
|
||||||
|
NOTREACHED();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TrackString* name = new TrackString(extension_name);
|
TrackString* name = new TrackString(extension_name);
|
||||||
TrackAdd(name);
|
TrackAdd(name);
|
||||||
|
|
Loading…
Reference in New Issue