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, ¶m))
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