Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / devtools / render_frame_devtools_agent_host.cc
blob1fa9c79aada887a499c126a5c8d336dc4c6a2b06
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.
5 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
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_frame_trace_recorder.h"
12 #include "content/browser/devtools/devtools_manager.h"
13 #include "content/browser/devtools/protocol/devtools_protocol_handler.h"
14 #include "content/browser/devtools/protocol/dom_handler.h"
15 #include "content/browser/devtools/protocol/emulation_handler.h"
16 #include "content/browser/devtools/protocol/input_handler.h"
17 #include "content/browser/devtools/protocol/inspector_handler.h"
18 #include "content/browser/devtools/protocol/network_handler.h"
19 #include "content/browser/devtools/protocol/page_handler.h"
20 #include "content/browser/devtools/protocol/power_handler.h"
21 #include "content/browser/devtools/protocol/service_worker_handler.h"
22 #include "content/browser/devtools/protocol/tracing_handler.h"
23 #include "content/browser/frame_host/render_frame_host_impl.h"
24 #include "content/browser/renderer_host/render_process_host_impl.h"
25 #include "content/browser/renderer_host/render_view_host_impl.h"
26 #include "content/browser/site_instance_impl.h"
27 #include "content/browser/web_contents/web_contents_impl.h"
28 #include "content/common/view_messages.h"
29 #include "content/public/browser/browser_context.h"
30 #include "content/public/browser/content_browser_client.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_types.h"
33 #include "content/public/browser/render_widget_host_iterator.h"
34 #include "content/public/browser/web_contents_delegate.h"
36 #if defined(OS_ANDROID)
37 #include "content/browser/power_save_blocker_impl.h"
38 #include "content/public/browser/render_widget_host_view.h"
39 #endif
41 namespace content {
43 typedef std::vector<RenderFrameDevToolsAgentHost*> Instances;
45 namespace {
46 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
48 static RenderFrameDevToolsAgentHost* FindAgentHost(RenderFrameHost* host) {
49 if (g_instances == NULL)
50 return NULL;
51 for (Instances::iterator it = g_instances.Get().begin();
52 it != g_instances.Get().end(); ++it) {
53 if ((*it)->HasRenderFrameHost(host))
54 return *it;
56 return NULL;
59 // Returns RenderFrameDevToolsAgentHost attached to any of RenderFrameHost
60 // instances associated with |web_contents|
61 static RenderFrameDevToolsAgentHost* FindAgentHost(WebContents* web_contents) {
62 if (g_instances == NULL)
63 return NULL;
64 for (Instances::iterator it = g_instances.Get().begin();
65 it != g_instances.Get().end(); ++it) {
66 if ((*it)->GetWebContents() == web_contents)
67 return *it;
69 return NULL;
72 bool ShouldCreateDevToolsFor(RenderFrameHost* rfh) {
73 return rfh->IsCrossProcessSubframe() || !rfh->GetParent();
76 } // namespace
78 scoped_refptr<DevToolsAgentHost>
79 DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
80 RenderFrameDevToolsAgentHost* result = FindAgentHost(web_contents);
81 if (!result)
82 result = new RenderFrameDevToolsAgentHost(web_contents->GetMainFrame());
83 return result;
86 // static
87 scoped_refptr<DevToolsAgentHost> RenderFrameDevToolsAgentHost::GetOrCreateFor(
88 RenderFrameHost* host) {
89 RenderFrameDevToolsAgentHost* result = FindAgentHost(host);
90 if (!result)
91 result = new RenderFrameDevToolsAgentHost(host);
92 return result;
95 // static
96 void RenderFrameDevToolsAgentHost::AppendAgentHostForFrameIfApplicable(
97 DevToolsAgentHost::List* result,
98 RenderFrameHost* host) {
99 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(host);
100 if (!rfh->IsRenderFrameLive())
101 return;
102 if (ShouldCreateDevToolsFor(rfh))
103 result->push_back(RenderFrameDevToolsAgentHost::GetOrCreateFor(rfh));
106 // static
107 bool DevToolsAgentHost::HasFor(WebContents* web_contents) {
108 return FindAgentHost(web_contents) != NULL;
111 // static
112 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
113 RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(web_contents);
114 return agent_host && agent_host->IsAttached();
117 //static
118 void RenderFrameDevToolsAgentHost::AddAllAgentHosts(
119 DevToolsAgentHost::List* result) {
120 base::Callback<void(RenderFrameHost*)> callback = base::Bind(
121 RenderFrameDevToolsAgentHost::AppendAgentHostForFrameIfApplicable,
122 base::Unretained(result));
123 for (const auto& wc : WebContentsImpl::GetAllWebContents())
124 wc->ForEachFrame(callback);
127 // static
128 void RenderFrameDevToolsAgentHost::OnCancelPendingNavigation(
129 RenderFrameHost* pending,
130 RenderFrameHost* current) {
131 RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(pending);
132 if (!agent_host)
133 return;
134 agent_host->DisconnectRenderFrameHost();
135 agent_host->ConnectRenderFrameHost(current);
138 RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(RenderFrameHost* rfh)
139 : render_frame_host_(NULL),
140 dom_handler_(new devtools::dom::DOMHandler()),
141 input_handler_(new devtools::input::InputHandler()),
142 inspector_handler_(new devtools::inspector::InspectorHandler()),
143 network_handler_(new devtools::network::NetworkHandler()),
144 page_handler_(new devtools::page::PageHandler()),
145 power_handler_(new devtools::power::PowerHandler()),
146 service_worker_handler_(
147 new devtools::service_worker::ServiceWorkerHandler()),
148 tracing_handler_(new devtools::tracing::TracingHandler(
149 devtools::tracing::TracingHandler::Renderer)),
150 emulation_handler_(new devtools::emulation::EmulationHandler(
151 page_handler_.get())),
152 frame_trace_recorder_(new DevToolsFrameTraceRecorder()),
153 reattaching_(false) {
154 DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher();
155 dispatcher->SetDOMHandler(dom_handler_.get());
156 dispatcher->SetEmulationHandler(emulation_handler_.get());
157 dispatcher->SetInputHandler(input_handler_.get());
158 dispatcher->SetInspectorHandler(inspector_handler_.get());
159 dispatcher->SetNetworkHandler(network_handler_.get());
160 dispatcher->SetPageHandler(page_handler_.get());
161 dispatcher->SetPowerHandler(power_handler_.get());
162 dispatcher->SetServiceWorkerHandler(service_worker_handler_.get());
163 dispatcher->SetTracingHandler(tracing_handler_.get());
164 SetRenderFrameHost(rfh);
165 g_instances.Get().push_back(this);
166 AddRef(); // Balanced in RenderFrameHostDestroyed.
167 DevToolsManager::GetInstance()->AgentHostChanged(this);
170 BrowserContext* RenderFrameDevToolsAgentHost::GetBrowserContext() {
171 WebContents* contents = web_contents();
172 return contents ? contents->GetBrowserContext() : nullptr;
175 WebContents* RenderFrameDevToolsAgentHost::GetWebContents() {
176 return web_contents();
179 void RenderFrameDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) {
180 if (!render_frame_host_)
181 return;
182 msg->set_routing_id(render_frame_host_->GetRoutingID());
183 render_frame_host_->Send(msg);
186 void RenderFrameDevToolsAgentHost::OnClientAttached() {
187 if (!render_frame_host_)
188 return;
190 InnerOnClientAttached();
192 // TODO(kaznacheev): Move this call back to DevToolsManager when
193 // extensions::ProcessManager no longer relies on this notification.
194 if (!reattaching_)
195 DevToolsAgentHostImpl::NotifyCallbacks(this, true);
198 void RenderFrameDevToolsAgentHost::InnerOnClientAttached() {
199 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
200 render_frame_host_->GetProcess()->GetID());
202 #if defined(OS_ANDROID)
203 power_save_blocker_.reset(static_cast<PowerSaveBlockerImpl*>(
204 PowerSaveBlocker::Create(
205 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
206 PowerSaveBlocker::kReasonOther, "DevTools").release()));
207 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
208 render_frame_host_->GetRenderViewHost());
209 if (rvh->GetView()) {
210 power_save_blocker_.get()->
211 InitDisplaySleepBlocker(rvh->GetView()->GetNativeView());
213 #endif
216 void RenderFrameDevToolsAgentHost::OnClientDetached() {
217 #if defined(OS_ANDROID)
218 power_save_blocker_.reset();
219 #endif
220 emulation_handler_->Detached();
221 page_handler_->Detached();
222 power_handler_->Detached();
223 service_worker_handler_->Detached();
224 tracing_handler_->Detached();
225 ClientDetachedFromRenderer();
227 // TODO(kaznacheev): Move this call back to DevToolsManager when
228 // extensions::ProcessManager no longer relies on this notification.
229 if (!reattaching_)
230 DevToolsAgentHostImpl::NotifyCallbacks(this, false);
233 void RenderFrameDevToolsAgentHost::ClientDetachedFromRenderer() {
234 if (!render_frame_host_)
235 return;
237 InnerClientDetachedFromRenderer();
240 void RenderFrameDevToolsAgentHost::InnerClientDetachedFromRenderer() {
241 bool process_has_agents = false;
242 RenderProcessHost* render_process_host = render_frame_host_->GetProcess();
243 for (Instances::iterator it = g_instances.Get().begin();
244 it != g_instances.Get().end(); ++it) {
245 if (*it == this || !(*it)->IsAttached())
246 continue;
247 RenderFrameHost* rfh = (*it)->render_frame_host_;
248 if (rfh && rfh->GetProcess() == render_process_host)
249 process_has_agents = true;
252 // We are the last to disconnect from the renderer -> revoke permissions.
253 if (!process_has_agents) {
254 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
255 render_process_host->GetID());
259 RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() {
260 Instances::iterator it = std::find(g_instances.Get().begin(),
261 g_instances.Get().end(),
262 this);
263 if (it != g_instances.Get().end())
264 g_instances.Get().erase(it);
267 // TODO(creis): Consider removing this in favor of RenderFrameHostChanged.
268 void RenderFrameDevToolsAgentHost::AboutToNavigateRenderFrame(
269 RenderFrameHost* old_host,
270 RenderFrameHost* new_host) {
271 if (render_frame_host_ != old_host)
272 return;
274 // TODO(creis): This will need to be updated for --site-per-process, since
275 // RenderViewHost is going away and navigations could happen in any frame.
276 if (render_frame_host_ == new_host) {
277 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
278 render_frame_host_->GetRenderViewHost());
279 if (rvh->render_view_termination_status() ==
280 base::TERMINATION_STATUS_STILL_RUNNING)
281 return;
283 ReattachToRenderFrameHost(new_host);
286 void RenderFrameDevToolsAgentHost::RenderFrameHostChanged(
287 RenderFrameHost* old_host,
288 RenderFrameHost* new_host) {
289 if (old_host == render_frame_host_ && new_host != render_frame_host_) {
290 // AboutToNavigateRenderFrame was not called for renderer-initiated
291 // navigation.
292 ReattachToRenderFrameHost(new_host);
296 void
297 RenderFrameDevToolsAgentHost::ReattachToRenderFrameHost(RenderFrameHost* rfh) {
298 if (!ShouldCreateDevToolsFor(rfh)) {
299 DestroyOnRenderFrameGone();
300 // |this| may be deleted at this point.
301 return;
304 DCHECK(!reattaching_);
305 reattaching_ = true;
306 DisconnectRenderFrameHost();
307 ConnectRenderFrameHost(rfh);
308 reattaching_ = false;
311 void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) {
312 if (rfh != render_frame_host_)
313 return;
314 DestroyOnRenderFrameGone();
315 // |this| may be deleted at this point.
318 void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() {
319 DCHECK(render_frame_host_);
320 scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
321 HostClosed();
322 ClearRenderFrameHost();
323 DevToolsManager::GetInstance()->AgentHostChanged(this);
324 Release();
327 void RenderFrameDevToolsAgentHost::RenderProcessGone(
328 base::TerminationStatus status) {
329 switch(status) {
330 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
331 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
332 case base::TERMINATION_STATUS_PROCESS_CRASHED:
333 #if defined(OS_ANDROID)
334 case base::TERMINATION_STATUS_OOM_PROTECTED:
335 #endif
336 RenderFrameCrashed();
337 break;
338 default:
339 break;
343 bool RenderFrameDevToolsAgentHost::OnMessageReceived(
344 const IPC::Message& message) {
345 if (!render_frame_host_)
346 return false;
347 if (message.type() == ViewHostMsg_SwapCompositorFrame::ID)
348 OnSwapCompositorFrame(message);
349 return false;
352 bool RenderFrameDevToolsAgentHost::OnMessageReceived(
353 const IPC::Message& message,
354 RenderFrameHost* render_frame_host) {
355 if (!render_frame_host_ || render_frame_host != render_frame_host_)
356 return false;
358 bool handled = true;
359 IPC_BEGIN_MESSAGE_MAP(RenderFrameDevToolsAgentHost, message)
360 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
361 OnDispatchOnInspectorFrontend)
362 IPC_MESSAGE_UNHANDLED(handled = false)
363 IPC_END_MESSAGE_MAP()
364 return handled;
367 void RenderFrameDevToolsAgentHost::DidAttachInterstitialPage() {
368 page_handler_->DidAttachInterstitialPage();
370 if (!render_frame_host_)
371 return;
372 // The rvh set in AboutToNavigateRenderFrame turned out to be interstitial.
373 // Connect back to the real one.
374 WebContents* web_contents =
375 WebContents::FromRenderFrameHost(render_frame_host_);
376 if (!web_contents)
377 return;
378 DisconnectRenderFrameHost();
379 ConnectRenderFrameHost(web_contents->GetMainFrame());
382 void RenderFrameDevToolsAgentHost::DidDetachInterstitialPage() {
383 page_handler_->DidDetachInterstitialPage();
386 void RenderFrameDevToolsAgentHost::TitleWasSet(
387 NavigationEntry* entry, bool explicit_set) {
388 DevToolsManager::GetInstance()->AgentHostChanged(this);
391 void RenderFrameDevToolsAgentHost::NavigationEntryCommitted(
392 const LoadCommittedDetails& load_details) {
393 DevToolsManager::GetInstance()->AgentHostChanged(this);
396 void RenderFrameDevToolsAgentHost::DidCommitProvisionalLoadForFrame(
397 RenderFrameHost* render_frame_host,
398 const GURL& url,
399 ui::PageTransition transition_type) {
400 service_worker_handler_->UpdateHosts();
403 void RenderFrameDevToolsAgentHost::Observe(int type,
404 const NotificationSource& source,
405 const NotificationDetails& details) {
406 if (type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) {
407 bool visible = *Details<bool>(details).ptr();
408 page_handler_->OnVisibilityChanged(visible);
412 void RenderFrameDevToolsAgentHost::SetRenderFrameHost(RenderFrameHost* rfh) {
413 DCHECK(ShouldCreateDevToolsFor(rfh));
414 DCHECK(!render_frame_host_);
415 render_frame_host_ = static_cast<RenderFrameHostImpl*>(rfh);
417 WebContentsObserver::Observe(WebContents::FromRenderFrameHost(rfh));
418 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
419 rfh->GetRenderViewHost());
420 dom_handler_->SetRenderFrameHost(render_frame_host_);
421 emulation_handler_->SetRenderViewHost(rvh);
422 input_handler_->SetRenderViewHost(rvh);
423 network_handler_->SetRenderFrameHost(render_frame_host_);
424 page_handler_->SetRenderViewHost(rvh);
425 service_worker_handler_->SetRenderFrameHost(render_frame_host_);
427 registrar_.Add(
428 this,
429 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
430 content::Source<RenderWidgetHost>(rvh));
433 void RenderFrameDevToolsAgentHost::ClearRenderFrameHost() {
434 DCHECK(render_frame_host_);
435 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
436 render_frame_host_->GetRenderViewHost());
437 registrar_.Remove(
438 this,
439 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
440 content::Source<RenderWidgetHost>(rvh));
441 render_frame_host_ = nullptr;
442 dom_handler_->SetRenderFrameHost(nullptr);
443 emulation_handler_->SetRenderViewHost(nullptr);
444 input_handler_->SetRenderViewHost(nullptr);
445 network_handler_->SetRenderFrameHost(nullptr);
446 page_handler_->SetRenderViewHost(nullptr);
447 service_worker_handler_->SetRenderFrameHost(nullptr);
450 void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
451 DisconnectRenderFrameHost();
454 void RenderFrameDevToolsAgentHost::ConnectWebContents(WebContents* wc) {
455 ConnectRenderFrameHost(wc->GetMainFrame());
458 DevToolsAgentHost::Type RenderFrameDevToolsAgentHost::GetType() {
459 return IsChildFrame() ? TYPE_FRAME : TYPE_WEB_CONTENTS;
462 std::string RenderFrameDevToolsAgentHost::GetTitle() {
463 if (IsChildFrame())
464 return GetURL().spec();
465 if (WebContents* web_contents = GetWebContents())
466 return base::UTF16ToUTF8(web_contents->GetTitle());
467 return "";
470 GURL RenderFrameDevToolsAgentHost::GetURL() {
471 WebContents* web_contents = GetWebContents();
472 if (web_contents && !IsChildFrame())
473 return web_contents->GetVisibleURL();
474 return render_frame_host_ ?
475 render_frame_host_->GetLastCommittedURL() : GURL();
478 bool RenderFrameDevToolsAgentHost::Activate() {
479 if (render_frame_host_) {
480 render_frame_host_->GetRenderViewHost()->GetDelegate()->Activate();
481 return true;
483 return false;
486 bool RenderFrameDevToolsAgentHost::Close() {
487 if (render_frame_host_) {
488 render_frame_host_->GetRenderViewHost()->ClosePage();
489 return true;
491 return false;
494 void RenderFrameDevToolsAgentHost::ConnectRenderFrameHost(
495 RenderFrameHost* rfh) {
496 SetRenderFrameHost(rfh);
497 if (IsAttached())
498 Reattach();
501 void RenderFrameDevToolsAgentHost::DisconnectRenderFrameHost() {
502 ClientDetachedFromRenderer();
503 ClearRenderFrameHost();
506 void RenderFrameDevToolsAgentHost::RenderFrameCrashed() {
507 inspector_handler_->TargetCrashed();
510 void RenderFrameDevToolsAgentHost::OnSwapCompositorFrame(
511 const IPC::Message& message) {
512 ViewHostMsg_SwapCompositorFrame::Param param;
513 if (!ViewHostMsg_SwapCompositorFrame::Read(&message, &param))
514 return;
515 page_handler_->OnSwapCompositorFrame(get<1>(param).metadata);
516 frame_trace_recorder_->OnSwapCompositorFrame(
517 render_frame_host_, get<1>(param).metadata);
520 void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame(
521 const cc::CompositorFrameMetadata& frame_metadata) {
522 if (!render_frame_host_)
523 return;
524 page_handler_->OnSwapCompositorFrame(frame_metadata);
525 frame_trace_recorder_->OnSwapCompositorFrame(
526 render_frame_host_, frame_metadata);
529 bool RenderFrameDevToolsAgentHost::HasRenderFrameHost(
530 RenderFrameHost* host) {
531 return host == render_frame_host_;
534 void RenderFrameDevToolsAgentHost::OnDispatchOnInspectorFrontend(
535 const DevToolsMessageChunk& message) {
536 if (!IsAttached() || !render_frame_host_)
537 return;
538 ProcessChunkedMessageFromAgent(message);
541 bool RenderFrameDevToolsAgentHost::IsChildFrame() {
542 return render_frame_host_ && render_frame_host_->GetParent();
545 } // namespace content