Home | History | Annotate | Download | only in devtools
      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:
     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 
     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 
     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 
     86 DevToolsAgent::~DevToolsAgent() {
     87   g_agent_for_routing_id.Get().erase(routing_id());
     88   setTraceEventCallback(NULL);
     89 }
     90 
     91 // Called on the Renderer thread.
     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 
    115 void DevToolsAgent::sendMessageToInspectorFrontend(
    116     const blink::WebString& message) {
    117   Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(routing_id(),
    118                                                          message.utf8()));
    119 }
    120 
    121 int DevToolsAgent::hostIdentifier() {
    122   return routing_id();
    123 }
    124 
    125 void DevToolsAgent::saveAgentRuntimeState(
    126     const blink::WebString& state) {
    127   Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8()));
    128 }
    129 
    130 blink::WebDevToolsAgentClient::WebKitClientMessageLoop*
    131     DevToolsAgent::createClientMessageLoop() {
    132   return new WebKitClientMessageLoopImpl();
    133 }
    134 
    135 void DevToolsAgent::clearBrowserCache() {
    136   Send(new DevToolsHostMsg_ClearBrowserCache(routing_id()));
    137 }
    138 
    139 void DevToolsAgent::clearBrowserCookies() {
    140   Send(new DevToolsHostMsg_ClearBrowserCookies(routing_id()));
    141 }
    142 
    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
    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 
    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 
    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 
    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 
    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 
    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)
    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 
    238 void DevToolsAgent::visitAllocatedObjects(AllocatedObjectVisitor* visitor) {
    239 #if defined(USE_TCMALLOC) && !defined(OS_WIN)
    240   IterateAllocatedObjects(&AllocationVisitor, visitor);
    241 #endif
    242 }
    243 
    244 // static
    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 
    253 void DevToolsAgent::OnAttach() {
    254   WebDevToolsAgent* web_agent = GetWebAgent();
    255   if (web_agent) {
    256     web_agent->attach();
    257     is_attached_ = true;
    258   }
    259 }
    260 
    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 
    269 void DevToolsAgent::OnDetach() {
    270   WebDevToolsAgent* web_agent = GetWebAgent();
    271   if (web_agent) {
    272     web_agent->detach();
    273     is_attached_ = false;
    274   }
    275 }
    276 
    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 
    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 
    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 
    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 
    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 
    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 
    344 bool DevToolsAgent::IsAttached() {
    345   return is_attached_;
    346 }
    347 
    348 }  // namespace content
    349