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