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.
5 #include "content/renderer/devtools/devtools_agent.h"
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_handle.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"
32 #if defined(USE_TCMALLOC)
33 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
36 using blink::WebConsoleMessage
;
37 using blink::WebDevToolsAgent
;
38 using blink::WebDevToolsAgentClient
;
39 using blink::WebFrame
;
40 using blink::WebPoint
;
41 using blink::WebString
;
42 using blink::WebCString
;
43 using blink::WebVector
;
46 using base::debug::TraceLog
;
47 using base::debug::TraceOptions
;
51 base::subtle::AtomicWord
DevToolsAgent::event_callback_
;
55 const size_t kMaxMessageChunkSize
= IPC::Channel::kMaximumMessageSize
/ 4;
57 class WebKitClientMessageLoopImpl
58 : public WebDevToolsAgentClient::WebKitClientMessageLoop
{
60 WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {}
61 virtual ~WebKitClientMessageLoopImpl() { message_loop_
= NULL
; }
63 base::MessageLoop::ScopedNestableTaskAllower
allow(message_loop_
);
66 virtual void quitNow() {
67 message_loop_
->QuitNow();
70 base::MessageLoop
* message_loop_
;
73 typedef std::map
<int, DevToolsAgent
*> IdToAgentMap
;
74 base::LazyInstance
<IdToAgentMap
>::Leaky
75 g_agent_for_routing_id
= LAZY_INSTANCE_INITIALIZER
;
79 DevToolsAgent::DevToolsAgent(RenderViewImpl
* render_view
)
80 : RenderViewObserver(render_view
),
82 is_devtools_client_(false),
83 gpu_route_id_(MSG_ROUTING_NONE
),
84 paused_in_mouse_move_(false) {
85 g_agent_for_routing_id
.Get()[routing_id()] = this;
87 render_view
->webview()->setDevToolsAgentClient(this);
90 DevToolsAgent::~DevToolsAgent() {
91 g_agent_for_routing_id
.Get().erase(routing_id());
92 resetTraceEventCallback();
95 // Called on the Renderer thread.
96 bool DevToolsAgent::OnMessageReceived(const IPC::Message
& message
) {
98 IPC_BEGIN_MESSAGE_MAP(DevToolsAgent
, message
)
99 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Attach
, OnAttach
)
100 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Reattach
, OnReattach
)
101 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Detach
, OnDetach
)
102 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend
,
103 OnDispatchOnInspectorBackend
)
104 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_InspectElement
, OnInspectElement
)
105 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_AddMessageToConsole
,
106 OnAddMessageToConsole
)
107 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_GpuTasksChunk
, OnGpuTasksChunk
)
108 IPC_MESSAGE_HANDLER(DevToolsMsg_SetupDevToolsClient
, OnSetupDevToolsClient
)
109 IPC_MESSAGE_UNHANDLED(handled
= false)
110 IPC_END_MESSAGE_MAP()
112 if (message
.type() == FrameMsg_Navigate::ID
||
113 message
.type() == ViewMsg_Close::ID
)
114 ContinueProgram(); // Don't want to swallow the message.
119 void DevToolsAgent::sendMessageToInspectorFrontend(
120 const blink::WebString
& message
) {
121 std::string
msg(message
.utf8());
122 if (msg
.length() < kMaxMessageChunkSize
) {
123 Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
124 routing_id(), msg
, msg
.size()));
128 for (size_t pos
= 0; pos
< msg
.length(); pos
+= kMaxMessageChunkSize
) {
129 Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
131 msg
.substr(pos
, kMaxMessageChunkSize
),
132 pos
? 0 : msg
.size()));
136 long DevToolsAgent::processId() {
137 return base::GetCurrentProcId();
140 int DevToolsAgent::debuggerId() {
144 void DevToolsAgent::saveAgentRuntimeState(
145 const blink::WebString
& state
) {
146 Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state
.utf8()));
149 blink::WebDevToolsAgentClient::WebKitClientMessageLoop
*
150 DevToolsAgent::createClientMessageLoop() {
151 return new WebKitClientMessageLoopImpl();
154 void DevToolsAgent::willEnterDebugLoop() {
155 RenderViewImpl
* impl
= static_cast<RenderViewImpl
*>(render_view());
156 paused_in_mouse_move_
= impl
->SendAckForMouseMoveFromDebugger();
159 void DevToolsAgent::didExitDebugLoop() {
160 RenderViewImpl
* impl
= static_cast<RenderViewImpl
*>(render_view());
161 if (paused_in_mouse_move_
) {
162 impl
->IgnoreAckForMouseMoveFromDebugger();
163 paused_in_mouse_move_
= false;
167 void DevToolsAgent::resetTraceEventCallback()
169 TraceLog::GetInstance()->SetEventCallbackDisabled();
170 base::subtle::NoBarrier_Store(&event_callback_
, 0);
173 void DevToolsAgent::setTraceEventCallback(const WebString
& category_filter
,
174 TraceEventCallback cb
) {
175 TraceLog
* trace_log
= TraceLog::GetInstance();
176 base::subtle::NoBarrier_Store(&event_callback_
,
177 reinterpret_cast<base::subtle::AtomicWord
>(cb
));
179 trace_log
->SetEventCallbackEnabled(base::debug::CategoryFilter(
180 category_filter
.utf8()), TraceEventCallbackWrapper
);
182 trace_log
->SetEventCallbackDisabled();
186 void DevToolsAgent::enableTracing(const WebString
& category_filter
) {
187 TraceLog
* trace_log
= TraceLog::GetInstance();
188 trace_log
->SetEnabled(base::debug::CategoryFilter(category_filter
.utf8()),
189 TraceLog::RECORDING_MODE
,
193 void DevToolsAgent::disableTracing() {
194 TraceLog::GetInstance()->SetDisabled();
198 void DevToolsAgent::TraceEventCallbackWrapper(
199 base::TimeTicks timestamp
,
201 const unsigned char* category_group_enabled
,
203 unsigned long long id
,
205 const char* const arg_names
[],
206 const unsigned char arg_types
[],
207 const unsigned long long arg_values
[],
208 unsigned char flags
) {
209 TraceEventCallback callback
=
210 reinterpret_cast<TraceEventCallback
>(
211 base::subtle::NoBarrier_Load(&event_callback_
));
213 double timestamp_seconds
= (timestamp
- base::TimeTicks()).InSecondsF();
214 callback(phase
, category_group_enabled
, name
, id
, num_args
,
215 arg_names
, arg_types
, arg_values
, flags
, timestamp_seconds
);
219 void DevToolsAgent::startGPUEventsRecording() {
220 GpuChannelHost
* gpu_channel_host
=
221 RenderThreadImpl::current()->GetGpuChannel();
222 if (!gpu_channel_host
)
224 DCHECK(gpu_route_id_
== MSG_ROUTING_NONE
);
225 int32 route_id
= gpu_channel_host
->GenerateRouteID();
226 bool succeeded
= false;
227 gpu_channel_host
->Send(
228 new GpuChannelMsg_DevToolsStartEventsRecording(route_id
, &succeeded
));
231 gpu_route_id_
= route_id
;
232 gpu_channel_host
->AddRoute(gpu_route_id_
, AsWeakPtr());
236 void DevToolsAgent::stopGPUEventsRecording() {
237 GpuChannelHost
* gpu_channel_host
=
238 RenderThreadImpl::current()->GetGpuChannel();
239 if (!gpu_channel_host
|| gpu_route_id_
== MSG_ROUTING_NONE
)
241 gpu_channel_host
->Send(new GpuChannelMsg_DevToolsStopEventsRecording());
242 gpu_channel_host
->RemoveRoute(gpu_route_id_
);
243 gpu_route_id_
= MSG_ROUTING_NONE
;
246 void DevToolsAgent::OnGpuTasksChunk(const std::vector
<GpuTaskInfo
>& tasks
) {
247 WebDevToolsAgent
* web_agent
= GetWebAgent();
250 for (size_t i
= 0; i
< tasks
.size(); i
++) {
251 const GpuTaskInfo
& task
= tasks
[i
];
252 WebDevToolsAgent::GPUEvent
event(
253 task
.timestamp
, task
.phase
, task
.foreign
, task
.gpu_memory_used_bytes
);
254 event
.limitGPUMemoryBytes
= task
.gpu_memory_limit_bytes
;
255 web_agent
->processGPUEvent(event
);
259 void DevToolsAgent::enableDeviceEmulation(
260 const blink::WebDeviceEmulationParams
& params
) {
261 RenderViewImpl
* impl
= static_cast<RenderViewImpl
*>(render_view());
262 impl
->EnableScreenMetricsEmulation(params
);
265 void DevToolsAgent::disableDeviceEmulation() {
266 RenderViewImpl
* impl
= static_cast<RenderViewImpl
*>(render_view());
267 impl
->DisableScreenMetricsEmulation();
271 DevToolsAgent
* DevToolsAgent::FromRoutingId(int routing_id
) {
272 IdToAgentMap::iterator it
= g_agent_for_routing_id
.Get().find(routing_id
);
273 if (it
!= g_agent_for_routing_id
.Get().end()) {
279 void DevToolsAgent::OnAttach(const std::string
& host_id
) {
280 WebDevToolsAgent
* web_agent
= GetWebAgent();
282 web_agent
->attach(WebString::fromUTF8(host_id
));
287 void DevToolsAgent::OnReattach(const std::string
& host_id
,
288 const std::string
& agent_state
) {
289 WebDevToolsAgent
* web_agent
= GetWebAgent();
291 web_agent
->reattach(WebString::fromUTF8(host_id
),
292 WebString::fromUTF8(agent_state
));
297 void DevToolsAgent::OnDetach() {
298 WebDevToolsAgent
* web_agent
= GetWebAgent();
301 is_attached_
= false;
305 void DevToolsAgent::OnDispatchOnInspectorBackend(const std::string
& message
) {
306 TRACE_EVENT0("devtools", "DevToolsAgent::OnDispatchOnInspectorBackend");
307 WebDevToolsAgent
* web_agent
= GetWebAgent();
309 web_agent
->dispatchOnInspectorBackend(WebString::fromUTF8(message
));
312 void DevToolsAgent::OnInspectElement(
313 const std::string
& host_id
, int x
, int y
) {
314 WebDevToolsAgent
* web_agent
= GetWebAgent();
316 web_agent
->attach(WebString::fromUTF8(host_id
));
317 web_agent
->inspectElementAt(WebPoint(x
, y
));
322 void DevToolsAgent::OnAddMessageToConsole(ConsoleMessageLevel level
,
323 const std::string
& message
) {
324 WebView
* web_view
= render_view()->GetWebView();
328 WebFrame
* main_frame
= web_view
->mainFrame();
332 WebConsoleMessage::Level target_level
= WebConsoleMessage::LevelLog
;
334 case CONSOLE_MESSAGE_LEVEL_DEBUG
:
335 target_level
= WebConsoleMessage::LevelDebug
;
337 case CONSOLE_MESSAGE_LEVEL_LOG
:
338 target_level
= WebConsoleMessage::LevelLog
;
340 case CONSOLE_MESSAGE_LEVEL_WARNING
:
341 target_level
= WebConsoleMessage::LevelWarning
;
343 case CONSOLE_MESSAGE_LEVEL_ERROR
:
344 target_level
= WebConsoleMessage::LevelError
;
347 main_frame
->addMessageToConsole(
348 WebConsoleMessage(target_level
, WebString::fromUTF8(message
)));
351 void DevToolsAgent::ContinueProgram() {
352 WebDevToolsAgent
* web_agent
= GetWebAgent();
354 web_agent
->continueProgram();
357 void DevToolsAgent::OnSetupDevToolsClient() {
358 // We only want to register once per render view.
359 if (is_devtools_client_
)
361 is_devtools_client_
= true;
362 new DevToolsClient(static_cast<RenderViewImpl
*>(render_view()));
365 WebDevToolsAgent
* DevToolsAgent::GetWebAgent() {
366 WebView
* web_view
= render_view()->GetWebView();
369 return web_view
->devToolsAgent();
372 bool DevToolsAgent::IsAttached() {
376 } // namespace content