- Fix memory leaks in V8 usage (issue #323).

- Improve performance of V8 string conversions (issue #323).
- Add V8 performance test to cefclient (issue #323).
- Add the ability to return exceptions from V8 accessors (issue #327).
- Return undefined instead of null from a V8 handler if no return value is specified (issue #329).


git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@291 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2011-09-20 20:41:54 +00:00
parent 792e78795f
commit 32402b2287
16 changed files with 376 additions and 72 deletions

View File

@ -55,6 +55,7 @@
'tests/cefclient/mac/English.lproj/MainMenu.xib',
'tests/cefclient/mac/Info.plist',
'tests/cefclient/res/domaccess.html',
'tests/cefclient/res/extensionperf.html',
'tests/cefclient/res/localstorage.html',
'tests/cefclient/res/logo.png',
'tests/cefclient/res/xmlhttprequest.html',

View File

@ -2210,7 +2210,8 @@ public:
/*--cef()--*/
virtual bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval) =0;
CefRefPtr<CefV8Value>& retval,
CefString& exception) =0;
///
// Called to set an accessor value. |name| is the name of the property being
@ -2221,7 +2222,8 @@ public:
/*--cef()--*/
virtual bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value) =0;
const CefRefPtr<CefV8Value> value,
CefString& exception) =0;
};

View File

@ -2002,7 +2002,7 @@ typedef struct _cef_v8accessor_t
///
int (CEF_CALLBACK *get)(struct _cef_v8accessor_t* self,
const cef_string_t* name, struct _cef_v8value_t* object,
struct _cef_v8value_t** retval);
struct _cef_v8value_t** retval, cef_string_t* exception);
///
// Called to set an accessor value. |name| is the name of the property being
@ -2012,7 +2012,7 @@ typedef struct _cef_v8accessor_t
///
int (CEF_CALLBACK *set)(struct _cef_v8accessor_t* self,
const cef_string_t* name, struct _cef_v8value_t* object,
struct _cef_v8value_t* value);
struct _cef_v8value_t* value, cef_string_t* exception);
} cef_v8accessor_t;

View File

@ -97,29 +97,77 @@ CefRefPtr<CefBrowserImpl> FindBrowserForFrame(WebKit::WebFrame *frame)
return NULL;
}
// Convert a wide string to a V8 string.
// Convert a CefString to a V8::String.
v8::Handle<v8::String> GetV8String(const CefString& str)
{
#if defined(CEF_STRING_TYPE_UTF16)
// Already a UTF16 string.
return v8::String::New(
reinterpret_cast<uint16_t*>(
const_cast<CefString::char_type*>(str.c_str())),
str.length());
#elif defined(CEF_STRING_TYPE_UTF8)
// Already a UTF8 string.
return v8::String::New(const_cast<char*>(str.c_str()), str.length());
#else
// Convert the string to UTF8.
std::string tmpStr = str;
return v8::String::New(tmpStr.c_str(), tmpStr.length());
#endif
}
// Convert a V8 string to a UTF8 string.
std::string GetString(v8::Handle<v8::String> str)
#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::Handle<v8::String> str, CefString& out)
{
#if defined(CEF_STRING_TYPE_WIDE)
// 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);
// Perform conversion to the wide type.
cef_string_t* retws = out.GetWritableStruct();
cef_string_utf8_to_wide(buf, len, retws);
delete [] buf;
return ret;
#else // !defined(CEF_STRING_TYPE_WIDE)
#if defined(CEF_STRING_TYPE_UTF16)
int len = str->Length();
char16* buf = new char16[len + 1];
str->Write(reinterpret_cast<uint16_t*>(buf), 0, len + 1);
#else
// Allocate enough space for a worst-case conversion.
int len = str->Utf8Length();
char* buf = new char[len + 1];
str->WriteUtf8(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.
v8::Handle<v8::Value> FunctionCallbackImpl(const v8::Arguments& args)
{
v8::HandleScope handle_scope;
CefV8Handler* handler =
static_cast<CefV8Handler*>(v8::External::Unwrap(args.Data()));
@ -127,24 +175,24 @@ v8::Handle<v8::Value> FunctionCallbackImpl(const v8::Arguments& args)
for(int i = 0; i < args.Length(); i++)
params.push_back(new CefV8ValueImpl(args[i]));
CefString func_name =
GetString(v8::Handle<v8::String>::Cast(args.Callee()->GetName()));
CefString func_name;
GetCefString(v8::Handle<v8::String>::Cast(args.Callee()->GetName()),
func_name);
CefRefPtr<CefV8Value> object = new CefV8ValueImpl(args.This());
CefRefPtr<CefV8Value> retval;
CefString exception;
v8::Handle<v8::Value> value = v8::Null();
if(handler->Execute(func_name, object, params, retval, exception)) {
if (!exception.empty())
value = v8::ThrowException(GetV8String(exception));
return v8::ThrowException(v8::Exception::Error(GetV8String(exception)));
else {
CefV8ValueImpl* rv = static_cast<CefV8ValueImpl*>(retval.get());
if (rv)
value = rv->GetHandle();
return rv->GetHandle();
}
}
return value;
return v8::Undefined();
}
// V8 Accessor callbacks
@ -153,7 +201,6 @@ v8::Handle<v8::Value> AccessorGetterCallbackImpl(v8::Local<v8::String> property,
{
v8::HandleScope handle_scope;
v8::Handle<v8::Value> value = v8::Undefined();
v8::Handle<v8::Object> obj = info.This();
v8::Handle<v8::String> key = v8::String::New("Cef::Accessor");
@ -166,14 +213,21 @@ v8::Handle<v8::Value> AccessorGetterCallbackImpl(v8::Local<v8::String> property,
if (accessorPtr) {
CefRefPtr<CefV8Value> retval;
CefRefPtr<CefV8Value> object = new CefV8ValueImpl(obj);
CefString name = GetString(property);
if (accessorPtr->Get(name, object, retval)) {
CefString name, exception;
GetCefString(property, name);
if (accessorPtr->Get(name, object, retval, exception)) {
if (!exception.empty()) {
return v8::ThrowException(
v8::Exception::Error(GetV8String(exception)));
} else {
CefV8ValueImpl* rv = static_cast<CefV8ValueImpl*>(retval.get());
if (rv)
value = rv->GetHandle();
return rv->GetHandle();
}
}
return value;
}
return v8::Undefined();
}
void AccessorSetterCallbackImpl(v8::Local<v8::String> property,
@ -194,8 +248,13 @@ void AccessorSetterCallbackImpl(v8::Local<v8::String> property,
if (accessorPtr) {
CefRefPtr<CefV8Value> object = new CefV8ValueImpl(obj);
CefRefPtr<CefV8Value> cefValue = new CefV8ValueImpl(value);
CefString name = GetString(property);
accessorPtr->Set(name, object, cefValue);
CefString name, exception;
GetCefString(property, name);
accessorPtr->Set(name, object, cefValue, exception);
if (!exception.empty()) {
v8::ThrowException(v8::Exception::Error(GetV8String(exception)));
return;
}
}
}
@ -216,7 +275,7 @@ public:
v8::Handle<v8::String> name)
{
return v8::FunctionTemplate::New(FunctionCallbackImpl,
v8::External::New(handler_));
v8::External::Wrap(handler_));
}
void UIT_RegisterExtension()
@ -379,9 +438,13 @@ WebKit::WebFrame* CefV8ContextImpl::GetWebFrame()
// thread.
CefV8ValueHandle::~CefV8ValueHandle()
{
if(tracker_)
if(tracker_) {
TrackAdd(tracker_);
v8_handle_.MakeWeak(tracker_, TrackDestructor);
} else {
v8_handle_.Dispose();
v8_handle_.Clear();
}
tracker_ = NULL;
}
@ -669,7 +732,7 @@ CefString CefV8ValueImpl::GetStringValue()
CefString rv;
CEF_REQUIRE_UI_THREAD(rv);
v8::HandleScope handle_scope;
rv = GetString(GetHandle()->ToString());
GetCefString(GetHandle()->ToString(), rv);
return rv;
}
@ -782,6 +845,10 @@ bool CefV8ValueImpl::SetValue(const CefString& key,
bool CefV8ValueImpl::SetValue(int index, CefRefPtr<CefV8Value> value)
{
CEF_REQUIRE_UI_THREAD(false);
if (index < 0)
return false;
if(!GetHandle()->IsObject()) {
NOTREACHED();
return false;
@ -791,7 +858,7 @@ bool CefV8ValueImpl::SetValue(int index, CefRefPtr<CefV8Value> value)
if(impl) {
v8::HandleScope handle_scope;
v8::Local<v8::Object> obj = GetHandle()->ToObject();
return obj->Set(v8::Number::New(index), impl->GetHandle());
return obj->Set(index, impl->GetHandle());
} else {
NOTREACHED();
return false;
@ -834,7 +901,8 @@ bool CefV8ValueImpl::GetKeys(std::vector<CefString>& keys)
uint32_t len = arr_keys->Length();
for(uint32_t i = 0; i < len; ++i) {
v8::Local<v8::Value> value = arr_keys->Get(v8::Integer::New(i));
CefString str = GetString(value->ToString());
CefString str;
GetCefString(value->ToString(), str);
if(!IsReservedKey(str))
keys.push_back(str);
}
@ -883,7 +951,7 @@ CefString CefV8ValueImpl::GetFunctionName()
v8::HandleScope handle_scope;
v8::Local<v8::Object> obj = GetHandle()->ToObject();
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(obj);
rv = GetString(v8::Handle<v8::String>::Cast(func->GetName()));
GetCefString(v8::Handle<v8::String>::Cast(func->GetName()), rv);
return rv;
}
@ -963,7 +1031,7 @@ bool CefV8ValueImpl::ExecuteFunctionWithContext(
v8::TryCatch try_catch;
v8::Local<v8::Value> func_rv = func->Call(recv, argc, argv);
if (try_catch.HasCaught())
exception = GetString(try_catch.Message()->Get());
GetCefString(try_catch.Message()->Get(), exception);
else
retval = new CefV8ValueImpl(func_rv);

View File

@ -18,7 +18,7 @@
int CEF_CALLBACK v8accessor_get(struct _cef_v8accessor_t* self,
const cef_string_t* name, struct _cef_v8value_t* object,
struct _cef_v8value_t** retval)
struct _cef_v8value_t** retval, cef_string_t* exception)
{
DCHECK(self);
if(!self)
@ -29,8 +29,9 @@ int CEF_CALLBACK v8accessor_get(struct _cef_v8accessor_t* self,
objectPtr = CefV8ValueCToCpp::Wrap(object);
CefRefPtr<CefV8Value> retValPtr;
CefString exceptionStr(exception);
bool rv = CefV8AccessorCppToC::Get(self)->Get(CefString(name), objectPtr,
retValPtr);
retValPtr, exceptionStr);
if(rv) {
if(retValPtr.get() && retval)
*retval = CefV8ValueCToCpp::Unwrap(retValPtr);
@ -41,7 +42,7 @@ int CEF_CALLBACK v8accessor_get(struct _cef_v8accessor_t* self,
int CEF_CALLBACK v8accessor_set(struct _cef_v8accessor_t* self,
const cef_string_t* name, struct _cef_v8value_t* object,
struct _cef_v8value_t* value)
struct _cef_v8value_t* value, cef_string_t* exception)
{
DCHECK(self);
if(!self)
@ -55,8 +56,9 @@ int CEF_CALLBACK v8accessor_set(struct _cef_v8accessor_t* self,
if(value)
valuePtr = CefV8ValueCToCpp::Wrap(value);
CefString exceptionStr(exception);
bool rv = CefV8AccessorCppToC::Get(self)->Set(CefString(name), objectPtr,
valuePtr);
valuePtr, exceptionStr);
return rv;
}

View File

@ -17,7 +17,8 @@
// VIRTUAL METHODS - Body may be edited by hand.
bool CefV8AccessorCToCpp::Get(const CefString& name,
const CefRefPtr<CefV8Value> object, CefRefPtr<CefV8Value>& retval)
const CefRefPtr<CefV8Value> object, CefRefPtr<CefV8Value>& retval,
CefString& exception)
{
if(CEF_MEMBER_MISSING(struct_, get))
return false;
@ -25,7 +26,8 @@ bool CefV8AccessorCToCpp::Get(const CefString& name,
cef_v8value_t* retvalStruct = NULL;
int rv = struct_->get(struct_, name.GetStruct(),
CefV8ValueCppToC::Wrap(object), &retvalStruct);
CefV8ValueCppToC::Wrap(object), &retvalStruct,
exception.GetWritableStruct());
if(retvalStruct)
retval = CefV8ValueCppToC::Unwrap(retvalStruct);
@ -33,14 +35,16 @@ bool CefV8AccessorCToCpp::Get(const CefString& name,
}
bool CefV8AccessorCToCpp::Set(const CefString& name,
const CefRefPtr<CefV8Value> object, const CefRefPtr<CefV8Value> value)
const CefRefPtr<CefV8Value> object, const CefRefPtr<CefV8Value> value,
CefString& exception)
{
if(CEF_MEMBER_MISSING(struct_, set))
return false;
int rv = struct_->set(struct_, name.GetStruct(),
CefV8ValueCppToC::Wrap(object),
CefV8ValueCppToC::Wrap(value));
CefV8ValueCppToC::Wrap(value),
exception.GetWritableStruct());
return rv ? true : false;
}

View File

@ -32,9 +32,9 @@ public:
// CefV8Accessor methods
virtual bool Get(const CefString& name, const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval) OVERRIDE;
CefRefPtr<CefV8Value>& retval, CefString& exception) OVERRIDE;
virtual bool Set(const CefString& name, const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value) OVERRIDE;
const CefRefPtr<CefV8Value> value, CefString& exception) OVERRIDE;
};
#endif // BUILDING_CEF_SHARED

View File

@ -37,6 +37,7 @@ IDS_XMLHTTPREQUEST BINARY "res\\xmlhttprequest.html"
IDS_DOMACCESS BINARY "res\\domaccess.html"
IDS_MODALMAIN BINARY "res\\modalmain.html"
IDS_MODALDIALOG BINARY "res\\modaldialog.html"
IDS_EXTENSIONPERF BINARY "res\\extensionperf.html"
/////////////////////////////////////////////////////////////////////////////
//
@ -73,6 +74,7 @@ BEGIN
MENUITEM "Get Text", ID_TESTS_GETTEXT
MENUITEM "JavaScript Binding Handler", ID_TESTS_JAVASCRIPT_BINDING
MENUITEM "JavaScript Extension Handler",ID_TESTS_JAVASCRIPT_EXTENSION
MENUITEM "JavaScript Extension Performance",ID_TESTS_JAVASCRIPT_PERFORMANCE
MENUITEM "JavaScript Execute", ID_TESTS_JAVASCRIPT_EXECUTE
MENUITEM "Plugin", ID_TESTS_PLUGIN
MENUITEM "Popup Window", ID_TESTS_POPUP

View File

@ -188,6 +188,7 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
- (IBAction)testGetText:(id)sender;
- (IBAction)testJSBinding:(id)sender;
- (IBAction)testJSExtension:(id)sender;
- (IBAction)testJSExtensionPerf:(id)sender;
- (IBAction)testJSExecute:(id)sender;
- (IBAction)testRequest:(id)sender;
- (IBAction)testLocalStorage:(id)sender;
@ -236,6 +237,9 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
[testMenu addItemWithTitle:@"JavaScript Extension Handler"
action:@selector(testJSExtension:)
keyEquivalent:@""];
[testMenu addItemWithTitle:@"JavaScript Extension Performance"
action:@selector(testJSExtensionPerf:)
keyEquivalent:@""];
[testMenu addItemWithTitle:@"JavaScript Execute"
action:@selector(testJSExecute:)
keyEquivalent:@""];
@ -397,6 +401,11 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
RunExtensionTest(g_handler->GetBrowser());
}
- (IBAction)testJSExtensionPerf:(id)sender {
if(g_handler.get() && g_handler->GetBrowserHwnd())
RunExtensionPerfTest(g_handler->GetBrowser());
}
- (IBAction)testJSExecute:(id)sender {
if(g_handler.get() && g_handler->GetBrowserHwnd())
RunJavaScriptExecuteTest(g_handler->GetBrowser());

View File

@ -476,6 +476,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if(browser.get())
RunExtensionTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_PERFORMANCE: // Test the V8 performance
if(browser.get())
RunExtensionPerfTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_EXECUTE: // Test execution of javascript
if(browser.get())
RunJavaScriptExecuteTest(browser);

View File

@ -1,9 +1,9 @@
// Copyright (c) 2008-2009 The Chromium Embedded Framework Authors. All rights
// Copyright (c) 2011 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 "extension_test.h"
#include "resource_util.h"
// Implementation of the V8 handler class for the "cef.test" extension.
class ClientV8ExtensionHandler : public CefV8Handler
@ -20,7 +20,12 @@ public:
CefRefPtr<CefV8Value>& retval,
CefString& exception)
{
if(name == "SetTestParam")
if(name == "Dummy")
{
// Used for performance testing.
return true;
}
else if(name == "SetTestParam")
{
// Handle the SetTestParam native function by saving the string argument
// into the local member.
@ -89,6 +94,10 @@ void InitExtensionTest()
" native function GetTestObject();"
" return GetTestObject();"
" };"
" cef.test.dummy = function() {"
" native function Dummy();"
" return Dummy();"
" };"
"})();";
CefRegisterExtension("v8/test", code, new ClientV8ExtensionHandler());
}
@ -111,3 +120,14 @@ void RunExtensionTest(CefRefPtr<CefBrowser> browser)
"</pre></body></html>";
browser->GetMainFrame()->LoadString(html, "about:blank");
}
void RunExtensionPerfTest(CefRefPtr<CefBrowser> browser)
{
CefRefPtr<CefStreamReader> resourceStream;
#if defined(OS_WIN)
resourceStream = GetBinaryResourceReader(IDS_EXTENSIONPERF);
#elif defined(OS_MACOSX)
resourceStream = GetBinaryResourceReader("extensionperf.html");
#endif
browser->GetMainFrame()->LoadStream(resourceStream, "about:blank");
}

View File

@ -12,5 +12,6 @@ void InitExtensionTest();
// Run the test.
void RunExtensionTest(CefRefPtr<CefBrowser> browser);
void RunExtensionPerfTest(CefRefPtr<CefBrowser> browser);
#endif // _CEFCLIENT_EXTENSION_TEST_H

View File

@ -0,0 +1,131 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JavaScript Extension: Performance</title>
<style>
body { font-family: Tahoma, Serif; font-size: 9pt; }
</style>
</head>
<body>
<h1>JavaScript Extension: Performance</h1>
<div><span id="statusBox"></span> <progress id="progressBox" value="0" style="display:none"></progress></div>
<div style="padding-top:10px; padding-bottom:10px">
<table id="resultTable" border="1" cellspacing="1" cellpadding="4" width="100%">
<thead>
<tr>
<td>Name</td>
<td>Min</td>
<td>Avg</td>
<td>Max</td>
<td>Probes</td>
</tr>
</thead>
<!-- result rows here -->
</table>
</div>
<script type="text/javascript">
(function () {
var asyncExecution = true;
var testIterations = 100000;
var totalProbes = 10;
var probeDelay = 0;
var collectProbes = false;
function dummyCallTest() {
for (var i = 0; i < testIterations; i++) {
cef.test.dummy();
}
}
function execTestFunc(func) {
var begin = new Date();
func();
var end = new Date();
return (end - begin);
}
function execTest(test) {
function nextStep() {
if (asyncExecution) {
setTimeout(function () { execTest(test); }, probeDelay);
} else {
execTest(test);
}
}
function nextTest() {
appendResult(test);
// ...
}
updateStatus(test);
if (!test.warmedUp) {
execTestFunc(test.func);
test.warmedUp = true;
return nextStep();
}
if (test.probe >= test.totalProbes) {
test.avg = test.total / test.totalProbes;
return nextTest();
}
var elapsed = execTestFunc(test.func);
test.total += elapsed;
if (!test.min) test.min = elapsed;
else if (test.min > elapsed) test.min = elapsed;
if (!test.max) test.max = elapsed;
else if (test.max < elapsed) test.max = elapsed;
if (collectProbes) {
test.results.push(elapsed);
}
test.probe++;
return nextStep();
}
function updateStatus(test) {
var statusBox = document.getElementById("statusBox");
var progressBox = document.getElementById("progressBox");
if (test.probe >= test.totalProbes) {
statusBox.innerText = test.name + " completed.";
progressBox.style.display = 'none';
} else {
statusBox.innerText = test.name + " (" + test.probe + "/" + test.totalProbes + ")";
progressBox.value = (test.probe / test.totalProbes);
progressBox.style.display = 'inline';
}
}
function appendResult(test) {
var e = document.getElementById("resultTable");
e.insertAdjacentHTML("beforeEnd",
["<tr>",
"<td>", test.name, "</td>",
"<td>", test.min, "ms</td>",
"<td>", test.avg, "ms</td>",
"<td>", test.max, "ms</td>",
"<td>", test.results.join(", "), "</td>",
"<tr>"
].join("")
);
}
function runTest(name, func) {
var test = { name: name, func: func, warmedUp: false, total: 0, totalProbes: totalProbes, probe: 0, results: [] };
setTimeout(function () { execTest(test); }, 10);
}
runTest("dummyCall", dummyCallTest);
})();
</script>
</body>
</html>

View File

@ -52,6 +52,7 @@
#define ID_TESTS_DRAGDROP 32792
#define ID_TESTS_OSRAPP 32793
#define ID_TESTS_MODALDIALOG 32794
#define ID_TESTS_JAVASCRIPT_PERFORMANCE 32795
#define IDC_STATIC -1
#define IDS_LOGO 1000
#define IDS_UIPLUGIN 1001
@ -62,6 +63,7 @@
#define IDS_OSRPLUGIN 1006
#define IDS_MODALMAIN 1007
#define IDS_MODALDIALOG 1008
#define IDS_EXTENSIONPERF 1009
// Avoid files associated with MacOS
#define _X86_

View File

@ -9,6 +9,8 @@
#if defined(OS_WIN)
#include "resource.h"
// Load a resource of type BINARY
bool LoadBinaryResource(int binaryId, DWORD &dwSize, LPBYTE &pBytes);
CefRefPtr<CefStreamReader> GetBinaryResourceReader(int binaryId);

View File

@ -396,11 +396,15 @@ public:
CefRefPtr<CefV8Value>& retval,
CefString& exception) = 0;
virtual bool Get(const CefString& name, const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval) = 0;
virtual bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) = 0;
virtual bool Set(const CefString& name, const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value) = 0;
virtual bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) = 0;
};
class DelegatingV8Handler : public CefV8Handler
@ -417,7 +421,7 @@ public:
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception)
CefString& exception) OVERRIDE
{
return delegate_->Execute(name, object, arguments, retval, exception);
}
@ -434,16 +438,20 @@ public:
DelegatingV8Accessor(CefV8HandlerDelegate *delegate)
: delegate_(delegate) { }
bool Get(const CefString& name, const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval)
bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE
{
return delegate_->Get(name, object, retval);
return delegate_->Get(name, object, retval, exception);
}
bool Set(const CefString& name, const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value)
bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) OVERRIDE
{
return delegate_->Set(name, object, value);
return delegate_->Set(name, object, value, exception);
}
private:
@ -505,6 +513,11 @@ public:
"comp(false,\"a\",\"b\");\n"
"try { point.x = -1; } catch(e) { }\n" // should not have any effect.
"try { point.y = point.x; theY = point.y; } catch(e) { point.y = 4321; }\n"
// Test get and set exceptions.
"try { exceptObj.makeException = 1; }"
" catch(e) { gotSetException(e.toString()); }\n"
"try { var x = exceptObj.makeException; }"
" catch(e) { gotGetException(e.toString()); }\n"
"hello(\"main\", callIFrame);"
"function callIFrame() {"
" var iframe = document.getElementById('iframe');"
@ -598,6 +611,14 @@ public:
CefV8Value::CreateFunction("comp", funcHandler);
object->SetValue("comp", compFunc);
// Used for testing exceptions returned from accessors.
CefRefPtr<CefV8Value> gotGetExceptionFunc =
CefV8Value::CreateFunction("gotGetException", funcHandler);
object->SetValue("gotGetException", gotGetExceptionFunc);
CefRefPtr<CefV8Value> gotSetExceptionFunc =
CefV8Value::CreateFunction("gotSetException", funcHandler);
object->SetValue("gotSetException", gotSetExceptionFunc);
// Create an object with accessor based properties:
CefRefPtr<CefBase> blankBase;
CefRefPtr<CefV8Accessor> accessor(new DelegatingV8Accessor(this));
@ -609,6 +630,15 @@ public:
V8_PROPERTY_ATTRIBUTE_NONE);
object->SetValue("point", point);
// Create another object with accessor based properties:
CefRefPtr<CefV8Value> exceptObj =
CefV8Value::CreateObject(NULL, new DelegatingV8Accessor(this));
exceptObj->SetValue("makeException", V8_ACCESS_CONTROL_DEFAULT,
V8_PROPERTY_ATTRIBUTE_NONE);
object->SetValue("exceptObj", exceptObj);
}
void CallIFrame()
@ -704,7 +734,7 @@ public:
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception)
CefString& exception) OVERRIDE
{
CefRefPtr<CefV8Context> cc = CefV8Context::GetCurrentContext();
CefRefPtr<CefV8Context> ec = CefV8Context::GetEnteredContext();
@ -797,12 +827,26 @@ public:
got_testcomplete_.yes();
DestroyTest();
return true;
} else if (name == "gotGetException") {
if (arguments.size() == 1 &&
arguments[0]->GetStringValue() == "Error: My Get Exception") {
got_getexception_.yes();
}
return true;
} else if (name == "gotSetException") {
if (arguments.size() == 1 &&
arguments[0]->GetStringValue() == "Error: My Set Exception") {
got_setexception_.yes();
}
return true;
}
return false;
}
bool Get(const CefString& name, const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval)
bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE
{
if(name == "x") {
got_point_x_read_.yes();
@ -812,18 +856,26 @@ public:
got_point_y_read_.yes();
retval = CefV8Value::CreateInt(y_);
return true;
} else if(name == "makeException") {
exception = "My Get Exception";
return true;
}
return false;
}
bool Set(const CefString& name, const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value)
bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) OVERRIDE
{
if(name == "y") {
y_ = value->GetIntValue();
if( y_ == 1234)
got_point_y_write_.yes();
return true;
} else if(name == "makeException") {
exception = "My Set Exception";
return true;
}
return false;
}
@ -845,6 +897,8 @@ public:
TrackCallback got_iframe_as_entered_url_;
TrackCallback got_no_context_;
TrackCallback got_exception_;
TrackCallback got_getexception_;
TrackCallback got_setexception_;
TrackCallback got_navigation_;
TrackCallback got_testcomplete_;
@ -870,6 +924,8 @@ TEST(V8Test, Context)
EXPECT_TRUE(handler->got_correct_entered_url_);
EXPECT_TRUE(handler->got_correct_current_url_);
EXPECT_TRUE(handler->got_exception_);
EXPECT_TRUE(handler->got_getexception_);
EXPECT_TRUE(handler->got_setexception_);
EXPECT_TRUE(handler->got_navigation_);
EXPECT_TRUE(handler->got_testcomplete_);
}