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