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
34using blink::WebConsoleMessage;
35using blink::WebDevToolsAgent;
36using blink::WebDevToolsAgentClient;
37using blink::WebFrame;
38using blink::WebPoint;
39using blink::WebString;
40using blink::WebCString;
41using blink::WebVector;
42using blink::WebView;
43
44using base::debug::TraceLog;
45
46namespace content {
47
48base::subtle::AtomicWord DevToolsAgent::event_callback_;
49
50namespace {
51
52class WebKitClientMessageLoopImpl
53    : public WebDevToolsAgentClient::WebKitClientMessageLoop {
54 public:
55  WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {}
56  virtual ~WebKitClientMessageLoopImpl() { message_loop_ = NULL; }
57  virtual void run() {
58    base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_);
59    message_loop_->Run();
60  }
61  virtual void quitNow() {
62    message_loop_->QuitNow();
63  }
64 private:
65  base::MessageLoop* message_loop_;
66};
67
68typedef std::map<int, DevToolsAgent*> IdToAgentMap;
69base::LazyInstance<IdToAgentMap>::Leaky
70    g_agent_for_routing_id = LAZY_INSTANCE_INITIALIZER;
71
72} //  namespace
73
74DevToolsAgent::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
86DevToolsAgent::~DevToolsAgent() {
87  g_agent_for_routing_id.Get().erase(routing_id());
88  setTraceEventCallback(NULL);
89}
90
91// Called on the Renderer thread.
92bool 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
115void DevToolsAgent::sendMessageToInspectorFrontend(
116    const blink::WebString& message) {
117  Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(routing_id(),
118                                                         message.utf8()));
119}
120
121int DevToolsAgent::hostIdentifier() {
122  return routing_id();
123}
124
125void DevToolsAgent::saveAgentRuntimeState(
126    const blink::WebString& state) {
127  Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8()));
128}
129
130blink::WebDevToolsAgentClient::WebKitClientMessageLoop*
131    DevToolsAgent::createClientMessageLoop() {
132  return new WebKitClientMessageLoopImpl();
133}
134
135void DevToolsAgent::clearBrowserCache() {
136  Send(new DevToolsHostMsg_ClearBrowserCache(routing_id()));
137}
138
139void DevToolsAgent::clearBrowserCookies() {
140  Send(new DevToolsHostMsg_ClearBrowserCookies(routing_id()));
141}
142
143void 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
157void 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
178void 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
192void 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
202void 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
214void 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
225void DevToolsAgent::disableDeviceEmulation() {
226  RenderViewImpl* impl = static_cast<RenderViewImpl*>(render_view());
227  impl->DisableScreenMetricsEmulation();
228}
229
230#if defined(USE_TCMALLOC) && !defined(OS_WIN)
231static 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
238void DevToolsAgent::visitAllocatedObjects(AllocatedObjectVisitor* visitor) {
239#if defined(USE_TCMALLOC) && !defined(OS_WIN)
240  IterateAllocatedObjects(&AllocationVisitor, visitor);
241#endif
242}
243
244// static
245DevToolsAgent* 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
253void DevToolsAgent::OnAttach() {
254  WebDevToolsAgent* web_agent = GetWebAgent();
255  if (web_agent) {
256    web_agent->attach();
257    is_attached_ = true;
258  }
259}
260
261void 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
269void DevToolsAgent::OnDetach() {
270  WebDevToolsAgent* web_agent = GetWebAgent();
271  if (web_agent) {
272    web_agent->detach();
273    is_attached_ = false;
274  }
275}
276
277void 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
284void 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
292void 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
321void 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
329void 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
337WebDevToolsAgent* DevToolsAgent::GetWebAgent() {
338  WebView* web_view = render_view()->GetWebView();
339  if (!web_view)
340    return NULL;
341  return web_view->devToolsAgent();
342}
343
344bool DevToolsAgent::IsAttached() {
345  return is_attached_;
346}
347
348}  // namespace content
349