Support implicit detachment of CEF V8 references when the associated context is released (issue #484).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@883 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt
2012-10-29 21:54:57 +00:00
parent 3ac5952636
commit 59fe4fbab3
25 changed files with 522 additions and 43 deletions

View File

@ -3,6 +3,7 @@
// can be found in the LICENSE file.
#include <string>
#include <vector>
#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<CefTrackManager> 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<CefV8ContextState> GetContextState(
v8::Handle<v8::Context> context) {
if (context_safety_impl_ == IMPL_DISABLED)
return scoped_refptr<CefV8ContextState>();
if (context.IsEmpty()) {
if (v8::Context::InContext())
context = v8::Context::GetCurrent();
else
return scoped_refptr<CefV8ContextState>();
}
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<CefV8ContextState> state = new CefV8ContextState();
context_map_.insert(std::make_pair(hash, state));
return state;
} else {
if (context_state_key_.IsEmpty()) {
context_state_key_ =
v8::Persistent<v8::String>::New(v8::String::New(kCefContextState));
}
v8::Handle<v8::Object> object = context->Global();
v8::Handle<v8::Value> value = object->GetHiddenValue(context_state_key_);
if (!value.IsEmpty())
return static_cast<CefV8ContextState*>(v8::External::Unwrap(value));
scoped_refptr<CefV8ContextState> 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<v8::Context> 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<v8::Object> object = context->Global();
v8::Handle<v8::Value> value = object->GetHiddenValue(context_state_key_);
scoped_refptr<CefV8ContextState> state =
static_cast<CefV8ContextState*>(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<int, scoped_refptr<CefV8ContextState> > ContextMap;
ContextMap context_map_;
// Used with IMPL_VALUE.
v8::Persistent<v8::String> context_state_key_;
};
base::LazyInstance<CefV8TrackManager> 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<v8::Context> context) {
g_v8_tracker.Pointer()->ReleaseContext(context);
}
bool CefRegisterExtension(const CefString& extension_name,
const CefString& javascript_code,
CefRefPtr<CefV8Handler> handler) {
@ -437,6 +542,13 @@ bool CefRegisterExtension(const CefString& extension_name,
}
// CefV8HandleBase
CefV8HandleBase::CefV8HandleBase(v8::Handle<v8::Context> 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<v8::Context> 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<CefBrowser> CefV8ContextImpl::GetBrowser() {
CefRefPtr<CefBrowser> browser;
CEF_REQUIRE_UI_THREAD(browser);
CEF_V8_REQUIRE_VALID_RETURN(browser);
WebKit::WebFrame* webframe = GetWebFrame();
if (webframe)
@ -517,6 +644,7 @@ CefRefPtr<CefBrowser> CefV8ContextImpl::GetBrowser() {
CefRefPtr<CefFrame> CefV8ContextImpl::GetFrame() {
CefRefPtr<CefFrame> frame;
CEF_REQUIRE_UI_THREAD(frame);
CEF_V8_REQUIRE_VALID_RETURN(frame);
WebKit::WebFrame* webframe = GetWebFrame();
if (webframe) {
@ -531,6 +659,7 @@ CefRefPtr<CefFrame> CefV8ContextImpl::GetFrame() {
CefRefPtr<CefV8Value> 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<CefV8Value> 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<CefV8Context> 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<CefV8Value>& retval,
CefRefPtr<CefV8Exception>& 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> CefV8Value::CreateFunction(
CefV8ValueImpl::CefV8ValueImpl(v8::Handle<v8::Value> value,
CefTrackNode* tracker)
: handle_(new Handle(value, tracker)),
: handle_(new Handle(v8::Handle<v8::Context>(), 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<CefV8Value> that) {
CEF_REQUIRE_UI_THREAD(false);
CEF_V8_REQUIRE_VALID_RETURN(false);
v8::HandleScope handle_scope;
@ -900,6 +1053,7 @@ bool CefV8ValueImpl::IsSame(CefRefPtr<CefV8Value> 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<v8::Int32> 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<v8::Uint32> 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<v8::Number> 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<v8::Number> 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> CefV8StackTrace::GetCurrent(int frame_limit) {
CEF_REQUIRE_VALID_CONTEXT(NULL);
CEF_REQUIRE_UI_THREAD(NULL);
v8::HandleScope handle_scope;
v8::Handle<v8::StackTrace> stackTrace =
v8::StackTrace::CurrentStackTrace(
frame_limit, v8::StackTrace::kDetailed);
@ -1379,20 +1539,27 @@ CefRefPtr<CefV8StackTrace> CefV8StackTrace::GetCurrent(int frame_limit) {
// CefV8StackTraceImpl
CefV8StackTraceImpl::CefV8StackTraceImpl(v8::Handle<v8::StackTrace> handle)
: handle_(new Handle(handle)) {
: handle_(new Handle(v8::Handle<v8::Context>(), 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<CefV8StackFrame> CefV8StackTraceImpl::GetFrame(int index) {
CEF_REQUIRE_UI_THREAD(NULL);
CEF_V8_REQUIRE_VALID_RETURN(NULL);
v8::HandleScope handle_scope;
v8::Handle<v8::StackFrame> stackFrame = GetHandle()->GetFrame(index);
if (stackFrame.IsEmpty())
@ -1404,15 +1571,21 @@ CefRefPtr<CefV8StackFrame> CefV8StackTraceImpl::GetFrame(int index) {
// CefV8StackFrameImpl
CefV8StackFrameImpl::CefV8StackFrameImpl(v8::Handle<v8::StackFrame> handle)
: handle_(new Handle(handle)) {
: handle_(new Handle(v8::Handle<v8::Context>(), 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<v8::String>::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<v8::String>::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<v8::String>::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();
}