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
35namespace content {
36
37typedef std::vector<RenderViewDevToolsAgentHost*> Instances;
38
39namespace {
40base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
41
42//Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
43//instances associated with |web_contents|
44static 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
57scoped_refptr<DevToolsAgentHost>
58DevToolsAgentHost::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
66bool DevToolsAgentHost::HasFor(WebContents* web_contents) {
67  return FindAgentHost(web_contents) != NULL;
68}
69
70// static
71bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
72  RenderViewDevToolsAgentHost* agent_host = FindAgentHost(web_contents);
73  return agent_host && agent_host->IsAttached();
74}
75
76//static
77std::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
99void 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
110RenderViewDevToolsAgentHost::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
128WebContents* RenderViewDevToolsAgentHost::GetWebContents() {
129  return web_contents();
130}
131
132void 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
169void 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
176void 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
188void 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
205void 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
220void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
221  if (!render_view_host_)
222    return;
223
224  InnerClientDetachedFromRenderer();
225}
226
227void 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
246RenderViewDevToolsAgentHost::~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
254void 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
266void 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
276void
277RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost* rvh) {
278  DCHECK(!reattaching_);
279  reattaching_ = true;
280  DisconnectRenderViewHost();
281  ConnectRenderViewHost(rvh);
282  reattaching_ = false;
283}
284
285void 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
296void 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
312bool RenderViewDevToolsAgentHost::OnMessageReceived(
313    const IPC::Message& message,
314    RenderFrameHost* render_frame_host) {
315  return DispatchIPCMessage(message);
316}
317
318bool RenderViewDevToolsAgentHost::OnMessageReceived(
319    const IPC::Message& message) {
320  return DispatchIPCMessage(message);
321}
322
323void 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
338void RenderViewDevToolsAgentHost::DidDetachInterstitialPage() {
339  overrides_handler_->DidDetachInterstitialPage();
340}
341
342void 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
351void 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
364void 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
374void RenderViewDevToolsAgentHost::DisconnectWebContents() {
375  DisconnectRenderViewHost();
376}
377
378void RenderViewDevToolsAgentHost::ConnectWebContents(WebContents* wc) {
379  ConnectRenderViewHost(wc->GetRenderViewHost());
380}
381
382DevToolsAgentHost::Type RenderViewDevToolsAgentHost::GetType() {
383  return TYPE_WEB_CONTENTS;
384}
385
386std::string RenderViewDevToolsAgentHost::GetTitle() {
387  if (WebContents* web_contents = GetWebContents())
388    return base::UTF16ToUTF8(web_contents->GetTitle());
389  return "";
390}
391
392GURL 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
399bool RenderViewDevToolsAgentHost::Activate() {
400  if (render_view_host_) {
401    render_view_host_->GetDelegate()->Activate();
402    return true;
403  }
404  return false;
405}
406
407bool RenderViewDevToolsAgentHost::Close() {
408  if (render_view_host_) {
409    render_view_host_->ClosePage();
410    return true;
411  }
412  return false;
413}
414
415void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
416  SetRenderViewHost(rvh);
417  if (IsAttached())
418    Reattach(state_);
419}
420
421void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
422  ClientDetachedFromRenderer();
423  ClearRenderViewHost();
424}
425
426void RenderViewDevToolsAgentHost::RenderViewCrashed() {
427  scoped_refptr<DevToolsProtocol::Notification> notification =
428      DevToolsProtocol::CreateNotification(
429          devtools::Inspector::targetCrashed::kName, NULL);
430  SendMessageToClient(notification->Serialize());
431}
432
433bool 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
451void 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
459void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
460    const cc::CompositorFrameMetadata& frame_metadata) {
461  if (!render_view_host_)
462    return;
463  overrides_handler_->OnSwapCompositorFrame(frame_metadata);
464}
465
466void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
467    const std::string& state) {
468  if (!render_view_host_)
469    return;
470  state_ = state;
471}
472
473void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
474    const std::string& message) {
475  if (!render_view_host_)
476    return;
477  SendMessageToClient(message);
478}
479
480}  // namespace content
481