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/browser/devtools/render_view_devtools_agent_host.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/lazy_instance.h"
      9 #include "content/browser/child_process_security_policy_impl.h"
     10 #include "content/browser/devtools/devtools_manager_impl.h"
     11 #include "content/browser/devtools/devtools_power_handler.h"
     12 #include "content/browser/devtools/devtools_protocol.h"
     13 #include "content/browser/devtools/devtools_protocol_constants.h"
     14 #include "content/browser/devtools/devtools_tracing_handler.h"
     15 #include "content/browser/devtools/renderer_overrides_handler.h"
     16 #include "content/browser/renderer_host/render_process_host_impl.h"
     17 #include "content/browser/renderer_host/render_view_host_impl.h"
     18 #include "content/browser/site_instance_impl.h"
     19 #include "content/browser/web_contents/web_contents_impl.h"
     20 #include "content/common/devtools_messages.h"
     21 #include "content/common/view_messages.h"
     22 #include "content/public/browser/content_browser_client.h"
     23 #include "content/public/browser/devtools_manager_delegate.h"
     24 #include "content/public/browser/notification_service.h"
     25 #include "content/public/browser/notification_types.h"
     26 #include "content/public/browser/render_widget_host_iterator.h"
     27 
     28 #if defined(OS_ANDROID)
     29 #include "content/browser/power_save_blocker_impl.h"
     30 #include "content/public/browser/render_widget_host_view.h"
     31 #endif
     32 
     33 namespace content {
     34 
     35 typedef std::vector<RenderViewDevToolsAgentHost*> Instances;
     36 
     37 namespace {
     38 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
     39 
     40 //Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
     41 //instances associated with |web_contents|
     42 static RenderViewDevToolsAgentHost* FindAgentHost(WebContents* web_contents) {
     43   if (g_instances == NULL)
     44     return NULL;
     45   RenderViewHostDelegate* delegate =
     46       static_cast<WebContentsImpl*>(web_contents);
     47   for (Instances::iterator it = g_instances.Get().begin();
     48        it != g_instances.Get().end(); ++it) {
     49     RenderViewHost* rvh = (*it)->render_view_host();
     50     if (rvh && rvh->GetDelegate() == delegate)
     51       return *it;
     52   }
     53   return NULL;
     54 }
     55 
     56 static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) {
     57   if (g_instances == NULL)
     58     return NULL;
     59   for (Instances::iterator it = g_instances.Get().begin();
     60        it != g_instances.Get().end(); ++it) {
     61     if (rvh == (*it)->render_view_host())
     62       return *it;
     63   }
     64   return NULL;
     65 }
     66 
     67 }  // namespace
     68 
     69 scoped_refptr<DevToolsAgentHost>
     70 DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
     71   RenderViewDevToolsAgentHost* result = FindAgentHost(web_contents);
     72   if (!result)
     73     result = new RenderViewDevToolsAgentHost(web_contents->GetRenderViewHost());
     74   return result;
     75 }
     76 
     77 // static
     78 scoped_refptr<DevToolsAgentHost>
     79 DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) {
     80   RenderViewDevToolsAgentHost* result = FindAgentHost(rvh);
     81   if (!result)
     82     result = new RenderViewDevToolsAgentHost(rvh);
     83   return result;
     84 }
     85 
     86 // static
     87 bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) {
     88   return FindAgentHost(rvh) != NULL;
     89 }
     90 
     91 // static
     92 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
     93   if (g_instances == NULL)
     94     return false;
     95   DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
     96   if (!devtools_manager)
     97     return false;
     98   RenderViewHostDelegate* delegate =
     99       static_cast<WebContentsImpl*>(web_contents);
    100   for (Instances::iterator it = g_instances.Get().begin();
    101        it != g_instances.Get().end(); ++it) {
    102     RenderViewHost* rvh = (*it)->render_view_host_;
    103     if (rvh && rvh->GetDelegate() != delegate)
    104       continue;
    105     if ((*it)->IsAttached())
    106       return true;
    107   }
    108   return false;
    109 }
    110 
    111 //static
    112 std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() {
    113   std::vector<RenderViewHost*> result;
    114   scoped_ptr<RenderWidgetHostIterator> widgets(
    115       RenderWidgetHost::GetRenderWidgetHosts());
    116   while (RenderWidgetHost* widget = widgets->GetNextHost()) {
    117     // Ignore processes that don't have a connection, such as crashed contents.
    118     if (!widget->GetProcess()->HasConnection())
    119       continue;
    120     if (!widget->IsRenderView())
    121       continue;
    122 
    123     RenderViewHost* rvh = RenderViewHost::From(widget);
    124     WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
    125     if (!web_contents)
    126       continue;
    127 
    128     // Don't report a RenderViewHost if it is not the current RenderViewHost
    129     // for some WebContents (this filters out pre-render RVHs and similar).
    130     // However report a RenderViewHost created for an out of process iframe.
    131     // TODO (kaznacheev): Revisit this when it is clear how OOP iframes
    132     // interact with pre-rendering.
    133     // TODO (kaznacheev): GetMainFrame() call is a temporary hack. Iterate over
    134     // all RenderFrameHost instances when multiple OOP frames are supported.
    135     if (rvh != web_contents->GetRenderViewHost() &&
    136         !rvh->GetMainFrame()->IsCrossProcessSubframe()) {
    137       continue;
    138     }
    139 
    140     result.push_back(rvh);
    141   }
    142   return result;
    143 }
    144 
    145 // static
    146 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
    147     RenderViewHost* pending,
    148     RenderViewHost* current) {
    149   RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending);
    150   if (!agent_host)
    151     return;
    152   agent_host->DisconnectRenderViewHost();
    153   agent_host->ConnectRenderViewHost(current);
    154 }
    155 
    156 // static
    157 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
    158     RenderViewHost* source,
    159     const IPC::Message& message) {
    160   RenderViewDevToolsAgentHost* agent_host = FindAgentHost(source);
    161   return agent_host && agent_host->DispatchIPCMessage(message);
    162 }
    163 
    164 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost* rvh)
    165     : render_view_host_(NULL),
    166       overrides_handler_(new RendererOverridesHandler(this)),
    167       tracing_handler_(
    168           new DevToolsTracingHandler(DevToolsTracingHandler::Renderer)),
    169       power_handler_(new DevToolsPowerHandler()),
    170       reattaching_(false) {
    171   SetRenderViewHost(rvh);
    172   DevToolsProtocol::Notifier notifier(base::Bind(
    173       &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend,
    174       base::Unretained(this)));
    175   overrides_handler_->SetNotifier(notifier);
    176   tracing_handler_->SetNotifier(notifier);
    177   power_handler_->SetNotifier(notifier);
    178   g_instances.Get().push_back(this);
    179   AddRef();  // Balanced in RenderViewHostDestroyed.
    180 }
    181 
    182 RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() {
    183   return render_view_host_;
    184 }
    185 
    186 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
    187     const std::string& message) {
    188   std::string error_message;
    189 
    190   scoped_ptr<base::DictionaryValue> message_dict(
    191       DevToolsProtocol::ParseMessage(message, &error_message));
    192   scoped_refptr<DevToolsProtocol::Command> command =
    193       DevToolsProtocol::ParseCommand(message_dict.get(), &error_message);
    194 
    195   if (command) {
    196     scoped_refptr<DevToolsProtocol::Response> overridden_response;
    197 
    198     DevToolsManagerDelegate* delegate =
    199         DevToolsManagerImpl::GetInstance()->delegate();
    200     if (delegate) {
    201       scoped_ptr<base::DictionaryValue> overridden_response_value(
    202           delegate->HandleCommand(this, message_dict.get()));
    203       if (overridden_response_value)
    204         overridden_response = DevToolsProtocol::ParseResponse(
    205             overridden_response_value.get());
    206     }
    207     if (!overridden_response)
    208       overridden_response = overrides_handler_->HandleCommand(command);
    209     if (!overridden_response)
    210       overridden_response = tracing_handler_->HandleCommand(command);
    211     if (!overridden_response)
    212       overridden_response = power_handler_->HandleCommand(command);
    213     if (overridden_response) {
    214       if (!overridden_response->is_async_promise())
    215         OnDispatchOnInspectorFrontend(overridden_response->Serialize());
    216       return;
    217     }
    218   }
    219 
    220   IPCDevToolsAgentHost::DispatchOnInspectorBackend(message);
    221 }
    222 
    223 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) {
    224   if (!render_view_host_)
    225     return;
    226   msg->set_routing_id(render_view_host_->GetRoutingID());
    227   render_view_host_->Send(msg);
    228 }
    229 
    230 void RenderViewDevToolsAgentHost::OnClientAttached() {
    231   if (!render_view_host_)
    232     return;
    233 
    234   InnerOnClientAttached();
    235 
    236   // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
    237   // extensions::ProcessManager no longer relies on this notification.
    238   if (!reattaching_)
    239     DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
    240 }
    241 
    242 void RenderViewDevToolsAgentHost::InnerOnClientAttached() {
    243   ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
    244       render_view_host_->GetProcess()->GetID());
    245 
    246 #if defined(OS_ANDROID)
    247   power_save_blocker_.reset(
    248       static_cast<PowerSaveBlockerImpl*>(
    249           PowerSaveBlocker::Create(
    250               PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
    251               "DevTools").release()));
    252   if (render_view_host_->GetView()) {
    253     power_save_blocker_.get()->
    254         InitDisplaySleepBlocker(render_view_host_->GetView()->GetNativeView());
    255   }
    256 #endif
    257 }
    258 
    259 void RenderViewDevToolsAgentHost::OnClientDetached() {
    260 #if defined(OS_ANDROID)
    261   power_save_blocker_.reset();
    262 #endif
    263   overrides_handler_->OnClientDetached();
    264   tracing_handler_->OnClientDetached();
    265   ClientDetachedFromRenderer();
    266 }
    267 
    268 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
    269   if (!render_view_host_)
    270     return;
    271 
    272   InnerClientDetachedFromRenderer();
    273 
    274   // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
    275   // extensions::ProcessManager no longer relies on this notification.
    276   if (!reattaching_)
    277     DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
    278 }
    279 
    280 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() {
    281   bool process_has_agents = false;
    282   RenderProcessHost* render_process_host = render_view_host_->GetProcess();
    283   for (Instances::iterator it = g_instances.Get().begin();
    284        it != g_instances.Get().end(); ++it) {
    285     if (*it == this || !(*it)->IsAttached())
    286       continue;
    287     RenderViewHost* rvh = (*it)->render_view_host();
    288     if (rvh && rvh->GetProcess() == render_process_host)
    289       process_has_agents = true;
    290   }
    291 
    292   // We are the last to disconnect from the renderer -> revoke permissions.
    293   if (!process_has_agents) {
    294     ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
    295         render_process_host->GetID());
    296   }
    297 }
    298 
    299 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
    300   Instances::iterator it = std::find(g_instances.Get().begin(),
    301                                      g_instances.Get().end(),
    302                                      this);
    303   if (it != g_instances.Get().end())
    304     g_instances.Get().erase(it);
    305 }
    306 
    307 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
    308     RenderViewHost* dest_rvh) {
    309   if (!render_view_host_)
    310     return;
    311 
    312   if (render_view_host_ == dest_rvh && static_cast<RenderViewHostImpl*>(
    313           render_view_host_)->render_view_termination_status() ==
    314               base::TERMINATION_STATUS_STILL_RUNNING)
    315     return;
    316   ReattachToRenderViewHost(dest_rvh);
    317 }
    318 
    319 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
    320     RenderViewHost* old_host,
    321     RenderViewHost* new_host) {
    322   if (new_host != render_view_host_) {
    323     // AboutToNavigateRenderView was not called for renderer-initiated
    324     // navigation.
    325     ReattachToRenderViewHost(new_host);
    326   }
    327 }
    328 
    329 void
    330 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost* rvh) {
    331   DCHECK(!reattaching_);
    332   reattaching_ = true;
    333   DisconnectRenderViewHost();
    334   ConnectRenderViewHost(rvh);
    335   reattaching_ = false;
    336 }
    337 
    338 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost* rvh) {
    339   if (rvh != render_view_host_)
    340     return;
    341 
    342   DCHECK(render_view_host_);
    343   scoped_refptr<RenderViewDevToolsAgentHost> protect(this);
    344   NotifyCloseListener();
    345   ClearRenderViewHost();
    346   Release();
    347 }
    348 
    349 void RenderViewDevToolsAgentHost::RenderProcessGone(
    350     base::TerminationStatus status) {
    351   switch(status) {
    352     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
    353     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
    354     case base::TERMINATION_STATUS_PROCESS_CRASHED:
    355 #if defined(OS_ANDROID)
    356     case base::TERMINATION_STATUS_OOM_PROTECTED:
    357 #endif
    358       RenderViewCrashed();
    359       break;
    360     default:
    361       break;
    362   }
    363 }
    364 
    365 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
    366   if (!render_view_host_)
    367     return;
    368   // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
    369   // Connect back to the real one.
    370   WebContents* web_contents =
    371     WebContents::FromRenderViewHost(render_view_host_);
    372   if (!web_contents)
    373     return;
    374   DisconnectRenderViewHost();
    375   ConnectRenderViewHost(web_contents->GetRenderViewHost());
    376 }
    377 
    378 void RenderViewDevToolsAgentHost::Observe(int type,
    379                                           const NotificationSource& source,
    380                                           const NotificationDetails& details) {
    381   if (type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) {
    382     bool visible = *Details<bool>(details).ptr();
    383     overrides_handler_->OnVisibilityChanged(visible);
    384   }
    385 }
    386 
    387 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) {
    388   DCHECK(!render_view_host_);
    389   render_view_host_ = rvh;
    390 
    391   WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh));
    392 
    393   registrar_.Add(
    394       this,
    395       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
    396       content::Source<RenderWidgetHost>(render_view_host_));
    397 }
    398 
    399 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
    400   DCHECK(render_view_host_);
    401   registrar_.Remove(
    402       this,
    403       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
    404       content::Source<RenderWidgetHost>(render_view_host_));
    405   render_view_host_ = NULL;
    406 }
    407 
    408 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
    409   SetRenderViewHost(rvh);
    410   if (IsAttached())
    411     Reattach(state_);
    412 }
    413 
    414 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
    415   ClientDetachedFromRenderer();
    416   ClearRenderViewHost();
    417 }
    418 
    419 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
    420   scoped_refptr<DevToolsProtocol::Notification> notification =
    421       DevToolsProtocol::CreateNotification(
    422           devtools::Inspector::targetCrashed::kName, NULL);
    423   DevToolsManagerImpl::GetInstance()->
    424       DispatchOnInspectorFrontend(this, notification->Serialize());
    425 }
    426 
    427 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
    428     const IPC::Message& msg) {
    429   if (!render_view_host_)
    430     return false;
    431 
    432   bool handled = true;
    433   IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, msg)
    434     IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
    435                         OnDispatchOnInspectorFrontend)
    436     IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
    437                         OnSaveAgentRuntimeState)
    438     IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
    439                                 handled = false; OnSwapCompositorFrame(msg))
    440     IPC_MESSAGE_UNHANDLED(handled = false)
    441   IPC_END_MESSAGE_MAP()
    442   return handled;
    443 }
    444 
    445 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
    446     const IPC::Message& message) {
    447   ViewHostMsg_SwapCompositorFrame::Param param;
    448   if (!ViewHostMsg_SwapCompositorFrame::Read(&message, &param))
    449     return;
    450   overrides_handler_->OnSwapCompositorFrame(param.b.metadata);
    451 }
    452 
    453 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
    454     const cc::CompositorFrameMetadata& frame_metadata) {
    455   if (!render_view_host_)
    456     return;
    457   overrides_handler_->OnSwapCompositorFrame(frame_metadata);
    458 }
    459 
    460 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
    461     const std::string& state) {
    462   if (!render_view_host_)
    463     return;
    464   state_ = state;
    465 }
    466 
    467 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
    468     const std::string& message) {
    469   if (!render_view_host_)
    470     return;
    471   DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
    472       this, message);
    473 }
    474 
    475 }  // namespace content
    476