Add CefV8ContextHandler::OnUncaughtException callback (issue #736).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@857 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2012-10-12 16:38:24 +00:00
parent cd03d80623
commit f51f171b43
19 changed files with 281 additions and 3 deletions

View File

@ -71,6 +71,17 @@ typedef struct _cef_v8context_handler_t {
void (CEF_CALLBACK *on_context_released)(
struct _cef_v8context_handler_t* self, struct _cef_browser_t* browser,
struct _cef_frame_t* frame, struct _cef_v8context_t* context);
///
// Called for global uncaught exceptions. Execution of this callback is
// disabled by default. To enable set
// CefSettings.uncaught_exception_stack_size > 0.
///
void (CEF_CALLBACK *on_uncaught_exception)(
struct _cef_v8context_handler_t* self, struct _cef_browser_t* browser,
struct _cef_frame_t* frame, struct _cef_v8context_t* context,
struct _cef_v8exception_t* exception,
struct _cef_v8stack_trace_t* stackTrace);
} cef_v8context_handler_t;

View File

@ -68,6 +68,18 @@ class CefV8ContextHandler : public virtual CefBase {
virtual void OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {}
///
// Called for global uncaught exceptions. Execution of this callback is
// disabled by default. To enable set
// CefSettings.uncaught_exception_stack_size > 0.
///
/*--cef()--*/
virtual void OnUncaughtException(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {}
};
#endif // CEF_INCLUDE_CEF_V8CONTEXT_HANDLER_H_

View File

@ -210,6 +210,14 @@ typedef struct _cef_settings_t {
// is disabled.
///
bool pack_loading_disabled;
///
// The number of stack trace frames to capture for uncaught exceptions.
// Specify a positive value to enable the CefV8ContextHandler::
// OnUncaughtException() callback. Specify 0 (default value) and
// OnUncaughtException() will not be called.
///
int uncaught_exception_stack_size;
} cef_settings_t;
///

View File

@ -299,6 +299,7 @@ struct CefSettingsTraits {
cef_string_set(src->locales_dir_path.str, src->locales_dir_path.length,
&target->locales_dir_path, copy);
target->pack_loading_disabled = src->pack_loading_disabled;
target->uncaught_exception_stack_size = src->uncaught_exception_stack_size;
}
};

View File

@ -1621,6 +1621,7 @@ net::URLRequestContext* CefBrowserImpl::request_context_proxy() {
void CefBrowserImpl::UIT_CreateDevToolsClient(BrowserDevToolsAgent *agent) {
dev_tools_client_.reset(new BrowserDevToolsClient(this, agent));
_Context->UIT_DevToolsClientCreated();
}
void CefBrowserImpl::UIT_DestroyDevToolsClient() {
@ -1628,6 +1629,7 @@ void CefBrowserImpl::UIT_DestroyDevToolsClient() {
// Free the client. This will cause the client to clear pending messages
// and detach from the agent.
dev_tools_client_.reset();
_Context->UIT_DevToolsClientDestroyed();
}
}

View File

@ -6,6 +6,7 @@
#include "libcef/browser_devtools_scheme_handler.h"
#include "libcef/browser_impl.h"
#include "libcef/browser_webkit_glue.h"
#include "libcef/v8_impl.h"
#include "base/bind.h"
#include "base/file_util.h"
@ -226,7 +227,8 @@ CefContext::CefContext()
shutting_down_(false),
request_context_(NULL),
next_browser_id_(kNextBrowserIdReset),
current_webviewhost_(NULL) {
current_webviewhost_(NULL),
dev_tools_client_count_(0) {
}
CefContext::~CefContext() {
@ -601,3 +603,20 @@ void CefContext::UIT_FinishShutdown(
browser_shutdown_event->Signal();
}
}
void CefContext::UIT_DevToolsClientCreated() {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
++dev_tools_client_count_;
}
void CefContext::UIT_DevToolsClientDestroyed() {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
--dev_tools_client_count_;
if (dev_tools_client_count_ == 0) {
if (settings_.uncaught_exception_stack_size > 0) {
v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
settings_.uncaught_exception_stack_size,
v8::StackTrace::kDetailed);
}
}
}

View File

@ -96,6 +96,11 @@ class CefContext : public CefBase {
static bool ImplementsThreadSafeReferenceCounting() { return true; }
// Keep count of DevTools windows, need to re-enable stack trace
// functionality for uncaught exceptions.
void UIT_DevToolsClientCreated();
void UIT_DevToolsClientDestroyed();
private:
// Performs shutdown actions that need to occur on the UI thread before any
// threads are destroyed.
@ -132,6 +137,8 @@ class CefContext : public CefBase {
scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
int dev_tools_client_count_;
IMPLEMENT_REFCOUNTING(CefContext);
IMPLEMENT_LOCKING(CefContext);
};

View File

@ -11,6 +11,7 @@
#include "libcef/browser_webkit_glue.h"
#include "libcef/browser_webkit_init.h"
#include "libcef/cef_context.h"
#include "libcef/v8_impl.h"
#include "base/bind.h"
#include "base/command_line.h"
@ -127,6 +128,13 @@ void CefProcessUIThread::Init() {
webkit_glue::SetJavaScriptFlags(CefString(&settings.javascript_flags));
}
if (settings.uncaught_exception_stack_size > 0) {
v8::V8::AddMessageListener(&CefV8MessageHandler);
v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
settings.uncaught_exception_stack_size,
v8::StackTrace::kDetailed);
}
#if defined(OS_WIN)
if (settings.graphics_implementation == ANGLE_IN_PROCESS ||
settings.graphics_implementation == ANGLE_IN_PROCESS_COMMAND_BUFFER) {

View File

@ -1460,3 +1460,28 @@ bool CefV8StackFrameImpl::IsConstructor() {
v8::HandleScope handle_scope;
return GetHandle()->IsConstructor();
}
void CefV8MessageHandler(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data) {
CEF_REQUIRE_VALID_CONTEXT(void());
CEF_REQUIRE_UI_THREAD(void());
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
CefRefPtr<CefBrowser> browser = context->GetBrowser();
CefRefPtr<CefFrame> frame = context->GetFrame();
v8::Handle<v8::StackTrace> v8Stack = message->GetStackTrace();
DCHECK(!v8Stack.IsEmpty());
CefRefPtr<CefV8StackTrace> stackTrace = new CefV8StackTraceImpl(v8Stack);
CefRefPtr<CefClient> client = browser->GetClient();
if (!client.get())
return;
CefRefPtr<CefV8ContextHandler> handler = client->GetV8ContextHandler();
if (!handler.get())
return;
CefRefPtr<CefV8Exception> exception = new CefV8ExceptionImpl(message);
handler->OnUncaughtException(browser, frame, context, exception, stackTrace);
}

View File

@ -220,4 +220,8 @@ class CefV8StackFrameImpl : public CefV8StackFrame {
DISALLOW_COPY_AND_ASSIGN(CefV8StackFrameImpl);
};
// Used to catch global exceptions.
void CefV8MessageHandler(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data);
#endif // CEF_LIBCEF_V8_IMPL_H_

View File

@ -14,6 +14,8 @@
#include "libcef_dll/ctocpp/browser_ctocpp.h"
#include "libcef_dll/ctocpp/frame_ctocpp.h"
#include "libcef_dll/ctocpp/v8context_ctocpp.h"
#include "libcef_dll/ctocpp/v8exception_ctocpp.h"
#include "libcef_dll/ctocpp/v8stack_trace_ctocpp.h"
// MEMBER FUNCTIONS - Body may be edited by hand.
@ -74,6 +76,45 @@ void CEF_CALLBACK v8context_handler_on_context_released(
CefV8ContextCToCpp::Wrap(context));
}
void CEF_CALLBACK v8context_handler_on_uncaught_exception(
struct _cef_v8context_handler_t* self, cef_browser_t* browser,
cef_frame_t* frame, cef_v8context_t* context, cef_v8exception_t* exception,
cef_v8stack_trace_t* stackTrace) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Verify param: frame; type: refptr_diff
DCHECK(frame);
if (!frame)
return;
// Verify param: context; type: refptr_diff
DCHECK(context);
if (!context)
return;
// Verify param: exception; type: refptr_diff
DCHECK(exception);
if (!exception)
return;
// Verify param: stackTrace; type: refptr_diff
DCHECK(stackTrace);
if (!stackTrace)
return;
// Execute
CefV8ContextHandlerCppToC::Get(self)->OnUncaughtException(
CefBrowserCToCpp::Wrap(browser),
CefFrameCToCpp::Wrap(frame),
CefV8ContextCToCpp::Wrap(context),
CefV8ExceptionCToCpp::Wrap(exception),
CefV8StackTraceCToCpp::Wrap(stackTrace));
}
// CONSTRUCTOR - Do not edit by hand.
@ -82,6 +123,8 @@ CefV8ContextHandlerCppToC::CefV8ContextHandlerCppToC(CefV8ContextHandler* cls)
cef_v8context_handler_t>(cls) {
struct_.struct_.on_context_created = v8context_handler_on_context_created;
struct_.struct_.on_context_released = v8context_handler_on_context_released;
struct_.struct_.on_uncaught_exception =
v8context_handler_on_uncaught_exception;
}
#ifndef NDEBUG

View File

@ -13,6 +13,8 @@
#include "libcef_dll/cpptoc/browser_cpptoc.h"
#include "libcef_dll/cpptoc/frame_cpptoc.h"
#include "libcef_dll/cpptoc/v8context_cpptoc.h"
#include "libcef_dll/cpptoc/v8exception_cpptoc.h"
#include "libcef_dll/cpptoc/v8stack_trace_cpptoc.h"
#include "libcef_dll/ctocpp/v8context_handler_ctocpp.h"
@ -72,6 +74,45 @@ void CefV8ContextHandlerCToCpp::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefV8ContextCppToC::Wrap(context));
}
void CefV8ContextHandlerCToCpp::OnUncaughtException(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context, CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) {
if (CEF_MEMBER_MISSING(struct_, on_uncaught_exception))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Verify param: frame; type: refptr_diff
DCHECK(frame.get());
if (!frame.get())
return;
// Verify param: context; type: refptr_diff
DCHECK(context.get());
if (!context.get())
return;
// Verify param: exception; type: refptr_diff
DCHECK(exception.get());
if (!exception.get())
return;
// Verify param: stackTrace; type: refptr_diff
DCHECK(stackTrace.get());
if (!stackTrace.get())
return;
// Execute
struct_->on_uncaught_exception(struct_,
CefBrowserCppToC::Wrap(browser),
CefFrameCppToC::Wrap(frame),
CefV8ContextCppToC::Wrap(context),
CefV8ExceptionCppToC::Wrap(exception),
CefV8StackTraceCppToC::Wrap(stackTrace));
}
#ifndef NDEBUG
template<> long CefCToCpp<CefV8ContextHandlerCToCpp, CefV8ContextHandler,

View File

@ -38,6 +38,10 @@ class CefV8ContextHandlerCToCpp
CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE;
virtual void OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE;
virtual void OnUncaughtException(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE;
};
#endif // BUILDING_CEF_SHARED

View File

@ -257,6 +257,8 @@ void AppGetSettings(CefSettings& settings, CefRefPtr<CefApp>& app) {
settings.pack_loading_disabled =
g_command_line->HasSwitch(cefclient::kPackLoadingDisabled);
settings.uncaught_exception_stack_size = GetIntValue(
g_command_line->GetSwitchValue(cefclient::kUncaughtExceptionStackSize));
// Retrieve command-line proxy configuration, if any.
bool has_proxy = false;

View File

@ -35,6 +35,7 @@ const char kJavascriptFlags[] = "javascript-flags";
const char kResourcesDirPath[] = "resources-dir-path";
const char kLocalesDirPath[] = "locales-dir-path";
const char kPackLoadingDisabled[] = "pack-loading-disabled";
const char kUncaughtExceptionStackSize[] = "uncaught-exception-stack-size";
// CefBrowserSettings attributes.
const char kDragDropDisabled[] = "drag-drop-disabled";

View File

@ -37,6 +37,7 @@ extern const char kJavascriptFlags[];
extern const char kResourcesDirPath[];
extern const char kLocalesDirPath[];
extern const char kPackLoadingDisabled[];
extern const char kUncaughtExceptionStackSize[];
// CefBrowserSettings attributes.
extern const char kDragDropDisabled[];

View File

@ -87,7 +87,7 @@ class TestHandler : public CefClient,
protected:
// Destroy the browser window. Once the window is destroyed test completion
// will be signaled.
void DestroyTest();
virtual void DestroyTest();
void CreateBrowser(const CefString& url);

View File

@ -124,6 +124,9 @@ void CefTestSuite::GetSettings(CefSettings& settings) {
settings.pack_loading_disabled =
commandline_->HasSwitch(cefclient::kPackLoadingDisabled);
// Necessary for V8Test.OnUncaughtException tests.
settings.uncaught_exception_stack_size = 10;
}
// static

View File

@ -5,6 +5,7 @@
#include "include/cef_runnable.h"
#include "include/cef_v8.h"
#include "tests/unittests/test_handler.h"
#include "tests/unittests/test_suite.h"
#include "testing/gtest/include/gtest/gtest.h"
// How to add a new test:
@ -21,6 +22,8 @@ const char* kV8TestUrl = "http://tests/V8Test.Test";
const char* kV8BindingTestUrl = "http://tests/V8Test.BindingTest";
const char* kV8ContextParentTestUrl = "http://tests/V8Test.ContextParentTest";
const char* kV8ContextChildTestUrl = "http://tests/V8Test.ContextChildTest";
const char* kV8OnUncaughtExceptionTestUrl =
"http://tests/V8Test.OnUncaughtException";
enum V8TestMode {
V8TEST_NULL_CREATE = 0,
@ -56,6 +59,8 @@ enum V8TestMode {
V8TEST_CONTEXT_ENTERED,
V8TEST_BINDING,
V8TEST_STACK_TRACE,
V8TEST_ON_UNCAUGHT_EXCEPTION,
V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS,
};
@ -75,6 +80,17 @@ class V8TestHandler : public TestHandler {
AddResource(kV8ContextChildTestUrl, "<html><body>CHILD</body></html>",
"text/html");
CreateBrowser(kV8ContextParentTestUrl);
} else if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION ||
test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
AddResource(kV8OnUncaughtExceptionTestUrl, "<html><body>"
"<h1>OnUncaughtException</h1>"
"<script>\n"
"function test(){ test2(); }\n"
"function test2(){ asd(); }\n"
"</script>\n"
"</body></html>\n",
"text/html");
CreateBrowser(kV8OnUncaughtExceptionTestUrl);
} else {
EXPECT_TRUE(test_url_ != NULL);
AddResource(test_url_, "<html><body>TEST</body></html>", "text/html");
@ -184,6 +200,12 @@ class V8TestHandler : public TestHandler {
case V8TEST_STACK_TRACE:
RunStackTraceTest();
break;
case V8TEST_ON_UNCAUGHT_EXCEPTION:
RunOnUncaughtExceptionTest();
break;
case V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS:
RunOnUncaughtExceptionDevToolsTest();
break;
default:
ADD_FAILURE();
DestroyTest();
@ -1522,11 +1544,57 @@ class V8TestHandler : public TestHandler {
DestroyTest();
}
void RunOnUncaughtExceptionTest() {
on_uncaught_exception_context_ =
GetBrowser()->GetMainFrame()->GetV8Context();
GetBrowser()->GetMainFrame()->ExecuteJavaScript("test()",
CefString(), 0);
}
void RunOnUncaughtExceptionDevToolsTest() {
on_uncaught_exception_context_ =
GetBrowser()->GetMainFrame()->GetV8Context();
GetBrowser()->ShowDevTools();
}
void OnUncaughtException(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context,
CefRefPtr<CefV8Exception> exception,
CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE {
got_on_uncaught_exception_.yes();
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION ||
test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
EXPECT_TRUE(on_uncaught_exception_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"
"at () in on line 0\n";
EXPECT_STREQ(stackFormattedShouldBe, stackFormatted.str().c_str());
DestroyTest();
}
}
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) OVERRIDE {
if (frame->IsMain())
RunTest(test_mode_);
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS &&
browser->IsPopup()) {
GetBrowser()->CloseDevTools();
GetBrowser()->GetMainFrame()->ExecuteJavaScript("test()",
CefString(), 0);
}
}
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
@ -1586,6 +1654,16 @@ class V8TestHandler : public TestHandler {
}
}
virtual void DestroyTest() OVERRIDE {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION ||
test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
EXPECT_TRUE(got_on_uncaught_exception_);
}
got_destroy_test_.yes();
TestHandler::DestroyTest();
}
// Return the V8 context.
CefRefPtr<CefV8Context> GetContext() {
CefRefPtr<CefV8Context> context =
@ -1596,6 +1674,10 @@ class V8TestHandler : public TestHandler {
V8TestMode test_mode_;
const char* test_url_;
CefRefPtr<CefV8Context> on_uncaught_exception_context_;
TrackCallback got_destroy_test_;
TrackCallback got_on_uncaught_exception_;
};
} // namespace
@ -1607,6 +1689,7 @@ class V8TestHandler : public TestHandler {
CefRefPtr<V8TestHandler> handler = \
new V8TestHandler(test_mode, test_url); \
handler->ExecuteTest(); \
EXPECT_TRUE(handler->got_destroy_test_); \
}
#define V8_TEST(name, test_mode) \
@ -1647,6 +1730,8 @@ V8_TEST(ContextEvalException, V8TEST_CONTEXT_EVAL_EXCEPTION);
V8_TEST_EX(ContextEntered, V8TEST_CONTEXT_ENTERED, NULL);
V8_TEST_EX(Binding, V8TEST_BINDING, kV8BindingTestUrl);
V8_TEST(StackTrace, V8TEST_STACK_TRACE);
V8_TEST(OnUncaughtException, V8TEST_ON_UNCAUGHT_EXCEPTION);
V8_TEST(OnUncaughtExceptionDevTools, V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS);
namespace {
@ -1737,7 +1822,8 @@ TEST(V8Test, ExternalMemoryAllocation) {
Test* test = new Test();
CefRegisterExtension("v8/externalMemory", test->GetExtensionCode(), test);
V8ExternalMemTestHandler* test_handler = new V8ExternalMemTestHandler(test->GetTestCode());
V8ExternalMemTestHandler* test_handler =
new V8ExternalMemTestHandler(test->GetTestCode());
test_handler->ExecuteTest();
ASSERT_TRUE(test->object_created_);