Support custom V8 bindings on WebWorker threads (issue #451).

- Add CefRenderProcessHandler callbacks for WebWorker context creation, release and uncaught exceptions.
- Add CefTaskRunner class that supports posting of tasks to standard threads and WebWorker threads.
- Organize V8 internal state information on a per-thread/per-Isolate basis.
- Add webkit_451.patch that provides the WebKit implementation.

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@972 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt
2013-01-03 17:24:24 +00:00
parent f30fbb3d48
commit afe3cb1d67
44 changed files with 2434 additions and 406 deletions

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// 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.
@@ -215,7 +215,6 @@ void ClientApp::OnContextInitialized() {
ASSERT(manager.get());
manager->SetSupportedSchemes(cookieable_schemes_);
// Execute delegate callbacks.
BrowserDelegateSet::iterator it = browser_delegates_.begin();
for (; it != browser_delegates_.end(); ++it)
(*it)->OnContextInitialized(this);
@@ -223,7 +222,6 @@ void ClientApp::OnContextInitialized() {
void ClientApp::OnBeforeChildProcessLaunch(
CefRefPtr<CefCommandLine> command_line) {
// Execute delegate callbacks.
BrowserDelegateSet::iterator it = browser_delegates_.begin();
for (; it != browser_delegates_.end(); ++it)
(*it)->OnBeforeChildProcessLaunch(this, command_line);
@@ -231,7 +229,6 @@ void ClientApp::OnBeforeChildProcessLaunch(
void ClientApp::OnRenderProcessThreadCreated(
CefRefPtr<CefListValue> extra_info) {
// Execute delegate callbacks.
BrowserDelegateSet::iterator it = browser_delegates_.begin();
for (; it != browser_delegates_.end(); ++it)
(*it)->OnRenderProcessThreadCreated(this, extra_info);
@@ -245,7 +242,6 @@ void ClientApp::GetProxyForUrl(const CefString& url,
}
void ClientApp::OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) {
// Execute delegate callbacks.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnRenderThreadCreated(this, extra_info);
@@ -274,21 +270,18 @@ void ClientApp::OnWebKitInitialized() {
CefRegisterExtension("v8/app", app_code,
new ClientAppExtensionHandler(this));
// Execute delegate callbacks.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnWebKitInitialized(this);
}
void ClientApp::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
// Execute delegate callbacks.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnBrowserCreated(this, browser);
}
void ClientApp::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) {
// Execute delegate callbacks.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnBrowserDestroyed(this, browser);
@@ -299,7 +292,6 @@ bool ClientApp::OnBeforeNavigation(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefRequest> request,
NavigationType navigation_type,
bool is_redirect) {
// Execute delegate callbacks.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it) {
if ((*it)->OnBeforeNavigation(this, browser, frame, request,
@@ -312,18 +304,16 @@ bool ClientApp::OnBeforeNavigation(CefRefPtr<CefBrowser> browser,
}
void ClientApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Execute delegate callbacks.
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnContextCreated(this, browser, frame, context);
}
void ClientApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Execute delegate callbacks.
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnContextReleased(this, browser, frame, context);
@@ -342,11 +332,10 @@ void ClientApp::OnContextReleased(CefRefPtr<CefBrowser> browser,
}
void ClientApp::OnUncaughtException(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {
// Execute delegate callbacks.
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it) {
(*it)->OnUncaughtException(this, browser, frame, context, exception,
@@ -354,10 +343,38 @@ void ClientApp::OnUncaughtException(CefRefPtr<CefBrowser> browser,
}
}
void ClientApp::OnWorkerContextCreated(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) {
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnWorkerContextCreated(this, worker_id, url, context);
}
void ClientApp::OnWorkerContextReleased(int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) {
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnWorkerContextReleased(this, worker_id, url, context);
}
void ClientApp::OnWorkerUncaughtException(
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it) {
(*it)->OnWorkerUncaughtException(this, worker_id, url, context, exception,
stackTrace);
}
}
void ClientApp::OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) {
// Execute delegate callbacks.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it)
(*it)->OnFocusedNodeChanged(this, browser, frame, node);
@@ -371,7 +388,6 @@ bool ClientApp::OnProcessMessageReceived(
bool handled = false;
// Execute delegate callbacks.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end() && !handled; ++it) {
handled = (*it)->OnProcessMessageReceived(this, browser, source_process,

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// 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.
@@ -20,62 +20,38 @@ class ClientApp : public CefApp,
public:
// Interface for browser delegates. All BrowserDelegates must be returned via
// CreateBrowserDelegates. Do not perform work in the BrowserDelegate
// constructor.
// constructor. See CefBrowserProcessHandler for documentation.
class BrowserDelegate : public virtual CefBase {
public:
// Called on the browser process UI thread immediately after the CEF context
// has been initialized.
virtual void OnContextInitialized(CefRefPtr<ClientApp> app) {
}
virtual void OnContextInitialized(CefRefPtr<ClientApp> app) {}
// Called on the browser process IO thread before a child process is
// launched. Provides an opportunity to modify the child process command
// line. Do not keep a reference to |command_line| outside of this method.
virtual void OnBeforeChildProcessLaunch(
CefRefPtr<ClientApp> app,
CefRefPtr<CefCommandLine> command_line) {
}
CefRefPtr<CefCommandLine> command_line) {}
// Called on the browser process IO thread after the main thread has been
// created for a new render process. Provides an opportunity to specify
// extra information that will be passed to
// CefRenderProcessHandler::OnRenderThreadCreated() in the render process.
// Do not keep a reference to |extra_info| outside of this method.
virtual void OnRenderProcessThreadCreated(
CefRefPtr<ClientApp> app,
CefRefPtr<CefListValue> extra_info) {
}
CefRefPtr<CefListValue> extra_info) {}
};
typedef std::set<CefRefPtr<BrowserDelegate> > BrowserDelegateSet;
// Interface for renderer delegates. All RenderDelegates must be returned via
// CreateRenderDelegates. Do not perform work in the RenderDelegate
// constructor.
// constructor. See CefRenderProcessHandler for documentation.
class RenderDelegate : public virtual CefBase {
public:
// Called after the render process main thread has been created.
virtual void OnRenderThreadCreated(CefRefPtr<ClientApp> app,
CefRefPtr<CefListValue> extra_info) {
}
CefRefPtr<CefListValue> extra_info) {}
// Called when WebKit is initialized. Used to register V8 extensions.
virtual void OnWebKitInitialized(CefRefPtr<ClientApp> app) {
}
virtual void OnWebKitInitialized(CefRefPtr<ClientApp> app) {}
// Called after a browser has been created.
virtual void OnBrowserCreated(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser) {
}
CefRefPtr<CefBrowser> browser) {}
// Called before a browser is destroyed.
virtual void OnBrowserDestroyed(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser) {
}
CefRefPtr<CefBrowser> browser) {}
// Called before browser navigation. Return true to cancel the navigation or
// false to allow the navigation to proceed. The |request| object cannot be
// modified in this callback.
virtual bool OnBeforeNavigation(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
@@ -85,40 +61,45 @@ class ClientApp : public CefApp,
return false;
}
// Called when a V8 context is created. Used to create V8 window bindings
// and set message callbacks. RenderDelegates should check for unique URLs
// to avoid interfering with each other.
virtual void OnContextCreated(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
}
CefRefPtr<CefV8Context> context) {}
// Called when a V8 context is released. Used to clean up V8 window
// bindings. RenderDelegates should check for unique URLs to avoid
// interfering with each other.
virtual void OnContextReleased(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
}
CefRefPtr<CefV8Context> context) {}
// Global V8 exception handler, disabled by default, to enable set
// CefSettings.uncaught_exception_stack_size > 0.
virtual void OnUncaughtException(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {
}
CefRefPtr<CefV8StackTrace> stackTrace) {}
virtual void OnWorkerContextCreated(CefRefPtr<ClientApp> app,
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) {}
virtual void OnWorkerContextReleased(CefRefPtr<ClientApp> app,
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) {}
virtual void OnWorkerUncaughtException(
CefRefPtr<ClientApp> app,
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {}
// Called when the focused node in a frame has changed.
virtual void OnFocusedNodeChanged(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) {
}
CefRefPtr<CefDOMNode> node) {}
// Called when a process message is received. Return true if the message was
// handled and should not be passed on to other handlers. RenderDelegates
@@ -218,6 +199,21 @@ class ClientApp : public CefApp,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace)
OVERRIDE;
virtual void OnWorkerContextCreated(
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) OVERRIDE;
virtual void OnWorkerContextReleased(
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) OVERRIDE;
virtual void OnWorkerUncaughtException(
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace)
OVERRIDE;
virtual void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) OVERRIDE;

View File

@@ -0,0 +1,202 @@
// 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.
#include "include/cef_runnable.h"
#include "include/cef_task.h"
#include "tests/unittests/test_handler.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
void GetForCurrentThread(bool* ran_test) {
// Currently on the UI thread.
CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForCurrentThread();
EXPECT_TRUE(runner.get());
EXPECT_TRUE(runner->BelongsToCurrentThread());
EXPECT_TRUE(runner->BelongsToThread(TID_UI));
EXPECT_FALSE(runner->BelongsToThread(TID_IO));
EXPECT_TRUE(runner->IsSame(runner));
CefRefPtr<CefTaskRunner> runner2 = CefTaskRunner::GetForCurrentThread();
EXPECT_TRUE(runner2.get());
EXPECT_TRUE(runner->IsSame(runner2));
EXPECT_TRUE(runner2->IsSame(runner));
// Not on the IO thread.
CefRefPtr<CefTaskRunner> runner3 = CefTaskRunner::GetForThread(TID_IO);
EXPECT_TRUE(runner3.get());
EXPECT_FALSE(runner->IsSame(runner3));
EXPECT_FALSE(runner3->IsSame(runner));
*ran_test = true;
}
void GetForThread(bool* ran_test) {
// Currently on the UI thread.
CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForThread(TID_IO);
EXPECT_TRUE(runner.get());
EXPECT_FALSE(runner->BelongsToCurrentThread());
EXPECT_TRUE(runner->BelongsToThread(TID_IO));
EXPECT_FALSE(runner->BelongsToThread(TID_UI));
EXPECT_TRUE(runner->IsSame(runner));
CefRefPtr<CefTaskRunner> runner2 = CefTaskRunner::GetForThread(TID_IO);
EXPECT_TRUE(runner2.get());
EXPECT_TRUE(runner->IsSame(runner2));
EXPECT_TRUE(runner2->IsSame(runner));
CefRefPtr<CefTaskRunner> runner3 = CefTaskRunner::GetForThread(TID_UI);
EXPECT_TRUE(runner3.get());
EXPECT_FALSE(runner->IsSame(runner3));
EXPECT_FALSE(runner3->IsSame(runner));
*ran_test = true;
}
void PostTaskEvent1(bool* got_it, CefRefPtr<CefTaskRunner> runner) {
// Currently on the IO thread.
EXPECT_TRUE(runner->BelongsToCurrentThread());
EXPECT_TRUE(runner->BelongsToThread(TID_IO));
EXPECT_FALSE(runner->BelongsToThread(TID_UI));
// Current thread should be the IO thread.
CefRefPtr<CefTaskRunner> runner2 = CefTaskRunner::GetForCurrentThread();
EXPECT_TRUE(runner2.get());
EXPECT_TRUE(runner2->BelongsToCurrentThread());
EXPECT_TRUE(runner2->BelongsToThread(TID_IO));
EXPECT_FALSE(runner2->BelongsToThread(TID_UI));
EXPECT_TRUE(runner->IsSame(runner2));
EXPECT_TRUE(runner2->IsSame(runner));
// Current thread should be the IO thread.
CefRefPtr<CefTaskRunner> runner3 = CefTaskRunner::GetForThread(TID_IO);
EXPECT_TRUE(runner3.get());
EXPECT_TRUE(runner3->BelongsToCurrentThread());
EXPECT_TRUE(runner3->BelongsToThread(TID_IO));
EXPECT_FALSE(runner3->BelongsToThread(TID_UI));
EXPECT_TRUE(runner->IsSame(runner3));
EXPECT_TRUE(runner3->IsSame(runner));
// Current thread should not be the UI thread.
CefRefPtr<CefTaskRunner> runner4 = CefTaskRunner::GetForThread(TID_UI);
EXPECT_TRUE(runner4.get());
EXPECT_FALSE(runner4->BelongsToCurrentThread());
EXPECT_FALSE(runner4->BelongsToThread(TID_IO));
EXPECT_TRUE(runner4->BelongsToThread(TID_UI));
EXPECT_FALSE(runner->IsSame(runner4));
EXPECT_FALSE(runner4->IsSame(runner));
*got_it = true;
}
void PostTask1(bool* ran_test) {
// Currently on the UI thread.
CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForThread(TID_IO);
EXPECT_TRUE(runner.get());
EXPECT_FALSE(runner->BelongsToCurrentThread());
EXPECT_TRUE(runner->BelongsToThread(TID_IO));
bool got_it = false;
runner->PostTask(NewCefRunnableFunction(&PostTaskEvent1, &got_it, runner));
WaitForThread(runner);
EXPECT_TRUE(got_it);
*ran_test = true;
}
void PostDelayedTask1(bool* ran_test) {
// Currently on the UI thread.
CefRefPtr<CefTaskRunner> runner = CefTaskRunner::GetForThread(TID_IO);
EXPECT_TRUE(runner.get());
EXPECT_FALSE(runner->BelongsToCurrentThread());
EXPECT_TRUE(runner->BelongsToThread(TID_IO));
bool got_it = false;
runner->PostDelayedTask(
NewCefRunnableFunction(&PostTaskEvent1, &got_it, runner), 0);
WaitForThread(runner);
EXPECT_TRUE(got_it);
*ran_test = true;
}
void PostTaskEvent2(bool* got_it) {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
EXPECT_FALSE(CefCurrentlyOn(TID_UI));
*got_it = true;
}
void PostTask2(bool* ran_test) {
// Currently on the UI thread.
EXPECT_FALSE(CefCurrentlyOn(TID_IO));
bool got_it = false;
CefPostTask(TID_IO, NewCefRunnableFunction(&PostTaskEvent2, &got_it));
WaitForThread(TID_IO);
EXPECT_TRUE(got_it);
*ran_test = true;
}
void PostDelayedTask2(bool* ran_test) {
// Currently on the UI thread.
EXPECT_FALSE(CefCurrentlyOn(TID_IO));
bool got_it = false;
CefPostDelayedTask(TID_IO,
NewCefRunnableFunction(&PostTaskEvent2, &got_it), 0);
WaitForThread(TID_IO);
EXPECT_TRUE(got_it);
*ran_test = true;
}
} // namespace
TEST(TaskTest, GetForCurrentThread) {
bool ran_test = false;
CefPostTask(TID_UI, NewCefRunnableFunction(&GetForCurrentThread, &ran_test));
WaitForThread(TID_UI);
EXPECT_TRUE(ran_test);
}
TEST(TaskTest, GetForThread) {
bool ran_test = false;
CefPostTask(TID_UI, NewCefRunnableFunction(&GetForThread, &ran_test));
WaitForThread(TID_UI);
EXPECT_TRUE(ran_test);
}
TEST(TaskTest, PostTask1) {
bool ran_test = false;
CefPostTask(TID_UI, NewCefRunnableFunction(&PostTask1, &ran_test));
WaitForThread(TID_UI);
EXPECT_TRUE(ran_test);
}
TEST(TaskTest, PostDelayedTask1) {
bool ran_test = false;
CefPostTask(TID_UI, NewCefRunnableFunction(&PostDelayedTask2, &ran_test));
WaitForThread(TID_UI);
EXPECT_TRUE(ran_test);
}
TEST(TaskTest, PostTask2) {
bool ran_test = false;
CefPostTask(TID_UI, NewCefRunnableFunction(&PostTask2, &ran_test));
WaitForThread(TID_UI);
EXPECT_TRUE(ran_test);
}
TEST(TaskTest, PostDelayedTask2) {
bool ran_test = false;
CefPostTask(TID_UI, NewCefRunnableFunction(&PostDelayedTask2, &ran_test));
WaitForThread(TID_UI);
EXPECT_TRUE(ran_test);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// 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.
@@ -130,6 +130,12 @@ void WaitForThread(CefThreadId thread_id) {
event.Wait();
}
void WaitForThread(CefRefPtr<CefTaskRunner> task_runner) {
base::WaitableEvent event(true, false);
task_runner->PostTask(NewCefRunnableFunction(&NotifyEvent, &event));
event.Wait();
}
bool TestFailed() {
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::GetGlobalCommandLine();

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
// 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.
@@ -123,6 +123,7 @@ class TestHandler : public CefClient,
// Post a task to the specified thread and wait for the task to execute as
// indication that all previously pending tasks on that thread have completed.
void WaitForThread(CefThreadId thread_id);
void WaitForThread(CefRefPtr<CefTaskRunner> task_runner);
#define WaitForIOThread() WaitForThread(TID_IO)
#define WaitForUIThread() WaitForThread(TID_UI)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// 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.
@@ -69,7 +69,7 @@ class BeginTracingTask : public CefTask {
: client_(client) {
}
virtual void Execute(CefThreadId threadId) OVERRIDE {
virtual void Execute() OVERRIDE {
EXPECT_TRUE(CefBeginTracing(client_, kTraceTestCategory));
}
@@ -86,7 +86,7 @@ class EndTracingTask : public CefTask {
public:
EndTracingTask() {}
virtual void Execute(CefThreadId threadId) OVERRIDE {
virtual void Execute() OVERRIDE {
EXPECT_TRUE(CefEndTracingAsync());
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// 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.
@@ -27,6 +27,8 @@ const char kV8ContextChildTestUrl[] = "http://tests/V8Test.ContextChildTest";
const char kV8NavTestUrl[] = "http://tests/V8Test.NavTest";
const char kV8OnUncaughtExceptionTestUrl[] =
"http://tests/V8Test.OnUncaughtException";
const char kV8WorkerParentTestUrl[] = "http://tests/V8Test.WorkerParent";
const char kV8WorkerTestUrl[] = "http://tests/V8Test.Worker.js";
const char kV8TestMsg[] = "V8Test.Test";
const char kV8TestCmdArg[] = "v8-test";
const char kV8DevToolsURLMsg[] = "V8Test.DevToolsURL";
@@ -68,9 +70,12 @@ enum V8TestMode {
V8TEST_CONTEXT_INVALID,
V8TEST_BINDING,
V8TEST_STACK_TRACE,
V8TEST_EXTENSION,
V8TEST_ON_UNCAUGHT_EXCEPTION,
V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS,
V8TEST_EXTENSION,
V8TEST_WORKER_BINDING,
V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION,
V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS,
};
// Set to the current test being run in the browser process. Will always be
@@ -214,11 +219,16 @@ class V8RendererTest : public ClientApp::RenderDelegate {
RunStackTraceTest();
break;
case V8TEST_ON_UNCAUGHT_EXCEPTION:
case V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION:
RunOnUncaughtExceptionTest();
break;
case V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS:
case V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS:
RunOnUncaughtExceptionDevToolsTest();
break;
case V8TEST_WORKER_BINDING:
// The test is triggered in OnWorkerContextCreated().
break;
default:
// Was a startup test.
EXPECT_TRUE(startup_test_success_);
@@ -1588,32 +1598,6 @@ class V8RendererTest : public ClientApp::RenderDelegate {
"window.open('" + devtools_url_ + "');", "about:blank", 0);
}
void OnUncaughtException(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION ||
test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
EXPECT_TRUE(test_context_->IsSame(context));
EXPECT_STREQ("Uncaught ReferenceError: asd is not defined",
exception->GetMessage().ToString().c_str());
std::ostringstream stackFormatted;
for (int i = 0; i < stackTrace->GetFrameCount(); ++i) {
stackFormatted << "at "
<< stackTrace->GetFrame(i)->GetFunctionName().ToString()
<< "() in " << stackTrace->GetFrame(i)->GetScriptName().ToString()
<< " on line " << stackTrace->GetFrame(i)->GetLineNumber() << "\n";
}
const char* stackFormattedShouldBe =
"at test2() in http://tests/V8Test.OnUncaughtException on line 3\n"
"at test() in http://tests/V8Test.OnUncaughtException on line 2\n";
EXPECT_STREQ(stackFormattedShouldBe, stackFormatted.str().c_str());
DestroyTest();
}
}
// Test execution of a native function when the extension is loaded.
void RunExtensionTest() {
std::string code = "native function v8_extension_test();"
@@ -1665,7 +1649,8 @@ class V8RendererTest : public ClientApp::RenderDelegate {
if (test_mode_ == V8TEST_NONE)
return;
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS ||
test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (browser_.get() == NULL) {
app_ = app;
browser_ = browser;
@@ -1758,12 +1743,217 @@ class V8RendererTest : public ClientApp::RenderDelegate {
}
}
void OnUncaughtException(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE {
if (test_mode_ == V8TEST_NONE)
return;
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION ||
test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
EXPECT_TRUE(test_context_->IsSame(context));
EXPECT_STREQ("Uncaught ReferenceError: asd is not defined",
exception->GetMessage().ToString().c_str());
std::ostringstream stackFormatted;
for (int i = 0; i < stackTrace->GetFrameCount(); ++i) {
stackFormatted << "at "
<< stackTrace->GetFrame(i)->GetFunctionName().ToString()
<< "() in " << stackTrace->GetFrame(i)->GetScriptName().ToString()
<< " on line " << stackTrace->GetFrame(i)->GetLineNumber() << "\n";
}
const char* stackFormattedShouldBe =
"at test2() in http://tests/V8Test.OnUncaughtException on line 3\n"
"at test() in http://tests/V8Test.OnUncaughtException on line 2\n";
EXPECT_STREQ(stackFormattedShouldBe, stackFormatted.str().c_str());
DestroyTest();
}
}
virtual void OnWorkerContextCreated(
CefRefPtr<ClientApp> app,
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) OVERRIDE {
if (test_mode_ == V8TEST_NONE)
return;
EXPECT_STREQ(kV8WorkerTestUrl, url.ToString().c_str());
EXPECT_TRUE(context.get());
EXPECT_TRUE(context->IsValid());
test_context_worker_ = context;
CefRefPtr<CefTaskRunner> task_runner = context->GetTaskRunner();
EXPECT_TRUE(task_runner.get());
EXPECT_TRUE(task_runner->BelongsToCurrentThread());
test_task_runner_worker_ = task_runner;
EXPECT_FALSE(context->GetBrowser().get());
EXPECT_FALSE(context->GetFrame().get());
if (test_mode_ == V8TEST_WORKER_BINDING) {
static const char test_func[] = "run_async";
class Handler : public CefV8Handler {
public:
Handler() {}
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE {
EXPECT_STREQ(test_func, name.ToString().c_str());
EXPECT_EQ((size_t)1, arguments.size());
callback_ = arguments[0];
EXPECT_TRUE(callback_->IsFunction());
EXPECT_TRUE(context_->IsValid());
CefRefPtr<CefV8Context> current_context =
CefV8Context::GetCurrentContext();
EXPECT_TRUE(current_context->IsSame(context_));
CefRefPtr<CefTaskRunner> task_runner =
current_context->GetTaskRunner();
EXPECT_TRUE(task_runner.get());
EXPECT_TRUE(task_runner->BelongsToCurrentThread());
EXPECT_TRUE(task_runner->IsSame(task_runner_));
// Execute the callback asynchronously.
task_runner->PostTask(
NewCefRunnableMethod(this, &Handler::ExecCallback));
retval = CefV8Value::CreateBool(true);
return true;
}
void ExecCallback() {
EXPECT_TRUE(callback_->IsValid());
EXPECT_TRUE(context_->IsValid());
CefRefPtr<CefTaskRunner> task_runner = context_->GetTaskRunner();
EXPECT_TRUE(task_runner.get());
EXPECT_TRUE(task_runner->BelongsToCurrentThread());
EXPECT_TRUE(task_runner->IsSame(task_runner_));
CefRefPtr<CefV8Value> retval =
callback_->ExecuteFunctionWithContext(context_, NULL,
CefV8ValueList());
EXPECT_TRUE(retval.get());
EXPECT_TRUE(retval->IsValid());
EXPECT_TRUE(retval->IsBool());
EXPECT_EQ(true, retval->GetBoolValue());
callback_ = NULL;
context_ = NULL;
task_runner_ = NULL;
}
CefRefPtr<CefV8Value> callback_;
CefRefPtr<CefV8Context> context_;
CefRefPtr<CefTaskRunner> task_runner_;
IMPLEMENT_REFCOUNTING(Handler);
};
Handler* handler = new Handler();
CefRefPtr<CefV8Handler> handlerPtr(handler);
handler->context_ = context;
handler->task_runner_ = task_runner;
CefRefPtr<CefV8Value> global = context->GetGlobal();
EXPECT_TRUE(global.get());
global->SetValue(test_func,
CefV8Value::CreateFunction(test_func, handlerPtr),
V8_PROPERTY_ATTRIBUTE_NONE);
}
}
virtual void OnWorkerContextReleased(
CefRefPtr<ClientApp> app,
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context) OVERRIDE {
if (test_mode_ == V8TEST_NONE)
return;
EXPECT_STREQ(kV8WorkerTestUrl, url.ToString().c_str());
EXPECT_TRUE(context.get());
EXPECT_TRUE(context->IsValid());
CefRefPtr<CefTaskRunner> task_runner = context->GetTaskRunner();
EXPECT_TRUE(task_runner.get());
EXPECT_TRUE(task_runner->BelongsToCurrentThread());
EXPECT_TRUE(task_runner->IsSame(test_task_runner_worker_));
EXPECT_TRUE(context->IsSame(test_context_worker_));
test_context_worker_ = NULL;
test_task_runner_worker_ = NULL;
if (test_mode_ == V8TEST_WORKER_BINDING) {
// Destroy the test on the renderer thread.
CefPostTask(TID_RENDERER,
NewCefRunnableMethod(this, &V8RendererTest::DestroyTest));
}
}
void OnWorkerUncaughtException(
CefRefPtr<ClientApp> app,
int worker_id,
const CefString& url,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE {
if (test_mode_ == V8TEST_NONE)
return;
EXPECT_TRUE(context.get());
EXPECT_TRUE(context->IsValid());
CefRefPtr<CefTaskRunner> task_runner = context->GetTaskRunner();
EXPECT_TRUE(task_runner.get());
EXPECT_TRUE(task_runner->BelongsToCurrentThread());
EXPECT_TRUE(task_runner->IsSame(test_task_runner_worker_));
EXPECT_TRUE(context->IsSame(test_context_worker_));
if (test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION ||
test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
EXPECT_STREQ("Uncaught ReferenceError: asd is not defined",
exception->GetMessage().ToString().c_str());
std::ostringstream stackFormatted;
for (int i = 0; i < stackTrace->GetFrameCount(); ++i) {
stackFormatted << "at "
<< stackTrace->GetFrame(i)->GetFunctionName().ToString()
<< "() in " << stackTrace->GetFrame(i)->GetScriptName().ToString()
<< " on line " << stackTrace->GetFrame(i)->GetLineNumber() << "\n";
}
const char* stackFormattedShouldBe =
"at test2() in http://tests/V8Test.Worker.js on line 2\n"
"at test() in http://tests/V8Test.Worker.js on line 1\n";
EXPECT_STREQ(stackFormattedShouldBe, stackFormatted.str().c_str());
// Destroy the test on the renderer thread.
CefPostTask(TID_RENDERER,
NewCefRunnableMethod(this, &V8RendererTest::DestroyTest));
}
}
virtual void OnBrowserDestroyed(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser) OVERRIDE {
if (test_mode_ == V8TEST_NONE)
return;
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS ||
test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (browser->IsPopup()) {
// After window destruction there is still a call to
// ScriptController::setCaptureCallStackForUncaughtExceptions(0),
@@ -1782,7 +1972,8 @@ class V8RendererTest : public ClientApp::RenderDelegate {
if (test_mode_ == V8TEST_NONE)
return false;
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS ||
test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
EXPECT_TRUE(browser.get());
EXPECT_EQ(PID_BROWSER, source_process);
EXPECT_TRUE(message.get());
@@ -1883,6 +2074,8 @@ class V8RendererTest : public ClientApp::RenderDelegate {
protected:
// Return from the test.
void DestroyTest() {
EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER));
if (test_mode_ == V8TEST_CONTEXT_INVALID) {
// Verify that objects related to a particular context are not valid after
// OnContextReleased is called for that context.
@@ -1921,6 +2114,9 @@ class V8RendererTest : public ClientApp::RenderDelegate {
CefRefPtr<CefV8Context> test_context_;
CefRefPtr<CefV8Value> test_object_;
CefRefPtr<CefV8Context> test_context_worker_;
CefRefPtr<CefTaskRunner> test_task_runner_worker_;
// Used by startup tests to indicate success.
TrackCallback startup_test_success_;
@@ -1957,6 +2153,28 @@ class V8TestHandler : public TestHandler {
"</body></html>\n",
"text/html");
CreateBrowser(kV8OnUncaughtExceptionTestUrl);
} else if (test_mode_ == V8TEST_WORKER_BINDING) {
AddResource(kV8WorkerParentTestUrl, "<html><body>"
"<script>var worker = new Worker('V8Test.Worker.js');</script>"
"</body></html>", "text/html");
AddResource(kV8WorkerTestUrl,
"function cb() { self.close(); return true; }\n"
"self.run_async(cb);",
"application/javascript");
CreateBrowser(kV8WorkerParentTestUrl);
} else if (test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION ||
test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
AddResource(kV8WorkerParentTestUrl, "<html><body>"
"<script>var worker = new Worker('V8Test.Worker.js');\n"
"function test() { worker.postMessage('test'); }\n"
"</script>"
"</body></html>", "text/html");
AddResource(kV8WorkerTestUrl,
"function test(){ test2(); }\n"
"function test2(){ asd(); }\n"
"self.onmessage = function(event) { self.setTimeout(test, 0); }",
"application/javascript");
CreateBrowser(kV8WorkerParentTestUrl);
} else {
EXPECT_TRUE(test_url_ != NULL);
AddResource(test_url_, "<html><body>"
@@ -1968,7 +2186,8 @@ class V8TestHandler : public TestHandler {
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) OVERRIDE {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS ||
test_mode_ == V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (browser->IsPopup()) {
EXPECT_STREQ(
GetBrowser()->GetHost()->GetDevToolsURL(true).ToString().c_str(),
@@ -2083,3 +2302,7 @@ V8_TEST(StackTrace, V8TEST_STACK_TRACE);
V8_TEST(OnUncaughtException, V8TEST_ON_UNCAUGHT_EXCEPTION);
V8_TEST(OnUncaughtExceptionDevTools, V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS);
V8_TEST(Extension, V8TEST_EXTENSION);
V8_TEST(WorkerBinding, V8TEST_WORKER_BINDING);
V8_TEST(WorkerOnUncaughtException, V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION);
V8_TEST(WorkerOnUncaughtExceptionDevTools,
V8TEST_WORKER_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS);

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// 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.
@@ -88,7 +88,7 @@ class BinaryTask : public CefTask {
data_(data),
data_size_(data_size) {}
virtual void Execute(CefThreadId threadId) OVERRIDE {
virtual void Execute() OVERRIDE {
TestBinary(value_, data_, data_size_);
}
@@ -295,7 +295,7 @@ class DictionaryTask : public CefTask {
binary_data_(binary_data),
binary_data_size_(binary_data_size) {}
virtual void Execute(CefThreadId threadId) OVERRIDE {
virtual void Execute() OVERRIDE {
TestDictionary(value_, binary_data_, binary_data_size_);
}
@@ -554,7 +554,7 @@ class ListTask : public CefTask {
binary_data_(binary_data),
binary_data_size_(binary_data_size) {}
virtual void Execute(CefThreadId threadId) OVERRIDE {
virtual void Execute() OVERRIDE {
TestList(value_, binary_data_, binary_data_size_);
}