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; 47 48namespace content { 49 50base::subtle::AtomicWord DevToolsAgent::event_callback_; 51 52namespace { 53 54class WebKitClientMessageLoopImpl 55 : public WebDevToolsAgentClient::WebKitClientMessageLoop { 56 public: 57 WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {} 58 virtual ~WebKitClientMessageLoopImpl() { message_loop_ = NULL; } 59 virtual void run() { 60 base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_); 61 message_loop_->Run(); 62 } 63 virtual void quitNow() { 64 message_loop_->QuitNow(); 65 } 66 private: 67 base::MessageLoop* message_loop_; 68}; 69 70typedef std::map<int, DevToolsAgent*> IdToAgentMap; 71base::LazyInstance<IdToAgentMap>::Leaky 72 g_agent_for_routing_id = LAZY_INSTANCE_INITIALIZER; 73 74} // namespace 75 76DevToolsAgent::DevToolsAgent(RenderViewImpl* render_view) 77 : RenderViewObserver(render_view), 78 is_attached_(false), 79 is_devtools_client_(false), 80 gpu_route_id_(MSG_ROUTING_NONE), 81 paused_in_mouse_move_(false) { 82 g_agent_for_routing_id.Get()[routing_id()] = this; 83 84 render_view->webview()->setDevToolsAgentClient(this); 85 render_view->webview()->devToolsAgent()->setProcessId( 86 base::Process::Current().pid()); 87} 88 89DevToolsAgent::~DevToolsAgent() { 90 g_agent_for_routing_id.Get().erase(routing_id()); 91 resetTraceEventCallback(); 92} 93 94// Called on the Renderer thread. 95bool DevToolsAgent::OnMessageReceived(const IPC::Message& message) { 96 bool handled = true; 97 IPC_BEGIN_MESSAGE_MAP(DevToolsAgent, message) 98 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Attach, OnAttach) 99 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Reattach, OnReattach) 100 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Detach, OnDetach) 101 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend, 102 OnDispatchOnInspectorBackend) 103 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_InspectElement, OnInspectElement) 104 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_AddMessageToConsole, 105 OnAddMessageToConsole) 106 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_GpuTasksChunk, OnGpuTasksChunk) 107 IPC_MESSAGE_HANDLER(DevToolsMsg_SetupDevToolsClient, OnSetupDevToolsClient) 108 IPC_MESSAGE_UNHANDLED(handled = false) 109 IPC_END_MESSAGE_MAP() 110 111 if (message.type() == FrameMsg_Navigate::ID || 112 message.type() == ViewMsg_Close::ID) 113 ContinueProgram(); // Don't want to swallow the message. 114 115 return handled; 116} 117 118void DevToolsAgent::sendMessageToInspectorFrontend( 119 const blink::WebString& message) { 120 Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(routing_id(), 121 message.utf8())); 122} 123 124int DevToolsAgent::debuggerId() { 125 return routing_id(); 126} 127 128void DevToolsAgent::saveAgentRuntimeState( 129 const blink::WebString& state) { 130 Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8())); 131} 132 133blink::WebDevToolsAgentClient::WebKitClientMessageLoop* 134 DevToolsAgent::createClientMessageLoop() { 135 return new WebKitClientMessageLoopImpl(); 136} 137 138void DevToolsAgent::willEnterDebugLoop() { 139 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 140 paused_in_mouse_move_ = impl->SendAckForMouseMoveFromDebugger(); 141} 142 143void DevToolsAgent::didExitDebugLoop() { 144 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 145 if (paused_in_mouse_move_) { 146 impl->IgnoreAckForMouseMoveFromDebugger(); 147 paused_in_mouse_move_ = false; 148 } 149} 150 151void DevToolsAgent::resetTraceEventCallback() 152{ 153 TraceLog::GetInstance()->SetEventCallbackDisabled(); 154 base::subtle::NoBarrier_Store(&event_callback_, 0); 155} 156 157void DevToolsAgent::setTraceEventCallback(const WebString& category_filter, 158 TraceEventCallback cb) { 159 TraceLog* trace_log = TraceLog::GetInstance(); 160 base::subtle::NoBarrier_Store(&event_callback_, 161 reinterpret_cast<base::subtle::AtomicWord>(cb)); 162 if (!!cb) { 163 trace_log->SetEventCallbackEnabled(base::debug::CategoryFilter( 164 category_filter.utf8()), TraceEventCallbackWrapper); 165 } else { 166 trace_log->SetEventCallbackDisabled(); 167 } 168} 169 170void DevToolsAgent::enableTracing(const WebString& category_filter) { 171 TraceLog* trace_log = TraceLog::GetInstance(); 172 trace_log->SetEnabled(base::debug::CategoryFilter(category_filter.utf8()), 173 TraceLog::RECORDING_MODE, 174 TraceLog::RECORD_UNTIL_FULL); 175} 176 177void DevToolsAgent::disableTracing() { 178 TraceLog::GetInstance()->SetDisabled(); 179} 180 181// static 182void DevToolsAgent::TraceEventCallbackWrapper( 183 base::TimeTicks timestamp, 184 char phase, 185 const unsigned char* category_group_enabled, 186 const char* name, 187 unsigned long long id, 188 int num_args, 189 const char* const arg_names[], 190 const unsigned char arg_types[], 191 const unsigned long long arg_values[], 192 unsigned char flags) { 193 TraceEventCallback callback = 194 reinterpret_cast<TraceEventCallback>( 195 base::subtle::NoBarrier_Load(&event_callback_)); 196 if (callback) { 197 double timestamp_seconds = (timestamp - base::TimeTicks()).InSecondsF(); 198 callback(phase, category_group_enabled, name, id, num_args, 199 arg_names, arg_types, arg_values, flags, timestamp_seconds); 200 } 201} 202 203void DevToolsAgent::startGPUEventsRecording() { 204 GpuChannelHost* gpu_channel_host = 205 RenderThreadImpl::current()->GetGpuChannel(); 206 if (!gpu_channel_host) 207 return; 208 DCHECK(gpu_route_id_ == MSG_ROUTING_NONE); 209 int32 route_id = gpu_channel_host->GenerateRouteID(); 210 bool succeeded = false; 211 gpu_channel_host->Send( 212 new GpuChannelMsg_DevToolsStartEventsRecording(route_id, &succeeded)); 213 DCHECK(succeeded); 214 if (succeeded) { 215 gpu_route_id_ = route_id; 216 gpu_channel_host->AddRoute(gpu_route_id_, AsWeakPtr()); 217 } 218} 219 220void DevToolsAgent::stopGPUEventsRecording() { 221 GpuChannelHost* gpu_channel_host = 222 RenderThreadImpl::current()->GetGpuChannel(); 223 if (!gpu_channel_host || gpu_route_id_ == MSG_ROUTING_NONE) 224 return; 225 gpu_channel_host->Send(new GpuChannelMsg_DevToolsStopEventsRecording()); 226 gpu_channel_host->RemoveRoute(gpu_route_id_); 227 gpu_route_id_ = MSG_ROUTING_NONE; 228} 229 230void DevToolsAgent::OnGpuTasksChunk(const std::vector<GpuTaskInfo>& tasks) { 231 WebDevToolsAgent* web_agent = GetWebAgent(); 232 if (!web_agent) 233 return; 234 for (size_t i = 0; i < tasks.size(); i++) { 235 const GpuTaskInfo& task = tasks[i]; 236 WebDevToolsAgent::GPUEvent event( 237 task.timestamp, task.phase, task.foreign, task.gpu_memory_used_bytes); 238 event.limitGPUMemoryBytes = task.gpu_memory_limit_bytes; 239 web_agent->processGPUEvent(event); 240 } 241} 242 243void DevToolsAgent::enableDeviceEmulation( 244 const blink::WebDeviceEmulationParams& params) { 245 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 246 impl->EnableScreenMetricsEmulation(params); 247} 248 249void DevToolsAgent::disableDeviceEmulation() { 250 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 251 impl->DisableScreenMetricsEmulation(); 252} 253 254void DevToolsAgent::setTouchEventEmulationEnabled( 255 bool enabled, bool allow_pinch) { 256 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 257 impl->Send(new ViewHostMsg_SetTouchEventEmulationEnabled( 258 impl->routing_id(), enabled, allow_pinch)); 259} 260 261#if defined(USE_TCMALLOC) && !defined(OS_WIN) 262static void AllocationVisitor(void* data, const void* ptr) { 263 typedef blink::WebDevToolsAgentClient::AllocatedObjectVisitor Visitor; 264 Visitor* visitor = reinterpret_cast<Visitor*>(data); 265 visitor->visitObject(ptr); 266} 267#endif 268 269void DevToolsAgent::visitAllocatedObjects(AllocatedObjectVisitor* visitor) { 270#if defined(USE_TCMALLOC) && !defined(OS_WIN) 271 IterateAllocatedObjects(&AllocationVisitor, visitor); 272#endif 273} 274 275// static 276DevToolsAgent* DevToolsAgent::FromRoutingId(int routing_id) { 277 IdToAgentMap::iterator it = g_agent_for_routing_id.Get().find(routing_id); 278 if (it != g_agent_for_routing_id.Get().end()) { 279 return it->second; 280 } 281 return NULL; 282} 283 284void DevToolsAgent::OnAttach(const std::string& host_id) { 285 WebDevToolsAgent* web_agent = GetWebAgent(); 286 if (web_agent) { 287 web_agent->attach(WebString::fromUTF8(host_id)); 288 is_attached_ = true; 289 } 290} 291 292void DevToolsAgent::OnReattach(const std::string& host_id, 293 const std::string& agent_state) { 294 WebDevToolsAgent* web_agent = GetWebAgent(); 295 if (web_agent) { 296 web_agent->reattach(WebString::fromUTF8(host_id), 297 WebString::fromUTF8(agent_state)); 298 is_attached_ = true; 299 } 300} 301 302void DevToolsAgent::OnDetach() { 303 WebDevToolsAgent* web_agent = GetWebAgent(); 304 if (web_agent) { 305 web_agent->detach(); 306 is_attached_ = false; 307 } 308} 309 310void DevToolsAgent::OnDispatchOnInspectorBackend(const std::string& message) { 311 TRACE_EVENT0("devtools", "DevToolsAgent::OnDispatchOnInspectorBackend"); 312 WebDevToolsAgent* web_agent = GetWebAgent(); 313 if (web_agent) 314 web_agent->dispatchOnInspectorBackend(WebString::fromUTF8(message)); 315} 316 317void DevToolsAgent::OnInspectElement( 318 const std::string& host_id, int x, int y) { 319 WebDevToolsAgent* web_agent = GetWebAgent(); 320 if (web_agent) { 321 web_agent->attach(WebString::fromUTF8(host_id)); 322 web_agent->inspectElementAt(WebPoint(x, y)); 323 is_attached_ = true; 324 } 325} 326 327void DevToolsAgent::OnAddMessageToConsole(ConsoleMessageLevel level, 328 const std::string& message) { 329 WebView* web_view = render_view()->GetWebView(); 330 if (!web_view) 331 return; 332 333 WebFrame* main_frame = web_view->mainFrame(); 334 if (!main_frame) 335 return; 336 337 WebConsoleMessage::Level target_level = WebConsoleMessage::LevelLog; 338 switch (level) { 339 case CONSOLE_MESSAGE_LEVEL_DEBUG: 340 target_level = WebConsoleMessage::LevelDebug; 341 break; 342 case CONSOLE_MESSAGE_LEVEL_LOG: 343 target_level = WebConsoleMessage::LevelLog; 344 break; 345 case CONSOLE_MESSAGE_LEVEL_WARNING: 346 target_level = WebConsoleMessage::LevelWarning; 347 break; 348 case CONSOLE_MESSAGE_LEVEL_ERROR: 349 target_level = WebConsoleMessage::LevelError; 350 break; 351 } 352 main_frame->addMessageToConsole( 353 WebConsoleMessage(target_level, WebString::fromUTF8(message))); 354} 355 356void DevToolsAgent::ContinueProgram() { 357 WebDevToolsAgent* web_agent = GetWebAgent(); 358 // TODO(pfeldman): rename didNavigate to continueProgram upstream. 359 // That is in fact the purpose of the signal. 360 if (web_agent) 361 web_agent->didNavigate(); 362} 363 364void DevToolsAgent::OnSetupDevToolsClient() { 365 // We only want to register once per render view. 366 if (is_devtools_client_) 367 return; 368 is_devtools_client_ = true; 369 new DevToolsClient(static_cast<RenderViewImpl*>(render_view())); 370} 371 372WebDevToolsAgent* DevToolsAgent::GetWebAgent() { 373 WebView* web_view = render_view()->GetWebView(); 374 if (!web_view) 375 return NULL; 376 return web_view->devToolsAgent(); 377} 378 379bool DevToolsAgent::IsAttached() { 380 return is_attached_; 381} 382 383} // namespace content 384