// Copyright (c) 2013 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. // MSVC++ requires this to be set before any other includes to get M_PI. // Otherwise there will be compile errors in wtf/MathExtras.h. #define _USE_MATH_DEFINES #include #include #include "base/command_line.h" #include "base/compiler_specific.h" // Enable deprecation warnings for MSVC and Clang. See http://crbug.com/585142. #if defined(OS_WIN) #if defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic error "-Wdeprecated-declarations" #else #pragma warning(push) #pragma warning(default : 4996) #endif #endif #include "libcef/renderer/v8_impl.h" #include "libcef/common/app_manager.h" #include "libcef/common/cef_switches.h" #include "libcef/common/task_runner_impl.h" #include "libcef/common/tracker.h" #include "libcef/renderer/blink_glue.h" #include "libcef/renderer/browser_impl.h" #include "libcef/renderer/render_frame_util.h" #include "libcef/renderer/thread_util.h" #include "base/bind.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" #include "base/threading/thread_local.h" #include "third_party/blink/public/web/blink.h" #include "third_party/blink/public/web/web_frame.h" #include "third_party/blink/public/web/web_local_frame.h" #include "url/gurl.h" namespace { static const char kCefTrackObject[] = "Cef::TrackObject"; void MessageListenerCallbackImpl(v8::Handle message, v8::Handle data); // The following *Private functions are convenience wrappers for methods on // v8::Object with the corresponding names. // Based on extensions/renderer/object_backed_native_handler.cc. void SetPrivate(v8::Local context, v8::Local obj, const char* key, v8::Local value) { v8::Isolate* isolate = context->GetIsolate(); obj->SetPrivate(context, v8::Private::ForApi( isolate, v8::String::NewFromUtf8( isolate, key, v8::NewStringType::kNormal) .ToLocalChecked()), value) .FromJust(); } bool GetPrivate(v8::Local context, v8::Local obj, const char* key, v8::Local* result) { v8::Isolate* isolate = context->GetIsolate(); return obj ->GetPrivate(context, v8::Private::ForApi( isolate, v8::String::NewFromUtf8( isolate, key, v8::NewStringType::kNormal) .ToLocalChecked())) .ToLocal(result); } // Manages memory and state information associated with a single Isolate. class CefV8IsolateManager { public: CefV8IsolateManager() : isolate_(v8::Isolate::GetCurrent()), task_runner_(CEF_RENDER_TASK_RUNNER()), message_listener_registered_(false), worker_id_(0) { DCHECK(isolate_); DCHECK(task_runner_.get()); } ~CefV8IsolateManager() { DCHECK_EQ(isolate_, v8::Isolate::GetCurrent()); DCHECK(context_map_.empty()); } scoped_refptr GetContextState( v8::Local context) { DCHECK_EQ(isolate_, v8::Isolate::GetCurrent()); DCHECK(context.IsEmpty() || isolate_ == context->GetIsolate()); if (context.IsEmpty()) { if (isolate_->InContext()) context = isolate_->GetCurrentContext(); else return scoped_refptr(); } 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; } void ReleaseContext(v8::Local context) { DCHECK_EQ(isolate_, v8::Isolate::GetCurrent()); DCHECK_EQ(isolate_, context->GetIsolate()); int hash = context->Global()->GetIdentityHash(); ContextMap::iterator it = context_map_.find(hash); if (it != context_map_.end()) { it->second->Detach(); context_map_.erase(it); } } void AddGlobalTrackObject(CefTrackNode* object) { DCHECK_EQ(isolate_, v8::Isolate::GetCurrent()); global_manager_.Add(object); } void DeleteGlobalTrackObject(CefTrackNode* object) { DCHECK_EQ(isolate_, v8::Isolate::GetCurrent()); global_manager_.Delete(object); } void SetUncaughtExceptionStackSize(int stack_size) { if (stack_size <= 0) return; if (!message_listener_registered_) { isolate_->AddMessageListener(&MessageListenerCallbackImpl); message_listener_registered_ = true; } isolate_->SetCaptureStackTraceForUncaughtExceptions( true, stack_size, v8::StackTrace::kDetailed); } void SetWorkerAttributes(int worker_id, const GURL& worker_url) { worker_id_ = worker_id; worker_url_ = worker_url; } v8::Isolate* isolate() const { return isolate_; } scoped_refptr task_runner() const { return task_runner_; } int worker_id() const { return worker_id_; } const GURL& worker_url() const { return worker_url_; } private: v8::Isolate* isolate_; scoped_refptr task_runner_; typedef std::map> ContextMap; ContextMap context_map_; // Used for globally tracked objects that are not associated with a particular // context. CefTrackManager global_manager_; // True if the message listener has been registered. bool message_listener_registered_; // Attributes associated with WebWorker threads. int worker_id_; GURL worker_url_; }; // Chromium uses the default Isolate for the main render process thread and a // new Isolate for each WebWorker thread. Continue this pattern by tracking // Isolate information on a per-thread basis. This implementation will need to // be re-worked (perhaps using a map keyed on v8::Isolate::GetCurrent()) if // in the future Chromium begins using the same Isolate across multiple threads. class CefV8StateManager { public: CefV8StateManager() {} void CreateIsolateManager() { DCHECK(!current_tls_.Get()); current_tls_.Set(new CefV8IsolateManager()); } void DestroyIsolateManager() { DCHECK(current_tls_.Get()); delete current_tls_.Get(); current_tls_.Set(nullptr); } CefV8IsolateManager* GetIsolateManager() { CefV8IsolateManager* manager = current_tls_.Get(); DCHECK(manager); return manager; } private: base::ThreadLocalPointer current_tls_; }; base::LazyInstance::Leaky g_v8_state = LAZY_INSTANCE_INITIALIZER; CefV8IsolateManager* GetIsolateManager() { return g_v8_state.Pointer()->GetIsolateManager(); } class V8TrackObject : public CefTrackNode { public: explicit V8TrackObject(v8::Isolate* isolate) : isolate_(isolate), external_memory_(0) { DCHECK(isolate_); isolate_->AdjustAmountOfExternalAllocatedMemory( static_cast(sizeof(V8TrackObject))); } ~V8TrackObject() { isolate_->AdjustAmountOfExternalAllocatedMemory( -static_cast(sizeof(V8TrackObject)) - external_memory_); } inline int GetExternallyAllocatedMemory() { return external_memory_; } int AdjustExternallyAllocatedMemory(int change_in_bytes) { int new_value = external_memory_ + change_in_bytes; if (new_value < 0) { NOTREACHED() << "External memory usage cannot be less than 0 bytes"; change_in_bytes = -(external_memory_); new_value = 0; } if (change_in_bytes != 0) isolate_->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); external_memory_ = new_value; return new_value; } inline void SetAccessor(CefRefPtr accessor) { accessor_ = accessor; } inline CefRefPtr GetAccessor() { return accessor_; } inline void SetInterceptor(CefRefPtr interceptor) { interceptor_ = interceptor; } inline CefRefPtr GetInterceptor() { return interceptor_; } inline void SetHandler(CefRefPtr handler) { handler_ = handler; } inline CefRefPtr GetHandler() { return handler_; } inline void SetUserData(CefRefPtr user_data) { user_data_ = user_data; } inline CefRefPtr GetUserData() { return user_data_; } // Attach this track object to the specified V8 object. void AttachTo(v8::Local context, v8::Local object) { SetPrivate(context, object, kCefTrackObject, v8::External::New(isolate_, this)); } // Retrieve the track object for the specified V8 object. static V8TrackObject* Unwrap(v8::Local context, v8::Local object) { v8::Local value; if (GetPrivate(context, object, kCefTrackObject, &value)) return static_cast(v8::External::Cast(*value)->Value()); return nullptr; } private: v8::Isolate* isolate_; CefRefPtr accessor_; CefRefPtr interceptor_; CefRefPtr handler_; CefRefPtr user_data_; int external_memory_; }; class V8TrackString : public CefTrackNode { public: explicit V8TrackString(const std::string& str) : string_(str) {} const char* GetString() { return string_.c_str(); } private: std::string string_; }; class V8TrackArrayBuffer : public CefTrackNode { public: explicit V8TrackArrayBuffer( v8::Isolate* isolate, CefRefPtr release_callback) : isolate_(isolate), release_callback_(release_callback) { DCHECK(isolate_); isolate_->AdjustAmountOfExternalAllocatedMemory( static_cast(sizeof(V8TrackArrayBuffer))); } ~V8TrackArrayBuffer() { isolate_->AdjustAmountOfExternalAllocatedMemory( -static_cast(sizeof(V8TrackArrayBuffer))); } CefRefPtr GetReleaseCallback() { return release_callback_; } // Attach this track object to the specified V8 object. void AttachTo(v8::Local context, v8::Local arrayBuffer) { SetPrivate(context, arrayBuffer, kCefTrackObject, v8::External::New(isolate_, this)); } // Retrieve the track object for the specified V8 object. static V8TrackArrayBuffer* Unwrap(v8::Local context, v8::Local object) { v8::Local value; if (GetPrivate(context, object, kCefTrackObject, &value)) return static_cast( v8::External::Cast(*value)->Value()); return nullptr; } private: v8::Isolate* isolate_; CefRefPtr release_callback_; }; // Object wrapped in a v8::External and passed as the Data argument to // v8::FunctionTemplate::New. class V8FunctionData { public: static v8::Local Create(v8::Isolate* isolate, const CefString& function_name, CefRefPtr handler) { // |data| will be deleted if/when the returned v8::External is GC'd. V8FunctionData* data = new V8FunctionData(isolate, function_name, handler); return data->CreateExternal(); } static V8FunctionData* Unwrap(v8::Local data) { DCHECK(data->IsExternal()); return static_cast(v8::External::Cast(*data)->Value()); } CefString function_name() const { return function_name_; } CefV8Handler* handler() const { if (!handler_) return nullptr; return handler_.get(); } private: V8FunctionData(v8::Isolate* isolate, const CefString& function_name, CefRefPtr handler) : isolate_(isolate), function_name_(function_name), handler_(handler) { DCHECK(isolate_); DCHECK(handler_); } ~V8FunctionData() { isolate_->AdjustAmountOfExternalAllocatedMemory( -static_cast(sizeof(V8FunctionData))); handler_ = nullptr; function_name_ = "FreedFunction"; } v8::Local CreateExternal() { v8::Local external = v8::External::New(isolate_, this); isolate_->AdjustAmountOfExternalAllocatedMemory( static_cast(sizeof(V8FunctionData))); handle_.Reset(isolate_, external); handle_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter); return external; } static void FirstWeakCallback( const v8::WeakCallbackInfo& data) { V8FunctionData* wrapper = data.GetParameter(); wrapper->handle_.Reset(); data.SetSecondPassCallback(SecondWeakCallback); } static void SecondWeakCallback( const v8::WeakCallbackInfo& data) { V8FunctionData* wrapper = data.GetParameter(); delete wrapper; } v8::Isolate* isolate_; CefString function_name_; CefRefPtr handler_; v8::Persistent handle_; }; // Convert a CefString to a V8::String. v8::Local GetV8String(v8::Isolate* isolate, const CefString& str) { #if defined(CEF_STRING_TYPE_UTF16) // Already a UTF16 string. return v8::String::NewFromTwoByte( isolate, reinterpret_cast( const_cast(str.c_str())), v8::NewStringType::kNormal, str.length()) .ToLocalChecked(); #elif defined(CEF_STRING_TYPE_UTF8) // Already a UTF8 string. return v8::String::NewFromUtf8(isolate, const_cast(str.c_str()), v8::NewStringType::kNormal, str.length()) .ToLocalChecked(); #else // Convert the string to UTF8. std::string tmpStr = str; return v8::String::NewFromUtf8(isolate, tmpStr.c_str(), v8::NewStringType::kNormal, tmpStr.length()) .ToLocalChecked(); #endif } #if defined(CEF_STRING_TYPE_UTF16) void v8impl_string_dtor(char16* str) { delete[] str; } #elif defined(CEF_STRING_TYPE_UTF8) void v8impl_string_dtor(char* str) { delete[] str; } #endif // Convert a v8::String to CefString. void GetCefString(v8::Isolate* isolate, v8::Local str, CefString& out) { if (str.IsEmpty()) return; #if defined(CEF_STRING_TYPE_WIDE) // Allocate enough space for a worst-case conversion. int len = str->Utf8Length(); if (len == 0) return; char* buf = new char[len + 1]; str->WriteUtf8(isolate, buf, len + 1); // Perform conversion to the wide type. cef_string_t* retws = out.GetWritableStruct(); cef_string_utf8_to_wide(buf, len, retws); delete[] buf; #else // !defined(CEF_STRING_TYPE_WIDE) #if defined(CEF_STRING_TYPE_UTF16) int len = str->Length(); if (len == 0) return; char16* buf = new char16[len + 1]; str->Write(isolate, reinterpret_cast(buf), 0, len + 1); #else // Allocate enough space for a worst-case conversion. int len = str->Utf8Length(); if (len == 0) return; char* buf = new char[len + 1]; str->WriteUtf8(isolate, buf, len + 1); #endif // Don't perform an extra string copy. out.clear(); cef_string_t* retws = out.GetWritableStruct(); retws->str = buf; retws->length = len; retws->dtor = v8impl_string_dtor; #endif // !defined(CEF_STRING_TYPE_WIDE) } // V8 function callback. void FunctionCallbackImpl(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); V8FunctionData* data = V8FunctionData::Unwrap(info.Data()); if (!data->handler()) { // handler has gone away, bail! info.GetReturnValue().SetUndefined(); return; } CefV8ValueList params; for (int i = 0; i < info.Length(); i++) params.push_back(new CefV8ValueImpl(isolate, context, info[i])); CefRefPtr object = new CefV8ValueImpl(isolate, context, info.This()); CefRefPtr retval; CefString exception; if (data->handler()->Execute(data->function_name(), object, params, retval, exception)) { if (!exception.empty()) { info.GetReturnValue().Set(isolate->ThrowException( v8::Exception::Error(GetV8String(isolate, exception)))); return; } else { CefV8ValueImpl* rv = static_cast(retval.get()); if (rv && rv->IsValid()) { info.GetReturnValue().Set(rv->GetV8Value(true)); return; } } } info.GetReturnValue().SetUndefined(); } // V8 Accessor callbacks void AccessorNameGetterCallbackImpl( v8::Local property, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); v8::Local obj = info.This(); CefRefPtr accessorPtr; V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) accessorPtr = tracker->GetAccessor(); if (accessorPtr.get()) { CefRefPtr retval; CefRefPtr object = new CefV8ValueImpl(isolate, context, obj); CefString name, exception; GetCefString(isolate, v8::Local::Cast(property), name); if (accessorPtr->Get(name, object, retval, exception)) { if (!exception.empty()) { info.GetReturnValue().Set(isolate->ThrowException( v8::Exception::Error(GetV8String(isolate, exception)))); return; } else { CefV8ValueImpl* rv = static_cast(retval.get()); if (rv && rv->IsValid()) { info.GetReturnValue().Set(rv->GetV8Value(true)); return; } } } } return info.GetReturnValue().SetUndefined(); } void AccessorNameSetterCallbackImpl( v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); v8::Local obj = info.This(); CefRefPtr accessorPtr; V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) accessorPtr = tracker->GetAccessor(); if (accessorPtr.get()) { CefRefPtr object = new CefV8ValueImpl(isolate, context, obj); CefRefPtr cefValue = new CefV8ValueImpl(isolate, context, value); CefString name, exception; GetCefString(isolate, v8::Local::Cast(property), name); accessorPtr->Set(name, object, cefValue, exception); if (!exception.empty()) { isolate->ThrowException( v8::Exception::Error(GetV8String(isolate, exception))); return; } } } // Two helper functions for V8 Interceptor callbacks. CefString PropertyToIndex(v8::Isolate* isolate, v8::Local property) { CefString name; GetCefString(isolate, property.As(), name); return name; } int PropertyToIndex(v8::Isolate* isolate, uint32_t index) { return static_cast(index); } // V8 Interceptor callbacks. // T == v8::Local for named property handlers and // T == uint32_t for indexed property handlers template void InterceptorGetterCallbackImpl( T property, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); v8::Handle obj = info.This(); CefRefPtr interceptorPtr; V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) interceptorPtr = tracker->GetInterceptor(); if (!interceptorPtr.get()) return; CefRefPtr object = new CefV8ValueImpl(isolate, context, obj); CefRefPtr retval; CefString exception; interceptorPtr->Get(PropertyToIndex(isolate, property), object, retval, exception); if (!exception.empty()) { info.GetReturnValue().Set(isolate->ThrowException( v8::Exception::Error(GetV8String(isolate, exception)))); } else { CefV8ValueImpl* retval_impl = static_cast(retval.get()); if (retval_impl && retval_impl->IsValid()) { info.GetReturnValue().Set(retval_impl->GetV8Value(true)); } } } template void InterceptorSetterCallbackImpl( T property, v8::Local value, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); v8::Handle obj = info.This(); CefRefPtr interceptorPtr; V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) interceptorPtr = tracker->GetInterceptor(); if (!interceptorPtr.get()) return; CefRefPtr object = new CefV8ValueImpl(isolate, context, obj); CefRefPtr cefValue = new CefV8ValueImpl(isolate, context, value); CefString exception; interceptorPtr->Set(PropertyToIndex(isolate, property), object, cefValue, exception); if (!exception.empty()) { isolate->ThrowException( v8::Exception::Error(GetV8String(isolate, exception))); } } // V8 extension registration. class ExtensionWrapper : public v8::Extension { public: ExtensionWrapper(const char* extension_name, const char* javascript_code, CefV8Handler* handler) : v8::Extension(extension_name, javascript_code), handler_(handler) {} v8::Handle GetNativeFunctionTemplate( v8::Isolate* isolate, v8::Handle name) override { if (!handler_) return v8::Local(); CefString func_name; GetCefString(isolate, name, func_name); v8::Local function_data = V8FunctionData::Create(isolate, func_name, handler_); return v8::FunctionTemplate::New(isolate, FunctionCallbackImpl, function_data); } private: CefV8Handler* handler_; }; class CefV8ExceptionImpl : public CefV8Exception { public: CefV8ExceptionImpl(v8::Local context, v8::Local message) : line_number_(0), start_position_(0), end_position_(0), start_column_(0), end_column_(0) { if (message.IsEmpty()) return; v8::Isolate* isolate = context->GetIsolate(); GetCefString(isolate, message->Get(), message_); v8::MaybeLocal source_line = message->GetSourceLine(context); if (!source_line.IsEmpty()) GetCefString(isolate, source_line.ToLocalChecked(), source_line_); if (!message->GetScriptResourceName().IsEmpty()) { GetCefString( isolate, message->GetScriptResourceName()->ToString(context).ToLocalChecked(), script_); } v8::Maybe line_number = message->GetLineNumber(context); if (!line_number.IsNothing()) line_number_ = line_number.ToChecked(); start_position_ = message->GetStartPosition(); end_position_ = message->GetEndPosition(); start_column_ = message->GetStartColumn(context).FromJust(); end_column_ = message->GetEndColumn(context).FromJust(); } CefString GetMessage() override { return message_; } CefString GetSourceLine() override { return source_line_; } CefString GetScriptResourceName() override { return script_; } int GetLineNumber() override { return line_number_; } int GetStartPosition() override { return start_position_; } int GetEndPosition() override { return end_position_; } int GetStartColumn() override { return start_column_; } int GetEndColumn() override { return end_column_; } protected: CefString message_; CefString source_line_; CefString script_; int line_number_; int start_position_; int end_position_; int start_column_; int end_column_; IMPLEMENT_REFCOUNTING(CefV8ExceptionImpl); }; void MessageListenerCallbackImpl(v8::Handle message, v8::Handle data) { CefRefPtr application = CefAppManager::Get()->GetApplication(); if (!application.get()) return; CefRefPtr handler = application->GetRenderProcessHandler(); if (!handler.get()) return; v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr context = CefV8Context::GetCurrentContext(); v8::Local v8Stack = message->GetStackTrace(); CefRefPtr stackTrace = new CefV8StackTraceImpl(isolate, v8Stack); CefRefPtr exception = new CefV8ExceptionImpl( static_cast(context.get())->GetV8Context(), message); CefRefPtr browser = context->GetBrowser(); if (browser) { handler->OnUncaughtException(browser, context->GetFrame(), context, exception, stackTrace); } } } // namespace // Global functions. void CefV8IsolateCreated() { g_v8_state.Pointer()->CreateIsolateManager(); } void CefV8IsolateDestroyed() { g_v8_state.Pointer()->DestroyIsolateManager(); } void CefV8ReleaseContext(v8::Local context) { GetIsolateManager()->ReleaseContext(context); } void CefV8SetUncaughtExceptionStackSize(int stack_size) { GetIsolateManager()->SetUncaughtExceptionStackSize(stack_size); } void CefV8SetWorkerAttributes(int worker_id, const GURL& worker_url) { GetIsolateManager()->SetWorkerAttributes(worker_id, worker_url); } bool CefRegisterExtension(const CefString& extension_name, const CefString& javascript_code, CefRefPtr handler) { // Verify that this method was called on the correct thread. CEF_REQUIRE_RT_RETURN(false); CefV8IsolateManager* isolate_manager = GetIsolateManager(); V8TrackString* name = new V8TrackString(extension_name); isolate_manager->AddGlobalTrackObject(name); V8TrackString* code = new V8TrackString(javascript_code); isolate_manager->AddGlobalTrackObject(code); if (handler.get()) { // The reference will be released when the process exits. V8TrackObject* object = new V8TrackObject(isolate_manager->isolate()); object->SetHandler(handler); isolate_manager->AddGlobalTrackObject(object); } std::unique_ptr wrapper(new ExtensionWrapper( name->GetString(), code->GetString(), handler.get())); content::RenderThread::Get()->RegisterExtension(std::move(wrapper)); return true; } // Helper macros #define CEF_V8_HAS_ISOLATE() (!!GetIsolateManager()) #define CEF_V8_REQUIRE_ISOLATE_RETURN(var) \ if (!CEF_V8_HAS_ISOLATE()) { \ NOTREACHED() << "V8 isolate is not valid"; \ return var; \ } #define CEF_V8_CURRENTLY_ON_MLT() \ (!handle_.get() || handle_->BelongsToCurrentThread()) #define CEF_V8_REQUIRE_MLT_RETURN(var) \ CEF_V8_REQUIRE_ISOLATE_RETURN(var); \ if (!CEF_V8_CURRENTLY_ON_MLT()) { \ NOTREACHED() << "called on incorrect thread"; \ return var; \ } #define CEF_V8_HANDLE_IS_VALID() (handle_.get() && handle_->IsValid()) #define CEF_V8_REQUIRE_VALID_HANDLE_RETURN(ret) \ CEF_V8_REQUIRE_MLT_RETURN(ret); \ if (!CEF_V8_HANDLE_IS_VALID()) { \ NOTREACHED() << "V8 handle is not valid"; \ return ret; \ } #define CEF_V8_IS_VALID() \ (CEF_V8_HAS_ISOLATE() && CEF_V8_CURRENTLY_ON_MLT() && \ CEF_V8_HANDLE_IS_VALID()) #define CEF_V8_REQUIRE_OBJECT_RETURN(ret) \ CEF_V8_REQUIRE_VALID_HANDLE_RETURN(ret); \ if (type_ != TYPE_OBJECT) { \ NOTREACHED() << "V8 value is not an object"; \ return ret; \ } // CefV8HandleBase CefV8HandleBase::~CefV8HandleBase() { DCHECK(BelongsToCurrentThread()); } bool CefV8HandleBase::BelongsToCurrentThread() const { return task_runner_->RunsTasksInCurrentSequence(); } CefV8HandleBase::CefV8HandleBase(v8::Isolate* isolate, v8::Local context) : isolate_(isolate) { DCHECK(isolate_); CefV8IsolateManager* manager = GetIsolateManager(); DCHECK(manager); DCHECK_EQ(isolate_, manager->isolate()); task_runner_ = manager->task_runner(); context_state_ = manager->GetContextState(context); } // CefV8Context // static CefRefPtr CefV8Context::GetCurrentContext() { CefRefPtr context; CEF_V8_REQUIRE_ISOLATE_RETURN(context); v8::Isolate* isolate = GetIsolateManager()->isolate(); if (isolate->InContext()) { v8::HandleScope handle_scope(isolate); context = new CefV8ContextImpl(isolate, isolate->GetCurrentContext()); } return context; } // static CefRefPtr CefV8Context::GetEnteredContext() { CefRefPtr context; CEF_V8_REQUIRE_ISOLATE_RETURN(context); v8::Isolate* isolate = GetIsolateManager()->isolate(); if (isolate->InContext()) { v8::HandleScope handle_scope(isolate); context = new CefV8ContextImpl(isolate, isolate->GetEnteredOrMicrotaskContext()); } return context; } // static bool CefV8Context::InContext() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); v8::Isolate* isolate = GetIsolateManager()->isolate(); return isolate->InContext(); } // CefV8ContextImpl CefV8ContextImpl::CefV8ContextImpl(v8::Isolate* isolate, v8::Local context) : handle_(new Handle(isolate, context, context)), enter_count_(0) {} CefV8ContextImpl::~CefV8ContextImpl() { DLOG_ASSERT(0 == enter_count_); } CefRefPtr CefV8ContextImpl::GetTaskRunner() { return new CefTaskRunnerImpl(handle_->task_runner()); } bool CefV8ContextImpl::IsValid() { return CEF_V8_IS_VALID(); } CefRefPtr CefV8ContextImpl::GetBrowser() { CefRefPtr browser; CEF_V8_REQUIRE_VALID_HANDLE_RETURN(browser); blink::WebLocalFrame* webframe = GetWebFrame(); if (webframe) browser = CefBrowserImpl::GetBrowserForMainFrame(webframe->Top()); return browser; } CefRefPtr CefV8ContextImpl::GetFrame() { CefRefPtr frame; CEF_V8_REQUIRE_VALID_HANDLE_RETURN(frame); blink::WebLocalFrame* webframe = GetWebFrame(); if (webframe) { CefRefPtr browser = CefBrowserImpl::GetBrowserForMainFrame(webframe->Top()); if (browser) { frame = browser->GetFrame(render_frame_util::GetIdentifier(webframe)); } } return frame; } CefRefPtr CefV8ContextImpl::GetGlobal() { CEF_V8_REQUIRE_VALID_HANDLE_RETURN(nullptr); if (blink_glue::IsScriptForbidden()) return nullptr; v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = GetV8Context(); v8::Context::Scope context_scope(context); return new CefV8ValueImpl(isolate, context, context->Global()); } bool CefV8ContextImpl::Enter() { CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false); if (blink_glue::IsScriptForbidden()) return false; v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); if (!microtasks_scope_) { // Increment the MicrotasksScope recursion level. microtasks_scope_.reset( new v8::MicrotasksScope(isolate, v8::MicrotasksScope::kRunMicrotasks)); } ++enter_count_; handle_->GetNewV8Handle()->Enter(); return true; } bool CefV8ContextImpl::Exit() { CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false); if (blink_glue::IsScriptForbidden()) return false; if (enter_count_ <= 0) { LOG(ERROR) << "Call to CefV8Context::Exit() without matching call to " "CefV8Context::Enter()"; return false; } v8::HandleScope handle_scope(handle_->isolate()); handle_->GetNewV8Handle()->Exit(); if (--enter_count_ == 0) { // Decrement the MicrotasksScope recursion level. microtasks_scope_.reset(nullptr); } return true; } bool CefV8ContextImpl::IsSame(CefRefPtr that) { CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false); CefV8ContextImpl* impl = static_cast(that.get()); if (!impl || !impl->IsValid()) return false; return (handle_->GetPersistentV8Handle() == impl->handle_->GetPersistentV8Handle()); } bool CefV8ContextImpl::Eval(const CefString& code, const CefString& script_url, int start_line, CefRefPtr& retval, CefRefPtr& exception) { retval = nullptr; exception = nullptr; CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false); if (blink_glue::IsScriptForbidden()) return false; if (code.empty()) { NOTREACHED() << "invalid input parameter"; return false; } v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = GetV8Context(); v8::Context::Scope context_scope(context); const blink::WebString& source = blink::WebString::FromUTF16(code.ToString16()); const blink::WebString& source_url = blink::WebString::FromUTF16(script_url.ToString16()); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::Local func_rv = blink_glue::ExecuteV8ScriptAndReturnValue( source, source_url, start_line, context, try_catch); if (try_catch.HasCaught()) { exception = new CefV8ExceptionImpl(context, try_catch.Message()); return false; } else if (!func_rv.IsEmpty()) { retval = new CefV8ValueImpl(isolate, context, func_rv); return true; } NOTREACHED(); return false; } v8::Local CefV8ContextImpl::GetV8Context() { return handle_->GetNewV8Handle(); } blink::WebLocalFrame* CefV8ContextImpl::GetWebFrame() { CEF_REQUIRE_RT(); if (blink_glue::IsScriptForbidden()) return nullptr; v8::HandleScope handle_scope(handle_->isolate()); v8::Local context = GetV8Context(); v8::Context::Scope context_scope(context); return blink::WebLocalFrame::FrameForContext(context); } // CefV8ValueImpl::Handle CefV8ValueImpl::Handle::Handle(v8::Isolate* isolate, v8::Local context, handleType v, CefTrackNode* tracker) : CefV8HandleBase(isolate, context), handle_(isolate, v), tracker_(tracker), should_persist_(false), is_set_weak_(false) {} CefV8ValueImpl::Handle::~Handle() { DCHECK(BelongsToCurrentThread()); if (tracker_) { if (is_set_weak_) { if (context_state_.get()) { // If the associated context is still valid then delete |tracker_|. // Otherwise, |tracker_| will already have been deleted. if (context_state_->IsValid()) context_state_->DeleteTrackObject(tracker_); } else { GetIsolateManager()->DeleteGlobalTrackObject(tracker_); } } else { delete tracker_; } } if (is_set_weak_) { isolate_->AdjustAmountOfExternalAllocatedMemory( -static_cast(sizeof(Handle))); } else { // SetWeak was not called so reset now. handle_.Reset(); } } CefV8ValueImpl::Handle::handleType CefV8ValueImpl::Handle::GetNewV8Handle( bool should_persist) { DCHECK(IsValid()); if (should_persist && !should_persist_) should_persist_ = true; return handleType::New(isolate(), handle_); } CefV8ValueImpl::Handle::persistentType& CefV8ValueImpl::Handle::GetPersistentV8Handle() { return handle_; } void CefV8ValueImpl::Handle::SetWeakIfNecessary() { if (!BelongsToCurrentThread()) { task_runner()->PostTask( FROM_HERE, base::BindOnce(&CefV8ValueImpl::Handle::SetWeakIfNecessary, this)); return; } // Persist the handle (call SetWeak) if: // A. The handle has been passed into a V8 function or used as a return value // from a V8 callback, and // B. The associated context, if any, is still valid. if (should_persist_ && (!context_state_.get() || context_state_->IsValid())) { is_set_weak_ = true; if (tracker_) { if (context_state_.get()) { // |tracker_| will be deleted when: // A. The associated context is released, or // B. SecondWeakCallback is called for the weak handle. DCHECK(context_state_->IsValid()); context_state_->AddTrackObject(tracker_); } else { // |tracker_| will be deleted when: // A. The process shuts down, or // B. SecondWeakCallback is called for the weak handle. GetIsolateManager()->AddGlobalTrackObject(tracker_); } } isolate_->AdjustAmountOfExternalAllocatedMemory( static_cast(sizeof(Handle))); // The added reference will be released in SecondWeakCallback. AddRef(); handle_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter); } } // static void CefV8ValueImpl::Handle::FirstWeakCallback( const v8::WeakCallbackInfo& data) { Handle* wrapper = data.GetParameter(); wrapper->handle_.Reset(); data.SetSecondPassCallback(SecondWeakCallback); } // static void CefV8ValueImpl::Handle::SecondWeakCallback( const v8::WeakCallbackInfo& data) { Handle* wrapper = data.GetParameter(); wrapper->Release(); } // CefV8Value // static CefRefPtr CefV8Value::CreateUndefined() { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitUndefined(); return impl.get(); } // static CefRefPtr CefV8Value::CreateNull() { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitNull(); return impl.get(); } // static CefRefPtr CefV8Value::CreateBool(bool value) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitBool(value); return impl.get(); } // static CefRefPtr CefV8Value::CreateInt(int32 value) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitInt(value); return impl.get(); } // static CefRefPtr CefV8Value::CreateUInt(uint32 value) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitUInt(value); return impl.get(); } // static CefRefPtr CefV8Value::CreateDouble(double value) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitDouble(value); return impl.get(); } // static CefRefPtr CefV8Value::CreateDate(const CefTime& value) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitDate(value); return impl.get(); } // static CefRefPtr CefV8Value::CreateString(const CefString& value) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); CefRefPtr impl = new CefV8ValueImpl(isolate); CefString str(value); impl->InitString(str); return impl.get(); } // static CefRefPtr CefV8Value::CreateObject( CefRefPtr accessor, CefRefPtr interceptor) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } // Create the new V8 object. If an interceptor is passed, create object from // template and set property handlers. v8::Local obj; if (interceptor.get()) { v8::Local tmpl = v8::ObjectTemplate::New(isolate); tmpl->SetHandler(v8::NamedPropertyHandlerConfiguration( InterceptorGetterCallbackImpl>, InterceptorSetterCallbackImpl>, nullptr, nullptr, nullptr, v8::Local(), v8::PropertyHandlerFlags::kOnlyInterceptStrings)); tmpl->SetIndexedPropertyHandler(InterceptorGetterCallbackImpl, InterceptorSetterCallbackImpl); v8::MaybeLocal maybe_object = tmpl->NewInstance(context); if (!maybe_object.ToLocal(&obj)) { NOTREACHED() << "Failed to create V8 Object with interceptor"; return nullptr; } } else { obj = v8::Object::New(isolate); } // Create a tracker object that will cause the user data and/or accessor // and/or interceptor reference to be released when the V8 object is // destroyed. V8TrackObject* tracker = new V8TrackObject(isolate); tracker->SetAccessor(accessor); tracker->SetInterceptor(interceptor); // Attach the tracker object. tracker->AttachTo(context, obj); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitObject(obj, tracker); return impl.get(); } // static CefRefPtr CefV8Value::CreateArray(int length) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } // Create a tracker object that will cause the user data reference to be // released when the V8 object is destroyed. V8TrackObject* tracker = new V8TrackObject(isolate); // Create the new V8 array. v8::Local arr = v8::Array::New(isolate, length); // Attach the tracker object. tracker->AttachTo(context, arr); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitObject(arr, tracker); return impl.get(); } // static CefRefPtr CefV8Value::CreateArrayBuffer( void* buffer, size_t length, CefRefPtr release_callback) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } // Create a tracker object that will cause the user data reference to be // released when the V8 object is destroyed. V8TrackArrayBuffer* tracker = new V8TrackArrayBuffer(isolate, release_callback); if (release_callback) release_callback->AddRef(); auto deleter = [](void* data, size_t length, void* deleter_data) { auto* release_callback = reinterpret_cast(deleter_data); if (release_callback) { release_callback->ReleaseBuffer(data); release_callback->Release(); } }; std::unique_ptr backing = v8::ArrayBuffer::NewBackingStore( buffer, length, deleter, release_callback.get()); v8::Local ab = v8::ArrayBuffer::New(isolate, std::move(backing)); // Attach the tracker object. tracker->AttachTo(context, ab); CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitObject(ab, tracker); return impl.get(); } // static CefRefPtr CefV8Value::CreateFunction( const CefString& name, CefRefPtr handler) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); if (!handler.get()) { NOTREACHED() << "invalid parameter"; return nullptr; } v8::Isolate* isolate = GetIsolateManager()->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } v8::Local function_data = V8FunctionData::Create(isolate, name, handler); // Create a new V8 function template. v8::Local tmpl = v8::FunctionTemplate::New(isolate, FunctionCallbackImpl, function_data); // Retrieve the function object and set the name. v8::MaybeLocal maybe_func = tmpl->GetFunction(context); v8::Local func; if (!maybe_func.ToLocal(&func)) { NOTREACHED() << "failed to create V8 function"; return nullptr; } func->SetName(GetV8String(isolate, name)); // Create a tracker object that will cause the user data and/or handler // reference to be released when the V8 object is destroyed. V8TrackObject* tracker = new V8TrackObject(isolate); tracker->SetHandler(handler); // Attach the tracker object. tracker->AttachTo(context, func); // Create the CefV8ValueImpl and provide a tracker object that will cause // the handler reference to be released when the V8 object is destroyed. CefRefPtr impl = new CefV8ValueImpl(isolate); impl->InitObject(func, tracker); return impl.get(); } // CefV8ValueImpl CefV8ValueImpl::CefV8ValueImpl(v8::Isolate* isolate) : isolate_(isolate), type_(TYPE_INVALID), rethrow_exceptions_(false) { DCHECK(isolate_); } CefV8ValueImpl::CefV8ValueImpl(v8::Isolate* isolate, v8::Local context, v8::Local value) : isolate_(isolate), type_(TYPE_INVALID), rethrow_exceptions_(false) { DCHECK(isolate_); InitFromV8Value(context, value); } CefV8ValueImpl::~CefV8ValueImpl() { if (type_ == TYPE_STRING) cef_string_clear(&string_value_); if (handle_.get()) handle_->SetWeakIfNecessary(); } void CefV8ValueImpl::InitFromV8Value(v8::Local context, v8::Local value) { if (value->IsUndefined()) { InitUndefined(); } else if (value->IsNull()) { InitNull(); } else if (value->IsTrue()) { InitBool(true); } else if (value->IsFalse()) { InitBool(false); } else if (value->IsBoolean()) { InitBool(value->ToBoolean(context->GetIsolate())->Value()); } else if (value->IsInt32()) { InitInt(value->ToInt32(context).ToLocalChecked()->Value()); } else if (value->IsUint32()) { InitUInt(value->ToUint32(context).ToLocalChecked()->Value()); } else if (value->IsNumber()) { InitDouble(value->ToNumber(context).ToLocalChecked()->Value()); } else if (value->IsDate()) { // Convert from milliseconds to seconds. InitDate( CefTime(value->ToNumber(context).ToLocalChecked()->Value() / 1000)); } else if (value->IsString()) { CefString rv; GetCefString(context->GetIsolate(), value->ToString(context).ToLocalChecked(), rv); InitString(rv); } else if (value->IsObject()) { InitObject(value, nullptr); } } void CefV8ValueImpl::InitUndefined() { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_UNDEFINED; } void CefV8ValueImpl::InitNull() { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_NULL; } void CefV8ValueImpl::InitBool(bool value) { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_BOOL; bool_value_ = value; } void CefV8ValueImpl::InitInt(int32 value) { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_INT; int_value_ = value; } void CefV8ValueImpl::InitUInt(uint32 value) { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_UINT; uint_value_ = value; } void CefV8ValueImpl::InitDouble(double value) { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_DOUBLE; double_value_ = value; } void CefV8ValueImpl::InitDate(const CefTime& value) { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_DATE; date_value_ = value; } void CefV8ValueImpl::InitString(CefString& value) { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_STRING; // Take ownership of the underling string value. const cef_string_t* str = value.GetStruct(); if (str) { string_value_ = *str; cef_string_t* writable_struct = value.GetWritableStruct(); writable_struct->str = nullptr; writable_struct->length = 0; } else { string_value_.str = nullptr; string_value_.length = 0; } } void CefV8ValueImpl::InitObject(v8::Local value, CefTrackNode* tracker) { DCHECK_EQ(type_, TYPE_INVALID); type_ = TYPE_OBJECT; handle_ = new Handle(isolate_, v8::Local(), value, tracker); } v8::Local CefV8ValueImpl::GetV8Value(bool should_persist) { switch (type_) { case TYPE_UNDEFINED: return v8::Undefined(isolate_); case TYPE_NULL: return v8::Null(isolate_); case TYPE_BOOL: return v8::Boolean::New(isolate_, bool_value_); case TYPE_INT: return v8::Int32::New(isolate_, int_value_); case TYPE_UINT: return v8::Uint32::New(isolate_, uint_value_); case TYPE_DOUBLE: return v8::Number::New(isolate_, double_value_); case TYPE_DATE: // Convert from seconds to milliseconds. return v8::Date::New(isolate_->GetCurrentContext(), CefTime(date_value_).GetDoubleT() * 1000) .ToLocalChecked(); case TYPE_STRING: return GetV8String(isolate_, CefString(&string_value_)); case TYPE_OBJECT: return handle_->GetNewV8Handle(should_persist); default: break; } NOTREACHED() << "Invalid type for CefV8ValueImpl"; return v8::Local(); } bool CefV8ValueImpl::IsValid() { if (!CEF_V8_HAS_ISOLATE() || type_ == TYPE_INVALID || (type_ == TYPE_OBJECT && (!handle_->BelongsToCurrentThread() || !handle_->IsValid()))) { return false; } return true; } bool CefV8ValueImpl::IsUndefined() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_UNDEFINED); } bool CefV8ValueImpl::IsNull() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_NULL); } bool CefV8ValueImpl::IsBool() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_BOOL); } bool CefV8ValueImpl::IsInt() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_INT || type_ == TYPE_UINT); } bool CefV8ValueImpl::IsUInt() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_INT || type_ == TYPE_UINT); } bool CefV8ValueImpl::IsDouble() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_INT || type_ == TYPE_UINT || type_ == TYPE_DOUBLE); } bool CefV8ValueImpl::IsDate() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_DATE); } bool CefV8ValueImpl::IsString() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_STRING); } bool CefV8ValueImpl::IsObject() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); return (type_ == TYPE_OBJECT); } bool CefV8ValueImpl::IsArray() { CEF_V8_REQUIRE_MLT_RETURN(false); if (type_ == TYPE_OBJECT) { v8::HandleScope handle_scope(handle_->isolate()); return handle_->GetNewV8Handle(false)->IsArray(); } else { return false; } } bool CefV8ValueImpl::IsArrayBuffer() { CEF_V8_REQUIRE_MLT_RETURN(false); if (type_ == TYPE_OBJECT) { v8::HandleScope handle_scope(handle_->isolate()); return handle_->GetNewV8Handle(false)->IsArrayBuffer(); } else { return false; } } bool CefV8ValueImpl::IsFunction() { CEF_V8_REQUIRE_MLT_RETURN(false); if (type_ == TYPE_OBJECT) { v8::HandleScope handle_scope(handle_->isolate()); return handle_->GetNewV8Handle(false)->IsFunction(); } else { return false; } } bool CefV8ValueImpl::IsSame(CefRefPtr that) { CEF_V8_REQUIRE_MLT_RETURN(false); CefV8ValueImpl* thatValue = static_cast(that.get()); if (!thatValue || !thatValue->IsValid() || type_ != thatValue->type_) return false; switch (type_) { case TYPE_UNDEFINED: case TYPE_NULL: return true; case TYPE_BOOL: return (bool_value_ == thatValue->bool_value_); case TYPE_INT: return (int_value_ == thatValue->int_value_); case TYPE_UINT: return (uint_value_ == thatValue->uint_value_); case TYPE_DOUBLE: return (double_value_ == thatValue->double_value_); case TYPE_DATE: return (CefTime(date_value_).GetTimeT() == CefTime(thatValue->date_value_).GetTimeT()); case TYPE_STRING: return (CefString(&string_value_) == CefString(&thatValue->string_value_)); case TYPE_OBJECT: { return (handle_->GetPersistentV8Handle() == thatValue->handle_->GetPersistentV8Handle()); } default: break; } return false; } bool CefV8ValueImpl::GetBoolValue() { CEF_V8_REQUIRE_ISOLATE_RETURN(false); if (type_ == TYPE_BOOL) return bool_value_; return false; } int32 CefV8ValueImpl::GetIntValue() { CEF_V8_REQUIRE_ISOLATE_RETURN(0); if (type_ == TYPE_INT || type_ == TYPE_UINT) return int_value_; return 0; } uint32 CefV8ValueImpl::GetUIntValue() { CEF_V8_REQUIRE_ISOLATE_RETURN(0); if (type_ == TYPE_INT || type_ == TYPE_UINT) return uint_value_; return 0; } double CefV8ValueImpl::GetDoubleValue() { CEF_V8_REQUIRE_ISOLATE_RETURN(0.); if (type_ == TYPE_DOUBLE) return double_value_; else if (type_ == TYPE_INT) return int_value_; else if (type_ == TYPE_UINT) return uint_value_; return 0.; } CefTime CefV8ValueImpl::GetDateValue() { CEF_V8_REQUIRE_ISOLATE_RETURN(CefTime(0.)); if (type_ == TYPE_DATE) return date_value_; return CefTime(0.); } CefString CefV8ValueImpl::GetStringValue() { CefString rv; CEF_V8_REQUIRE_ISOLATE_RETURN(rv); if (type_ == TYPE_STRING) rv = CefString(&string_value_); return rv; } bool CefV8ValueImpl::IsUserCreated() { CEF_V8_REQUIRE_OBJECT_RETURN(false); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); return (tracker != nullptr); } bool CefV8ValueImpl::HasException() { CEF_V8_REQUIRE_OBJECT_RETURN(false); return (last_exception_.get() != nullptr); } CefRefPtr CefV8ValueImpl::GetException() { CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); return last_exception_; } bool CefV8ValueImpl::ClearException() { CEF_V8_REQUIRE_OBJECT_RETURN(false); last_exception_ = nullptr; return true; } bool CefV8ValueImpl::WillRethrowExceptions() { CEF_V8_REQUIRE_OBJECT_RETURN(false); return rethrow_exceptions_; } bool CefV8ValueImpl::SetRethrowExceptions(bool rethrow) { CEF_V8_REQUIRE_OBJECT_RETURN(false); rethrow_exceptions_ = rethrow; return true; } bool CefV8ValueImpl::HasValue(const CefString& key) { CEF_V8_REQUIRE_OBJECT_RETURN(false); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); return obj->Has(context, GetV8String(isolate, key)).FromJust(); } bool CefV8ValueImpl::HasValue(int index) { CEF_V8_REQUIRE_OBJECT_RETURN(false); if (index < 0) { NOTREACHED() << "invalid input parameter"; return false; } v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); return obj->Has(context, index).FromJust(); } bool CefV8ValueImpl::DeleteValue(const CefString& key) { CEF_V8_REQUIRE_OBJECT_RETURN(false); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::Maybe del = obj->Delete(context, GetV8String(isolate, key)); return (!HasCaught(context, try_catch) && del.FromJust()); } bool CefV8ValueImpl::DeleteValue(int index) { CEF_V8_REQUIRE_OBJECT_RETURN(false); if (index < 0) { NOTREACHED() << "invalid input parameter"; return false; } v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::Maybe del = obj->Delete(context, index); return (!HasCaught(context, try_catch) && del.FromJust()); } CefRefPtr CefV8ValueImpl::GetValue(const CefString& key) { CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::MaybeLocal ret_value = obj->Get(context, GetV8String(isolate, key)); if (!HasCaught(context, try_catch) && !ret_value.IsEmpty()) { return new CefV8ValueImpl(isolate, context, ret_value.ToLocalChecked()); } return nullptr; } CefRefPtr CefV8ValueImpl::GetValue(int index) { CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); if (index < 0) { NOTREACHED() << "invalid input parameter"; return nullptr; } v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::MaybeLocal ret_value = obj->Get(context, v8::Number::New(isolate, index)); if (!HasCaught(context, try_catch) && !ret_value.IsEmpty()) { return new CefV8ValueImpl(isolate, context, ret_value.ToLocalChecked()); } return nullptr; } bool CefV8ValueImpl::SetValue(const CefString& key, CefRefPtr value, PropertyAttribute attribute) { CEF_V8_REQUIRE_OBJECT_RETURN(false); CefV8ValueImpl* impl = static_cast(value.get()); if (impl && impl->IsValid()) { v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); // TODO(cef): This usage may not exactly match the previous implementation. // Set will trigger interceptors and/or accessors whereas DefineOwnProperty // will not. It might be better to split this functionality into separate // methods. if (attribute == V8_PROPERTY_ATTRIBUTE_NONE) { v8::Maybe set = obj->Set(context, GetV8String(isolate, key), impl->GetV8Value(true)); return (!HasCaught(context, try_catch) && set.FromJust()); } else { v8::Maybe set = obj->DefineOwnProperty( context, GetV8String(isolate, key), impl->GetV8Value(true), static_cast(attribute)); return (!HasCaught(context, try_catch) && set.FromJust()); } } else { NOTREACHED() << "invalid input parameter"; return false; } } bool CefV8ValueImpl::SetValue(int index, CefRefPtr value) { CEF_V8_REQUIRE_OBJECT_RETURN(false); if (index < 0) { NOTREACHED() << "invalid input parameter"; return false; } CefV8ValueImpl* impl = static_cast(value.get()); if (impl && impl->IsValid()) { v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::Maybe set = obj->Set(context, index, impl->GetV8Value(true)); return (!HasCaught(context, try_catch) && set.FromJust()); } else { NOTREACHED() << "invalid input parameter"; return false; } } bool CefV8ValueImpl::SetValue(const CefString& key, AccessControl settings, PropertyAttribute attribute) { CEF_V8_REQUIRE_OBJECT_RETURN(false); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); CefRefPtr accessorPtr; V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) accessorPtr = tracker->GetAccessor(); // Verify that an accessor exists for this object. if (!accessorPtr.get()) return false; v8::AccessorNameGetterCallback getter = AccessorNameGetterCallbackImpl; v8::AccessorNameSetterCallback setter = (attribute & V8_PROPERTY_ATTRIBUTE_READONLY) ? nullptr : AccessorNameSetterCallbackImpl; v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::Maybe set = obj->SetAccessor(context, GetV8String(isolate, key), getter, setter, obj, static_cast(settings), static_cast(attribute)); return (!HasCaught(context, try_catch) && set.FromJust()); } bool CefV8ValueImpl::GetKeys(std::vector& keys) { CEF_V8_REQUIRE_OBJECT_RETURN(false); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::Local arr_keys = obj->GetPropertyNames(context).ToLocalChecked(); uint32_t len = arr_keys->Length(); for (uint32_t i = 0; i < len; ++i) { v8::Local value = arr_keys->Get(context, v8::Integer::New(isolate, i)).ToLocalChecked(); CefString str; GetCefString(isolate, value->ToString(context).ToLocalChecked(), str); keys.push_back(str); } return true; } bool CefV8ValueImpl::SetUserData(CefRefPtr user_data) { CEF_V8_REQUIRE_OBJECT_RETURN(false); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) { tracker->SetUserData(user_data); return true; } return false; } CefRefPtr CefV8ValueImpl::GetUserData() { CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) return tracker->GetUserData(); return nullptr; } int CefV8ValueImpl::GetExternallyAllocatedMemory() { CEF_V8_REQUIRE_OBJECT_RETURN(0); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return 0; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) return tracker->GetExternallyAllocatedMemory(); return 0; } int CefV8ValueImpl::AdjustExternallyAllocatedMemory(int change_in_bytes) { CEF_V8_REQUIRE_OBJECT_RETURN(0); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return 0; } v8::Local value = handle_->GetNewV8Handle(false); v8::Local obj = value->ToObject(context).ToLocalChecked(); V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) return tracker->AdjustExternallyAllocatedMemory(change_in_bytes); return 0; } int CefV8ValueImpl::GetArrayLength() { CEF_V8_REQUIRE_OBJECT_RETURN(0); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return 0; } v8::Local value = handle_->GetNewV8Handle(false); if (!value->IsArray()) { NOTREACHED() << "V8 value is not an array"; return 0; } v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::Local arr = v8::Local::Cast(obj); return arr->Length(); } CefRefPtr CefV8ValueImpl::GetArrayBufferReleaseCallback() { CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } v8::Local value = handle_->GetNewV8Handle(false); if (!value->IsArrayBuffer()) { NOTREACHED() << "V8 value is not an array buffer"; return nullptr; } v8::Local obj = value->ToObject(context).ToLocalChecked(); V8TrackArrayBuffer* tracker = V8TrackArrayBuffer::Unwrap(context, obj); if (tracker) return tracker->GetReleaseCallback(); return nullptr; } bool CefV8ValueImpl::NeuterArrayBuffer() { CEF_V8_REQUIRE_OBJECT_RETURN(0); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return false; } v8::Local value = handle_->GetNewV8Handle(false); if (!value->IsArrayBuffer()) { NOTREACHED() << "V8 value is not an array buffer"; return false; } v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::Local arr = v8::Local::Cast(obj); if (!arr->IsDetachable()) { return false; } arr->Detach(); return true; } CefString CefV8ValueImpl::GetFunctionName() { CefString rv; CEF_V8_REQUIRE_OBJECT_RETURN(rv); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return rv; } v8::Local value = handle_->GetNewV8Handle(false); if (!value->IsFunction()) { NOTREACHED() << "V8 value is not a function"; return rv; } v8::Local obj = value->ToObject(context).ToLocalChecked(); v8::Local func = v8::Local::Cast(obj); GetCefString(handle_->isolate(), v8::Handle::Cast(func->GetName()), rv); return rv; } CefRefPtr CefV8ValueImpl::GetFunctionHandler() { CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) { NOTREACHED() << "not currently in a V8 context"; return nullptr; } v8::Local value = handle_->GetNewV8Handle(false); if (!value->IsFunction()) { NOTREACHED() << "V8 value is not a function"; return nullptr; } v8::Local obj = value->ToObject(context).ToLocalChecked(); V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); if (tracker) return tracker->GetHandler(); return nullptr; } CefRefPtr CefV8ValueImpl::ExecuteFunction( CefRefPtr object, const CefV8ValueList& arguments) { // An empty context value defaults to the current context. CefRefPtr context; return ExecuteFunctionWithContext(context, object, arguments); } CefRefPtr CefV8ValueImpl::ExecuteFunctionWithContext( CefRefPtr context, CefRefPtr object, const CefV8ValueList& arguments) { CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); v8::Isolate* isolate = handle_->isolate(); v8::HandleScope handle_scope(isolate); v8::Local value = handle_->GetNewV8Handle(false); if (!value->IsFunction()) { NOTREACHED() << "V8 value is not a function"; return nullptr; } if (context.get() && !context->IsValid()) { NOTREACHED() << "invalid V8 context parameter"; return nullptr; } if (object.get() && (!object->IsValid() || !object->IsObject())) { NOTREACHED() << "invalid V8 object parameter"; return nullptr; } int argc = arguments.size(); if (argc > 0) { for (int i = 0; i < argc; ++i) { if (!arguments[i].get() || !arguments[i]->IsValid()) { NOTREACHED() << "invalid V8 arguments parameter"; return nullptr; } } } v8::Local context_local; if (context.get()) { CefV8ContextImpl* context_impl = static_cast(context.get()); context_local = context_impl->GetV8Context(); } else { context_local = isolate->GetCurrentContext(); } v8::Context::Scope context_scope(context_local); v8::Local obj = value->ToObject(context_local).ToLocalChecked(); v8::Local func = v8::Local::Cast(obj); v8::Local recv; // Default to the global object if no object was provided. if (object.get()) { CefV8ValueImpl* recv_impl = static_cast(object.get()); recv = v8::Local::Cast(recv_impl->GetV8Value(true)); } else { recv = context_local->Global(); } v8::Local* argv = nullptr; if (argc > 0) { argv = new v8::Local[argc]; for (int i = 0; i < argc; ++i) { argv[i] = static_cast(arguments[i].get())->GetV8Value(true); } } CefRefPtr retval; { v8::TryCatch try_catch(isolate); try_catch.SetVerbose(true); v8::MaybeLocal func_rv = blink_glue::CallV8Function( context_local, func, recv, argc, argv, handle_->isolate()); if (!HasCaught(context_local, try_catch) && !func_rv.IsEmpty()) { retval = new CefV8ValueImpl(isolate, context_local, func_rv.ToLocalChecked()); } } if (argv) delete[] argv; return retval; } bool CefV8ValueImpl::HasCaught(v8::Local context, v8::TryCatch& try_catch) { if (try_catch.HasCaught()) { last_exception_ = new CefV8ExceptionImpl(context, try_catch.Message()); if (rethrow_exceptions_) try_catch.ReThrow(); return true; } else { if (last_exception_.get()) last_exception_ = nullptr; return false; } } // CefV8StackTrace // static CefRefPtr CefV8StackTrace::GetCurrent(int frame_limit) { CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); v8::Isolate* isolate = GetIsolateManager()->isolate(); v8::HandleScope handle_scope(isolate); v8::Local stackTrace = v8::StackTrace::CurrentStackTrace( isolate, frame_limit, v8::StackTrace::kDetailed); if (stackTrace.IsEmpty()) return nullptr; return new CefV8StackTraceImpl(isolate, stackTrace); } // CefV8StackTraceImpl CefV8StackTraceImpl::CefV8StackTraceImpl(v8::Isolate* isolate, v8::Local handle) { if (!handle.IsEmpty()) { int frame_count = handle->GetFrameCount(); if (frame_count > 0) { frames_.reserve(frame_count); for (int i = 0; i < frame_count; ++i) frames_.push_back( new CefV8StackFrameImpl(isolate, handle->GetFrame(isolate, i))); } } } CefV8StackTraceImpl::~CefV8StackTraceImpl() {} bool CefV8StackTraceImpl::IsValid() { return true; } int CefV8StackTraceImpl::GetFrameCount() { return frames_.size(); } CefRefPtr CefV8StackTraceImpl::GetFrame(int index) { if (index < 0 || index >= static_cast(frames_.size())) return nullptr; return frames_[index]; } // CefV8StackFrameImpl CefV8StackFrameImpl::CefV8StackFrameImpl(v8::Isolate* isolate, v8::Local handle) : line_number_(0), column_(0), is_eval_(false), is_constructor_(false) { if (handle.IsEmpty()) return; GetCefString(isolate, handle->GetScriptName(), script_name_); GetCefString(isolate, handle->GetScriptNameOrSourceURL(), script_name_or_source_url_); GetCefString(isolate, handle->GetFunctionName(), function_name_); line_number_ = handle->GetLineNumber(); column_ = handle->GetColumn(); is_eval_ = handle->IsEval(); is_constructor_ = handle->IsConstructor(); } CefV8StackFrameImpl::~CefV8StackFrameImpl() {} bool CefV8StackFrameImpl::IsValid() { return true; } CefString CefV8StackFrameImpl::GetScriptName() { return script_name_; } CefString CefV8StackFrameImpl::GetScriptNameOrSourceURL() { return script_name_or_source_url_; } CefString CefV8StackFrameImpl::GetFunctionName() { return function_name_; } int CefV8StackFrameImpl::GetLineNumber() { return line_number_; } int CefV8StackFrameImpl::GetColumn() { return column_; } bool CefV8StackFrameImpl::IsEval() { return is_eval_; } bool CefV8StackFrameImpl::IsConstructor() { return is_constructor_; } // Enable deprecation warnings on Windows. See http://crbug.com/585142. #if defined(OS_WIN) #if defined(__clang__) #pragma GCC diagnostic pop #else #pragma warning(pop) #endif #endif