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