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

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@890 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2012-11-02 18:16:28 +00:00
parent 202cdc4eb4
commit e599cc1fab
23 changed files with 613 additions and 14 deletions

View File

@ -917,6 +917,8 @@
'libcef/renderer/dom_node_impl.h',
'libcef/renderer/frame_impl.cc',
'libcef/renderer/frame_impl.h',
'libcef/renderer/render_message_filter.cc',
'libcef/renderer/render_message_filter.h',
'libcef/renderer/render_process_observer.cc',
'libcef/renderer/render_process_observer.h',
'libcef/renderer/render_urlrequest_impl.cc',

View File

@ -100,6 +100,17 @@ typedef struct _cef_render_process_handler_t {
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_render_process_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);
///
// Called when a new node in the the browser gets focus. The |node| value may
// be NULL if no specific node has gained focus. The node object passed to

View File

@ -95,6 +95,18 @@ class CefRenderProcessHandler : public virtual CefBase {
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) {}
///
// Called when a new node in the the browser gets focus. The |node| value may
// be empty if no specific node has gained focus. The node object passed to

View File

@ -253,6 +253,14 @@ typedef struct _cef_settings_t {
///
int remote_debugging_port;
///
// 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;
///
// By default CEF V8 references will be invalidated (the IsValid() method will
// return false) after the owning context has been released. This reduces the
@ -757,28 +765,28 @@ enum cef_urlrequest_flags_t {
// Default behavior.
///
UR_FLAG_NONE = 0,
///
// If set the cache will be skipped when handling the request.
///
UR_FLAG_SKIP_CACHE = 1 << 0,
///
// If set user name, password, and cookies may be sent with the request.
///
UR_FLAG_ALLOW_CACHED_CREDENTIALS = 1 << 1,
///
// If set cookies may be sent with the request and saved from the response.
// UR_FLAG_ALLOW_CACHED_CREDENTIALS must also be set.
///
UR_FLAG_ALLOW_COOKIES = 1 << 2,
///
// If set upload progress events will be generated when a request has a body.
///
UR_FLAG_REPORT_UPLOAD_PROGRESS = 1 << 3,
///
// If set load timing info will be collected for the request.
///
@ -810,23 +818,23 @@ enum cef_urlrequest_status_t {
// Unknown status.
///
UR_UNKNOWN = 0,
///
// Request succeeded.
///
UR_SUCCESS,
///
// An IO request is pending, and the caller will be informed when it is
// completed.
///
UR_IO_PENDING,
///
// Request was canceled programatically.
///
UR_CANCELED,
///
// Request failed for some reason.
///
@ -1121,7 +1129,7 @@ typedef struct _cef_key_event_t {
// The type of keyboard event.
///
cef_key_event_type_t type;
///
// Bit flags describing any pressed modifier keys. See
// cef_key_event_modifiers_t for values.

View File

@ -300,6 +300,7 @@ struct CefSettingsTraits {
&target->locales_dir_path, copy);
target->pack_loading_disabled = src->pack_loading_disabled;
target->remote_debugging_port = src->remote_debugging_port;
target->uncaught_exception_stack_size = src->uncaught_exception_stack_size;
target->context_safety_implementation = src->context_safety_implementation;
}
};

View File

@ -279,6 +279,7 @@ void CefContentBrowserClient::AppendExtraCommandLineSwitches(
static const char* const kSwitchNames[] = {
switches::kContextSafetyImplementation,
switches::kProductVersion,
switches::kUncaughtExceptionStackSize,
};
command_line->CopySwitchesFrom(browser_cmd, kSwitchNames,
arraysize(kSwitchNames));

View File

@ -30,6 +30,9 @@ const char kLocalesDirPath[] = "locales-dir-path";
// Path to locales directory.
const char kPackLoadingDisabled[] = "pack-loading-disabled";
// Stack size for uncaught exceptions.
const char kUncaughtExceptionStackSize[] = "uncaught-exception-stack-size";
// Context safety implementation type.
const char kContextSafetyImplementation[] = "context-safety-implementation";

View File

@ -22,6 +22,7 @@ extern const char kLogSeverity_Disable[];
extern const char kResourcesDirPath[];
extern const char kLocalesDirPath[];
extern const char kPackLoadingDisabled[];
extern const char kUncaughtExceptionStackSize[];
extern const char kContextSafetyImplementation[];
} // namespace switches

View File

@ -252,6 +252,11 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) {
base::IntToString(settings.remote_debugging_port));
}
if (settings.uncaught_exception_stack_size > 0) {
command_line->AppendSwitchASCII(switches::kUncaughtExceptionStackSize,
base::IntToString(settings.uncaught_exception_stack_size));
}
if (settings.context_safety_implementation != 0) {
command_line->AppendSwitchASCII(switches::kContextSafetyImplementation,
base::IntToString(settings.context_safety_implementation));

View File

@ -17,6 +17,7 @@ MSVC_POP_WARNING();
#include "libcef/common/content_client.h"
#include "libcef/renderer/browser_impl.h"
#include "libcef/renderer/chrome_bindings.h"
#include "libcef/renderer/render_message_filter.h"
#include "libcef/renderer/render_process_observer.h"
#include "libcef/renderer/thread_util.h"
#include "libcef/renderer/v8_impl.h"
@ -71,7 +72,9 @@ struct CefContentRendererClient::SchemeInfo {
bool is_display_isolated;
};
CefContentRendererClient::CefContentRendererClient() {
CefContentRendererClient::CefContentRendererClient()
: devtools_agent_count_(0),
uncaught_exception_stack_size_(0) {
}
CefContentRendererClient::~CefContentRendererClient() {
@ -148,12 +151,33 @@ void CefContentRendererClient::RegisterCustomSchemes() {
}
}
void CefContentRendererClient::DevToolsAgentAttached() {
CEF_REQUIRE_RT();
++devtools_agent_count_;
}
void CefContentRendererClient::DevToolsAgentDetached() {
CEF_REQUIRE_RT();
--devtools_agent_count_;
if (devtools_agent_count_ == 0 && uncaught_exception_stack_size_ > 0) {
// When the last DevToolsAgent is detached the stack size is set to 0.
// Restore the user-specified stack size here.
v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
uncaught_exception_stack_size_, v8::StackTrace::kDetailed);
}
}
void CefContentRendererClient::SetUncaughtExceptionStackSize(int stackSize) {
uncaught_exception_stack_size_ = stackSize;
}
void CefContentRendererClient::RenderThreadStarted() {
render_loop_ = base::MessageLoopProxy::current();
observer_.reset(new CefRenderProcessObserver());
content::RenderThread* thread = content::RenderThread::Get();
thread->AddObserver(observer_.get());
thread->GetChannel()->AddFilter(new CefRenderMessageFilter);
WebKit::WebPrerenderingSupport::initialize(new CefPrerenderingSupport());

View File

@ -48,6 +48,10 @@ class CefContentRendererClient : public content::ContentRendererClient {
// Render thread message loop proxy.
base::MessageLoopProxy* render_loop() const { return render_loop_.get(); }
void DevToolsAgentAttached();
void DevToolsAgentDetached();
void SetUncaughtExceptionStackSize(int stackSize);
private:
// ContentRendererClient implementation.
virtual void RenderThreadStarted() OVERRIDE;
@ -71,6 +75,9 @@ class CefContentRendererClient : public content::ContentRendererClient {
struct SchemeInfo;
typedef std::list<SchemeInfo> SchemeInfoList;
SchemeInfoList scheme_info_list_;
int devtools_agent_count_;
int uncaught_exception_stack_size_;
};
#endif // CEF_LIBCEF_RENDERER_CONTENT_RENDERER_CLIENT_H_

View File

@ -0,0 +1,82 @@
/// Copyright (c) 2012 The Chromium Embedded Framework Authors.
// Portions (c) 2011 The Chromium 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 "libcef/renderer/render_message_filter.h"
#include "libcef/renderer/thread_util.h"
#include "libcef/common/cef_messages.h"
#include "base/bind.h"
#include "base/message_loop.h"
#include "content/common/devtools_messages.h"
#include "googleurl/src/gurl.h"
#include "googleurl/src/url_util.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
CefRenderMessageFilter::CefRenderMessageFilter()
: channel_(NULL) {
}
CefRenderMessageFilter::~CefRenderMessageFilter() {
}
void CefRenderMessageFilter::OnFilterAdded(IPC::Channel* channel) {
channel_ = channel;
}
void CefRenderMessageFilter::OnFilterRemoved() {
}
bool CefRenderMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
if (message.type() == DevToolsAgentMsg_Attach::ID ||
message.type() == DevToolsAgentMsg_Reattach::ID ||
message.type() == DevToolsAgentMsg_Detach::ID) {
// Observe the DevTools messages but don't handle them.
handled = false;
}
IPC_BEGIN_MESSAGE_MAP(CefRenderMessageFilter, message)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Attach, OnDevToolsAgentAttach)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Reattach, OnDevToolsAgentReattach)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Detach, OnDevToolsAgentDetach)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void CefRenderMessageFilter::OnDevToolsAgentAttach() {
CEF_POST_TASK_RT(
base::Bind(&CefRenderMessageFilter::OnDevToolsAgentAttach_RT, this));
}
void CefRenderMessageFilter::OnDevToolsAgentReattach(
const std::string& agent_state) {
// Treat reattach the same as attach.
OnDevToolsAgentAttach();
}
void CefRenderMessageFilter::OnDevToolsAgentDetach() {
// CefContentRendererClient::DevToolsAgentDetached() needs to be called after
// the IPC message has been handled by DevToolsAgent. A workaround for this is
// to first post to the IO thread and then post to the renderer thread.
MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&CefRenderMessageFilter::OnDevToolsAgentDetach_IOT, this));
}
void CefRenderMessageFilter::OnDevToolsAgentAttach_RT() {
CEF_REQUIRE_RT();
CefContentRendererClient::Get()->DevToolsAgentAttached();
}
void CefRenderMessageFilter::OnDevToolsAgentDetach_IOT() {
CEF_POST_TASK_RT(
base::Bind(&CefRenderMessageFilter::OnDevToolsAgentDetach_RT, this));
}
void CefRenderMessageFilter::OnDevToolsAgentDetach_RT() {
CEF_REQUIRE_RT();
CefContentRendererClient::Get()->DevToolsAgentDetached();
}

View File

@ -0,0 +1,39 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CEF_LIBCEF_RENDERER_RENDER_MESSAGE_FILTER_H_
#define CEF_LIBCEF_RENDERER_RENDER_MESSAGE_FILTER_H_
#include <string>
#include "ipc/ipc_channel_proxy.h"
// This class sends and receives control messages on the renderer process.
class CefRenderMessageFilter : public IPC::ChannelProxy::MessageFilter {
public:
CefRenderMessageFilter();
virtual ~CefRenderMessageFilter();
// IPC::ChannelProxy::MessageFilter implementation.
virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE;
virtual void OnFilterRemoved() OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
private:
// Message handlers called on the IO thread.
void OnDevToolsAgentAttach();
void OnDevToolsAgentReattach(const std::string& agent_state);
void OnDevToolsAgentDetach();
void OnDevToolsAgentAttach_RT();
void OnDevToolsAgentDetach_IOT();
void OnDevToolsAgentDetach_RT();
IPC::Channel* channel_;
DISALLOW_COPY_AND_ASSIGN(CefRenderMessageFilter);
};
#endif // CEF_LIBCEF_RENDERER_RENDER_MESSAGE_FILTER_H_

View File

@ -5,11 +5,15 @@
#include "libcef/renderer/render_process_observer.h"
#include "libcef/common/cef_messages.h"
#include "libcef/common/cef_switches.h"
#include "libcef/common/command_line_impl.h"
#include "libcef/common/content_client.h"
#include "libcef/renderer/content_renderer_client.h"
#include "libcef/renderer/v8_impl.h"
#include "base/bind.h"
#include "base/path_service.h"
#include "base/string_number_conversions.h"
#include "googleurl/src/gurl.h"
#include "googleurl/src/url_util.h"
#include "media/base/media.h"
@ -53,6 +57,23 @@ void CefRenderProcessObserver::WebKitInitialized() {
// Register any custom schemes with WebKit.
CefContentRendererClient::Get()->RegisterCustomSchemes();
// The number of stack trace frames to capture for uncaught exceptions.
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kUncaughtExceptionStackSize)) {
int uncaught_exception_stack_size = 0;
base::StringToInt(
command_line.GetSwitchValueASCII(switches::kUncaughtExceptionStackSize),
&uncaught_exception_stack_size);
if (uncaught_exception_stack_size > 0) {
CefContentRendererClient::Get()->SetUncaughtExceptionStackSize(
uncaught_exception_stack_size);
v8::V8::AddMessageListener(&CefV8MessageHandler);
v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
uncaught_exception_stack_size, v8::StackTrace::kDetailed);
}
}
// Notify the render process handler.
CefRefPtr<CefApp> application = CefContentClient::Get()->application();
if (application.get()) {

View File

@ -21,6 +21,7 @@ MSVC_POP_WARNING();
#include "libcef/renderer/v8_impl.h"
#include "libcef/common/cef_switches.h"
#include "libcef/common/content_client.h"
#include "libcef/common/tracker.h"
#include "libcef/renderer/browser_impl.h"
#include "libcef/renderer/thread_util.h"
@ -1665,3 +1666,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_RT_RETURN(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<CefApp> application = CefContentClient::Get()->application();
if (!application.get())
return;
CefRefPtr<CefRenderProcessHandler> handler =
application->GetRenderProcessHandler();
if (!handler.get())
return;
CefRefPtr<CefV8Exception> exception = new CefV8ExceptionImpl(message);
handler->OnUncaughtException(browser, frame, context, exception, stackTrace);
}

View File

@ -293,4 +293,7 @@ class CefV8StackFrameImpl : public CefV8StackFrame {
DISALLOW_COPY_AND_ASSIGN(CefV8StackFrameImpl);
};
void CefV8MessageHandler(v8::Handle<v8::Message> message,
v8::Handle<v8::Value> data);
#endif // CEF_LIBCEF_RENDERER_V8_IMPL_H_

View File

@ -16,6 +16,8 @@
#include "libcef_dll/ctocpp/frame_ctocpp.h"
#include "libcef_dll/ctocpp/process_message_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.
@ -134,6 +136,46 @@ void CEF_CALLBACK render_process_handler_on_context_released(
CefV8ContextCToCpp::Wrap(context));
}
void CEF_CALLBACK render_process_handler_on_uncaught_exception(
struct _cef_render_process_handler_t* self, cef_browser_t* browser,
cef_frame_t* frame, struct _cef_v8context_t* context,
struct _cef_v8exception_t* exception,
struct _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
CefRenderProcessHandlerCppToC::Get(self)->OnUncaughtException(
CefBrowserCToCpp::Wrap(browser),
CefFrameCToCpp::Wrap(frame),
CefV8ContextCToCpp::Wrap(context),
CefV8ExceptionCToCpp::Wrap(exception),
CefV8StackTraceCToCpp::Wrap(stackTrace));
}
void CEF_CALLBACK render_process_handler_on_focused_node_changed(
struct _cef_render_process_handler_t* self, cef_browser_t* browser,
cef_frame_t* frame, cef_domnode_t* node) {
@ -202,6 +244,8 @@ CefRenderProcessHandlerCppToC::CefRenderProcessHandlerCppToC(
render_process_handler_on_context_created;
struct_.struct_.on_context_released =
render_process_handler_on_context_released;
struct_.struct_.on_uncaught_exception =
render_process_handler_on_uncaught_exception;
struct_.struct_.on_focused_node_changed =
render_process_handler_on_focused_node_changed;
struct_.struct_.on_process_message_received =

View File

@ -15,6 +15,8 @@
#include "libcef_dll/cpptoc/frame_cpptoc.h"
#include "libcef_dll/cpptoc/process_message_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/render_process_handler_ctocpp.h"
@ -130,6 +132,45 @@ void CefRenderProcessHandlerCToCpp::OnContextReleased(
CefV8ContextCppToC::Wrap(context));
}
void CefRenderProcessHandlerCToCpp::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));
}
void CefRenderProcessHandlerCToCpp::OnFocusedNodeChanged(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) {

View File

@ -42,6 +42,10 @@ class CefRenderProcessHandlerCToCpp
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;
virtual void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefDOMNode> node) OVERRIDE;
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,

View File

@ -272,6 +272,13 @@ void ClientApp::OnWebKitInitialized() {
(*it)->OnWebKitInitialized(this);
}
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);
}
void ClientApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
@ -302,6 +309,19 @@ 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.
RenderDelegateSet::iterator it = render_delegates_.begin();
for (; it != render_delegates_.end(); ++it) {
(*it)->OnUncaughtException(this, browser, frame, context, exception,
stackTrace);
}
}
void ClientApp::OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) {

View File

@ -28,8 +28,9 @@ class ClientApp : public CefApp,
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.
// Called on the browser process IO thread before a child process is
// launched Provides an opportunity to modify the child process command
// line.
virtual void OnBeforeChildProcessLaunch(
CefRefPtr<ClientApp> app,
CefRefPtr<CefCommandLine> command_line) {
@ -51,6 +52,11 @@ class ClientApp : public CefApp,
virtual void OnWebKitInitialized(CefRefPtr<ClientApp> app) {
}
// Called before a browser is destroyed.
virtual void OnBrowserDestroyed(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser) {
}
// 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.
@ -69,6 +75,16 @@ class ClientApp : public CefApp,
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) {
}
// Called when the focused node in a frame has changed.
virtual void OnFocusedNodeChanged(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
@ -152,12 +168,19 @@ class ClientApp : public CefApp,
// CefRenderProcessHandler methods.
virtual void OnRenderThreadCreated() OVERRIDE;
virtual void OnWebKitInitialized() OVERRIDE;
virtual void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
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;
virtual void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefDOMNode> node) OVERRIDE;

View File

@ -58,6 +58,10 @@ void CefTestSuite::GetSettings(CefSettings& settings) {
if (!other_javascript_flags.empty())
javascript_flags += " " + other_javascript_flags;
CefString(&settings.javascript_flags) = javascript_flags;
// Necessary for V8Test.OnUncaughtException tests.
settings.uncaught_exception_stack_size = 10;
settings.remote_debugging_port = 12345;
}
// static

View File

@ -25,8 +25,12 @@ const char kV8BindingTestUrl[] = "http://tests/V8Test.BindingTest";
const char kV8ContextParentTestUrl[] = "http://tests/V8Test.ContextParentTest";
const char kV8ContextChildTestUrl[] = "http://tests/V8Test.ContextChildTest";
const char kV8NavTestUrl[] = "http://tests/V8Test.NavTest";
const char kV8OnUncaughtExceptionTestUrl[] =
"http://tests/V8Test.OnUncaughtException";
const char kV8TestMsg[] = "V8Test.Test";
const char kV8TestCmdArg[] = "v8-test";
const char kV8DevToolsURLMsg[] = "V8Test.DevToolsURL";
const char kV8DevToolsLoadHookMsg[] = "V8Test.DevToolsLoadHook";
enum V8TestMode {
V8TEST_NONE = 0,
@ -65,6 +69,8 @@ enum V8TestMode {
V8TEST_BINDING,
V8TEST_STACK_TRACE,
V8TEST_EXTENSION,
V8TEST_ON_UNCAUGHT_EXCEPTION,
V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS,
};
// Set to the current test being run in the browser process. Will always be
@ -97,7 +103,8 @@ class V8BrowserTest : public ClientApp::BrowserDelegate {
class V8RendererTest : public ClientApp::RenderDelegate {
public:
V8RendererTest()
: test_mode_(V8TEST_NONE) {
: test_mode_(V8TEST_NONE),
devtools_url_("") {
}
// Run a test when the process message is received from the browser.
@ -206,6 +213,12 @@ class V8RendererTest : public ClientApp::RenderDelegate {
case V8TEST_STACK_TRACE:
RunStackTraceTest();
break;
case V8TEST_ON_UNCAUGHT_EXCEPTION:
RunOnUncaughtExceptionTest();
break;
case V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS:
RunOnUncaughtExceptionDevToolsTest();
break;
default:
// Was a startup test.
EXPECT_TRUE(startup_test_success_);
@ -1558,6 +1571,49 @@ class V8RendererTest : public ClientApp::RenderDelegate {
DestroyTest();
}
void RunOnUncaughtExceptionTest() {
test_context_ =
browser_->GetMainFrame()->GetV8Context();
browser_->GetMainFrame()->ExecuteJavaScript(
"window.setTimeout(test, 0)", "about:blank", 0);
}
void RunOnUncaughtExceptionDevToolsTest() {
EXPECT_FALSE(browser_->IsPopup());
test_context_ =
browser_->GetMainFrame()->GetV8Context();
// Show DevTools.
EXPECT_FALSE(devtools_url_.empty());
browser_->GetMainFrame()->ExecuteJavaScript(
"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();"
@ -1606,6 +1662,16 @@ class V8RendererTest : public ClientApp::RenderDelegate {
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (browser_.get() == NULL) {
app_ = app;
browser_ = browser;
}
// The test is run from OnProcessMessageReceived(), after the message
// and the devtools url has been received from OnLoadEnd().
return;
}
app_ = app;
browser_ = browser;
@ -1686,6 +1752,119 @@ class V8RendererTest : public ClientApp::RenderDelegate {
}
}
virtual void OnBrowserDestroyed(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser) OVERRIDE {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
if (browser->IsPopup()) {
// After window destruction there is still a call to
// ScriptController::setCaptureCallStackForUncaughtExceptions(0),
// for which we have to wait.
CefPostDelayedTask(TID_RENDERER, NewCefRunnableMethod(
this, &V8RendererTest::DevToolsClosed), 1000);
}
}
}
virtual bool OnProcessMessageReceived(CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
OVERRIDE {
if (test_mode_ == V8TEST_ON_UNCAUGHT_EXCEPTION_DEV_TOOLS) {
EXPECT_TRUE(browser.get());
EXPECT_EQ(PID_BROWSER, source_process);
EXPECT_TRUE(message.get());
EXPECT_TRUE(message->IsReadOnly());
if (message->GetName() == kV8DevToolsURLMsg) {
EXPECT_FALSE(browser->IsPopup());
devtools_url_ = message->GetArgumentList()->GetString(0);
EXPECT_FALSE(devtools_url_.empty());
if (!TestFailed()) {
CefPostTask(TID_RENDERER,
NewCefRunnableMethod(this, &V8RendererTest::RunTest));
}
} else if (message->GetName() == kV8DevToolsLoadHookMsg) {
EXPECT_TRUE(browser->IsPopup());
DevToolsLoadHook(browser);
} else {
EXPECT_TRUE(false) << "not reached";
return false;
}
return true;
}
return false;
}
void DevToolsLoadHook(CefRefPtr<CefBrowser> browser) {
EXPECT_TRUE(browser->IsPopup());
CefRefPtr<CefV8Context> context = browser->GetMainFrame()->GetV8Context();
static const char* kFuncName = "DevToolsLoaded";
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(kFuncName, name.ToString().c_str());
if (name == kFuncName) {
EXPECT_TRUE(exception.empty());
retval = CefV8Value::CreateNull();
EXPECT_TRUE(retval.get());
renderer_test_->DevToolsLoaded(browser_);
return true;
}
return false;
}
CefRefPtr<V8RendererTest> renderer_test_;
CefRefPtr<CefBrowser> browser_;
IMPLEMENT_REFCOUNTING(Handler);
};
EXPECT_TRUE(context->Enter());
Handler* handler = new Handler;
handler->renderer_test_ = this;
handler->browser_ = browser;
CefRefPtr<CefV8Handler> handlerPtr(handler);
CefRefPtr<CefV8Value> func =
CefV8Value::CreateFunction(kFuncName, handler);
EXPECT_TRUE(func.get());
EXPECT_TRUE(context->GetGlobal()->SetValue(
kFuncName, func, V8_PROPERTY_ATTRIBUTE_NONE));
EXPECT_TRUE(context->Exit());
// Call DevToolsLoaded() when DevTools window completed loading.
std::string jsCode = "(function(){"
" var oldLoadCompleted = InspectorFrontendAPI.loadCompleted;"
" InspectorFrontendAPI.loadCompleted = function(){"
" oldLoadCompleted.call(InspectorFrontendAPI);"
" console.log('InspectorFrontendAPI.loadCompleted event fired');"
" window.DevToolsLoaded();"
" };"
"})();";
CefRefPtr<CefV8Value> retval;
CefRefPtr<CefV8Exception> exception;
EXPECT_TRUE(context->Eval(CefString(jsCode), retval, exception));
}
void DevToolsLoaded(CefRefPtr<CefBrowser> browser) {
EXPECT_TRUE(browser->IsPopup());
EXPECT_NE(browser->GetIdentifier(), browser_->GetIdentifier());
CefRefPtr<CefV8Value> retval;
CefRefPtr<CefV8Exception> exception;
EXPECT_TRUE(browser->GetMainFrame()->GetV8Context()->Eval(
"window.close()", retval, exception));
}
void DevToolsClosed() {
browser_->GetMainFrame()->ExecuteJavaScript(
"window.setTimeout(test, 0);", "about:blank", 0);
}
protected:
// Return from the test.
void DestroyTest() {
@ -1707,6 +1886,9 @@ class V8RendererTest : public ClientApp::RenderDelegate {
app_ = NULL;
browser_ = NULL;
test_context_ = NULL;
test_object_ = NULL;
devtools_url_ = "";
}
// Return the V8 context.
@ -1719,6 +1901,7 @@ class V8RendererTest : public ClientApp::RenderDelegate {
CefRefPtr<ClientApp> app_;
CefRefPtr<CefBrowser> browser_;
V8TestMode test_mode_;
std::string devtools_url_;
CefRefPtr<CefV8Context> test_context_;
CefRefPtr<CefV8Value> test_object_;
@ -1748,6 +1931,17 @@ class V8TestHandler : public TestHandler {
"<script>var i = 0;</script>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>"
@ -1756,6 +1950,27 @@ 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 (browser->IsPopup()) {
EXPECT_STREQ(GetBrowser()->GetHost()->GetDevToolsURL(true).c_str(),
frame->GetURL().c_str());
CefRefPtr<CefProcessMessage> return_msg =
CefProcessMessage::Create(kV8DevToolsLoadHookMsg);
EXPECT_TRUE(browser->SendProcessMessage(PID_RENDERER, return_msg));
} else {
// Send the DevTools url message only for the main browser.
CefRefPtr<CefProcessMessage> return_msg =
CefProcessMessage::Create(kV8DevToolsURLMsg);
EXPECT_TRUE(return_msg->GetArgumentList()->SetString(0,
browser->GetHost()->GetDevToolsURL(true)));
EXPECT_TRUE(browser->SendProcessMessage(PID_RENDERER, return_msg));
}
}
}
virtual bool OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
@ -1849,4 +2064,6 @@ V8_TEST_EX(ContextEntered, V8TEST_CONTEXT_ENTERED, NULL);
V8_TEST(ContextInvalid, V8TEST_CONTEXT_INVALID);
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);
V8_TEST(Extension, V8TEST_EXTENSION);