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:
WebKitClientMessageLoopImpl()55 WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {}
~WebKitClientMessageLoopImpl()56 virtual ~WebKitClientMessageLoopImpl() { message_loop_ = NULL; }
run()57 virtual void run() {
58 base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_);
59 message_loop_->Run();
60 }
quitNow()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
DevToolsAgent(RenderViewImpl * render_view)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
~DevToolsAgent()86 DevToolsAgent::~DevToolsAgent() {
87 g_agent_for_routing_id.Get().erase(routing_id());
88 setTraceEventCallback(NULL);
89 }
90
91 // Called on the Renderer thread.
OnMessageReceived(const IPC::Message & message)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
sendMessageToInspectorFrontend(const blink::WebString & message)115 void DevToolsAgent::sendMessageToInspectorFrontend(
116 const blink::WebString& message) {
117 Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(routing_id(),
118 message.utf8()));
119 }
120
hostIdentifier()121 int DevToolsAgent::hostIdentifier() {
122 return routing_id();
123 }
124
saveAgentRuntimeState(const blink::WebString & state)125 void DevToolsAgent::saveAgentRuntimeState(
126 const blink::WebString& state) {
127 Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8()));
128 }
129
130 blink::WebDevToolsAgentClient::WebKitClientMessageLoop*
createClientMessageLoop()131 DevToolsAgent::createClientMessageLoop() {
132 return new WebKitClientMessageLoopImpl();
133 }
134
clearBrowserCache()135 void DevToolsAgent::clearBrowserCache() {
136 Send(new DevToolsHostMsg_ClearBrowserCache(routing_id()));
137 }
138
clearBrowserCookies()139 void DevToolsAgent::clearBrowserCookies() {
140 Send(new DevToolsHostMsg_ClearBrowserCookies(routing_id()));
141 }
142
setTraceEventCallback(TraceEventCallback cb)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
TraceEventCallbackWrapper(base::TimeTicks timestamp,char phase,const unsigned char * category_group_enabled,const char * name,unsigned long long id,int num_args,const char * const arg_names[],const unsigned char arg_types[],const unsigned long long arg_values[],unsigned char flags)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
startGPUEventsRecording()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
stopGPUEventsRecording()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
OnGpuTasksChunk(const std::vector<GpuTaskInfo> & tasks)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
enableDeviceEmulation(const blink::WebRect & device_rect,const blink::WebRect & view_rect,float device_scale_factor,bool fit_to_view)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
disableDeviceEmulation()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)
AllocationVisitor(void * data,const void * ptr)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
visitAllocatedObjects(AllocatedObjectVisitor * visitor)238 void DevToolsAgent::visitAllocatedObjects(AllocatedObjectVisitor* visitor) {
239 #if defined(USE_TCMALLOC) && !defined(OS_WIN)
240 IterateAllocatedObjects(&AllocationVisitor, visitor);
241 #endif
242 }
243
244 // static
FromHostId(int host_id)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
OnAttach()253 void DevToolsAgent::OnAttach() {
254 WebDevToolsAgent* web_agent = GetWebAgent();
255 if (web_agent) {
256 web_agent->attach();
257 is_attached_ = true;
258 }
259 }
260
OnReattach(const std::string & agent_state)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
OnDetach()269 void DevToolsAgent::OnDetach() {
270 WebDevToolsAgent* web_agent = GetWebAgent();
271 if (web_agent) {
272 web_agent->detach();
273 is_attached_ = false;
274 }
275 }
276
OnDispatchOnInspectorBackend(const std::string & message)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
OnInspectElement(int x,int y)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
OnAddMessageToConsole(ConsoleMessageLevel level,const std::string & message)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
ContinueProgram()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
OnSetupDevToolsClient()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
GetWebAgent()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
IsAttached()344 bool DevToolsAgent::IsAttached() {
345 return is_attached_;
346 }
347
348 } // namespace content
349