// Copyright (c) 2009 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. #include "v8_impl.h" #include "cef_context.h" #include "tracker.h" #include "base/lazy_instance.h" #include "third_party/WebKit/WebKit/chromium/public/WebKit.h" #include "third_party/WebKit/WebKit/chromium/public/WebScriptController.h" // Memory manager. base::LazyInstance g_v8_tracker(base::LINKER_INITIALIZED); class TrackBase : public CefTrackObject { public: TrackBase(CefBase* base) { base_ = base; } protected: CefRefPtr base_; }; class TrackString : public CefTrackObject { public: TrackString(const std::string& str) : string_(str) {} const char* GetString() { return string_.c_str(); } private: std::string string_; }; static void TrackAdd(CefTrackObject* object) { g_v8_tracker.Pointer()->Add(object); } static void TrackDelete(CefTrackObject* object) { g_v8_tracker.Pointer()->Delete(object); } // Callback for weak persistent reference destruction. static void TrackDestructor(v8::Persistent object, void* parameter) { if(parameter) TrackDelete(static_cast(parameter)); object.Dispose(); object.Clear(); } // Convert a wide string to a V8 string. static v8::Handle GetV8String(const CefString& str) { std::string tmpStr = str; return v8::String::New(tmpStr.c_str(), tmpStr.length()); } // Convert a V8 string to a UTF8 string. static std::string GetString(v8::Handle str) { // Allocate enough space for a worst-case conversion. int len = str->Utf8Length(); char* buf = new char[len+1]; str->WriteUtf8(buf, len+1); std::string ret(buf, len); delete [] buf; return ret; } // V8 function callback static v8::Handle FunctionCallbackImpl(const v8::Arguments& args) { v8::HandleScope handle_scope; CefV8Handler* handler = static_cast(v8::External::Unwrap(args.Data())); CefV8ValueList params; for(int i = 0; i < args.Length(); i++) params.push_back(new CefV8ValueImpl(args[i])); CefString func_name = GetString(v8::Handle::Cast(args.Callee()->GetName())); CefRefPtr object = new CefV8ValueImpl(args.This()); CefRefPtr retval; CefString exception; v8::Handle value = v8::Null(); if(handler->Execute(func_name, object, params, retval, exception)) { if(!exception.empty()) value = v8::ThrowException(GetV8String(exception)); else { CefV8ValueImpl* rv = static_cast(retval.get()); if(rv) value = rv->GetValue(); } } return value; } // 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) { // The reference will be released when the application exits. TrackAdd(new TrackBase(handler)); } virtual v8::Handle GetNativeFunction( v8::Handle name) { return v8::FunctionTemplate::New(FunctionCallbackImpl, v8::External::New(handler_)); } void UIT_RegisterExtension() { WebKit::WebScriptController::registerExtension(this); } void AddRef() {} void Release() {} static bool ImplementsThreadSafeReferenceCounting() { return true; } private: CefV8Handler* handler_; }; bool CefRegisterExtension(const CefString& extension_name, const CefString& javascript_code, CefRefPtr handler) { // Verify that the context is already initialized if(!_Context.get()) return false; if(!handler.get()) return false; TrackString* name = new TrackString(extension_name); TrackAdd(name); TrackString* code = new TrackString(javascript_code); TrackAdd(name); ExtensionWrapper* wrapper = new ExtensionWrapper(name->GetString(), code->GetString(), handler.get()); CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(wrapper, &ExtensionWrapper::UIT_RegisterExtension)); return true; } // CefV8Value // static CefRefPtr CefV8Value::CreateUndefined() { v8::HandleScope handle_scope; return new CefV8ValueImpl(v8::Undefined()); } // static CefRefPtr CefV8Value::CreateNull() { v8::HandleScope handle_scope; return new CefV8ValueImpl(v8::Null()); } // static CefRefPtr CefV8Value::CreateBool(bool value) { v8::HandleScope handle_scope; return new CefV8ValueImpl(v8::Boolean::New(value)); } // static CefRefPtr CefV8Value::CreateInt(int value) { v8::HandleScope handle_scope; return new CefV8ValueImpl(v8::Int32::New(value)); } // static CefRefPtr CefV8Value::CreateDouble(double value) { v8::HandleScope handle_scope; return new CefV8ValueImpl(v8::Number::New(value)); } // static CefRefPtr CefV8Value::CreateString(const CefString& value) { v8::HandleScope handle_scope; return new CefV8ValueImpl(GetV8String(value)); } // static CefRefPtr CefV8Value::CreateObject(CefRefPtr user_data) { v8::HandleScope handle_scope; CefV8ValueImpl* impl = new CefV8ValueImpl(); // Create the new V8 object. v8::Local obj = v8::Object::New(); TrackBase *tracker = NULL; if(user_data.get()) { // Attach the user data to the V8 object. v8::Local data = v8::External::Wrap(user_data.get()); obj->Set(v8::String::New("Cef::UserData"), data); // Provide a tracker object that will cause the user data reference to be // released when the V8 object is destroyed. tracker = new TrackBase(user_data); } // Attach to the CefV8ValueImpl. impl->Attach(obj, tracker); return impl; } // static CefRefPtr CefV8Value::CreateArray() { v8::HandleScope handle_scope; return new CefV8ValueImpl(v8::Array::New()); } // static CefRefPtr CefV8Value::CreateFunction(const CefString& name, CefRefPtr handler) { v8::HandleScope handle_scope; CefV8ValueImpl* impl = new CefV8ValueImpl(); // Create a new V8 function template with one internal field. v8::Local tmpl = v8::FunctionTemplate::New(); v8::Local data = v8::External::Wrap(handler.get()); // Set the function handler callback. tmpl->SetCallHandler(FunctionCallbackImpl, data); // Retrieve the function object and set the name. v8::Local func = tmpl->GetFunction(); func->SetName(GetV8String(name)); // Attach the handler instance to the V8 object. func->Set(v8::String::New("Cef::Handler"), data); // Attach to the CefV8ValueImpl and provide a tracker object that will cause // the handler reference to be released when the V8 object is destroyed. impl->Attach(func, new TrackBase(handler)); return impl; } // CefV8ValueImpl CefV8ValueImpl::CefV8ValueImpl() : tracker_(NULL) { } CefV8ValueImpl::CefV8ValueImpl(v8::Handle value, CefTrackObject* tracker) { Attach(value, tracker); } CefV8ValueImpl::~CefV8ValueImpl() { Detach(); } bool CefV8ValueImpl::Attach(v8::Handle value, CefTrackObject* tracker) { bool rv = false; Lock(); if(v8_value_.IsEmpty()) { v8_value_ = v8::Persistent::New(value); tracker_ = tracker; rv = true; } Unlock(); return rv; } void CefV8ValueImpl::Detach() { Lock(); if(tracker_) TrackAdd(tracker_); v8_value_.MakeWeak(tracker_, TrackDestructor); tracker_ = NULL; Unlock(); } v8::Handle CefV8ValueImpl::GetValue() { v8::HandleScope handle_scope; v8::Handle rv; Lock(); rv = v8_value_; Unlock(); return rv; } bool CefV8ValueImpl::IsReservedKey(const CefString& key) { std::string str = key; return (str.find("Cef::") == 0 || str.find("v8::") == 0); } bool CefV8ValueImpl::IsUndefined() { Lock(); bool rv = v8_value_->IsUndefined(); Unlock(); return rv; } bool CefV8ValueImpl::IsNull() { Lock(); bool rv = v8_value_->IsNull(); Unlock(); return rv; } bool CefV8ValueImpl::IsBool() { Lock(); bool rv = (v8_value_->IsBoolean() || v8_value_->IsTrue() || v8_value_->IsFalse()); Unlock(); return rv; } bool CefV8ValueImpl::IsInt() { Lock(); bool rv = v8_value_->IsInt32(); Unlock(); return rv; } bool CefV8ValueImpl::IsDouble() { Lock(); bool rv = (v8_value_->IsNumber() || v8_value_->IsDate()); Unlock(); return rv; } bool CefV8ValueImpl::IsString() { Lock(); bool rv = v8_value_->IsString(); Unlock(); return rv; } bool CefV8ValueImpl::IsObject() { Lock(); bool rv = v8_value_->IsObject(); Unlock(); return rv; } bool CefV8ValueImpl::IsArray() { Lock(); bool rv = v8_value_->IsArray(); Unlock(); return rv; } bool CefV8ValueImpl::IsFunction() { Lock(); bool rv = v8_value_->IsFunction(); Unlock(); return rv; } bool CefV8ValueImpl::GetBoolValue() { bool rv = false; Lock(); if(v8_value_->IsTrue()) rv = true; else if(v8_value_->IsFalse()) rv = false; else { v8::HandleScope handle_scope; v8::Local val = v8_value_->ToBoolean(); rv = val->Value(); } Unlock(); return rv; } int CefV8ValueImpl::GetIntValue() { int rv = 0; Lock(); v8::HandleScope handle_scope; v8::Local val = v8_value_->ToInt32(); rv = val->Value(); Unlock(); return rv; } double CefV8ValueImpl::GetDoubleValue() { double rv = 0.; Lock(); v8::HandleScope handle_scope; v8::Local val = v8_value_->ToNumber(); rv = val->Value(); Unlock(); return rv; } CefString CefV8ValueImpl::GetStringValue() { CefString rv; Lock(); v8::HandleScope handle_scope; rv = GetString(v8_value_->ToString()); Unlock(); return rv; } bool CefV8ValueImpl::HasValue(const CefString& key) { if(IsReservedKey(key)) return false; bool rv = false; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = obj->Has(GetV8String(key)); } Unlock(); return rv; } bool CefV8ValueImpl::HasValue(int index) { bool rv = false; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = obj->Has(index); } Unlock(); return rv; } bool CefV8ValueImpl::DeleteValue(const CefString& key) { if(IsReservedKey(key)) return false; bool rv = false; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = obj->Delete(GetV8String(key)); } Unlock(); return rv; } bool CefV8ValueImpl::DeleteValue(int index) { bool rv = false; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = obj->Delete(index); } Unlock(); return rv; } CefRefPtr CefV8ValueImpl::GetValue(const CefString& key) { if(IsReservedKey(key)) return false; CefRefPtr rv; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = new CefV8ValueImpl(obj->Get(GetV8String(key))); } Unlock(); return rv; } CefRefPtr CefV8ValueImpl::GetValue(int index) { CefRefPtr rv; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = new CefV8ValueImpl(obj->Get(v8::Number::New(index))); } Unlock(); return rv; } bool CefV8ValueImpl::SetValue(const CefString& key, CefRefPtr value) { if(IsReservedKey(key)) return false; bool rv = false; Lock(); if(v8_value_->IsObject()) { CefV8ValueImpl *impl = static_cast(value.get()); if(impl) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = obj->Set(GetV8String(key), impl->GetValue()); } } Unlock(); return rv; } bool CefV8ValueImpl::SetValue(int index, CefRefPtr value) { bool rv = false; Lock(); if(v8_value_->IsObject()) { CefV8ValueImpl *impl = static_cast(value.get()); if(impl) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); rv = obj->Set(v8::Number::New(index), impl->GetValue()); } } Unlock(); return rv; } bool CefV8ValueImpl::GetKeys(std::vector& keys) { bool rv = false; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); v8::Local arr_keys = obj->GetPropertyNames(); uint32_t len = arr_keys->Length(); for(uint32_t i = 0; i < len; ++i) { v8::Local value = arr_keys->Get(v8::Integer::New(i)); CefString str = GetString(value->ToString()); if(!IsReservedKey(str)) keys.push_back(str); } rv = true; } Unlock(); return rv; } CefRefPtr CefV8ValueImpl::GetUserData() { CefRefPtr rv; Lock(); if(v8_value_->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); v8::Local key = v8::String::New("Cef::UserData"); if(obj->Has(key)) rv = static_cast(v8::External::Unwrap(obj->Get(key))); } Unlock(); return rv; } int CefV8ValueImpl::GetArrayLength() { int rv = 0; Lock(); if(v8_value_->IsArray()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); v8::Local arr = v8::Local::Cast(obj); rv = arr->Length(); } Unlock(); return rv; } CefString CefV8ValueImpl::GetFunctionName() { CefString rv; Lock(); if(v8_value_->IsFunction()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); v8::Local func = v8::Local::Cast(obj); rv = GetString(v8::Handle::Cast(func->GetName())); } Unlock(); return rv; } CefRefPtr CefV8ValueImpl::GetFunctionHandler() { CefRefPtr rv; Lock(); if(v8_value_->IsFunction()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); v8::Local key = v8::String::New("Cef::Handler"); if(obj->Has(key)) rv = static_cast(v8::External::Unwrap(obj->Get(key))); } Unlock(); return rv; } bool CefV8ValueImpl::ExecuteFunction(CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { bool rv = false; Lock(); if(v8_value_->IsFunction() && object.get() && object->IsObject()) { v8::HandleScope handle_scope; v8::Local obj = v8_value_->ToObject(); v8::Local func = v8::Local::Cast(obj); CefV8ValueImpl* recv_impl = static_cast(object.get()); v8::Handle recv = v8::Handle::Cast(recv_impl->GetValue()); int argc = arguments.size(); v8::Handle *argv = NULL; if(argc > 0) { argv = new v8::Handle[argc]; for(int i = 0; i < argc; ++i) { argv[i] = static_cast(arguments[i].get())->GetValue(); } } v8::TryCatch try_catch; v8::Local func_rv = func->Call(recv, argc, argv); if (try_catch.HasCaught()) exception = GetString(try_catch.Message()->Get()); else retval = new CefV8ValueImpl(func_rv); rv = true; } Unlock(); return rv; }