mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
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:
@ -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();
|
||||
}
|
||||
|
Reference in New Issue
Block a user