• 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_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 
FindAgentHost(RenderViewHost * rvh)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>
GetOrCreateFor(RenderViewHost * rvh)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
HasFor(RenderViewHost * rvh)61 bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) {
62   return FindAgentHost(rvh) != NULL;
63 }
64 
65 // static
IsDebuggerAttached(WebContents * web_contents)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
GetValidRenderViewHosts()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
OnCancelPendingNavigation(RenderViewHost * pending,RenderViewHost * current)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 
RenderViewDevToolsAgentHost(RenderViewHost * rvh)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 
GetRenderViewHost()136 RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() {
137   return render_view_host_;
138 }
139 
DispatchOnInspectorBackend(const std::string & message)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 
SendMessageToAgent(IPC::Message * msg)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 
OnClientAttached()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 
OnClientDetached()192 void RenderViewDevToolsAgentHost::OnClientDetached() {
193 #if defined(OS_ANDROID)
194   power_save_blocker_.reset();
195 #endif
196   overrides_handler_->OnClientDetached();
197   ClientDetachedFromRenderer();
198 }
199 
ClientDetachedFromRenderer()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 
~RenderViewDevToolsAgentHost()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 
AboutToNavigateRenderView(RenderViewHost * dest_rvh)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 
RenderViewHostChanged(RenderViewHost * old_host,RenderViewHost * new_host)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 
RenderViewDeleted(RenderViewHost * rvh)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 
RenderProcessGone(base::TerminationStatus status)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 
DidAttachInterstitialPage()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 
Observe(int type,const NotificationSource & source,const NotificationDetails & details)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 
SetRenderViewHost(RenderViewHost * rvh)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 
ClearRenderViewHost()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 
ConnectRenderViewHost(RenderViewHost * rvh)325 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
326   SetRenderViewHost(rvh);
327   if (IsAttached())
328     Reattach(state_);
329 }
330 
DisconnectRenderViewHost()331 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
332   ClientDetachedFromRenderer();
333   ClearRenderViewHost();
334 }
335 
RenderViewCrashed()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 
OnMessageReceived(const IPC::Message & msg)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 
OnSwapCompositorFrame(const IPC::Message & message)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 
SynchronousSwapCompositorFrame(const cc::CompositorFrameMetadata & frame_metadata)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 
OnSaveAgentRuntimeState(const std::string & state)380 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
381     const std::string& state) {
382   if (!render_view_host_)
383     return;
384   state_ = state;
385 }
386 
OnDispatchOnInspectorFrontend(const std::string & message)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 
OnClearBrowserCache()395 void RenderViewDevToolsAgentHost::OnClearBrowserCache() {
396   if (render_view_host_)
397     GetContentClient()->browser()->ClearCache(render_view_host_);
398 }
399 
OnClearBrowserCookies()400 void RenderViewDevToolsAgentHost::OnClearBrowserCookies() {
401   if (render_view_host_)
402     GetContentClient()->browser()->ClearCookies(render_view_host_);
403 }
404 
405 }  // namespace content
406