blob: c84cecd72447725ee778f434f20e620848427c88 [file] [log] [blame]
// Copyright (c) 2012 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 "content/renderer/devtools/devtools_agent.h"
#include <map>
#include "base/debug/trace_event.h"
#include "base/lazy_instance.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process_handle.h"
#include "base/strings/string_number_conversions.h"
#include "content/common/devtools_messages.h"
#include "content/common/frame_messages.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/view_messages.h"
#include "content/renderer/devtools/devtools_agent_filter.h"
#include "content/renderer/devtools/devtools_client.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
#include "third_party/WebKit/public/platform/WebPoint.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/web/WebConsoleMessage.h"
#include "third_party/WebKit/public/web/WebConsoleMessage.h"
#include "third_party/WebKit/public/web/WebDevToolsAgent.h"
#include "third_party/WebKit/public/web/WebDeviceEmulationParams.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebSettings.h"
#include "third_party/WebKit/public/web/WebView.h"
#if defined(USE_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
#endif
using blink::WebConsoleMessage;
using blink::WebDevToolsAgent;
using blink::WebDevToolsAgentClient;
using blink::WebFrame;
using blink::WebPoint;
using blink::WebString;
using blink::WebCString;
using blink::WebVector;
using blink::WebView;
using base::debug::TraceLog;
using base::debug::TraceOptions;
namespace content {
base::subtle::AtomicWord DevToolsAgent::event_callback_;
namespace {
const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
class WebKitClientMessageLoopImpl
: public WebDevToolsAgentClient::WebKitClientMessageLoop {
public:
WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {}
virtual ~WebKitClientMessageLoopImpl() { message_loop_ = NULL; }
virtual void run() {
base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_);
message_loop_->Run();
}
virtual void quitNow() {
message_loop_->QuitNow();
}
private:
base::MessageLoop* message_loop_;
};
typedef std::map<int, DevToolsAgent*> IdToAgentMap;
base::LazyInstance<IdToAgentMap>::Leaky
g_agent_for_routing_id = LAZY_INSTANCE_INITIALIZER;
} // namespace
DevToolsAgent::DevToolsAgent(RenderFrame* main_render_frame)
: RenderFrameObserver(main_render_frame),
is_attached_(false),
is_devtools_client_(false),
gpu_route_id_(MSG_ROUTING_NONE),
paused_in_mouse_move_(false),
main_render_frame_(main_render_frame) {
g_agent_for_routing_id.Get()[routing_id()] = this;
main_render_frame_->GetRenderView()->GetWebView()->setDevToolsAgentClient(
this);
}
DevToolsAgent::~DevToolsAgent() {
g_agent_for_routing_id.Get().erase(routing_id());
resetTraceEventCallback();
}
// Called on the Renderer thread.
bool DevToolsAgent::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(DevToolsAgent, message)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Attach, OnAttach)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Reattach, OnReattach)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Detach, OnDetach)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend,
OnDispatchOnInspectorBackend)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_InspectElement, OnInspectElement)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_AddMessageToConsole,
OnAddMessageToConsole)
IPC_MESSAGE_HANDLER(DevToolsAgentMsg_GpuTasksChunk, OnGpuTasksChunk)
IPC_MESSAGE_HANDLER(DevToolsMsg_SetupDevToolsClient, OnSetupDevToolsClient)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
if (message.type() == FrameMsg_Navigate::ID ||
message.type() == ViewMsg_Close::ID)
ContinueProgram(); // Don't want to swallow the message.
return handled;
}
void DevToolsAgent::sendMessageToInspectorFrontend(
const blink::WebString& message) {
std::string msg(message.utf8());
if (msg.length() < kMaxMessageChunkSize) {
Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
routing_id(), msg, msg.size()));
return;
}
for (size_t pos = 0; pos < msg.length(); pos += kMaxMessageChunkSize) {
Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
routing_id(),
msg.substr(pos, kMaxMessageChunkSize),
pos ? 0 : msg.size()));
}
}
long DevToolsAgent::processId() {
return base::GetCurrentProcId();
}
int DevToolsAgent::debuggerId() {
return routing_id();
}
void DevToolsAgent::saveAgentRuntimeState(
const blink::WebString& state) {
Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8()));
}
blink::WebDevToolsAgentClient::WebKitClientMessageLoop*
DevToolsAgent::createClientMessageLoop() {
return new WebKitClientMessageLoopImpl();
}
void DevToolsAgent::willEnterDebugLoop() {
paused_in_mouse_move_ =
GetRenderViewImpl()->SendAckForMouseMoveFromDebugger();
}
void DevToolsAgent::didExitDebugLoop() {
if (paused_in_mouse_move_) {
GetRenderViewImpl()->IgnoreAckForMouseMoveFromDebugger();
paused_in_mouse_move_ = false;
}
}
void DevToolsAgent::resetTraceEventCallback()
{
TraceLog::GetInstance()->SetEventCallbackDisabled();
base::subtle::NoBarrier_Store(&event_callback_, 0);
}
void DevToolsAgent::setTraceEventCallback(const WebString& category_filter,
TraceEventCallback cb) {
TraceLog* trace_log = TraceLog::GetInstance();
base::subtle::NoBarrier_Store(&event_callback_,
reinterpret_cast<base::subtle::AtomicWord>(cb));
if (!!cb) {
trace_log->SetEventCallbackEnabled(base::debug::CategoryFilter(
category_filter.utf8()), TraceEventCallbackWrapper);
} else {
trace_log->SetEventCallbackDisabled();
}
}
void DevToolsAgent::enableTracing(const WebString& category_filter) {
TraceLog* trace_log = TraceLog::GetInstance();
trace_log->SetEnabled(base::debug::CategoryFilter(category_filter.utf8()),
TraceLog::RECORDING_MODE,
TraceOptions());
}
void DevToolsAgent::disableTracing() {
TraceLog::GetInstance()->SetDisabled();
}
// static
void DevToolsAgent::TraceEventCallbackWrapper(
base::TimeTicks timestamp,
char phase,
const unsigned char* category_group_enabled,
const char* name,
unsigned long long id,
int num_args,
const char* const arg_names[],
const unsigned char arg_types[],
const unsigned long long arg_values[],
unsigned char flags) {
TraceEventCallback callback =
reinterpret_cast<TraceEventCallback>(
base::subtle::NoBarrier_Load(&event_callback_));
if (callback) {
double timestamp_seconds = (timestamp - base::TimeTicks()).InSecondsF();
callback(phase, category_group_enabled, name, id, num_args,
arg_names, arg_types, arg_values, flags, timestamp_seconds);
}
}
void DevToolsAgent::startGPUEventsRecording() {
GpuChannelHost* gpu_channel_host =
RenderThreadImpl::current()->GetGpuChannel();
if (!gpu_channel_host)
return;
DCHECK(gpu_route_id_ == MSG_ROUTING_NONE);
int32 route_id = gpu_channel_host->GenerateRouteID();
bool succeeded = false;
gpu_channel_host->Send(
new GpuChannelMsg_DevToolsStartEventsRecording(route_id, &succeeded));
DCHECK(succeeded);
if (succeeded) {
gpu_route_id_ = route_id;
gpu_channel_host->AddRoute(gpu_route_id_, AsWeakPtr());
}
}
void DevToolsAgent::stopGPUEventsRecording() {
GpuChannelHost* gpu_channel_host =
RenderThreadImpl::current()->GetGpuChannel();
if (!gpu_channel_host || gpu_route_id_ == MSG_ROUTING_NONE)
return;
gpu_channel_host->Send(new GpuChannelMsg_DevToolsStopEventsRecording());
gpu_channel_host->RemoveRoute(gpu_route_id_);
gpu_route_id_ = MSG_ROUTING_NONE;
}
void DevToolsAgent::OnGpuTasksChunk(const std::vector<GpuTaskInfo>& tasks) {
WebDevToolsAgent* web_agent = GetWebAgent();
if (!web_agent)
return;
for (size_t i = 0; i < tasks.size(); i++) {
const GpuTaskInfo& task = tasks[i];
WebDevToolsAgent::GPUEvent event(
task.timestamp, task.phase, task.foreign, task.gpu_memory_used_bytes);
event.limitGPUMemoryBytes = task.gpu_memory_limit_bytes;
web_agent->processGPUEvent(event);
}
}
void DevToolsAgent::enableDeviceEmulation(
const blink::WebDeviceEmulationParams& params) {
GetRenderViewImpl()->EnableScreenMetricsEmulation(params);
}
void DevToolsAgent::disableDeviceEmulation() {
GetRenderViewImpl()->DisableScreenMetricsEmulation();
}
// static
DevToolsAgent* DevToolsAgent::FromRoutingId(int routing_id) {
IdToAgentMap::iterator it = g_agent_for_routing_id.Get().find(routing_id);
if (it != g_agent_for_routing_id.Get().end()) {
return it->second;
}
return NULL;
}
void DevToolsAgent::OnAttach(const std::string& host_id) {
WebDevToolsAgent* web_agent = GetWebAgent();
if (web_agent) {
web_agent->attach(WebString::fromUTF8(host_id));
is_attached_ = true;
}
}
void DevToolsAgent::OnReattach(const std::string& host_id,
const std::string& agent_state) {
WebDevToolsAgent* web_agent = GetWebAgent();
if (web_agent) {
web_agent->reattach(WebString::fromUTF8(host_id),
WebString::fromUTF8(agent_state));
is_attached_ = true;
}
}
void DevToolsAgent::OnDetach() {
WebDevToolsAgent* web_agent = GetWebAgent();
if (web_agent) {
web_agent->detach();
is_attached_ = false;
}
}
void DevToolsAgent::OnDispatchOnInspectorBackend(const std::string& message) {
TRACE_EVENT0("devtools", "DevToolsAgent::OnDispatchOnInspectorBackend");
WebDevToolsAgent* web_agent = GetWebAgent();
if (web_agent)
web_agent->dispatchOnInspectorBackend(WebString::fromUTF8(message));
}
void DevToolsAgent::OnInspectElement(
const std::string& host_id, int x, int y) {
WebDevToolsAgent* web_agent = GetWebAgent();
if (web_agent) {
web_agent->attach(WebString::fromUTF8(host_id));
web_agent->inspectElementAt(WebPoint(x, y));
is_attached_ = true;
}
}
void DevToolsAgent::OnAddMessageToConsole(ConsoleMessageLevel level,
const std::string& message) {
WebView* web_view = main_render_frame_->GetRenderView()->GetWebView();
if (!web_view)
return;
WebFrame* main_frame = web_view->mainFrame();
if (!main_frame)
return;
WebConsoleMessage::Level target_level = WebConsoleMessage::LevelLog;
switch (level) {
case CONSOLE_MESSAGE_LEVEL_DEBUG:
target_level = WebConsoleMessage::LevelDebug;
break;
case CONSOLE_MESSAGE_LEVEL_LOG:
target_level = WebConsoleMessage::LevelLog;
break;
case CONSOLE_MESSAGE_LEVEL_WARNING:
target_level = WebConsoleMessage::LevelWarning;
break;
case CONSOLE_MESSAGE_LEVEL_ERROR:
target_level = WebConsoleMessage::LevelError;
break;
}
main_frame->addMessageToConsole(
WebConsoleMessage(target_level, WebString::fromUTF8(message)));
}
void DevToolsAgent::ContinueProgram() {
WebDevToolsAgent* web_agent = GetWebAgent();
if (web_agent)
web_agent->continueProgram();
}
void DevToolsAgent::OnSetupDevToolsClient() {
// We only want to register once per render view.
if (is_devtools_client_)
return;
is_devtools_client_ = true;
new DevToolsClient(main_render_frame_);
}
WebDevToolsAgent* DevToolsAgent::GetWebAgent() {
WebView* web_view = main_render_frame_->GetRenderView()->GetWebView();
if (!web_view)
return NULL;
return web_view->devToolsAgent();
}
RenderViewImpl* DevToolsAgent::GetRenderViewImpl() {
return static_cast<RenderViewImpl*>(main_render_frame_->GetRenderView());
}
bool DevToolsAgent::IsAttached() {
return is_attached_;
}
} // namespace content