• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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