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 34 using blink::WebConsoleMessage; 35 using blink::WebDevToolsAgent; 36 using blink::WebDevToolsAgentClient; 37 using blink::WebFrame; 38 using blink::WebPoint; 39 using blink::WebString; 40 using blink::WebCString; 41 using blink::WebVector; 42 using blink::WebView; 43 44 using base::debug::TraceLog; 45 46 namespace content { 47 48 base::subtle::AtomicWord DevToolsAgent::event_callback_; 49 50 namespace { 51 52 class 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 68 typedef std::map<int, DevToolsAgent*> IdToAgentMap; 69 base::LazyInstance<IdToAgentMap>::Leaky 70 g_agent_for_routing_id = LAZY_INSTANCE_INITIALIZER; 71 72 } // namespace 73 74 DevToolsAgent::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 86 DevToolsAgent::~DevToolsAgent() { 87 g_agent_for_routing_id.Get().erase(routing_id()); 88 setTraceEventCallback(NULL); 89 } 90 91 // Called on the Renderer thread. 92 bool 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 115 void DevToolsAgent::sendMessageToInspectorFrontend( 116 const blink::WebString& message) { 117 Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(routing_id(), 118 message.utf8())); 119 } 120 121 int DevToolsAgent::hostIdentifier() { 122 return routing_id(); 123 } 124 125 void DevToolsAgent::saveAgentRuntimeState( 126 const blink::WebString& state) { 127 Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8())); 128 } 129 130 blink::WebDevToolsAgentClient::WebKitClientMessageLoop* 131 DevToolsAgent::createClientMessageLoop() { 132 return new WebKitClientMessageLoopImpl(); 133 } 134 135 void DevToolsAgent::clearBrowserCache() { 136 Send(new DevToolsHostMsg_ClearBrowserCache(routing_id())); 137 } 138 139 void DevToolsAgent::clearBrowserCookies() { 140 Send(new DevToolsHostMsg_ClearBrowserCookies(routing_id())); 141 } 142 143 void 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 157 void 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 178 void 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 192 void 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 202 void 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 214 void 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 225 void DevToolsAgent::disableDeviceEmulation() { 226 RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view()); 227 impl->DisableScreenMetricsEmulation(); 228 } 229 230 #if defined(USE_TCMALLOC) && !defined(OS_WIN) 231 static 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 238 void DevToolsAgent::visitAllocatedObjects(AllocatedObjectVisitor* visitor) { 239 #if defined(USE_TCMALLOC) && !defined(OS_WIN) 240 IterateAllocatedObjects(&AllocationVisitor, visitor); 241 #endif 242 } 243 244 // static 245 DevToolsAgent* 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 253 void DevToolsAgent::OnAttach() { 254 WebDevToolsAgent* web_agent = GetWebAgent(); 255 if (web_agent) { 256 web_agent->attach(); 257 is_attached_ = true; 258 } 259 } 260 261 void 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 269 void DevToolsAgent::OnDetach() { 270 WebDevToolsAgent* web_agent = GetWebAgent(); 271 if (web_agent) { 272 web_agent->detach(); 273 is_attached_ = false; 274 } 275 } 276 277 void 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 284 void 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 292 void 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 321 void 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 329 void 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 337 WebDevToolsAgent* DevToolsAgent::GetWebAgent() { 338 WebView* web_view = render_view()->GetWebView(); 339 if (!web_view) 340 return NULL; 341 return web_view->devToolsAgent(); 342 } 343 344 bool DevToolsAgent::IsAttached() { 345 return is_attached_; 346 } 347 348 } // namespace content 349