• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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|
FindAgentHost(WebContents * 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 
FindAgentHost(RenderViewHost * rvh)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>
GetOrCreateFor(WebContents * web_contents)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>
GetOrCreateFor(RenderViewHost * rvh)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
HasFor(RenderViewHost * rvh)87 bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) {
88   return FindAgentHost(rvh) != NULL;
89 }
90 
91 // static
IsDebuggerAttached(WebContents * web_contents)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
GetValidRenderViewHosts()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
OnCancelPendingNavigation(RenderViewHost * pending,RenderViewHost * current)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
DispatchIPCMessage(RenderViewHost * source,const IPC::Message & message)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 
RenderViewDevToolsAgentHost(RenderViewHost * rvh)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 
GetRenderViewHost()182 RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() {
183   return render_view_host_;
184 }
185 
DispatchOnInspectorBackend(const std::string & message)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 
SendMessageToAgent(IPC::Message * msg)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 
OnClientAttached()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 
InnerOnClientAttached()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 
OnClientDetached()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 
ClientDetachedFromRenderer()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 
InnerClientDetachedFromRenderer()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 
~RenderViewDevToolsAgentHost()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 
AboutToNavigateRenderView(RenderViewHost * dest_rvh)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 
RenderViewHostChanged(RenderViewHost * old_host,RenderViewHost * new_host)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
ReattachToRenderViewHost(RenderViewHost * rvh)330 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost* rvh) {
331   DCHECK(!reattaching_);
332   reattaching_ = true;
333   DisconnectRenderViewHost();
334   ConnectRenderViewHost(rvh);
335   reattaching_ = false;
336 }
337 
RenderViewDeleted(RenderViewHost * rvh)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 
RenderProcessGone(base::TerminationStatus status)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 
DidAttachInterstitialPage()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 
Observe(int type,const NotificationSource & source,const NotificationDetails & details)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 
SetRenderViewHost(RenderViewHost * rvh)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 
ClearRenderViewHost()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 
ConnectRenderViewHost(RenderViewHost * rvh)408 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
409   SetRenderViewHost(rvh);
410   if (IsAttached())
411     Reattach(state_);
412 }
413 
DisconnectRenderViewHost()414 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
415   ClientDetachedFromRenderer();
416   ClearRenderViewHost();
417 }
418 
RenderViewCrashed()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 
DispatchIPCMessage(const IPC::Message & msg)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 
OnSwapCompositorFrame(const IPC::Message & message)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 
SynchronousSwapCompositorFrame(const cc::CompositorFrameMetadata & frame_metadata)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 
OnSaveAgentRuntimeState(const std::string & state)460 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
461     const std::string& state) {
462   if (!render_view_host_)
463     return;
464   state_ = state;
465 }
466 
OnDispatchOnInspectorFrontend(const std::string & message)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