| // 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 <map> |
| #include <string> |
| |
| #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/cef_switches.h" |
| #include "libcef/common/content_client.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<v8::Message> message, |
| v8::Handle<v8::Value> 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<v8::Context> context, |
| v8::Local<v8::Object> obj, |
| const char* key, |
| v8::Local<v8::Value> 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<v8::Context> context, |
| v8::Local<v8::Object> obj, |
| const char* key, |
| v8::Local<v8::Value>* 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_(CefContentRendererClient::Get()->GetCurrentTaskRunner()), |
| 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<CefV8ContextState> GetContextState( |
| v8::Local<v8::Context> 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<CefV8ContextState>(); |
| } |
| |
| 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; |
| } |
| |
| void ReleaseContext(v8::Local<v8::Context> 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<base::SingleThreadTaskRunner> 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<base::SingleThreadTaskRunner> task_runner_; |
| |
| typedef std::map<int, scoped_refptr<CefV8ContextState>> 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<CefV8IsolateManager> current_tls_; |
| }; |
| |
| base::LazyInstance<CefV8StateManager>::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<int>(sizeof(V8TrackObject))); |
| } |
| ~V8TrackObject() { |
| isolate_->AdjustAmountOfExternalAllocatedMemory( |
| -static_cast<int>(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<CefV8Accessor> accessor) { |
| accessor_ = accessor; |
| } |
| |
| inline CefRefPtr<CefV8Accessor> GetAccessor() { return accessor_; } |
| |
| inline void SetInterceptor(CefRefPtr<CefV8Interceptor> interceptor) { |
| interceptor_ = interceptor; |
| } |
| |
| inline CefRefPtr<CefV8Interceptor> GetInterceptor() { return interceptor_; } |
| |
| inline void SetHandler(CefRefPtr<CefV8Handler> handler) { |
| handler_ = handler; |
| } |
| |
| inline CefRefPtr<CefV8Handler> GetHandler() { return handler_; } |
| |
| inline void SetUserData(CefRefPtr<CefBaseRefCounted> user_data) { |
| user_data_ = user_data; |
| } |
| |
| inline CefRefPtr<CefBaseRefCounted> GetUserData() { return user_data_; } |
| |
| // Attach this track object to the specified V8 object. |
| void AttachTo(v8::Local<v8::Context> context, v8::Local<v8::Object> object) { |
| SetPrivate(context, object, kCefTrackObject, |
| v8::External::New(isolate_, this)); |
| } |
| |
| // Retrieve the track object for the specified V8 object. |
| static V8TrackObject* Unwrap(v8::Local<v8::Context> context, |
| v8::Local<v8::Object> object) { |
| v8::Local<v8::Value> value; |
| if (GetPrivate(context, object, kCefTrackObject, &value)) |
| return static_cast<V8TrackObject*>(v8::External::Cast(*value)->Value()); |
| |
| return nullptr; |
| } |
| |
| private: |
| v8::Isolate* isolate_; |
| CefRefPtr<CefV8Accessor> accessor_; |
| CefRefPtr<CefV8Interceptor> interceptor_; |
| CefRefPtr<CefV8Handler> handler_; |
| CefRefPtr<CefBaseRefCounted> 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, |
| void* buffer, |
| CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) |
| : isolate_(isolate), |
| buffer_(buffer), |
| release_callback_(release_callback) { |
| DCHECK(isolate_); |
| isolate_->AdjustAmountOfExternalAllocatedMemory( |
| static_cast<int>(sizeof(V8TrackArrayBuffer))); |
| } |
| |
| ~V8TrackArrayBuffer() { |
| if (buffer_ != nullptr) { |
| release_callback_->ReleaseBuffer(buffer_); |
| } |
| isolate_->AdjustAmountOfExternalAllocatedMemory( |
| -static_cast<int>(sizeof(V8TrackArrayBuffer))); |
| } |
| |
| CefRefPtr<CefV8ArrayBufferReleaseCallback> GetReleaseCallback() { |
| return release_callback_; |
| } |
| |
| void Detach() { buffer_ = nullptr; } |
| |
| // Attach this track object to the specified V8 object. |
| void AttachTo(v8::Local<v8::Context> context, |
| v8::Local<v8::ArrayBuffer> arrayBuffer) { |
| SetPrivate(context, arrayBuffer, kCefTrackObject, |
| v8::External::New(isolate_, this)); |
| } |
| |
| // Retrieve the track object for the specified V8 object. |
| static V8TrackArrayBuffer* Unwrap(v8::Local<v8::Context> context, |
| v8::Local<v8::Object> object) { |
| v8::Local<v8::Value> value; |
| if (GetPrivate(context, object, kCefTrackObject, &value)) |
| return static_cast<V8TrackArrayBuffer*>( |
| v8::External::Cast(*value)->Value()); |
| |
| return nullptr; |
| } |
| |
| private: |
| v8::Isolate* isolate_; |
| void* buffer_; |
| CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback_; |
| }; |
| |
| // Object wrapped in a v8::External and passed as the Data argument to |
| // v8::FunctionTemplate::New. |
| class V8FunctionData { |
| public: |
| static v8::Local<v8::External> Create(v8::Isolate* isolate, |
| const CefString& function_name, |
| CefRefPtr<CefV8Handler> 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<v8::Value> data) { |
| DCHECK(data->IsExternal()); |
| return static_cast<V8FunctionData*>(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<CefV8Handler> handler) |
| : isolate_(isolate), function_name_(function_name), handler_(handler) { |
| DCHECK(isolate_); |
| DCHECK(handler_); |
| } |
| |
| ~V8FunctionData() { |
| isolate_->AdjustAmountOfExternalAllocatedMemory( |
| -static_cast<int>(sizeof(V8FunctionData))); |
| handler_ = nullptr; |
| function_name_ = "FreedFunction"; |
| } |
| |
| v8::Local<v8::External> CreateExternal() { |
| v8::Local<v8::External> external = v8::External::New(isolate_, this); |
| |
| isolate_->AdjustAmountOfExternalAllocatedMemory( |
| static_cast<int>(sizeof(V8FunctionData))); |
| |
| handle_.Reset(isolate_, external); |
| handle_.SetWeak(this, FirstWeakCallback, v8::WeakCallbackType::kParameter); |
| |
| return external; |
| } |
| |
| static void FirstWeakCallback( |
| const v8::WeakCallbackInfo<V8FunctionData>& data) { |
| V8FunctionData* wrapper = data.GetParameter(); |
| wrapper->handle_.Reset(); |
| data.SetSecondPassCallback(SecondWeakCallback); |
| } |
| |
| static void SecondWeakCallback( |
| const v8::WeakCallbackInfo<V8FunctionData>& data) { |
| V8FunctionData* wrapper = data.GetParameter(); |
| delete wrapper; |
| } |
| |
| v8::Isolate* isolate_; |
| CefString function_name_; |
| CefRefPtr<CefV8Handler> handler_; |
| v8::Persistent<v8::External> handle_; |
| }; |
| |
| // Convert a CefString to a V8::String. |
| v8::Local<v8::String> GetV8String(v8::Isolate* isolate, const CefString& str) { |
| #if defined(CEF_STRING_TYPE_UTF16) |
| // Already a UTF16 string. |
| return v8::String::NewFromTwoByte( |
| isolate, |
| reinterpret_cast<uint16_t*>( |
| const_cast<CefString::char_type*>(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<char*>(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<v8::String> 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<uint16_t*>(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<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Context> 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<CefV8Value> object = |
| new CefV8ValueImpl(isolate, context, info.This()); |
| CefRefPtr<CefV8Value> 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<CefV8ValueImpl*>(retval.get()); |
| if (rv && rv->IsValid()) { |
| info.GetReturnValue().Set(rv->GetV8Value(true)); |
| return; |
| } |
| } |
| } |
| |
| info.GetReturnValue().SetUndefined(); |
| } |
| |
| // V8 Accessor callbacks |
| void AccessorNameGetterCallbackImpl( |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| |
| v8::Local<v8::Object> obj = info.This(); |
| |
| CefRefPtr<CefV8Accessor> accessorPtr; |
| |
| V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); |
| if (tracker) |
| accessorPtr = tracker->GetAccessor(); |
| |
| if (accessorPtr.get()) { |
| CefRefPtr<CefV8Value> retval; |
| CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj); |
| CefString name, exception; |
| GetCefString(isolate, v8::Local<v8::String>::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<CefV8ValueImpl*>(retval.get()); |
| if (rv && rv->IsValid()) { |
| info.GetReturnValue().Set(rv->GetV8Value(true)); |
| return; |
| } |
| } |
| } |
| } |
| |
| return info.GetReturnValue().SetUndefined(); |
| } |
| |
| void AccessorNameSetterCallbackImpl( |
| v8::Local<v8::Name> property, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<void>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| |
| v8::Local<v8::Object> obj = info.This(); |
| |
| CefRefPtr<CefV8Accessor> accessorPtr; |
| |
| V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); |
| if (tracker) |
| accessorPtr = tracker->GetAccessor(); |
| |
| if (accessorPtr.get()) { |
| CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj); |
| CefRefPtr<CefV8Value> cefValue = |
| new CefV8ValueImpl(isolate, context, value); |
| CefString name, exception; |
| GetCefString(isolate, v8::Local<v8::String>::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<v8::Name> property) { |
| CefString name; |
| GetCefString(isolate, property.As<v8::String>(), name); |
| return name; |
| } |
| |
| int PropertyToIndex(v8::Isolate* isolate, uint32_t index) { |
| return static_cast<int>(index); |
| } |
| |
| // V8 Interceptor callbacks. |
| // T == v8::Local<v8::Name> for named property handlers and |
| // T == uint32_t for indexed property handlers |
| template <typename T> |
| void InterceptorGetterCallbackImpl( |
| T property, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| |
| v8::Handle<v8::Object> obj = info.This(); |
| CefRefPtr<CefV8Interceptor> interceptorPtr; |
| |
| V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); |
| if (tracker) |
| interceptorPtr = tracker->GetInterceptor(); |
| if (!interceptorPtr.get()) |
| return; |
| |
| CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj); |
| CefRefPtr<CefV8Value> 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<CefV8ValueImpl*>(retval.get()); |
| if (retval_impl && retval_impl->IsValid()) { |
| info.GetReturnValue().Set(retval_impl->GetV8Value(true)); |
| } |
| } |
| } |
| |
| template <typename T> |
| void InterceptorSetterCallbackImpl( |
| T property, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| v8::Handle<v8::Object> obj = info.This(); |
| CefRefPtr<CefV8Interceptor> interceptorPtr; |
| |
| V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); |
| if (tracker) |
| interceptorPtr = tracker->GetInterceptor(); |
| |
| if (!interceptorPtr.get()) |
| return; |
| CefRefPtr<CefV8Value> object = new CefV8ValueImpl(isolate, context, obj); |
| CefRefPtr<CefV8Value> 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<v8::FunctionTemplate> GetNativeFunctionTemplate( |
| v8::Isolate* isolate, |
| v8::Handle<v8::String> name) override { |
| if (!handler_) |
| return v8::Local<v8::FunctionTemplate>(); |
| |
| CefString func_name; |
| GetCefString(isolate, name, func_name); |
| |
| v8::Local<v8::External> 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<v8::Context> context, |
| v8::Local<v8::Message> 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<v8::String> 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<int> 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<v8::Message> message, |
| v8::Handle<v8::Value> data) { |
| CefRefPtr<CefApp> application = CefContentClient::Get()->application(); |
| if (!application.get()) |
| return; |
| |
| CefRefPtr<CefRenderProcessHandler> handler = |
| application->GetRenderProcessHandler(); |
| if (!handler.get()) |
| return; |
| |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext(); |
| v8::Local<v8::StackTrace> v8Stack = message->GetStackTrace(); |
| CefRefPtr<CefV8StackTrace> stackTrace = |
| new CefV8StackTraceImpl(isolate, v8Stack); |
| |
| CefRefPtr<CefV8Exception> exception = new CefV8ExceptionImpl( |
| static_cast<CefV8ContextImpl*>(context.get())->GetV8Context(), message); |
| |
| CefRefPtr<CefBrowser> 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<v8::Context> 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<CefV8Handler> 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<v8::Extension> 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<v8::Context> 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> CefV8Context::GetCurrentContext() { |
| CefRefPtr<CefV8Context> 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> CefV8Context::GetEnteredContext() { |
| CefRefPtr<CefV8Context> 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<v8::Context> context) |
| : handle_(new Handle(isolate, context, context)), enter_count_(0) {} |
| |
| CefV8ContextImpl::~CefV8ContextImpl() { |
| DLOG_ASSERT(0 == enter_count_); |
| } |
| |
| CefRefPtr<CefTaskRunner> CefV8ContextImpl::GetTaskRunner() { |
| return new CefTaskRunnerImpl(handle_->task_runner()); |
| } |
| |
| bool CefV8ContextImpl::IsValid() { |
| return CEF_V8_IS_VALID(); |
| } |
| |
| CefRefPtr<CefBrowser> CefV8ContextImpl::GetBrowser() { |
| CefRefPtr<CefBrowser> browser; |
| CEF_V8_REQUIRE_VALID_HANDLE_RETURN(browser); |
| |
| blink::WebLocalFrame* webframe = GetWebFrame(); |
| if (webframe) |
| browser = CefBrowserImpl::GetBrowserForMainFrame(webframe->Top()); |
| |
| return browser; |
| } |
| |
| CefRefPtr<CefFrame> CefV8ContextImpl::GetFrame() { |
| CefRefPtr<CefFrame> frame; |
| CEF_V8_REQUIRE_VALID_HANDLE_RETURN(frame); |
| |
| blink::WebLocalFrame* webframe = GetWebFrame(); |
| if (webframe) { |
| CefRefPtr<CefBrowserImpl> browser = |
| CefBrowserImpl::GetBrowserForMainFrame(webframe->Top()); |
| if (browser) { |
| frame = browser->GetFrame(render_frame_util::GetIdentifier(webframe)); |
| } |
| } |
| |
| return frame; |
| } |
| |
| CefRefPtr<CefV8Value> 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<v8::Context> 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<CefV8Context> that) { |
| CEF_V8_REQUIRE_VALID_HANDLE_RETURN(false); |
| |
| CefV8ContextImpl* impl = static_cast<CefV8ContextImpl*>(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<CefV8Value>& retval, |
| CefRefPtr<CefV8Exception>& 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<v8::Context> 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::MaybeLocal<v8::Value> func_rv = blink_glue::ExecuteV8ScriptAndReturnValue( |
| source, source_url, start_line, context, isolate, try_catch, |
| blink::SanitizeScriptErrors::kSanitize); |
| |
| 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.ToLocalChecked()); |
| return true; |
| } |
| |
| NOTREACHED(); |
| return false; |
| } |
| |
| v8::Local<v8::Context> 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<v8::Context> context = GetV8Context(); |
| v8::Context::Scope context_scope(context); |
| return blink::WebLocalFrame::FrameForContext(context); |
| } |
| |
| // CefV8ValueImpl::Handle |
| |
| CefV8ValueImpl::Handle::Handle(v8::Isolate* isolate, |
| v8::Local<v8::Context> 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<int>(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::Bind(&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<int>(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<Handle>& data) { |
| Handle* wrapper = data.GetParameter(); |
| wrapper->handle_.Reset(); |
| data.SetSecondPassCallback(SecondWeakCallback); |
| } |
| |
| // static |
| void CefV8ValueImpl::Handle::SecondWeakCallback( |
| const v8::WeakCallbackInfo<Handle>& data) { |
| Handle* wrapper = data.GetParameter(); |
| wrapper->Release(); |
| } |
| |
| // CefV8Value |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateUndefined() { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitUndefined(); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateNull() { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitNull(); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateBool(bool value) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitBool(value); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateInt(int32 value) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitInt(value); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateUInt(uint32 value) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitUInt(value); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateDouble(double value) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitDouble(value); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateDate(const CefTime& value) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitDate(value); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateString(const CefString& value) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| CefString str(value); |
| impl->InitString(str); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateObject( |
| CefRefPtr<CefV8Accessor> accessor, |
| CefRefPtr<CefV8Interceptor> interceptor) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> 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<v8::Object> obj; |
| if (interceptor.get()) { |
| v8::Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate); |
| tmpl->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| InterceptorGetterCallbackImpl<v8::Local<v8::Name>>, |
| InterceptorSetterCallbackImpl<v8::Local<v8::Name>>, nullptr, nullptr, |
| nullptr, v8::Local<v8::Value>(), |
| v8::PropertyHandlerFlags::kOnlyInterceptStrings)); |
| |
| tmpl->SetIndexedPropertyHandler(InterceptorGetterCallbackImpl<uint32_t>, |
| InterceptorSetterCallbackImpl<uint32_t>); |
| |
| v8::MaybeLocal<v8::Object> maybe_object = tmpl->NewInstance(context); |
| if (!maybe_object.ToLocal<v8::Object>(&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<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitObject(obj, tracker); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateArray(int length) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> 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<v8::Array> arr = v8::Array::New(isolate, length); |
| |
| // Attach the tracker object. |
| tracker->AttachTo(context, arr); |
| |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitObject(arr, tracker); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer( |
| void* buffer, |
| size_t length, |
| CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::Context> 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, buffer, release_callback); |
| v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, buffer, length); |
| |
| // Attach the tracker object. |
| tracker->AttachTo(context, ab); |
| |
| CefRefPtr<CefV8ValueImpl> impl = new CefV8ValueImpl(isolate); |
| impl->InitObject(ab, tracker); |
| return impl.get(); |
| } |
| |
| // static |
| CefRefPtr<CefV8Value> CefV8Value::CreateFunction( |
| const CefString& name, |
| CefRefPtr<CefV8Handler> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::External> function_data = |
| V8FunctionData::Create(isolate, name, handler); |
| |
| // Create a new V8 function template. |
| v8::Local<v8::FunctionTemplate> tmpl = |
| v8::FunctionTemplate::New(isolate, FunctionCallbackImpl, function_data); |
| |
| // Retrieve the function object and set the name. |
| v8::MaybeLocal<v8::Function> maybe_func = tmpl->GetFunction(context); |
| v8::Local<v8::Function> 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<CefV8ValueImpl> 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<v8::Context> context, |
| v8::Local<v8::Value> 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<v8::Context> context, |
| v8::Local<v8::Value> 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<v8::Value> value, |
| CefTrackNode* tracker) { |
| DCHECK_EQ(type_, TYPE_INVALID); |
| type_ = TYPE_OBJECT; |
| handle_ = new Handle(isolate_, v8::Local<v8::Context>(), value, tracker); |
| } |
| |
| v8::Local<v8::Value> 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<v8::Value>(); |
| } |
| |
| 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<CefV8Value> that) { |
| CEF_V8_REQUIRE_MLT_RETURN(false); |
| |
| CefV8ValueImpl* thatValue = static_cast<CefV8ValueImpl*>(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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> 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<CefV8Exception> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| v8::Maybe<bool> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| v8::Maybe<bool> del = obj->Delete(context, index); |
| return (!HasCaught(context, try_catch) && del.FromJust()); |
| } |
| |
| CefRefPtr<CefV8Value> CefV8ValueImpl::GetValue(const CefString& key) { |
| CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); |
| |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| v8::MaybeLocal<v8::Value> 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<CefV8Value> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| v8::MaybeLocal<v8::Value> 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<CefV8Value> value, |
| PropertyAttribute attribute) { |
| CEF_V8_REQUIRE_OBJECT_RETURN(false); |
| |
| CefV8ValueImpl* impl = static_cast<CefV8ValueImpl*>(value.get()); |
| if (impl && impl->IsValid()) { |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> 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<bool> set = |
| obj->Set(context, GetV8String(isolate, key), impl->GetV8Value(true)); |
| return (!HasCaught(context, try_catch) && set.FromJust()); |
| } else { |
| v8::Maybe<bool> set = obj->DefineOwnProperty( |
| context, GetV8String(isolate, key), impl->GetV8Value(true), |
| static_cast<v8::PropertyAttribute>(attribute)); |
| return (!HasCaught(context, try_catch) && set.FromJust()); |
| } |
| } else { |
| NOTREACHED() << "invalid input parameter"; |
| return false; |
| } |
| } |
| |
| bool CefV8ValueImpl::SetValue(int index, CefRefPtr<CefV8Value> value) { |
| CEF_V8_REQUIRE_OBJECT_RETURN(false); |
| |
| if (index < 0) { |
| NOTREACHED() << "invalid input parameter"; |
| return false; |
| } |
| |
| CefV8ValueImpl* impl = static_cast<CefV8ValueImpl*>(value.get()); |
| if (impl && impl->IsValid()) { |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| v8::Maybe<bool> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| |
| CefRefPtr<CefV8Accessor> 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<bool> set = |
| obj->SetAccessor(context, GetV8String(isolate, key), getter, setter, obj, |
| static_cast<v8::AccessControl>(settings), |
| static_cast<v8::PropertyAttribute>(attribute)); |
| return (!HasCaught(context, try_catch) && set.FromJust()); |
| } |
| |
| bool CefV8ValueImpl::GetKeys(std::vector<CefString>& keys) { |
| CEF_V8_REQUIRE_OBJECT_RETURN(false); |
| |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| v8::Local<v8::Array> arr_keys = |
| obj->GetPropertyNames(context).ToLocalChecked(); |
| |
| uint32_t len = arr_keys->Length(); |
| for (uint32_t i = 0; i < len; ++i) { |
| v8::Local<v8::Value> 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<CefBaseRefCounted> user_data) { |
| CEF_V8_REQUIRE_OBJECT_RETURN(false); |
| |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| |
| V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); |
| if (tracker) { |
| tracker->SetUserData(user_data); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| CefRefPtr<CefBaseRefCounted> CefV8ValueImpl::GetUserData() { |
| CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); |
| |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return 0; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return 0; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| v8::Local<v8::Object> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return 0; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| if (!value->IsArray()) { |
| NOTREACHED() << "V8 value is not an array"; |
| return 0; |
| } |
| |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| v8::Local<v8::Array> arr = v8::Local<v8::Array>::Cast(obj); |
| return arr->Length(); |
| } |
| |
| CefRefPtr<CefV8ArrayBufferReleaseCallback> |
| CefV8ValueImpl::GetArrayBufferReleaseCallback() { |
| CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); |
| |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| if (!value->IsArrayBuffer()) { |
| NOTREACHED() << "V8 value is not an array buffer"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::Object> 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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return false; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| if (!value->IsArrayBuffer()) { |
| NOTREACHED() << "V8 value is not an array buffer"; |
| return false; |
| } |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| v8::Local<v8::ArrayBuffer> arr = v8::Local<v8::ArrayBuffer>::Cast(obj); |
| if (!arr->IsDetachable()) { |
| return false; |
| } |
| arr->Detach(); |
| V8TrackArrayBuffer* tracker = V8TrackArrayBuffer::Unwrap(context, obj); |
| tracker->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<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return rv; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| if (!value->IsFunction()) { |
| NOTREACHED() << "V8 value is not a function"; |
| return rv; |
| } |
| |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(obj); |
| GetCefString(handle_->isolate(), |
| v8::Handle<v8::String>::Cast(func->GetName()), rv); |
| return rv; |
| } |
| |
| CefRefPtr<CefV8Handler> CefV8ValueImpl::GetFunctionHandler() { |
| CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); |
| |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| if (context.IsEmpty()) { |
| NOTREACHED() << "not currently in a V8 context"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::Value> value = handle_->GetNewV8Handle(false); |
| if (!value->IsFunction()) { |
| NOTREACHED() << "V8 value is not a function"; |
| return nullptr; |
| } |
| |
| v8::Local<v8::Object> obj = value->ToObject(context).ToLocalChecked(); |
| V8TrackObject* tracker = V8TrackObject::Unwrap(context, obj); |
| if (tracker) |
| return tracker->GetHandler(); |
| |
| return nullptr; |
| } |
| |
| CefRefPtr<CefV8Value> CefV8ValueImpl::ExecuteFunction( |
| CefRefPtr<CefV8Value> object, |
| const CefV8ValueList& arguments) { |
| // An empty context value defaults to the current context. |
| CefRefPtr<CefV8Context> context; |
| return ExecuteFunctionWithContext(context, object, arguments); |
| } |
| |
| CefRefPtr<CefV8Value> CefV8ValueImpl::ExecuteFunctionWithContext( |
| CefRefPtr<CefV8Context> context, |
| CefRefPtr<CefV8Value> object, |
| const CefV8ValueList& arguments) { |
| CEF_V8_REQUIRE_OBJECT_RETURN(nullptr); |
| |
| v8::Isolate* isolate = handle_->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::Value> 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<v8::Context> context_local; |
| if (context.get()) { |
| CefV8ContextImpl* context_impl = |
| static_cast<CefV8ContextImpl*>(context.get()); |
| context_local = context_impl->GetV8Context(); |
| } else { |
| context_local = isolate->GetCurrentContext(); |
| } |
| |
| v8::Context::Scope context_scope(context_local); |
| |
| v8::Local<v8::Object> obj = value->ToObject(context_local).ToLocalChecked(); |
| v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(obj); |
| v8::Local<v8::Object> recv; |
| |
| // Default to the global object if no object was provided. |
| if (object.get()) { |
| CefV8ValueImpl* recv_impl = static_cast<CefV8ValueImpl*>(object.get()); |
| recv = v8::Local<v8::Object>::Cast(recv_impl->GetV8Value(true)); |
| } else { |
| recv = context_local->Global(); |
| } |
| |
| v8::Local<v8::Value>* argv = nullptr; |
| if (argc > 0) { |
| argv = new v8::Local<v8::Value>[argc]; |
| for (int i = 0; i < argc; ++i) { |
| argv[i] = |
| static_cast<CefV8ValueImpl*>(arguments[i].get())->GetV8Value(true); |
| } |
| } |
| |
| CefRefPtr<CefV8Value> retval; |
| |
| { |
| v8::TryCatch try_catch(isolate); |
| try_catch.SetVerbose(true); |
| |
| v8::MaybeLocal<v8::Value> 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<v8::Context> 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> CefV8StackTrace::GetCurrent(int frame_limit) { |
| CEF_V8_REQUIRE_ISOLATE_RETURN(nullptr); |
| |
| v8::Isolate* isolate = GetIsolateManager()->isolate(); |
| v8::HandleScope handle_scope(isolate); |
| v8::Local<v8::StackTrace> 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<v8::StackTrace> 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<CefV8StackFrame> CefV8StackTraceImpl::GetFrame(int index) { |
| if (index < 0 || index >= static_cast<int>(frames_.size())) |
| return nullptr; |
| return frames_[index]; |
| } |
| |
| // CefV8StackFrameImpl |
| |
| CefV8StackFrameImpl::CefV8StackFrameImpl(v8::Isolate* isolate, |
| v8::Local<v8::StackFrame> 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 |