blob: 0c2d3238ad62ad18baf81f6766d9b751bbf141ec [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// 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
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00007#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"
Ben Murdochca12bfa2013-07-23 11:17:05 +010013#include "content/browser/devtools/devtools_tracing_handler.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000014#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/public/browser/content_browser_client.h"
21#include "content/public/browser/notification_service.h"
22#include "content/public/browser/notification_types.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000023
24namespace content {
25
26typedef std::vector<RenderViewDevToolsAgentHost*> Instances;
27
28namespace {
29base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
30
31static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) {
32 if (g_instances == NULL)
33 return NULL;
34 for (Instances::iterator it = g_instances.Get().begin();
35 it != g_instances.Get().end(); ++it) {
36 if (rvh == (*it)->render_view_host())
37 return *it;
38 }
39 return NULL;
40}
41
42} // namespace
43
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000044class RenderViewDevToolsAgentHost::DevToolsAgentHostRvhObserver
45 : public RenderViewHostObserver {
46 public:
47 DevToolsAgentHostRvhObserver(RenderViewHost* rvh,
48 RenderViewDevToolsAgentHost* agent_host)
49 : RenderViewHostObserver(rvh),
50 agent_host_(agent_host) {
51 }
52 virtual ~DevToolsAgentHostRvhObserver() {}
53
54 // RenderViewHostObserver overrides.
55 virtual void RenderViewHostDestroyed(RenderViewHost* rvh) OVERRIDE {
56 agent_host_->RenderViewHostDestroyed(rvh);
57 }
58 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
59 return agent_host_->OnRvhMessageReceived(message);
60 }
61 private:
62 RenderViewDevToolsAgentHost* agent_host_;
63 DISALLOW_COPY_AND_ASSIGN(DevToolsAgentHostRvhObserver);
64};
65
66// static
67scoped_refptr<DevToolsAgentHost>
68DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) {
69 RenderViewDevToolsAgentHost* result = FindAgentHost(rvh);
70 if (!result)
71 result = new RenderViewDevToolsAgentHost(rvh);
72 return result;
73}
74
75// static
76bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) {
77 return FindAgentHost(rvh) != NULL;
78}
79
80// static
81bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
82 if (g_instances == NULL)
83 return false;
84 DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
85 if (!devtools_manager)
86 return false;
87 RenderViewHostDelegate* delegate =
88 static_cast<WebContentsImpl*>(web_contents);
89 for (Instances::iterator it = g_instances.Get().begin();
90 it != g_instances.Get().end(); ++it) {
91 RenderViewHost* rvh = (*it)->render_view_host_;
92 if (rvh && rvh->GetDelegate() != delegate)
93 continue;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010094 if ((*it)->IsAttached())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000095 return true;
96 }
97 return false;
98}
99
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000100//static
101std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() {
102 std::vector<RenderViewHost*> result;
Ben Murdocheb525c52013-07-10 11:40:50 +0100103 RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts();
104 for (size_t i = 0; i < widgets.size(); ++i) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000105 // Ignore processes that don't have a connection, such as crashed contents.
Ben Murdocheb525c52013-07-10 11:40:50 +0100106 if (!widgets[i]->GetProcess()->HasConnection())
107 continue;
108 if (!widgets[i]->IsRenderView())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000109 continue;
110
Ben Murdocheb525c52013-07-10 11:40:50 +0100111 RenderViewHost* rvh = RenderViewHost::From(widgets[i]);
Ben Murdocheb525c52013-07-10 11:40:50 +0100112 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
113 // Don't report a RenderViewHost if it is not the current RenderViewHost
114 // for some WebContents.
115 if (!web_contents || rvh != web_contents->GetRenderViewHost())
116 continue;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000117
Ben Murdocheb525c52013-07-10 11:40:50 +0100118 result.push_back(rvh);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000119 }
120 return result;
121}
122
123// static
124void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
125 RenderViewHost* pending,
126 RenderViewHost* current) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100127 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending);
128 if (!agent_host)
129 return;
130 agent_host->DisconnectRenderViewHost();
131 agent_host->ConnectRenderViewHost(current);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000132}
133
134RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(
135 RenderViewHost* rvh)
Ben Murdochca12bfa2013-07-23 11:17:05 +0100136 : overrides_handler_(new RendererOverridesHandler(this)),
137 tracing_handler_(new DevToolsTracingHandler())
138 {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100139 SetRenderViewHost(rvh);
Ben Murdochca12bfa2013-07-23 11:17:05 +0100140 DevToolsProtocol::Notifier notifier(base::Bind(
141 &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend,
142 base::Unretained(this)));
143 overrides_handler_->SetNotifier(notifier);
144 tracing_handler_->SetNotifier(notifier);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000145 g_instances.Get().push_back(this);
146 RenderViewHostDelegate* delegate = render_view_host_->GetDelegate();
147 if (delegate && delegate->GetAsWebContents())
148 Observe(delegate->GetAsWebContents());
149 AddRef(); // Balanced in RenderViewHostDestroyed.
150}
151
152RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() {
153 return render_view_host_;
154}
155
156void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
157 const std::string& message) {
158 std::string error_message;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100159 scoped_refptr<DevToolsProtocol::Command> command =
160 DevToolsProtocol::ParseCommand(message, &error_message);
161
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100162 if (command) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100163 scoped_refptr<DevToolsProtocol::Response> overridden_response =
164 overrides_handler_->HandleCommand(command);
Ben Murdochca12bfa2013-07-23 11:17:05 +0100165 if (!overridden_response)
Ben Murdochbb1529c2013-08-08 10:24:53 +0100166 overridden_response = tracing_handler_->HandleCommand(command);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100167 if (overridden_response) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100168 if (!overridden_response->is_async_promise())
169 OnDispatchOnInspectorFrontend(overridden_response->Serialize());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100170 return;
171 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000172 }
173
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100174 IPCDevToolsAgentHost::DispatchOnInspectorBackend(message);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000175}
176
177void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) {
178 if (!render_view_host_)
179 return;
180 msg->set_routing_id(render_view_host_->GetRoutingID());
181 render_view_host_->Send(msg);
182}
183
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100184void RenderViewDevToolsAgentHost::OnClientAttached() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000185 if (!render_view_host_)
186 return;
187
188 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
189 render_view_host_->GetProcess()->GetID());
Ben Murdocheb525c52013-07-10 11:40:50 +0100190
191 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
192 // ExtensionProcessManager no longer relies on this notification.
193 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000194}
195
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100196void RenderViewDevToolsAgentHost::OnClientDetached() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000197 if (!render_view_host_)
198 return;
199
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000200 bool process_has_agents = false;
201 RenderProcessHost* render_process_host = render_view_host_->GetProcess();
202 for (Instances::iterator it = g_instances.Get().begin();
203 it != g_instances.Get().end(); ++it) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100204 if (*it == this || !(*it)->IsAttached())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000205 continue;
206 RenderViewHost* rvh = (*it)->render_view_host();
207 if (rvh && rvh->GetProcess() == render_process_host)
208 process_has_agents = true;
209 }
210
211 // We are the last to disconnect from the renderer -> revoke permissions.
212 if (!process_has_agents) {
213 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
214 render_process_host->GetID());
215 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100216
217 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
218 // ExtensionProcessManager no longer relies on this notification.
219 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000220}
221
222RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
223 Instances::iterator it = std::find(g_instances.Get().begin(),
224 g_instances.Get().end(),
225 this);
226 if (it != g_instances.Get().end())
227 g_instances.Get().erase(it);
228}
229
230void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
231 RenderViewHost* dest_rvh) {
232 if (!render_view_host_)
233 return;
234
235 if (render_view_host_ == dest_rvh && static_cast<RenderViewHostImpl*>(
236 render_view_host_)->render_view_termination_status() ==
237 base::TERMINATION_STATUS_STILL_RUNNING)
238 return;
239 DisconnectRenderViewHost();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100240 ConnectRenderViewHost(dest_rvh);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241}
242
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100243void RenderViewDevToolsAgentHost::RenderProcessGone(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000244 base::TerminationStatus status) {
245 switch(status) {
246 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
247 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
248 case base::TERMINATION_STATUS_PROCESS_CRASHED:
249 RenderViewCrashed();
250 break;
251 default:
252 break;
253 }
254}
255
Ben Murdocheb525c52013-07-10 11:40:50 +0100256void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
257 if (!render_view_host_)
258 return;
259 // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
260 // Connect back to the real one.
261 WebContents* web_contents =
262 WebContents::FromRenderViewHost(render_view_host_);
263 if (!web_contents)
264 return;
265 DisconnectRenderViewHost();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100266 ConnectRenderViewHost(web_contents->GetRenderViewHost());
Ben Murdocheb525c52013-07-10 11:40:50 +0100267}
268
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100269void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000270 render_view_host_ = rvh;
271 rvh_observer_.reset(new DevToolsAgentHostRvhObserver(rvh, this));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100272}
273
274void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
275 SetRenderViewHost(rvh);
276 Reattach(state_);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000277}
278
279void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100280 OnClientDetached();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000281 rvh_observer_.reset();
282 render_view_host_ = NULL;
283}
284
285void RenderViewDevToolsAgentHost::RenderViewHostDestroyed(
286 RenderViewHost* rvh) {
287 DCHECK(render_view_host_);
288 scoped_refptr<RenderViewDevToolsAgentHost> protect(this);
289 NotifyCloseListener();
290 render_view_host_ = NULL;
291 Release();
292}
293
294void RenderViewDevToolsAgentHost::RenderViewCrashed() {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100295 scoped_refptr<DevToolsProtocol::Notification> notification =
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100296 DevToolsProtocol::CreateNotification(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100297 devtools::Inspector::targetCrashed::kName, NULL);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000298 DevToolsManagerImpl::GetInstance()->
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100299 DispatchOnInspectorFrontend(this, notification->Serialize());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000300}
301
302bool RenderViewDevToolsAgentHost::OnRvhMessageReceived(
303 const IPC::Message& message) {
304 bool handled = true;
305 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, message)
306 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
307 OnDispatchOnInspectorFrontend)
308 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
309 OnSaveAgentRuntimeState)
310 IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCache, OnClearBrowserCache)
311 IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCookies,
312 OnClearBrowserCookies)
313 IPC_MESSAGE_UNHANDLED(handled = false)
314 IPC_END_MESSAGE_MAP()
315 return handled;
316}
317
318void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
319 const std::string& state) {
320 if (!render_view_host_)
321 return;
322 state_ = state;
323}
324
325void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
326 const std::string& message) {
327 if (!render_view_host_)
328 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000329 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100330 this, message);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000331}
332
333void RenderViewDevToolsAgentHost::OnClearBrowserCache() {
334 if (render_view_host_)
335 GetContentClient()->browser()->ClearCache(render_view_host_);
336}
337
338void RenderViewDevToolsAgentHost::OnClearBrowserCookies() {
339 if (render_view_host_)
340 GetContentClient()->browser()->ClearCookies(render_view_host_);
341}
342
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000343} // namespace content