- Fix bug in CefV8Value handling of weak references. (issue #72)

- Reintroduce support for CefHandler::HandleJSBinding(). (issue #72)

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@100 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2010-08-31 17:52:34 +00:00
parent e70fd64a52
commit e8cc5669f0
18 changed files with 381 additions and 18 deletions

View File

@ -25,6 +25,8 @@
],
'sources': [
'tests/cefclient/Resource.h',
'tests/cefclient/binding_test.cpp',
'tests/cefclient/binding_test.h',
'tests/cefclient/cefclient.cpp',
'tests/cefclient/cefclient.h',
'tests/cefclient/cefclient.ico',

View File

@ -699,6 +699,13 @@ public:
bool& retval,
std::wstring& result) =0;
// Event called for adding values to a frame's JavaScript 'window' object. The
// return value is currently ignored.
/*--cef()--*/
virtual RetVal HandleJSBinding(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object) =0;
// Called just before a window is closed. The return value is currently
// ignored.
/*--cef()--*/
@ -1025,8 +1032,9 @@ class CefV8Value : public CefBase
{
public:
// Create a new CefV8Value object of the specified type. These methods
// should only be called from within the JavaScript context in a
// CefV8Handler::Execute() callback.
// should only be called from within the JavaScript context -- either in a
// CefV8Handler::Execute() callback or a CefHandler::HandleJSBinding()
// callback.
/*--cef()--*/
static CefRefPtr<CefV8Value> CreateUndefined();
/*--cef()--*/

View File

@ -511,6 +511,12 @@ typedef struct _cef_handler_t
const wchar_t* message, const wchar_t* defaultValue, int* retval,
cef_string_t* result);
// Event called for adding values to a frame's JavaScript 'window' object. The
// return value is currently ignored.
enum cef_retval_t (CEF_CALLBACK *handle_jsbinding)(
struct _cef_handler_t* self, struct _cef_browser_t* browser,
struct _cef_frame_t* frame, struct _cef_v8value_t* object);
// Called just before a window is closed. The return value is currently
// ignored.
enum cef_retval_t (CEF_CALLBACK *handle_before_window_close)(
@ -906,8 +912,9 @@ typedef struct _cef_v8value_t
// Create a new cef_v8value_t object of the specified type. These functions
// should only be called from within the JavaScript context in a
// cef_v8handler_t::execute() callback.
// should only be called from within the JavaScript context -- either in a
// cef_v8handler_t::execute() callback or a cef_handler_t::handle_jsbinding()
// callback.
CEF_EXPORT cef_v8value_t* cef_v8value_create_undefined();
CEF_EXPORT cef_v8value_t* cef_v8value_create_null();
CEF_EXPORT cef_v8value_t* cef_v8value_create_bool(int value);

View File

@ -81,6 +81,13 @@ void InitializeTextEncoding() {
WebCore::UTF8Encoding();
}
v8::Handle<v8::Context> GetV8Context(WebKit::WebFrame* frame)
{
WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(frame);
WebCore::Frame* core_frame = webFrameImpl->frame();
return WebCore::V8Proxy::context(core_frame);
}
void CloseIdleConnections() {
// Used in benchmarking, Ignored for CEF.
}

View File

@ -33,6 +33,9 @@ void InitializeTextEncoding();
// This is called indirectly by the network layer to access resources.
base::StringPiece NetResourceProvider(int key);
// Retrieve the V8 context associated with the frame.
v8::Handle<v8::Context> GetV8Context(WebKit::WebFrame* frame);
// Clear all cached data.
void ClearCache();

View File

@ -700,6 +700,22 @@ void BrowserWebViewDelegate::didCommitProvisionalLoad(
}
}
void BrowserWebViewDelegate::didClearWindowObject(WebFrame* frame) {
CefRefPtr<CefHandler> handler = browser_->GetHandler();
if(handler.get()) {
v8::HandleScope handle_scope;
v8::Handle<v8::Context> context = webkit_glue::GetV8Context(frame);
if(context.IsEmpty())
return;
v8::Context::Scope scope(context);
CefRefPtr<CefFrame> cframe(browser_->GetCefFrame(frame));
CefRefPtr<CefV8Value> object = new CefV8ValueImpl(context->Global());
handler->HandleJSBinding(browser_, cframe, object);
}
}
void BrowserWebViewDelegate::didReceiveTitle(
WebFrame* frame, const WebString& title) {
std::wstring wtitle = UTF16ToWideHack(title);

View File

@ -152,6 +152,7 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient,
WebKit::WebFrame*, const WebKit::WebURLError&);
virtual void didCommitProvisionalLoad(
WebKit::WebFrame*, bool is_new_navigation);
virtual void didClearWindowObject(WebKit::WebFrame*);
virtual void didReceiveTitle(
WebKit::WebFrame*, const WebKit::WebString& title);
virtual void didFailLoad(

View File

@ -50,6 +50,8 @@ static void TrackDestructor(v8::Persistent<v8::Value> object,
{
if(parameter)
TrackDelete(static_cast<CefTrackObject*>(parameter));
object.Dispose();
object.Clear();
}
@ -305,7 +307,6 @@ void CefV8ValueImpl::Detach()
if(tracker_)
TrackAdd(tracker_);
v8_value_.MakeWeak(tracker_, TrackDestructor);
v8_value_.Clear();
tracker_ = NULL;
Unlock();
}

View File

@ -15,6 +15,7 @@
#include "libcef_dll/ctocpp/frame_ctocpp.h"
#include "libcef_dll/ctocpp/request_ctocpp.h"
#include "libcef_dll/ctocpp/stream_reader_ctocpp.h"
#include "libcef_dll/ctocpp/v8value_ctocpp.h"
#include "libcef_dll/transfer_util.h"
@ -419,6 +420,22 @@ enum cef_retval_t CEF_CALLBACK handler_handle_jsprompt(
return rv;
}
enum cef_retval_t CEF_CALLBACK handler_handle_jsbinding(
struct _cef_handler_t* self, cef_browser_t* browser, cef_frame_t* frame,
struct _cef_v8value_t* object)
{
DCHECK(self);
DCHECK(browser);
DCHECK(frame);
DCHECK(object);
if(!self || !browser || !frame || !object)
return RV_CONTINUE;
return CefHandlerCppToC::Get(self)->HandleJSBinding(
CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
CefV8ValueCToCpp::Wrap(object));
}
enum cef_retval_t CEF_CALLBACK handler_handle_before_window_close(
struct _cef_handler_t* self, cef_browser_t* browser)
{
@ -551,6 +568,7 @@ CefHandlerCppToC::CefHandlerCppToC(CefHandler* cls)
struct_.struct_.handle_jsalert = handler_handle_jsalert;
struct_.struct_.handle_jsconfirm = handler_handle_jsconfirm;
struct_.struct_.handle_jsprompt = handler_handle_jsprompt;
struct_.struct_.handle_jsbinding = handler_handle_jsbinding;
struct_.struct_.handle_before_window_close =
handler_handle_before_window_close;
struct_.struct_.handle_take_focus = handler_handle_take_focus;

View File

@ -14,6 +14,7 @@
#include "libcef_dll/cpptoc/frame_cpptoc.h"
#include "libcef_dll/cpptoc/request_cpptoc.h"
#include "libcef_dll/cpptoc/stream_reader_cpptoc.h"
#include "libcef_dll/cpptoc/v8value_cpptoc.h"
#include "libcef_dll/ctocpp/handler_ctocpp.h"
#include "libcef_dll/transfer_util.h"
@ -317,6 +318,17 @@ CefHandler::RetVal CefHandlerCToCpp::HandleJSPrompt(
return rv;
}
CefHandler::RetVal CefHandlerCToCpp::HandleJSBinding(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object)
{
if(CEF_MEMBER_MISSING(struct_, handle_jsbinding))
return RV_CONTINUE;
return struct_->handle_jsbinding(struct_, CefBrowserCppToC::Wrap(browser),
CefFrameCppToC::Wrap(frame), CefV8ValueCppToC::Wrap(object));
}
CefHandler::RetVal CefHandlerCToCpp::HandleBeforeWindowClose(
CefRefPtr<CefBrowser> browser)
{

View File

@ -73,6 +73,8 @@ public:
virtual RetVal HandleJSPrompt(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, const std::wstring& message,
const std::wstring& defaultValue, bool& retval, std::wstring& result);
virtual RetVal HandleJSBinding(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Value> object);
virtual RetVal HandleBeforeWindowClose(CefRefPtr<CefBrowser> browser);
virtual RetVal HandleTakeFocus(CefRefPtr<CefBrowser> browser, bool reverse);
virtual RetVal HandleSetFocus(CefRefPtr<CefBrowser> browser, bool isWidget);

View File

@ -0,0 +1,173 @@
// Copyright (c) 2008-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 "binding_test.h"
#include <sstream>
// Implementation of the V8 handler class for the "window.cef_test.Dump"
// function.
class ClientV8FunctionHandler : public CefThreadSafeBase<CefV8Handler>
{
public:
ClientV8FunctionHandler() {}
virtual ~ClientV8FunctionHandler() {}
// Execute with the specified argument list and return value. Return true if
// the method was handled.
virtual bool Execute(const std::wstring& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
std::wstring& exception)
{
if(name == L"Dump")
{
// The "Dump" function will return a human-readable dump of the input
// arguments.
std::wstringstream stream;
size_t i;
for(i = 0; i < arguments.size(); ++i)
{
stream << L"arg[" << i << L"] = ";
PrintValue(arguments[i], stream, 0);
stream << L"\n";
}
retval = CefV8Value::CreateString(stream.str());
return true;
}
else if(name == L"Call")
{
// The "Call" function will execute a function to get an object and then
// return the result of calling a function belonging to that object. The
// first arument is the function that will return an object and the second
// argument is the function that will be called on that returned object.
int argSize = arguments.size();
if(argSize < 2 || !arguments[0]->IsFunction()
|| !arguments[1]->IsString())
return false;
CefV8ValueList argList;
// Execute the function stored in the first argument to retrieve an
// object.
CefRefPtr<CefV8Value> objectPtr;
if(!arguments[0]->ExecuteFunction(object, argList, objectPtr, exception))
return false;
// Verify that the returned value is an object.
if(!objectPtr.get() || !objectPtr->IsObject())
return false;
// Retrieve the member function specified by name in the second argument
// from the object.
CefRefPtr<CefV8Value> funcPtr =
objectPtr->GetValue(arguments[1]->GetStringValue());
// Verify that the returned value is a function.
if(!funcPtr.get() || !funcPtr->IsFunction())
return false;
// Pass any additional arguments on to the member function.
for(int i = 2; i < argSize; ++i)
argList.push_back(arguments[i]);
// Execute the member function.
return funcPtr->ExecuteFunction(arguments[0], argList, retval, exception);
}
return false;
}
// Simple function for formatted output of a V8 value.
void PrintValue(CefRefPtr<CefV8Value> value, std::wstringstream &stream,
int indent)
{
std::wstringstream indent_stream;
for(int i = 0; i < indent; ++i)
indent_stream << L" ";
std::wstring indent_str = indent_stream.str();
if(value->IsUndefined())
stream << L"(undefined)";
else if(value->IsNull())
stream << L"(null)";
else if(value->IsBool())
stream << L"(bool) " << (value->GetBoolValue() ? L"true" : L"false");
else if(value->IsInt())
stream << L"(int) " << value->GetIntValue();
else if(value->IsDouble())
stream << L"(double) " << value->GetDoubleValue();
else if(value->IsString())
stream << L"(string) " << value->GetStringValue().c_str();
else if(value->IsFunction())
stream << L"(function) " << value->GetFunctionName().c_str();
else if(value->IsArray()) {
stream << L"(array) [";
int len = value->GetArrayLength();
for(int i = 0; i < len; ++i) {
stream << L"\n " << indent_str.c_str() << i << L" = ";
PrintValue(value->GetValue(i), stream, indent+1);
}
stream << L"\n" << indent_str.c_str() << L"]";
} else if(value->IsObject()) {
stream << L"(object) [";
std::vector<std::wstring> keys;
if(value->GetKeys(keys)) {
for(size_t i = 0; i < keys.size(); ++i) {
stream << L"\n " << indent_str.c_str() << keys[i].c_str() << L" = ";
PrintValue(value->GetValue(keys[i]), stream, indent+1);
}
}
stream << L"\n" << indent_str.c_str() << L"]";
}
}
};
void InitBindingTest(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object)
{
// Create the new V8 object.
CefRefPtr<CefV8Value> testObjPtr = CefV8Value::CreateObject(NULL);
// Add the new V8 object to the global window object with the name
// "cef_test".
object->SetValue(L"cef_test", testObjPtr);
// Create an instance of ClientV8FunctionHandler as the V8 handler.
CefRefPtr<CefV8Handler> handlerPtr = new ClientV8FunctionHandler();
// Add a new V8 function to the cef_test object with the name "Dump".
testObjPtr->SetValue(L"Dump",
CefV8Value::CreateFunction(L"Dump", handlerPtr));
// Add a new V8 function to the cef_test object with the name "Call".
testObjPtr->SetValue(L"Call",
CefV8Value::CreateFunction(L"Call", handlerPtr));
}
void RunBindingTest(CefRefPtr<CefBrowser> browser)
{
std::wstring html =
L"<html><body>ClientV8FunctionHandler says:<br><pre>"
L"<script language=\"JavaScript\">"
L"document.writeln(window.cef_test.Dump(false, 1, 7.6654,'bar',"
L" [false,true],[5, 7.654, 1, 'foo', [true, 'bar'], 8]));"
L"document.writeln(window.cef_test.Dump(cef));"
L"document.writeln("
L" window.cef_test.Call(cef.test.test_object, 'GetMessage'));"
L"function my_object() {"
L" var obj = {};"
L" (function() {"
L" obj.GetMessage = function(a) {"
L" return 'Calling a function with value '+a+' on a user object succeeded.';"
L" };"
L" })();"
L" return obj;"
L"};"
L"document.writeln("
L" window.cef_test.Call(my_object, 'GetMessage', 'foobar'));"
L"</script>"
L"</pre></body></html>";
browser->GetMainFrame()->LoadString(html, L"about:blank");
}

View File

@ -0,0 +1,14 @@
// 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.
#pragma once
#include "include/cef.h"
// Add the V8 bindings.
void InitBindingTest(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object);
// Run the test.
void RunBindingTest(CefRefPtr<CefBrowser> browser);

View File

@ -4,6 +4,7 @@
#include "include/cef.h"
#include "cefclient.h"
#include "binding_test.h"
#include "extension_test.h"
#include "plugin_test.h"
#include "resource_util.h"
@ -505,6 +506,18 @@ public:
return RV_CONTINUE;
}
// Event called for binding to a frame's JavaScript global object. The
// return value is currently ignored.
virtual RetVal HandleJSBinding(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object)
{
// Add the V8 bindings.
InitBindingTest(browser, frame, object);
return RV_HANDLED;
}
// Called just before a window is closed. The return value is currently
// ignored.
virtual RetVal HandleBeforeWindowClose(CefRefPtr<CefBrowser> browser)
@ -1013,7 +1026,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
#endif // !TEST_SINGLE_THREADED_MESSAGE_LOOP
}
return 0;
case ID_TESTS_JAVASCRIPT_HANDLER: // Test the V8 extension handler
case ID_TESTS_JAVASCRIPT_BINDING: // Test the V8 binding handler
if(browser.get())
RunBindingTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_EXTENSION: // Test the V8 extension handler
if(browser.get())
RunExtensionTest(browser);
return 0;

View File

@ -65,7 +65,8 @@ BEGIN
BEGIN
MENUITEM "Get Source", ID_TESTS_GETSOURCE
MENUITEM "Get Text", ID_TESTS_GETTEXT
MENUITEM "JavaScript Extension Handler",ID_TESTS_JAVASCRIPT_HANDLER
MENUITEM "JavaScript Binding Handler", ID_TESTS_JAVASCRIPT_BINDING
MENUITEM "JavaScript Extension Handler",ID_TESTS_JAVASCRIPT_EXTENSION
MENUITEM "JavaScript Execute", ID_TESTS_JAVASCRIPT_EXECUTE
MENUITEM "Plugin", ID_TESTS_PLUGIN
MENUITEM "Popup Window", ID_TESTS_POPUP

View File

@ -26,13 +26,14 @@
#define ID_PRINT 32002
#define ID_TESTS_GETSOURCE 32769
#define ID_TESTS_GETTEXT 32770
#define ID_TESTS_JAVASCRIPT_HANDLER 32771
#define ID_TESTS_JAVASCRIPT_EXECUTE 32772
#define ID_TESTS_PLUGIN 32773
#define ID_TESTS_POPUP 32774
#define ID_TESTS_REQUEST 32775
#define ID_TESTS_SCHEME_HANDLER 32776
#define ID_TESTS_UIAPP 32777
#define ID_TESTS_JAVASCRIPT_BINDING 32771
#define ID_TESTS_JAVASCRIPT_EXTENSION 32772
#define ID_TESTS_JAVASCRIPT_EXECUTE 32773
#define ID_TESTS_PLUGIN 32774
#define ID_TESTS_POPUP 32775
#define ID_TESTS_REQUEST 32776
#define ID_TESTS_SCHEME_HANDLER 32777
#define ID_TESTS_UIAPP 32778
#define IDC_STATIC -1
#define IDS_LOGO 1000
#define IDS_UIPLUGIN 1001

View File

@ -175,6 +175,13 @@ public:
return RV_CONTINUE;
}
virtual RetVal HandleJSBinding(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object)
{
return RV_CONTINUE;
}
virtual RetVal HandleBeforeWindowClose(CefRefPtr<CefBrowser> browser)
{
Lock();

View File

@ -215,12 +215,18 @@ public:
class V8TestHandler : public TestHandler
{
public:
V8TestHandler() {}
V8TestHandler(bool bindingTest) { binding_test_ = bindingTest; }
virtual void RunTest()
{
// extension uses a global object
std::string object = "test";
std::string object;
if(binding_test_) {
// binding uses the window object
object = "window.test";
} else {
// extension uses a global object
object = "test";
}
std::stringstream testHtml;
testHtml <<
@ -247,8 +253,75 @@ public:
DestroyTest();
return RV_CONTINUE;
}
virtual RetVal HandleJSBinding(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object)
{
if(binding_test_) {
TestHandleJSBinding(browser, frame, object);
return RV_HANDLED;
}
return RV_CONTINUE;
}
void TestHandleJSBinding(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Value> object)
{
// Create the new V8 object
CefRefPtr<CefV8Value> testObj = CefV8Value::CreateObject(NULL);
ASSERT_TRUE(testObj.get() != NULL);
ASSERT_TRUE(object->SetValue(L"test", testObj));
// Create an instance of V8ExecuteV8Handler
CefRefPtr<CefV8Handler> testHandler(new V8TestV8Handler(true));
ASSERT_TRUE(testHandler.get() != NULL);
// Add the functions
CefRefPtr<CefV8Value> testFunc;
testFunc = CefV8Value::CreateFunction(L"execute", testHandler);
ASSERT_TRUE(testFunc.get() != NULL);
ASSERT_TRUE(testObj->SetValue(L"execute", testFunc));
testFunc = CefV8Value::CreateFunction(L"execute2", testHandler);
ASSERT_TRUE(testFunc.get() != NULL);
ASSERT_TRUE(testObj->SetValue(L"execute2", testFunc));
// Add the values
ASSERT_TRUE(testObj->SetValue(L"intVal",
CefV8Value::CreateInt(12)));
ASSERT_TRUE(testObj->SetValue(L"doubleVal",
CefV8Value::CreateDouble(5.432)));
ASSERT_TRUE(testObj->SetValue(L"boolVal",
CefV8Value::CreateBool(true)));
ASSERT_TRUE(testObj->SetValue(L"stringVal",
CefV8Value::CreateString(L"the string")));
CefRefPtr<CefV8Value> testArray(CefV8Value::CreateArray());
ASSERT_TRUE(testArray.get() != NULL);
ASSERT_TRUE(testObj->SetValue(L"arrayVal", testArray));
ASSERT_TRUE(testArray->SetValue(0, CefV8Value::CreateInt(4)));
ASSERT_TRUE(testArray->SetValue(1, CefV8Value::CreateDouble(120.43)));
ASSERT_TRUE(testArray->SetValue(2, CefV8Value::CreateBool(true)));
ASSERT_TRUE(testArray->SetValue(3, CefV8Value::CreateString(L"a string")));
}
bool binding_test_;
};
// Verify window binding
TEST(V8Test, Binding)
{
g_V8TestV8HandlerExecuteCalled = false;
g_V8TestV8HandlerExecute2Called = false;
CefRefPtr<V8TestHandler> handler = new V8TestHandler(true);
handler->ExecuteTest();
ASSERT_TRUE(g_V8TestV8HandlerExecuteCalled);
ASSERT_TRUE(g_V8TestV8HandlerExecute2Called);
}
// Verify extensions
TEST(V8Test, Extension)
{
@ -271,7 +344,7 @@ TEST(V8Test, Extension)
L"})();";
CefRegisterExtension(L"v8/test", extensionCode, new V8TestV8Handler(false));
CefRefPtr<V8TestHandler> handler = new V8TestHandler();
CefRefPtr<V8TestHandler> handler = new V8TestHandler(false);
handler->ExecuteTest();
ASSERT_TRUE(g_V8TestV8HandlerExecuteCalled);