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/public/browser/content_browser_client.h"
     21 #include "content/public/browser/notification_service.h"
     22 #include "content/public/browser/notification_types.h"
     23 
     24 namespace content {
     25 
     26 typedef std::vector<RenderViewDevToolsAgentHost*> Instances;
     27 
     28 namespace {
     29 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
     30 
     31 static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) {
     32   if (g_instances == NULL)
     33     return NULL;
     34   for (Instances::iterator it = g_instances.Get().begin();
     35        it != g_instances.Get().end(); ++it) {
     36     if (rvh == (*it)->render_view_host())
     37       return *it;
     38   }
     39   return NULL;
     40 }
     41 
     42 }  // namespace
     43 
     44 class RenderViewDevToolsAgentHost::DevToolsAgentHostRvhObserver
     45     : public RenderViewHostObserver {
     46  public:
     47   DevToolsAgentHostRvhObserver(RenderViewHost* rvh,
     48                                RenderViewDevToolsAgentHost* agent_host)
     49       : RenderViewHostObserver(rvh),
     50         agent_host_(agent_host) {
     51   }
     52   virtual ~DevToolsAgentHostRvhObserver() {}
     53 
     54   // RenderViewHostObserver overrides.
     55   virtual void RenderViewHostDestroyed(RenderViewHost* rvh) OVERRIDE {
     56     agent_host_->RenderViewHostDestroyed(rvh);
     57   }
     58   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
     59     return agent_host_->OnRvhMessageReceived(message);
     60   }
     61  private:
     62   RenderViewDevToolsAgentHost* agent_host_;
     63   DISALLOW_COPY_AND_ASSIGN(DevToolsAgentHostRvhObserver);
     64 };
     65 
     66 // static
     67 scoped_refptr<DevToolsAgentHost>
     68 DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) {
     69   RenderViewDevToolsAgentHost* result = FindAgentHost(rvh);
     70   if (!result)
     71     result = new RenderViewDevToolsAgentHost(rvh);
     72   return result;
     73 }
     74 
     75 // static
     76 bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) {
     77   return FindAgentHost(rvh) != NULL;
     78 }
     79 
     80 // static
     81 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
     82   if (g_instances == NULL)
     83     return false;
     84   DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
     85   if (!devtools_manager)
     86     return false;
     87   RenderViewHostDelegate* delegate =
     88       static_cast<WebContentsImpl*>(web_contents);
     89   for (Instances::iterator it = g_instances.Get().begin();
     90        it != g_instances.Get().end(); ++it) {
     91     RenderViewHost* rvh = (*it)->render_view_host_;
     92     if (rvh && rvh->GetDelegate() != delegate)
     93       continue;
     94     if ((*it)->IsAttached())
     95       return true;
     96   }
     97   return false;
     98 }
     99 
    100 //static
    101 std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() {
    102   std::vector<RenderViewHost*> result;
    103   RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts();
    104   for (size_t i = 0; i < widgets.size(); ++i) {
    105     // Ignore processes that don't have a connection, such as crashed contents.
    106     if (!widgets[i]->GetProcess()->HasConnection())
    107       continue;
    108     if (!widgets[i]->IsRenderView())
    109       continue;
    110 
    111     RenderViewHost* rvh = RenderViewHost::From(widgets[i]);
    112     WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
    113     // Don't report a RenderViewHost if it is not the current RenderViewHost
    114     // for some WebContents.
    115     if (!web_contents || rvh != web_contents->GetRenderViewHost())
    116       continue;
    117 
    118     result.push_back(rvh);
    119   }
    120   return result;
    121 }
    122 
    123 // static
    124 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
    125     RenderViewHost* pending,
    126     RenderViewHost* current) {
    127   RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending);
    128   if (!agent_host)
    129     return;
    130   agent_host->DisconnectRenderViewHost();
    131   agent_host->ConnectRenderViewHost(current);
    132 }
    133 
    134 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(
    135     RenderViewHost* rvh)
    136     : overrides_handler_(new RendererOverridesHandler(this)),
    137       tracing_handler_(new DevToolsTracingHandler())
    138  {
    139   SetRenderViewHost(rvh);
    140   DevToolsProtocol::Notifier notifier(base::Bind(
    141       &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend,
    142       base::Unretained(this)));
    143   overrides_handler_->SetNotifier(notifier);
    144   tracing_handler_->SetNotifier(notifier);
    145   g_instances.Get().push_back(this);
    146   RenderViewHostDelegate* delegate = render_view_host_->GetDelegate();
    147   if (delegate && delegate->GetAsWebContents())
    148     Observe(delegate->GetAsWebContents());
    149   AddRef();  // Balanced in RenderViewHostDestroyed.
    150 }
    151 
    152 RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() {
    153   return render_view_host_;
    154 }
    155 
    156 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
    157     const std::string& message) {
    158   std::string error_message;
    159   scoped_refptr<DevToolsProtocol::Command> command =
    160       DevToolsProtocol::ParseCommand(message, &error_message);
    161 
    162   if (command) {
    163     scoped_refptr<DevToolsProtocol::Response> overridden_response =
    164         overrides_handler_->HandleCommand(command);
    165     if (!overridden_response)
    166       overridden_response = tracing_handler_->HandleCommand(command);
    167     if (overridden_response) {
    168       if (!overridden_response->is_async_promise())
    169         OnDispatchOnInspectorFrontend(overridden_response->Serialize());
    170       return;
    171     }
    172   }
    173 
    174   IPCDevToolsAgentHost::DispatchOnInspectorBackend(message);
    175 }
    176 
    177 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) {
    178   if (!render_view_host_)
    179     return;
    180   msg->set_routing_id(render_view_host_->GetRoutingID());
    181   render_view_host_->Send(msg);
    182 }
    183 
    184 void RenderViewDevToolsAgentHost::OnClientAttached() {
    185   if (!render_view_host_)
    186     return;
    187 
    188   ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
    189       render_view_host_->GetProcess()->GetID());
    190 
    191   // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
    192   // ExtensionProcessManager no longer relies on this notification.
    193   DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
    194 }
    195 
    196 void RenderViewDevToolsAgentHost::OnClientDetached() {
    197   if (!render_view_host_)
    198     return;
    199 
    200   bool process_has_agents = false;
    201   RenderProcessHost* render_process_host = render_view_host_->GetProcess();
    202   for (Instances::iterator it = g_instances.Get().begin();
    203        it != g_instances.Get().end(); ++it) {
    204     if (*it == this || !(*it)->IsAttached())
    205       continue;
    206     RenderViewHost* rvh = (*it)->render_view_host();
    207     if (rvh && rvh->GetProcess() == render_process_host)
    208       process_has_agents = true;
    209   }
    210 
    211   // We are the last to disconnect from the renderer -> revoke permissions.
    212   if (!process_has_agents) {
    213     ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
    214         render_process_host->GetID());
    215   }
    216 
    217   // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
    218   // ExtensionProcessManager no longer relies on this notification.
    219   DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
    220 }
    221 
    222 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
    223   Instances::iterator it = std::find(g_instances.Get().begin(),
    224                                      g_instances.Get().end(),
    225                                      this);
    226   if (it != g_instances.Get().end())
    227     g_instances.Get().erase(it);
    228 }
    229 
    230 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
    231     RenderViewHost* dest_rvh) {
    232   if (!render_view_host_)
    233     return;
    234 
    235   if (render_view_host_ == dest_rvh && static_cast<RenderViewHostImpl*>(
    236           render_view_host_)->render_view_termination_status() ==
    237               base::TERMINATION_STATUS_STILL_RUNNING)
    238     return;
    239   DisconnectRenderViewHost();
    240   ConnectRenderViewHost(dest_rvh);
    241 }
    242 
    243 void RenderViewDevToolsAgentHost::RenderProcessGone(
    244     base::TerminationStatus status) {
    245   switch(status) {
    246     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
    247     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
    248     case base::TERMINATION_STATUS_PROCESS_CRASHED:
    249       RenderViewCrashed();
    250       break;
    251     default:
    252       break;
    253   }
    254 }
    255 
    256 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
    257   if (!render_view_host_)
    258     return;
    259   // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
    260   // Connect back to the real one.
    261   WebContents* web_contents =
    262     WebContents::FromRenderViewHost(render_view_host_);
    263   if (!web_contents)
    264     return;
    265   DisconnectRenderViewHost();
    266   ConnectRenderViewHost(web_contents->GetRenderViewHost());
    267 }
    268 
    269 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) {
    270   render_view_host_ = rvh;
    271   rvh_observer_.reset(new DevToolsAgentHostRvhObserver(rvh, this));
    272 }
    273 
    274 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
    275   SetRenderViewHost(rvh);
    276   if (IsAttached())
    277     Reattach(state_);
    278 }
    279 
    280 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
    281   OnClientDetached();
    282   rvh_observer_.reset();
    283   render_view_host_ = NULL;
    284 }
    285 
    286 void RenderViewDevToolsAgentHost::RenderViewHostDestroyed(
    287     RenderViewHost* rvh) {
    288   DCHECK(render_view_host_);
    289   scoped_refptr<RenderViewDevToolsAgentHost> protect(this);
    290   NotifyCloseListener();
    291   render_view_host_ = NULL;
    292   Release();
    293 }
    294 
    295 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
    296   scoped_refptr<DevToolsProtocol::Notification> notification =
    297       DevToolsProtocol::CreateNotification(
    298           devtools::Inspector::targetCrashed::kName, NULL);
    299   DevToolsManagerImpl::GetInstance()->
    300       DispatchOnInspectorFrontend(this, notification->Serialize());
    301 }
    302 
    303 bool RenderViewDevToolsAgentHost::OnRvhMessageReceived(
    304     const IPC::Message& message) {
    305   bool handled = true;
    306   IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, message)
    307     IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
    308                         OnDispatchOnInspectorFrontend)
    309     IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
    310                         OnSaveAgentRuntimeState)
    311     IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCache, OnClearBrowserCache)
    312     IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCookies,
    313                         OnClearBrowserCookies)
    314     IPC_MESSAGE_UNHANDLED(handled = false)
    315   IPC_END_MESSAGE_MAP()
    316   return handled;
    317 }
    318 
    319 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
    320     const std::string& state) {
    321   if (!render_view_host_)
    322     return;
    323   state_ = state;
    324 }
    325 
    326 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
    327     const std::string& message) {
    328   if (!render_view_host_)
    329     return;
    330   DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
    331       this, message);
    332 }
    333 
    334 void RenderViewDevToolsAgentHost::OnClearBrowserCache() {
    335   if (render_view_host_)
    336     GetContentClient()->browser()->ClearCache(render_view_host_);
    337 }
    338 
    339 void RenderViewDevToolsAgentHost::OnClearBrowserCookies() {
    340   if (render_view_host_)
    341     GetContentClient()->browser()->ClearCookies(render_view_host_);
    342 }
    343 
    344 }  // namespace content
    345