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