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