diff --git a/cef1/include/capi/cef_v8_capi.h b/cef1/include/capi/cef_v8_capi.h index 8c4c23db3..9f3093324 100644 --- a/cef1/include/capi/cef_v8_capi.h +++ b/cef1/include/capi/cef_v8_capi.h @@ -114,6 +114,12 @@ typedef struct _cef_v8context_t { /// cef_base_t base; + /// + // Returns true (1) if this object is valid. Do not call any other functions + // if this function returns false (0). + /// + int (CEF_CALLBACK *is_valid)(struct _cef_v8context_t* self); + /// // Returns the browser for this context. /// @@ -314,6 +320,12 @@ typedef struct _cef_v8value_t { /// cef_base_t base; + /// + // Returns true (1) if this object is valid. Do not call any other functions + // if this function returns false (0). + /// + int (CEF_CALLBACK *is_valid)(struct _cef_v8value_t* self); + /// // True if the value type is undefined. /// @@ -708,6 +720,12 @@ typedef struct _cef_v8stack_trace_t { /// cef_base_t base; + /// + // Returns true (1) if this object is valid. Do not call any other functions + // if this function returns false (0). + /// + int (CEF_CALLBACK *is_valid)(struct _cef_v8stack_trace_t* self); + /// // Returns the number of stack frames. /// @@ -738,6 +756,12 @@ typedef struct _cef_v8stack_frame_t { /// cef_base_t base; + /// + // Returns true (1) if this object is valid. Do not call any other functions + // if this function returns false (0). + /// + int (CEF_CALLBACK *is_valid)(struct _cef_v8stack_frame_t* self); + /// // Returns the name of the resource script that contains the function. /// diff --git a/cef1/include/cef_v8.h b/cef1/include/cef_v8.h index 983ed2979..03a16b09f 100644 --- a/cef1/include/cef_v8.h +++ b/cef1/include/cef_v8.h @@ -140,6 +140,13 @@ class CefV8Context : public virtual CefBase { /*--cef()--*/ static bool InContext(); + /// + // Returns true if this object is valid. Do not call any other methods if this + // method returns false. + /// + /*--cef()--*/ + virtual bool IsValid() =0; + /// // Returns the browser for this context. /// @@ -407,6 +414,13 @@ class CefV8Value : public virtual CefBase { static CefRefPtr CreateFunction(const CefString& name, CefRefPtr handler); + /// + // Returns true if this object is valid. Do not call any other methods if this + // method returns false. + /// + /*--cef()--*/ + virtual bool IsValid() =0; + /// // True if the value type is undefined. /// @@ -754,6 +768,13 @@ class CefV8StackTrace : public virtual CefBase { /*--cef()--*/ static CefRefPtr GetCurrent(int frame_limit); + /// + // Returns true if this object is valid. Do not call any other methods if this + // method returns false. + /// + /*--cef()--*/ + virtual bool IsValid() =0; + /// // Returns the number of stack frames. /// @@ -774,6 +795,13 @@ class CefV8StackTrace : public virtual CefBase { /*--cef(source=library)--*/ class CefV8StackFrame : public virtual CefBase { public: + /// + // Returns true if this object is valid. Do not call any other methods if this + // method returns false. + /// + /*--cef()--*/ + virtual bool IsValid() =0; + /// // Returns the name of the resource script that contains the function. /// diff --git a/cef1/include/internal/cef_types.h b/cef1/include/internal/cef_types.h index 62c484a8d..8212e390d 100644 --- a/cef1/include/internal/cef_types.h +++ b/cef1/include/internal/cef_types.h @@ -218,6 +218,25 @@ typedef struct _cef_settings_t { // OnUncaughtException() will not be called. /// int uncaught_exception_stack_size; + + /// + // By default CEF V8 references will be invalidated (the IsValid() method will + // return false) after the owning context has been released. This reduces the + // need for external record keeping and avoids crashes due to the use of V8 + // references after the associated context has been released. + // + // CEF currently offers two context safety implementations with different + // performance characteristics. The default implementation (value of 0) uses a + // map of hash values and should provide better performance in situations with + // a small number contexts. The alternate implementation (value of 1) uses a + // hidden value attached to each context and should provide better performance + // in situations with a large number of contexts. + // + // If you need better performance in the creation of V8 references and you + // plan to manually track context lifespan you can disable context safety by + // specifying a value of -1. + /// + int context_safety_implementation; } cef_settings_t; /// diff --git a/cef1/include/internal/cef_types_wrappers.h b/cef1/include/internal/cef_types_wrappers.h index 11252b2d5..f136e3a75 100644 --- a/cef1/include/internal/cef_types_wrappers.h +++ b/cef1/include/internal/cef_types_wrappers.h @@ -300,6 +300,7 @@ struct CefSettingsTraits { &target->locales_dir_path, copy); target->pack_loading_disabled = src->pack_loading_disabled; target->uncaught_exception_stack_size = src->uncaught_exception_stack_size; + target->context_safety_implementation = src->context_safety_implementation; } }; diff --git a/cef1/libcef/browser_webview_delegate.cc b/cef1/libcef/browser_webview_delegate.cc index 92af88f93..9f33b491a 100644 --- a/cef1/libcef/browser_webview_delegate.cc +++ b/cef1/libcef/browser_webview_delegate.cc @@ -921,20 +921,21 @@ void BrowserWebViewDelegate::didCreateScriptContext( void BrowserWebViewDelegate::willReleaseScriptContext( WebFrame* frame, v8::Handle context, int worldId) { CefRefPtr client = browser_->GetClient(); - if (!client.get()) - return; + if (client.get()) { + CefRefPtr handler = client->GetV8ContextHandler(); + if (handler.get()) { + v8::HandleScope handle_scope; + v8::Context::Scope scope(context); - CefRefPtr handler = client->GetV8ContextHandler(); - if (!handler.get()) - return; + CefRefPtr framePtr(browser_->UIT_GetCefFrame(frame)); + CefRefPtr contextPtr(new CefV8ContextImpl(context)); - v8::HandleScope handle_scope; - v8::Context::Scope scope(context); + handler->OnContextReleased(browser_, framePtr, contextPtr); + } + } - CefRefPtr framePtr(browser_->UIT_GetCefFrame(frame)); - CefRefPtr contextPtr(new CefV8ContextImpl(context)); - - handler->OnContextReleased(browser_, framePtr, contextPtr); + // Disconnect any handles still associated with the context. + CefV8ReleaseContext(context); } void BrowserWebViewDelegate::didReceiveTitle( diff --git a/cef1/libcef/v8_impl.cc b/cef1/libcef/v8_impl.cc index 048ae45ff..06c8ece20 100644 --- a/cef1/libcef/v8_impl.cc +++ b/cef1/libcef/v8_impl.cc @@ -3,6 +3,7 @@ // can be found in the LICENSE file. #include +#include #include "base/compiler_specific.h" @@ -43,10 +44,110 @@ MSVC_POP_WARNING(); namespace { static const char kCefTrackObject[] = "Cef::TrackObject"; +static const char kCefContextState[] = "Cef::ContextState"; // Memory manager. -base::LazyInstance g_v8_tracker = LAZY_INSTANCE_INITIALIZER; +class CefV8TrackManager : public CefTrackManager { + public: + CefV8TrackManager() { + const CefSettings& settings = _Context->settings(); + if (settings.context_safety_implementation < 0) + context_safety_impl_ = IMPL_DISABLED; + else if (settings.context_safety_implementation == 1) + context_safety_impl_ = IMPL_VALUE; + else + context_safety_impl_ = IMPL_HASH; + } + + scoped_refptr GetContextState( + v8::Handle context) { + if (context_safety_impl_ == IMPL_DISABLED) + return scoped_refptr(); + + if (context.IsEmpty()) { + if (v8::Context::InContext()) + context = v8::Context::GetCurrent(); + else + return scoped_refptr(); + } + + if (context_safety_impl_ == IMPL_HASH) { + int hash = context->Global()->GetIdentityHash(); + ContextMap::const_iterator it = context_map_.find(hash); + if (it != context_map_.end()) + return it->second; + + scoped_refptr state = new CefV8ContextState(); + context_map_.insert(std::make_pair(hash, state)); + + return state; + } else { + if (context_state_key_.IsEmpty()) { + context_state_key_ = + v8::Persistent::New(v8::String::New(kCefContextState)); + } + + v8::Handle object = context->Global(); + v8::Handle value = object->GetHiddenValue(context_state_key_); + if (!value.IsEmpty()) + return static_cast(v8::External::Unwrap(value)); + + scoped_refptr state = new CefV8ContextState(); + object->SetHiddenValue(context_state_key_, + v8::External::New(state.get())); + + // Reference will be released in ReleaseContext. + state->AddRef(); + + return state; + } + } + + void ReleaseContext(v8::Handle context) { + if (context_safety_impl_ == IMPL_DISABLED) + return; + + if (context_safety_impl_ == IMPL_HASH) { + int hash = context->Global()->GetIdentityHash(); + ContextMap::iterator it = context_map_.find(hash); + if (it != context_map_.end()) { + it->second->Detach(); + context_map_.erase(it); + } + } else { + if (context_state_key_.IsEmpty()) + return; + + v8::Handle object = context->Global(); + v8::Handle value = object->GetHiddenValue(context_state_key_); + scoped_refptr state = + static_cast(v8::External::Unwrap(value)); + state->Detach(); + object->DeleteHiddenValue(context_state_key_); + + // Match the AddRef in GetContextState. + state->Release(); + } + } + + private: + enum ContextSafetyImpl { + IMPL_DISABLED, + IMPL_HASH, + IMPL_VALUE, + }; + ContextSafetyImpl context_safety_impl_; + + // Used with IMPL_HASH. + typedef std::map > ContextMap; + ContextMap context_map_; + + // Used with IMPL_VALUE. + v8::Persistent context_state_key_; +}; + +base::LazyInstance g_v8_tracker = LAZY_INSTANCE_INITIALIZER; class V8TrackObject : public CefTrackNode { public: @@ -417,6 +518,10 @@ class CefV8ExceptionImpl : public CefV8Exception { // Global functions. +void CefV8ReleaseContext(v8::Handle context) { + g_v8_tracker.Pointer()->ReleaseContext(context); +} + bool CefRegisterExtension(const CefString& extension_name, const CefString& javascript_code, CefRefPtr handler) { @@ -437,6 +542,13 @@ bool CefRegisterExtension(const CefString& extension_name, } +// CefV8HandleBase + +CefV8HandleBase::CefV8HandleBase(v8::Handle context) { + context_state_ = g_v8_tracker.Pointer()->GetContextState(context); +} + + // CefV8Context // static @@ -473,26 +585,35 @@ bool CefV8Context::InContext() { // CefV8ContextImpl +#define CEF_V8_REQUIRE_VALID_RETURN(ret) \ + if (!handle_->IsValid()) { \ + NOTREACHED() << "V8 handle is not valid"; \ + return ret; \ + } + #define CEF_V8_REQUIRE_OBJECT_RETURN(ret) \ + CEF_V8_REQUIRE_VALID_RETURN(ret); \ if (!GetHandle()->IsObject()) { \ NOTREACHED() << "V8 value is not an object"; \ return ret; \ } #define CEF_V8_REQUIRE_ARRAY_RETURN(ret) \ + CEF_V8_REQUIRE_VALID_RETURN(ret); \ if (!GetHandle()->IsArray()) { \ NOTREACHED() << "V8 value is not an array"; \ return ret; \ } #define CEF_V8_REQUIRE_FUNCTION_RETURN(ret) \ + CEF_V8_REQUIRE_VALID_RETURN(ret); \ if (!GetHandle()->IsFunction()) { \ NOTREACHED() << "V8 value is not a function"; \ return ret; \ } CefV8ContextImpl::CefV8ContextImpl(v8::Handle context) - : handle_(new Handle(context)) + : handle_(new Handle(context, context)) #ifndef NDEBUG , enter_count_(0) #endif @@ -503,9 +624,15 @@ CefV8ContextImpl::~CefV8ContextImpl() { DLOG_ASSERT(0 == enter_count_); } +bool CefV8ContextImpl::IsValid() { + CEF_REQUIRE_UI_THREAD(false); + return handle_->IsValid(); +} + CefRefPtr CefV8ContextImpl::GetBrowser() { CefRefPtr browser; CEF_REQUIRE_UI_THREAD(browser); + CEF_V8_REQUIRE_VALID_RETURN(browser); WebKit::WebFrame* webframe = GetWebFrame(); if (webframe) @@ -517,6 +644,7 @@ CefRefPtr CefV8ContextImpl::GetBrowser() { CefRefPtr CefV8ContextImpl::GetFrame() { CefRefPtr frame; CEF_REQUIRE_UI_THREAD(frame); + CEF_V8_REQUIRE_VALID_RETURN(frame); WebKit::WebFrame* webframe = GetWebFrame(); if (webframe) { @@ -531,6 +659,7 @@ CefRefPtr CefV8ContextImpl::GetFrame() { CefRefPtr CefV8ContextImpl::GetGlobal() { CEF_REQUIRE_UI_THREAD(NULL); + CEF_V8_REQUIRE_VALID_RETURN(NULL); v8::HandleScope handle_scope; v8::Context::Scope context_scope(GetHandle()); @@ -539,6 +668,8 @@ CefRefPtr CefV8ContextImpl::GetGlobal() { bool CefV8ContextImpl::Enter() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); + GetHandle()->Enter(); #ifndef NDEBUG ++enter_count_; @@ -548,6 +679,8 @@ bool CefV8ContextImpl::Enter() { bool CefV8ContextImpl::Exit() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); + DLOG_ASSERT(enter_count_ > 0); GetHandle()->Exit(); #ifndef NDEBUG @@ -558,6 +691,7 @@ bool CefV8ContextImpl::Exit() { bool CefV8ContextImpl::IsSame(CefRefPtr that) { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); v8::HandleScope handle_scope; @@ -575,6 +709,7 @@ bool CefV8ContextImpl::Eval(const CefString& code, CefRefPtr& retval, CefRefPtr& exception) { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); if (code.empty()) { NOTREACHED() << "invalid input parameter"; @@ -820,71 +955,89 @@ CefRefPtr CefV8Value::CreateFunction( CefV8ValueImpl::CefV8ValueImpl(v8::Handle value, CefTrackNode* tracker) - : handle_(new Handle(value, tracker)), + : handle_(new Handle(v8::Handle(), value, tracker)), rethrow_exceptions_(false) { } CefV8ValueImpl::~CefV8ValueImpl() { } + +bool CefV8ValueImpl::IsValid() { + CEF_REQUIRE_UI_THREAD(false); + return handle_->IsValid(); +} + bool CefV8ValueImpl::IsUndefined() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsUndefined(); } bool CefV8ValueImpl::IsNull() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsNull(); } bool CefV8ValueImpl::IsBool() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return (GetHandle()->IsBoolean() || GetHandle()->IsTrue() || GetHandle()->IsFalse()); } bool CefV8ValueImpl::IsInt() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsInt32(); } bool CefV8ValueImpl::IsUInt() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsUint32(); } bool CefV8ValueImpl::IsDouble() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsNumber(); } bool CefV8ValueImpl::IsDate() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsDate(); } bool CefV8ValueImpl::IsString() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsString(); } bool CefV8ValueImpl::IsObject() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsObject(); } bool CefV8ValueImpl::IsArray() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsArray(); } bool CefV8ValueImpl::IsFunction() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); return GetHandle()->IsFunction(); } bool CefV8ValueImpl::IsSame(CefRefPtr that) { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); v8::HandleScope handle_scope; @@ -900,6 +1053,7 @@ bool CefV8ValueImpl::IsSame(CefRefPtr that) { bool CefV8ValueImpl::GetBoolValue() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); if (GetHandle()->IsTrue()) { return true; } else if (GetHandle()->IsFalse()) { @@ -913,6 +1067,7 @@ bool CefV8ValueImpl::GetBoolValue() { int32 CefV8ValueImpl::GetIntValue() { CEF_REQUIRE_UI_THREAD(0); + CEF_V8_REQUIRE_VALID_RETURN(0); v8::HandleScope handle_scope; v8::Local val = GetHandle()->ToInt32(); return val->Value(); @@ -920,6 +1075,7 @@ int32 CefV8ValueImpl::GetIntValue() { uint32 CefV8ValueImpl::GetUIntValue() { CEF_REQUIRE_UI_THREAD(0); + CEF_V8_REQUIRE_VALID_RETURN(0); v8::HandleScope handle_scope; v8::Local val = GetHandle()->ToUint32(); return val->Value(); @@ -927,6 +1083,7 @@ uint32 CefV8ValueImpl::GetUIntValue() { double CefV8ValueImpl::GetDoubleValue() { CEF_REQUIRE_UI_THREAD(0.); + CEF_V8_REQUIRE_VALID_RETURN(0.); v8::HandleScope handle_scope; v8::Local val = GetHandle()->ToNumber(); return val->Value(); @@ -934,6 +1091,7 @@ double CefV8ValueImpl::GetDoubleValue() { CefTime CefV8ValueImpl::GetDateValue() { CEF_REQUIRE_UI_THREAD(CefTime(0.)); + CEF_V8_REQUIRE_VALID_RETURN(CefTime(0.)); v8::HandleScope handle_scope; v8::Local val = GetHandle()->ToNumber(); // Convert from milliseconds to seconds. @@ -943,6 +1101,7 @@ CefTime CefV8ValueImpl::GetDateValue() { CefString CefV8ValueImpl::GetStringValue() { CefString rv; CEF_REQUIRE_UI_THREAD(rv); + CEF_V8_REQUIRE_VALID_RETURN(rv); v8::HandleScope handle_scope; GetCefString(GetHandle()->ToString(), rv); return rv; @@ -1367,6 +1526,7 @@ CefRefPtr CefV8StackTrace::GetCurrent(int frame_limit) { CEF_REQUIRE_VALID_CONTEXT(NULL); CEF_REQUIRE_UI_THREAD(NULL); + v8::HandleScope handle_scope; v8::Handle stackTrace = v8::StackTrace::CurrentStackTrace( frame_limit, v8::StackTrace::kDetailed); @@ -1379,20 +1539,27 @@ CefRefPtr CefV8StackTrace::GetCurrent(int frame_limit) { // CefV8StackTraceImpl CefV8StackTraceImpl::CefV8StackTraceImpl(v8::Handle handle) - : handle_(new Handle(handle)) { + : handle_(new Handle(v8::Handle(), handle)) { } CefV8StackTraceImpl::~CefV8StackTraceImpl() { } +bool CefV8StackTraceImpl::IsValid() { + CEF_REQUIRE_UI_THREAD(false); + return handle_->IsValid(); +} + int CefV8StackTraceImpl::GetFrameCount() { CEF_REQUIRE_UI_THREAD(0); + CEF_V8_REQUIRE_VALID_RETURN(0); v8::HandleScope handle_scope; return GetHandle()->GetFrameCount(); } CefRefPtr CefV8StackTraceImpl::GetFrame(int index) { CEF_REQUIRE_UI_THREAD(NULL); + CEF_V8_REQUIRE_VALID_RETURN(NULL); v8::HandleScope handle_scope; v8::Handle stackFrame = GetHandle()->GetFrame(index); if (stackFrame.IsEmpty()) @@ -1404,15 +1571,21 @@ CefRefPtr CefV8StackTraceImpl::GetFrame(int index) { // CefV8StackFrameImpl CefV8StackFrameImpl::CefV8StackFrameImpl(v8::Handle handle) - : handle_(new Handle(handle)) { + : handle_(new Handle(v8::Handle(), handle)) { } CefV8StackFrameImpl::~CefV8StackFrameImpl() { } +bool CefV8StackFrameImpl::IsValid() { + CEF_REQUIRE_UI_THREAD(false); + return handle_->IsValid(); +} + CefString CefV8StackFrameImpl::GetScriptName() { CefString rv; CEF_REQUIRE_UI_THREAD(rv); + CEF_V8_REQUIRE_VALID_RETURN(rv); v8::HandleScope handle_scope; GetCefString(v8::Handle::Cast(GetHandle()->GetScriptName()), rv); return rv; @@ -1421,6 +1594,7 @@ CefString CefV8StackFrameImpl::GetScriptName() { CefString CefV8StackFrameImpl::GetScriptNameOrSourceURL() { CefString rv; CEF_REQUIRE_UI_THREAD(rv); + CEF_V8_REQUIRE_VALID_RETURN(rv); v8::HandleScope handle_scope; GetCefString( v8::Handle::Cast(GetHandle()->GetScriptNameOrSourceURL()), @@ -1431,6 +1605,7 @@ CefString CefV8StackFrameImpl::GetScriptNameOrSourceURL() { CefString CefV8StackFrameImpl::GetFunctionName() { CefString rv; CEF_REQUIRE_UI_THREAD(rv); + CEF_V8_REQUIRE_VALID_RETURN(rv); v8::HandleScope handle_scope; GetCefString( v8::Handle::Cast(GetHandle()->GetFunctionName()), rv); @@ -1439,24 +1614,28 @@ CefString CefV8StackFrameImpl::GetFunctionName() { int CefV8StackFrameImpl::GetLineNumber() { CEF_REQUIRE_UI_THREAD(0); + CEF_V8_REQUIRE_VALID_RETURN(0); v8::HandleScope handle_scope; return GetHandle()->GetLineNumber(); } int CefV8StackFrameImpl::GetColumn() { CEF_REQUIRE_UI_THREAD(0); + CEF_V8_REQUIRE_VALID_RETURN(0); v8::HandleScope handle_scope; return GetHandle()->GetColumn(); } bool CefV8StackFrameImpl::IsEval() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); v8::HandleScope handle_scope; return GetHandle()->IsEval(); } bool CefV8StackFrameImpl::IsConstructor() { CEF_REQUIRE_UI_THREAD(false); + CEF_V8_REQUIRE_VALID_RETURN(false); v8::HandleScope handle_scope; return GetHandle()->IsConstructor(); } diff --git a/cef1/libcef/v8_impl.h b/cef1/libcef/v8_impl.h index 124eca777..e3209b58c 100644 --- a/cef1/libcef/v8_impl.h +++ b/cef1/libcef/v8_impl.h @@ -19,25 +19,65 @@ namespace WebKit { class WebFrame; }; +// Call to detach all handles associated with the specified contxt. +void CefV8ReleaseContext(v8::Handle context); + +// Used to detach handles when the associated context is released. +class CefV8ContextState : public base::RefCounted { + public: + CefV8ContextState() : valid_(true) {} + virtual ~CefV8ContextState() {} + + bool IsValid() { return valid_; } + void Detach() { valid_ = false; } + + private: + bool valid_; +}; + +// Base class for V8 Handle types. +class CefV8HandleBase : + public base::RefCountedThreadSafe { + public: + virtual ~CefV8HandleBase() {} + + // Returns true if there is no underlying context or if the underlying context + // is valid. + bool IsValid() { + return (!context_state_.get() || context_state_->IsValid()); + } + + protected: + // |context| is the context that owns this handle. If empty the current + // context will be used. + explicit CefV8HandleBase(v8::Handle context); + + private: + scoped_refptr context_state_; +}; + // Template for V8 Handle types. This class is used to ensure that V8 objects // are only released on the UI thread. template -class CefV8Handle : - public base::RefCountedThreadSafe, - CefThread::DeleteOnUIThread> { +class CefV8Handle : public CefV8HandleBase { public: typedef v8::Handle handleType; typedef v8::Persistent persistentType; - CefV8Handle(handleType v) - : handle_(persistentType::New(v)) { + CefV8Handle(v8::Handle context, handleType v) + : CefV8HandleBase(context), + handle_(persistentType::New(v)) { } - ~CefV8Handle() { + virtual ~CefV8Handle() { handle_.Dispose(); handle_.Clear(); } - handleType GetHandle() { return handle_; } + handleType GetHandle() { + DCHECK(IsValid()); + return handle_; + } protected: persistentType handle_; @@ -57,6 +97,7 @@ class CefV8ContextImpl : public CefV8Context { explicit CefV8ContextImpl(v8::Handle context); virtual ~CefV8ContextImpl(); + virtual bool IsValid() OVERRIDE; virtual CefRefPtr GetBrowser() OVERRIDE; virtual CefRefPtr GetFrame() OVERRIDE; virtual CefRefPtr GetGlobal() OVERRIDE; @@ -90,6 +131,7 @@ class CefV8ValueImpl : public CefV8Value { CefV8ValueImpl(v8::Handle value, CefTrackNode* tracker = NULL); virtual ~CefV8ValueImpl(); + virtual bool IsValid() OVERRIDE; virtual bool IsUndefined() OVERRIDE; virtual bool IsNull() OVERRIDE; virtual bool IsBool() OVERRIDE; @@ -147,19 +189,22 @@ class CefV8ValueImpl : public CefV8Value { // Test for and record any exception. bool HasCaught(v8::TryCatch& try_catch); - class Handle : - public base::RefCountedThreadSafe { + class Handle : public CefV8HandleBase { public: typedef v8::Handle handleType; typedef v8::Persistent persistentType; - Handle(handleType v, CefTrackNode* tracker) - : handle_(persistentType::New(v)), + Handle(v8::Handle context, handleType v, CefTrackNode* tracker) + : CefV8HandleBase(context), + handle_(persistentType::New(v)), tracker_(tracker) { } - ~Handle(); + virtual ~Handle(); - handleType GetHandle() { return handle_; } + handleType GetHandle() { + DCHECK(IsValid()); + return handle_; + } private: persistentType handle_; @@ -184,6 +229,7 @@ class CefV8StackTraceImpl : public CefV8StackTrace { explicit CefV8StackTraceImpl(v8::Handle handle); virtual ~CefV8StackTraceImpl(); + virtual bool IsValid() OVERRIDE; virtual int GetFrameCount() OVERRIDE; virtual CefRefPtr GetFrame(int index) OVERRIDE; @@ -202,6 +248,7 @@ class CefV8StackFrameImpl : public CefV8StackFrame { explicit CefV8StackFrameImpl(v8::Handle handle); virtual ~CefV8StackFrameImpl(); + virtual bool IsValid() OVERRIDE; virtual CefString GetScriptName() OVERRIDE; virtual CefString GetScriptNameOrSourceURL() OVERRIDE; virtual CefString GetFunctionName() OVERRIDE; diff --git a/cef1/libcef_dll/cpptoc/v8context_cpptoc.cc b/cef1/libcef_dll/cpptoc/v8context_cpptoc.cc index 6aa3149ee..245492b6d 100644 --- a/cef1/libcef_dll/cpptoc/v8context_cpptoc.cc +++ b/cef1/libcef_dll/cpptoc/v8context_cpptoc.cc @@ -52,6 +52,20 @@ CEF_EXPORT int cef_v8context_in_context() { // MEMBER FUNCTIONS - Body may be edited by hand. +int CEF_CALLBACK v8context_is_valid(struct _cef_v8context_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefV8ContextCppToC::Get(self)->IsValid(); + + // Return type: bool + return _retval; +} + cef_browser_t* CEF_CALLBACK v8context_get_browser( struct _cef_v8context_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -212,6 +226,7 @@ int CEF_CALLBACK v8context_eval(struct _cef_v8context_t* self, CefV8ContextCppToC::CefV8ContextCppToC(CefV8Context* cls) : CefCppToC(cls) { + struct_.struct_.is_valid = v8context_is_valid; struct_.struct_.get_browser = v8context_get_browser; struct_.struct_.get_frame = v8context_get_frame; struct_.struct_.get_global = v8context_get_global; diff --git a/cef1/libcef_dll/cpptoc/v8stack_frame_cpptoc.cc b/cef1/libcef_dll/cpptoc/v8stack_frame_cpptoc.cc index 3da6bc6c8..7dc1bf8f4 100644 --- a/cef1/libcef_dll/cpptoc/v8stack_frame_cpptoc.cc +++ b/cef1/libcef_dll/cpptoc/v8stack_frame_cpptoc.cc @@ -15,6 +15,20 @@ // MEMBER FUNCTIONS - Body may be edited by hand. +int CEF_CALLBACK v8stack_frame_is_valid(struct _cef_v8stack_frame_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefV8StackFrameCppToC::Get(self)->IsValid(); + + // Return type: bool + return _retval; +} + cef_string_userfree_t CEF_CALLBACK v8stack_frame_get_script_name( struct _cef_v8stack_frame_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -125,6 +139,7 @@ int CEF_CALLBACK v8stack_frame_is_constructor( CefV8StackFrameCppToC::CefV8StackFrameCppToC(CefV8StackFrame* cls) : CefCppToC( cls) { + struct_.struct_.is_valid = v8stack_frame_is_valid; struct_.struct_.get_script_name = v8stack_frame_get_script_name; struct_.struct_.get_script_name_or_source_url = v8stack_frame_get_script_name_or_source_url; diff --git a/cef1/libcef_dll/cpptoc/v8stack_trace_cpptoc.cc b/cef1/libcef_dll/cpptoc/v8stack_trace_cpptoc.cc index c7c884c5a..427b238eb 100644 --- a/cef1/libcef_dll/cpptoc/v8stack_trace_cpptoc.cc +++ b/cef1/libcef_dll/cpptoc/v8stack_trace_cpptoc.cc @@ -30,6 +30,20 @@ CEF_EXPORT cef_v8stack_trace_t* cef_v8stack_trace_get_current(int frame_limit) { // MEMBER FUNCTIONS - Body may be edited by hand. +int CEF_CALLBACK v8stack_trace_is_valid(struct _cef_v8stack_trace_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefV8StackTraceCppToC::Get(self)->IsValid(); + + // Return type: bool + return _retval; +} + int CEF_CALLBACK v8stack_trace_get_frame_count( struct _cef_v8stack_trace_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -68,6 +82,7 @@ struct _cef_v8stack_frame_t* CEF_CALLBACK v8stack_trace_get_frame( CefV8StackTraceCppToC::CefV8StackTraceCppToC(CefV8StackTrace* cls) : CefCppToC( cls) { + struct_.struct_.is_valid = v8stack_trace_is_valid; struct_.struct_.get_frame_count = v8stack_trace_get_frame_count; struct_.struct_.get_frame = v8stack_trace_get_frame; } diff --git a/cef1/libcef_dll/cpptoc/v8value_cpptoc.cc b/cef1/libcef_dll/cpptoc/v8value_cpptoc.cc index 1fd7add92..a9c514d6d 100644 --- a/cef1/libcef_dll/cpptoc/v8value_cpptoc.cc +++ b/cef1/libcef_dll/cpptoc/v8value_cpptoc.cc @@ -167,6 +167,20 @@ CEF_EXPORT cef_v8value_t* cef_v8value_create_function(const cef_string_t* name, // MEMBER FUNCTIONS - Body may be edited by hand. +int CEF_CALLBACK v8value_is_valid(struct _cef_v8value_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefV8ValueCppToC::Get(self)->IsValid(); + + // Return type: bool + return _retval; +} + int CEF_CALLBACK v8value_is_undefined(struct _cef_v8value_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -903,6 +917,7 @@ struct _cef_v8value_t* CEF_CALLBACK v8value_execute_function_with_context( CefV8ValueCppToC::CefV8ValueCppToC(CefV8Value* cls) : CefCppToC(cls) { + struct_.struct_.is_valid = v8value_is_valid; struct_.struct_.is_undefined = v8value_is_undefined; struct_.struct_.is_null = v8value_is_null; struct_.struct_.is_bool = v8value_is_bool; diff --git a/cef1/libcef_dll/ctocpp/v8context_ctocpp.cc b/cef1/libcef_dll/ctocpp/v8context_ctocpp.cc index bc1e0559b..7d8235a32 100644 --- a/cef1/libcef_dll/ctocpp/v8context_ctocpp.cc +++ b/cef1/libcef_dll/ctocpp/v8context_ctocpp.cc @@ -52,6 +52,19 @@ bool CefV8Context::InContext() { // VIRTUAL METHODS - Body may be edited by hand. +bool CefV8ContextCToCpp::IsValid() { + if (CEF_MEMBER_MISSING(struct_, is_valid)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = struct_->is_valid(struct_); + + // Return type: bool + return _retval?true:false; +} + CefRefPtr CefV8ContextCToCpp::GetBrowser() { if (CEF_MEMBER_MISSING(struct_, get_browser)) return NULL; diff --git a/cef1/libcef_dll/ctocpp/v8context_ctocpp.h b/cef1/libcef_dll/ctocpp/v8context_ctocpp.h index 314082fee..7a9c1c14a 100644 --- a/cef1/libcef_dll/ctocpp/v8context_ctocpp.h +++ b/cef1/libcef_dll/ctocpp/v8context_ctocpp.h @@ -32,6 +32,7 @@ class CefV8ContextCToCpp virtual ~CefV8ContextCToCpp() {} // CefV8Context methods + virtual bool IsValid() OVERRIDE; virtual CefRefPtr GetBrowser() OVERRIDE; virtual CefRefPtr GetFrame() OVERRIDE; virtual CefRefPtr GetGlobal() OVERRIDE; diff --git a/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.cc b/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.cc index 8d685df90..810ea91b2 100644 --- a/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.cc +++ b/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.cc @@ -15,6 +15,19 @@ // VIRTUAL METHODS - Body may be edited by hand. +bool CefV8StackFrameCToCpp::IsValid() { + if (CEF_MEMBER_MISSING(struct_, is_valid)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = struct_->is_valid(struct_); + + // Return type: bool + return _retval?true:false; +} + CefString CefV8StackFrameCToCpp::GetScriptName() { if (CEF_MEMBER_MISSING(struct_, get_script_name)) return CefString(); diff --git a/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.h b/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.h index 6a6a32999..c35874d1f 100644 --- a/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.h +++ b/cef1/libcef_dll/ctocpp/v8stack_frame_ctocpp.h @@ -34,6 +34,7 @@ class CefV8StackFrameCToCpp virtual ~CefV8StackFrameCToCpp() {} // CefV8StackFrame methods + virtual bool IsValid() OVERRIDE; virtual CefString GetScriptName() OVERRIDE; virtual CefString GetScriptNameOrSourceURL() OVERRIDE; virtual CefString GetFunctionName() OVERRIDE; diff --git a/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.cc b/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.cc index a569e3948..9e95155c2 100644 --- a/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.cc +++ b/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.cc @@ -30,6 +30,19 @@ CefRefPtr CefV8StackTrace::GetCurrent(int frame_limit) { // VIRTUAL METHODS - Body may be edited by hand. +bool CefV8StackTraceCToCpp::IsValid() { + if (CEF_MEMBER_MISSING(struct_, is_valid)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = struct_->is_valid(struct_); + + // Return type: bool + return _retval?true:false; +} + int CefV8StackTraceCToCpp::GetFrameCount() { if (CEF_MEMBER_MISSING(struct_, get_frame_count)) return 0; diff --git a/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.h b/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.h index 05ac92a90..c28bc2b32 100644 --- a/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.h +++ b/cef1/libcef_dll/ctocpp/v8stack_trace_ctocpp.h @@ -34,6 +34,7 @@ class CefV8StackTraceCToCpp virtual ~CefV8StackTraceCToCpp() {} // CefV8StackTrace methods + virtual bool IsValid() OVERRIDE; virtual int GetFrameCount() OVERRIDE; virtual CefRefPtr GetFrame(int index) OVERRIDE; }; diff --git a/cef1/libcef_dll/ctocpp/v8value_ctocpp.cc b/cef1/libcef_dll/ctocpp/v8value_ctocpp.cc index 5e28c4020..47f4cd1f0 100644 --- a/cef1/libcef_dll/ctocpp/v8value_ctocpp.cc +++ b/cef1/libcef_dll/ctocpp/v8value_ctocpp.cc @@ -159,6 +159,19 @@ CefRefPtr CefV8Value::CreateFunction(const CefString& name, // VIRTUAL METHODS - Body may be edited by hand. +bool CefV8ValueCToCpp::IsValid() { + if (CEF_MEMBER_MISSING(struct_, is_valid)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = struct_->is_valid(struct_); + + // Return type: bool + return _retval?true:false; +} + bool CefV8ValueCToCpp::IsUndefined() { if (CEF_MEMBER_MISSING(struct_, is_undefined)) return false; diff --git a/cef1/libcef_dll/ctocpp/v8value_ctocpp.h b/cef1/libcef_dll/ctocpp/v8value_ctocpp.h index 15efb7941..dc82c105a 100644 --- a/cef1/libcef_dll/ctocpp/v8value_ctocpp.h +++ b/cef1/libcef_dll/ctocpp/v8value_ctocpp.h @@ -33,6 +33,7 @@ class CefV8ValueCToCpp virtual ~CefV8ValueCToCpp() {} // CefV8Value methods + virtual bool IsValid() OVERRIDE; virtual bool IsUndefined() OVERRIDE; virtual bool IsNull() OVERRIDE; virtual bool IsBool() OVERRIDE; diff --git a/cef1/tests/cefclient/cefclient.cpp b/cef1/tests/cefclient/cefclient.cpp index e702dfc0b..4f52fe492 100644 --- a/cef1/tests/cefclient/cefclient.cpp +++ b/cef1/tests/cefclient/cefclient.cpp @@ -259,6 +259,8 @@ void AppGetSettings(CefSettings& settings, CefRefPtr& app) { g_command_line->HasSwitch(cefclient::kPackLoadingDisabled); settings.uncaught_exception_stack_size = GetIntValue( g_command_line->GetSwitchValue(cefclient::kUncaughtExceptionStackSize)); + settings.context_safety_implementation = GetIntValue( + g_command_line->GetSwitchValue(cefclient::kContextSafetyImplementation)); // Retrieve command-line proxy configuration, if any. bool has_proxy = false; diff --git a/cef1/tests/cefclient/cefclient_switches.cpp b/cef1/tests/cefclient/cefclient_switches.cpp index ef2abc199..f20673a14 100644 --- a/cef1/tests/cefclient/cefclient_switches.cpp +++ b/cef1/tests/cefclient/cefclient_switches.cpp @@ -36,6 +36,7 @@ const char kResourcesDirPath[] = "resources-dir-path"; const char kLocalesDirPath[] = "locales-dir-path"; const char kPackLoadingDisabled[] = "pack-loading-disabled"; const char kUncaughtExceptionStackSize[] = "uncaught-exception-stack-size"; +const char kContextSafetyImplementation[] = "context-safety-implementation"; // CefBrowserSettings attributes. const char kDragDropDisabled[] = "drag-drop-disabled"; diff --git a/cef1/tests/cefclient/cefclient_switches.h b/cef1/tests/cefclient/cefclient_switches.h index a18730cd0..03d9b5c5c 100644 --- a/cef1/tests/cefclient/cefclient_switches.h +++ b/cef1/tests/cefclient/cefclient_switches.h @@ -38,6 +38,7 @@ extern const char kResourcesDirPath[]; extern const char kLocalesDirPath[]; extern const char kPackLoadingDisabled[]; extern const char kUncaughtExceptionStackSize[]; +extern const char kContextSafetyImplementation[]; // CefBrowserSettings attributes. extern const char kDragDropDisabled[]; diff --git a/cef1/tests/cefclient/performance_test.cpp b/cef1/tests/cefclient/performance_test.cpp index e65aa1f8a..c851dc9b1 100644 --- a/cef1/tests/cefclient/performance_test.cpp +++ b/cef1/tests/cefclient/performance_test.cpp @@ -13,7 +13,13 @@ namespace performance_test { +// Use more interations for a Release build. +#ifdef NDEBUG +const size_t kDefaultIterations = 100000; +#else const size_t kDefaultIterations = 10000; +#endif + const char kTestUrl[] = "http://tests/performance"; namespace { diff --git a/cef1/tests/unittests/test_suite.cc b/cef1/tests/unittests/test_suite.cc index 5014e6cb7..a80bba246 100644 --- a/cef1/tests/unittests/test_suite.cc +++ b/cef1/tests/unittests/test_suite.cc @@ -3,6 +3,9 @@ // can be found in the LICENSE file. #include "tests/unittests/test_suite.h" + +#include + #include "tests/cefclient/cefclient_switches.h" #include "base/command_line.h" #include "base/logging.h" @@ -16,6 +19,18 @@ #include "base/test/test_timeouts.h" #endif +namespace { + +// Return the int representation of the specified string. +int GetIntValue(const std::string& str) { + if (str.empty()) + return 0; + + return atoi(str.c_str()); +} + +} // namespace + CommandLine* CefTestSuite::commandline_ = NULL; CefTestSuite::CefTestSuite(int argc, char** argv) @@ -127,6 +142,10 @@ void CefTestSuite::GetSettings(CefSettings& settings) { // Necessary for V8Test.OnUncaughtException tests. settings.uncaught_exception_stack_size = 10; + + settings.context_safety_implementation = GetIntValue( + commandline_->GetSwitchValueASCII( + cefclient::kContextSafetyImplementation)); } // static diff --git a/cef1/tests/unittests/v8_unittest.cc b/cef1/tests/unittests/v8_unittest.cc index 0488e82ac..f4f4c845c 100644 --- a/cef1/tests/unittests/v8_unittest.cc +++ b/cef1/tests/unittests/v8_unittest.cc @@ -18,12 +18,13 @@ namespace { // Unique values for V8 tests. -const char* kV8TestUrl = "http://tests/V8Test.Test"; -const char* kV8BindingTestUrl = "http://tests/V8Test.BindingTest"; -const char* kV8ContextParentTestUrl = "http://tests/V8Test.ContextParentTest"; -const char* kV8ContextChildTestUrl = "http://tests/V8Test.ContextChildTest"; -const char* kV8OnUncaughtExceptionTestUrl = +const char kV8TestUrl[] = "http://tests/V8Test.Test"; +const char kV8BindingTestUrl[] = "http://tests/V8Test.BindingTest"; +const char kV8ContextParentTestUrl[] = "http://tests/V8Test.ContextParentTest"; +const char kV8ContextChildTestUrl[] = "http://tests/V8Test.ContextChildTest"; +const char kV8OnUncaughtExceptionTestUrl[] = "http://tests/V8Test.OnUncaughtException"; +const char kV8NavTestUrl[] = "http://tests/V8Test.NavTest"; enum V8TestMode { V8TEST_NULL_CREATE = 0, @@ -57,6 +58,7 @@ enum V8TestMode { V8TEST_CONTEXT_EVAL, V8TEST_CONTEXT_EVAL_EXCEPTION, V8TEST_CONTEXT_ENTERED, + V8TEST_CONTEXT_INVALID, V8TEST_BINDING, V8TEST_STACK_TRACE, V8TEST_ON_UNCAUGHT_EXCEPTION, @@ -73,11 +75,14 @@ class V8TestHandler : public TestHandler { } virtual void RunTest() OVERRIDE { + // Nested script tag forces creation of the V8 context. if (test_mode_ == V8TEST_CONTEXT_ENTERED) { - AddResource(kV8ContextParentTestUrl, "" "", "text/html"); - AddResource(kV8ContextChildTestUrl, "CHILD", + AddResource(kV8ContextChildTestUrl, "" + "CHILD", "text/html"); CreateBrowser(kV8ContextParentTestUrl); } else if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION || @@ -92,8 +97,14 @@ class V8TestHandler : public TestHandler { "text/html"); CreateBrowser(kV8OnUncaughtExceptionTestUrl); } else { + if (test_mode_ == V8TEST_CONTEXT_INVALID) { + AddResource(kV8NavTestUrl, "" + "TEST", "text/html"); + } + EXPECT_TRUE(test_url_ != NULL); - AddResource(test_url_, "TEST", "text/html"); + AddResource(test_url_, "" + "TEST", "text/html"); CreateBrowser(test_url_); } } @@ -194,6 +205,10 @@ class V8TestHandler : public TestHandler { case V8TEST_CONTEXT_ENTERED: RunContextEnteredTest(); break; + case V8TEST_CONTEXT_INVALID: + // The test is triggered when the context is released. + GetBrowser()->GetMainFrame()->LoadURL(kV8NavTestUrl); + break; case V8TEST_BINDING: RunBindingTest(); break; @@ -1545,14 +1560,14 @@ class V8TestHandler : public TestHandler { } void RunOnUncaughtExceptionTest() { - on_uncaught_exception_context_ = + test_context_ = GetBrowser()->GetMainFrame()->GetV8Context(); GetBrowser()->GetMainFrame()->ExecuteJavaScript("test()", CefString(), 0); } void RunOnUncaughtExceptionDevToolsTest() { - on_uncaught_exception_context_ = + test_context_ = GetBrowser()->GetMainFrame()->GetV8Context(); GetBrowser()->ShowDevTools(); } @@ -1566,7 +1581,7 @@ class V8TestHandler : public TestHandler { if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION || test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) { - EXPECT_TRUE(on_uncaught_exception_context_->IsSame(context)); + EXPECT_TRUE(test_context_->IsSame(context)); EXPECT_STREQ("Uncaught ReferenceError: asd is not defined", exception->GetMessage().ToString().c_str()); std::ostringstream stackFormatted; @@ -1587,7 +1602,8 @@ class V8TestHandler : public TestHandler { virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) OVERRIDE { - if (frame->IsMain()) + std::string url = frame->GetURL(); + if (frame->IsMain() && url != kV8NavTestUrl) RunTest(test_mode_); if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS && browser->IsPopup()) { @@ -1654,8 +1670,25 @@ class V8TestHandler : public TestHandler { } } + virtual void OnContextReleased(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) OVERRIDE { + std::string url = frame->GetURL(); + if (test_mode_ == V8TEST_CONTEXT_INVALID && url == test_url_) { + test_context_ = context; + test_object_ = CefV8Value::CreateArray(10); + CefPostTask(TID_UI, + NewCefRunnableMethod(this, &V8TestHandler::DestroyTest)); + } + } + virtual void DestroyTest() OVERRIDE { - if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION || + if (test_mode_ == V8TEST_CONTEXT_INVALID) { + // Verify that objects related to a particular context are not valid after + // OnContextReleased is called for that context. + EXPECT_FALSE(test_context_->IsValid()); + EXPECT_FALSE(test_object_->IsValid()); + } else if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION || test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) { EXPECT_TRUE(got_on_uncaught_exception_); } @@ -1674,7 +1707,8 @@ class V8TestHandler : public TestHandler { V8TestMode test_mode_; const char* test_url_; - CefRefPtr on_uncaught_exception_context_; + CefRefPtr test_context_; + CefRefPtr test_object_; TrackCallback got_destroy_test_; TrackCallback got_on_uncaught_exception_; @@ -1728,6 +1762,7 @@ V8_TEST(FunctionHandlerWithContext, V8TEST_FUNCTION_HANDLER_WITH_CONTEXT); V8_TEST(ContextEval, V8TEST_CONTEXT_EVAL); V8_TEST(ContextEvalException, V8TEST_CONTEXT_EVAL_EXCEPTION); V8_TEST_EX(ContextEntered, V8TEST_CONTEXT_ENTERED, NULL); +V8_TEST(ContextInvalid, V8TEST_CONTEXT_INVALID); V8_TEST_EX(Binding, V8TEST_BINDING, kV8BindingTestUrl); V8_TEST(StackTrace, V8TEST_STACK_TRACE); V8_TEST(OnUncaughtException, V8TEST_ON_UNCAUGHT_EXCEPTION);