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