1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/devtools/devtools_agent.h" 6 7#include <map> 8 9#include "base/debug/trace_event.h" 10#include "base/lazy_instance.h" 11#include "base/message_loop/message_loop.h" 12#include "base/process/process.h" 13#include "base/strings/string_number_conversions.h" 14#include "content/common/devtools_messages.h" 15#include "content/common/frame_messages.h" 16#include "content/common/gpu/gpu_messages.h" 17#include "content/common/view_messages.h" 18#include "content/renderer/devtools/devtools_agent_filter.h" 19#include "content/renderer/devtools/devtools_client.h" 20#include "content/renderer/render_thread_impl.h" 21#include "content/renderer/render_view_impl.h" 22#include "third_party/WebKit/public/platform/WebPoint.h" 23#include "third_party/WebKit/public/platform/WebString.h" 24#include "third_party/WebKit/public/web/WebConsoleMessage.h" 25#include "third_party/WebKit/public/web/WebConsoleMessage.h" 26#include "third_party/WebKit/public/web/WebDevToolsAgent.h" 27#include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" 28#include "third_party/WebKit/public/web/WebFrame.h" 29#include "third_party/WebKit/public/web/WebSettings.h" 30#include "third_party/WebKit/public/web/WebView.h" 31 32#if defined(USE_TCMALLOC) 33#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" 34#endif 35 36using blink::WebConsoleMessage; 37using blink::WebDevToolsAgent; 38using blink::WebDevToolsAgentClient; 39using blink::WebFrame; 40using blink::WebPoint; 41using blink::WebString; 42using blink::WebCString; 43using blink::WebVector; 44using blink::WebView; 45 46using base::debug::TraceLog; 47using base::debug::TraceOptions; 48 49namespace content { 50 51base::subtle::AtomicWord DevToolsAgent::event_callback_; 52 53namespace { 54 55class WebKitClientMessageLoopImpl 56 : public WebDevToolsAgentClient::WebKitClientMessageLoop { 57 public: 58 WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {} 59 virtual ~WebKitClientMessageLoopImpl() { message_loop_ = NULL; } 60 virtual void run() { 61 base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_); 62 message_loop_->Run(); 63 } 64 virtual void quitNow() { 65 message_loop_->QuitNow(); 66 } 67 private: 68 base::MessageLoop* message_loop_; 69}; 70 71typedef std::map<int, DevToolsAgent*> IdToAgentMap; 72base::LazyInstance<IdToAgentMap>::Leaky 73 g_agent_for_routing_id = LAZY_INSTANCE_INITIALIZER; 74 75} // namespace 76 77DevToolsAgent::DevToolsAgent(RenderViewImpl* render_view) 78 : RenderViewObserver(render_view), 79 is_attached_(false), 80 is_devtools_client_(false), 81 gpu_route_id_(MSG_ROUTING_NONE), 82 paused_in_mouse_move_(false) { 83 g_agent_for_routing_id.Get()[routing_id()] = this; 84 85 render_view->webview()->setDevToolsAgentClient(this); 86} 87 88DevToolsAgent::~DevToolsAgent() { 89 g_agent_for_routing_id.Get().erase(routing_id()); 90 resetTraceEventCallback(); 91} 92 93// Called on the Renderer thread. 94bool DevToolsAgent::OnMessageReceived(const IPC::Message& message) { 95 bool handled = true; 96 IPC_BEGIN_MESSAGE_MAP(DevToolsAgent, message) 97 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Attach, OnAttach) 98 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Reattach, OnReattach) 99 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Detach, OnDetach) 100 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend, 101 OnDispatchOnInspectorBackend) 102 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_InspectElement, OnInspectElement) 103 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_AddMessageToConsole, 104 OnAddMessageToConsole) 105 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_GpuTasksChunk, OnGpuTasksChunk) 106 IPC_MESSAGE_HANDLER(DevToolsMsg_SetupDevToolsClient, OnSetupDevToolsClient) 107 IPC_MESSAGE_UNHANDLED(handled = false) 108 IPC_END_MESSAGE_MAP() 109 110 if (message.type() == FrameMsg_Navigate::ID || 111 message.type() == ViewMsg_Close::ID) 112 ContinueProgram(); // Don't want to swallow the message. 113 114 return handled; 115} 116 117void DevToolsAgent::sendMessageToInspectorFrontend( 118 const blink::WebString& message) { 119 Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(routing_id(), 120 message.utf8())); 121} 122 123long DevToolsAgent::processId() { 124 return base::Process::Current().pid(); 125} 126 127int DevToolsAgent::debuggerId() { 128 return routing_id(); 129} 130 131void DevToolsAgent::saveAgentRuntimeState( 132 const blink::WebString& state) { 133 Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8())); 134} 135 136blink::WebDevToolsAgentClient::WebKitClientMessageLoop* 137 DevToolsAgent::createClientMessageLoop() { 138 return new WebKitClientMessageLoopImpl(); 139} 140 141void DevToolsAgent::willEnterDebugLoop() { 142 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 143 paused_in_mouse_move_ = impl->SendAckForMouseMoveFromDebugger(); 144} 145 146void DevToolsAgent::didExitDebugLoop() { 147 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 148 if (paused_in_mouse_move_) { 149 impl->IgnoreAckForMouseMoveFromDebugger(); 150 paused_in_mouse_move_ = false; 151 } 152} 153 154void DevToolsAgent::resetTraceEventCallback() 155{ 156 TraceLog::GetInstance()->SetEventCallbackDisabled(); 157 base::subtle::NoBarrier_Store(&event_callback_, 0); 158} 159 160void DevToolsAgent::setTraceEventCallback(const WebString& category_filter, 161 TraceEventCallback cb) { 162 TraceLog* trace_log = TraceLog::GetInstance(); 163 base::subtle::NoBarrier_Store(&event_callback_, 164 reinterpret_cast<base::subtle::AtomicWord>(cb)); 165 if (!!cb) { 166 trace_log->SetEventCallbackEnabled(base::debug::CategoryFilter( 167 category_filter.utf8()), TraceEventCallbackWrapper); 168 } else { 169 trace_log->SetEventCallbackDisabled(); 170 } 171} 172 173void DevToolsAgent::enableTracing(const WebString& category_filter) { 174 TraceLog* trace_log = TraceLog::GetInstance(); 175 trace_log->SetEnabled(base::debug::CategoryFilter(category_filter.utf8()), 176 TraceLog::RECORDING_MODE, 177 TraceOptions()); 178} 179 180void DevToolsAgent::disableTracing() { 181 TraceLog::GetInstance()->SetDisabled(); 182} 183 184// static 185void DevToolsAgent::TraceEventCallbackWrapper( 186 base::TimeTicks timestamp, 187 char phase, 188 const unsigned char* category_group_enabled, 189 const char* name, 190 unsigned long long id, 191 int num_args, 192 const char* const arg_names[], 193 const unsigned char arg_types[], 194 const unsigned long long arg_values[], 195 unsigned char flags) { 196 TraceEventCallback callback = 197 reinterpret_cast<TraceEventCallback>( 198 base::subtle::NoBarrier_Load(&event_callback_)); 199 if (callback) { 200 double timestamp_seconds = (timestamp - base::TimeTicks()).InSecondsF(); 201 callback(phase, category_group_enabled, name, id, num_args, 202 arg_names, arg_types, arg_values, flags, timestamp_seconds); 203 } 204} 205 206void DevToolsAgent::startGPUEventsRecording() { 207 GpuChannelHost* gpu_channel_host = 208 RenderThreadImpl::current()->GetGpuChannel(); 209 if (!gpu_channel_host) 210 return; 211 DCHECK(gpu_route_id_ == MSG_ROUTING_NONE); 212 int32 route_id = gpu_channel_host->GenerateRouteID(); 213 bool succeeded = false; 214 gpu_channel_host->Send( 215 new GpuChannelMsg_DevToolsStartEventsRecording(route_id, &succeeded)); 216 DCHECK(succeeded); 217 if (succeeded) { 218 gpu_route_id_ = route_id; 219 gpu_channel_host->AddRoute(gpu_route_id_, AsWeakPtr()); 220 } 221} 222 223void DevToolsAgent::stopGPUEventsRecording() { 224 GpuChannelHost* gpu_channel_host = 225 RenderThreadImpl::current()->GetGpuChannel(); 226 if (!gpu_channel_host || gpu_route_id_ == MSG_ROUTING_NONE) 227 return; 228 gpu_channel_host->Send(new GpuChannelMsg_DevToolsStopEventsRecording()); 229 gpu_channel_host->RemoveRoute(gpu_route_id_); 230 gpu_route_id_ = MSG_ROUTING_NONE; 231} 232 233void DevToolsAgent::OnGpuTasksChunk(const std::vector<GpuTaskInfo>& tasks) { 234 WebDevToolsAgent* web_agent = GetWebAgent(); 235 if (!web_agent) 236 return; 237 for (size_t i = 0; i < tasks.size(); i++) { 238 const GpuTaskInfo& task = tasks[i]; 239 WebDevToolsAgent::GPUEvent event( 240 task.timestamp, task.phase, task.foreign, task.gpu_memory_used_bytes); 241 event.limitGPUMemoryBytes = task.gpu_memory_limit_bytes; 242 web_agent->processGPUEvent(event); 243 } 244} 245 246void DevToolsAgent::enableDeviceEmulation( 247 const blink::WebDeviceEmulationParams& params) { 248 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 249 impl->EnableScreenMetricsEmulation(params); 250} 251 252void DevToolsAgent::disableDeviceEmulation() { 253 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 254 impl->DisableScreenMetricsEmulation(); 255} 256 257// static 258DevToolsAgent* DevToolsAgent::FromRoutingId(int routing_id) { 259 IdToAgentMap::iterator it = g_agent_for_routing_id.Get().find(routing_id); 260 if (it != g_agent_for_routing_id.Get().end()) { 261 return it->second; 262 } 263 return NULL; 264} 265 266void DevToolsAgent::OnAttach(const std::string& host_id) { 267 WebDevToolsAgent* web_agent = GetWebAgent(); 268 if (web_agent) { 269 web_agent->attach(WebString::fromUTF8(host_id)); 270 is_attached_ = true; 271 } 272} 273 274void DevToolsAgent::OnReattach(const std::string& host_id, 275 const std::string& agent_state) { 276 WebDevToolsAgent* web_agent = GetWebAgent(); 277 if (web_agent) { 278 web_agent->reattach(WebString::fromUTF8(host_id), 279 WebString::fromUTF8(agent_state)); 280 is_attached_ = true; 281 } 282} 283 284void DevToolsAgent::OnDetach() { 285 WebDevToolsAgent* web_agent = GetWebAgent(); 286 if (web_agent) { 287 web_agent->detach(); 288 is_attached_ = false; 289 } 290} 291 292void DevToolsAgent::OnDispatchOnInspectorBackend(const std::string& message) { 293 TRACE_EVENT0("devtools", "DevToolsAgent::OnDispatchOnInspectorBackend"); 294 WebDevToolsAgent* web_agent = GetWebAgent(); 295 if (web_agent) 296 web_agent->dispatchOnInspectorBackend(WebString::fromUTF8(message)); 297} 298 299void DevToolsAgent::OnInspectElement( 300 const std::string& host_id, int x, int y) { 301 WebDevToolsAgent* web_agent = GetWebAgent(); 302 if (web_agent) { 303 web_agent->attach(WebString::fromUTF8(host_id)); 304 web_agent->inspectElementAt(WebPoint(x, y)); 305 is_attached_ = true; 306 } 307} 308 309void DevToolsAgent::OnAddMessageToConsole(ConsoleMessageLevel level, 310 const std::string& message) { 311 WebView* web_view = render_view()->GetWebView(); 312 if (!web_view) 313 return; 314 315 WebFrame* main_frame = web_view->mainFrame(); 316 if (!main_frame) 317 return; 318 319 WebConsoleMessage::Level target_level = WebConsoleMessage::LevelLog; 320 switch (level) { 321 case CONSOLE_MESSAGE_LEVEL_DEBUG: 322 target_level = WebConsoleMessage::LevelDebug; 323 break; 324 case CONSOLE_MESSAGE_LEVEL_LOG: 325 target_level = WebConsoleMessage::LevelLog; 326 break; 327 case CONSOLE_MESSAGE_LEVEL_WARNING: 328 target_level = WebConsoleMessage::LevelWarning; 329 break; 330 case CONSOLE_MESSAGE_LEVEL_ERROR: 331 target_level = WebConsoleMessage::LevelError; 332 break; 333 } 334 main_frame->addMessageToConsole( 335 WebConsoleMessage(target_level, WebString::fromUTF8(message))); 336} 337 338void DevToolsAgent::ContinueProgram() { 339 WebDevToolsAgent* web_agent = GetWebAgent(); 340 if (web_agent) 341 web_agent->continueProgram(); 342} 343 344void DevToolsAgent::OnSetupDevToolsClient() { 345 // We only want to register once per render view. 346 if (is_devtools_client_) 347 return; 348 is_devtools_client_ = true; 349 new DevToolsClient(static_cast<RenderViewImpl*>(render_view())); 350} 351 352WebDevToolsAgent* DevToolsAgent::GetWebAgent() { 353 WebView* web_view = render_view()->GetWebView(); 354 if (!web_view) 355 return NULL; 356 return web_view->devToolsAgent(); 357} 358 359bool DevToolsAgent::IsAttached() { 360 return is_attached_; 361} 362 363} // namespace content 364