OLD | NEW |
| (Empty) |
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/json/json_writer.h" | |
9 #include "base/lazy_instance.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
11 #include "content/browser/child_process_security_policy_impl.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/input_handler.h" | |
16 #include "content/browser/devtools/protocol/inspector_handler.h" | |
17 #include "content/browser/devtools/protocol/network_handler.h" | |
18 #include "content/browser/devtools/protocol/page_handler.h" | |
19 #include "content/browser/devtools/protocol/power_handler.h" | |
20 #include "content/browser/devtools/protocol/tracing_handler.h" | |
21 #include "content/browser/frame_host/render_frame_host_impl.h" | |
22 #include "content/browser/renderer_host/render_process_host_impl.h" | |
23 #include "content/browser/renderer_host/render_view_host_impl.h" | |
24 #include "content/browser/site_instance_impl.h" | |
25 #include "content/browser/web_contents/web_contents_impl.h" | |
26 #include "content/common/view_messages.h" | |
27 #include "content/public/browser/browser_context.h" | |
28 #include "content/public/browser/content_browser_client.h" | |
29 #include "content/public/browser/devtools_manager_delegate.h" | |
30 #include "content/public/browser/notification_service.h" | |
31 #include "content/public/browser/notification_types.h" | |
32 #include "content/public/browser/render_widget_host_iterator.h" | |
33 #include "content/public/browser/web_contents_delegate.h" | |
34 | |
35 #if defined(OS_ANDROID) | |
36 #include "content/browser/power_save_blocker_impl.h" | |
37 #include "content/public/browser/render_widget_host_view.h" | |
38 #endif | |
39 | |
40 namespace content { | |
41 | |
42 typedef std::vector<RenderViewDevToolsAgentHost*> Instances; | |
43 | |
44 namespace { | |
45 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER; | |
46 | |
47 static RenderViewDevToolsAgentHost* FindAgentHost(RenderFrameHost* host) { | |
48 if (g_instances == NULL) | |
49 return NULL; | |
50 for (Instances::iterator it = g_instances.Get().begin(); | |
51 it != g_instances.Get().end(); ++it) { | |
52 if ((*it)->HasRenderFrameHost(host)) | |
53 return *it; | |
54 } | |
55 return NULL; | |
56 } | |
57 | |
58 // Returns RenderViewDevToolsAgentHost attached to any of RenderFrameHost | |
59 // instances associated with |web_contents| | |
60 static RenderViewDevToolsAgentHost* FindAgentHost(WebContents* web_contents) { | |
61 if (g_instances == NULL) | |
62 return NULL; | |
63 for (Instances::iterator it = g_instances.Get().begin(); | |
64 it != g_instances.Get().end(); ++it) { | |
65 if ((*it)->GetWebContents() == web_contents) | |
66 return *it; | |
67 } | |
68 return NULL; | |
69 } | |
70 | |
71 } // namespace | |
72 | |
73 scoped_refptr<DevToolsAgentHost> | |
74 DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) { | |
75 RenderViewDevToolsAgentHost* result = FindAgentHost(web_contents); | |
76 if (!result) | |
77 result = new RenderViewDevToolsAgentHost(web_contents->GetMainFrame()); | |
78 return result; | |
79 } | |
80 | |
81 // static | |
82 bool DevToolsAgentHost::HasFor(WebContents* web_contents) { | |
83 return FindAgentHost(web_contents) != NULL; | |
84 } | |
85 | |
86 // static | |
87 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) { | |
88 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(web_contents); | |
89 return agent_host && agent_host->IsAttached(); | |
90 } | |
91 | |
92 //static | |
93 std::vector<WebContents*> DevToolsAgentHostImpl::GetInspectableWebContents() { | |
94 std::set<WebContents*> set; | |
95 scoped_ptr<RenderWidgetHostIterator> widgets( | |
96 RenderWidgetHost::GetRenderWidgetHosts()); | |
97 while (RenderWidgetHost* widget = widgets->GetNextHost()) { | |
98 // Ignore processes that don't have a connection, such as crashed contents. | |
99 if (!widget->GetProcess()->HasConnection()) | |
100 continue; | |
101 if (!widget->IsRenderView()) | |
102 continue; | |
103 | |
104 RenderViewHost* rvh = RenderViewHost::From(widget); | |
105 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); | |
106 if (web_contents) | |
107 set.insert(web_contents); | |
108 } | |
109 std::vector<WebContents*> result(set.size()); | |
110 std::copy(set.begin(), set.end(), result.begin()); | |
111 return result; | |
112 } | |
113 | |
114 // static | |
115 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation( | |
116 RenderFrameHost* pending, | |
117 RenderFrameHost* current) { | |
118 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending); | |
119 if (!agent_host) | |
120 return; | |
121 agent_host->DisconnectRenderFrameHost(); | |
122 agent_host->ConnectRenderFrameHost(current); | |
123 } | |
124 | |
125 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderFrameHost* rfh) | |
126 : render_frame_host_(NULL), | |
127 dom_handler_(new devtools::dom::DOMHandler()), | |
128 input_handler_(new devtools::input::InputHandler()), | |
129 inspector_handler_(new devtools::inspector::InspectorHandler()), | |
130 network_handler_(new devtools::network::NetworkHandler()), | |
131 page_handler_(new devtools::page::PageHandler()), | |
132 power_handler_(new devtools::power::PowerHandler()), | |
133 tracing_handler_(new devtools::tracing::TracingHandler( | |
134 devtools::tracing::TracingHandler::Renderer)), | |
135 protocol_handler_(new DevToolsProtocolHandler( | |
136 base::Bind(&RenderViewDevToolsAgentHost::DispatchOnInspectorFrontend, | |
137 base::Unretained(this)))), | |
138 reattaching_(false) { | |
139 DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher(); | |
140 dispatcher->SetDOMHandler(dom_handler_.get()); | |
141 dispatcher->SetInputHandler(input_handler_.get()); | |
142 dispatcher->SetInspectorHandler(inspector_handler_.get()); | |
143 dispatcher->SetNetworkHandler(network_handler_.get()); | |
144 dispatcher->SetPageHandler(page_handler_.get()); | |
145 dispatcher->SetPowerHandler(power_handler_.get()); | |
146 dispatcher->SetTracingHandler(tracing_handler_.get()); | |
147 SetRenderFrameHost(rfh); | |
148 g_instances.Get().push_back(this); | |
149 AddRef(); // Balanced in RenderFrameHostDestroyed. | |
150 DevToolsManager::GetInstance()->AgentHostChanged(this); | |
151 } | |
152 | |
153 BrowserContext* RenderViewDevToolsAgentHost::GetBrowserContext() { | |
154 WebContents* contents = web_contents(); | |
155 return contents ? contents->GetBrowserContext() : nullptr; | |
156 } | |
157 | |
158 WebContents* RenderViewDevToolsAgentHost::GetWebContents() { | |
159 return web_contents(); | |
160 } | |
161 | |
162 void RenderViewDevToolsAgentHost::DispatchProtocolMessage( | |
163 const std::string& message) { | |
164 scoped_ptr<base::DictionaryValue> command = | |
165 protocol_handler_->ParseCommand(message); | |
166 if (!command) | |
167 return; | |
168 | |
169 DevToolsManagerDelegate* delegate = | |
170 DevToolsManager::GetInstance()->delegate(); | |
171 if (delegate) { | |
172 scoped_ptr<base::DictionaryValue> response( | |
173 delegate->HandleCommand(this, command.get())); | |
174 if (response) { | |
175 std::string json_response; | |
176 base::JSONWriter::Write(response.get(), &json_response); | |
177 DispatchOnInspectorFrontend(json_response); | |
178 return; | |
179 } | |
180 } | |
181 | |
182 if (protocol_handler_->HandleOptionalCommand(command.Pass())) | |
183 return; | |
184 | |
185 IPCDevToolsAgentHost::DispatchProtocolMessage(message); | |
186 } | |
187 | |
188 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) { | |
189 if (!render_frame_host_) | |
190 return; | |
191 msg->set_routing_id(render_frame_host_->GetRoutingID()); | |
192 render_frame_host_->Send(msg); | |
193 } | |
194 | |
195 void RenderViewDevToolsAgentHost::OnClientAttached() { | |
196 if (!render_frame_host_) | |
197 return; | |
198 | |
199 InnerOnClientAttached(); | |
200 | |
201 // TODO(kaznacheev): Move this call back to DevToolsManager when | |
202 // extensions::ProcessManager no longer relies on this notification. | |
203 if (!reattaching_) | |
204 DevToolsAgentHostImpl::NotifyCallbacks(this, true); | |
205 } | |
206 | |
207 void RenderViewDevToolsAgentHost::InnerOnClientAttached() { | |
208 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies( | |
209 render_frame_host_->GetProcess()->GetID()); | |
210 | |
211 #if defined(OS_ANDROID) | |
212 power_save_blocker_.reset( | |
213 static_cast<PowerSaveBlockerImpl*>( | |
214 PowerSaveBlocker::Create( | |
215 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, | |
216 "DevTools").release())); | |
217 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( | |
218 render_frame_host_->GetRenderViewHost()); | |
219 if (rvh->GetView()) { | |
220 power_save_blocker_.get()-> | |
221 InitDisplaySleepBlocker(rvh->GetView()->GetNativeView()); | |
222 } | |
223 #endif | |
224 } | |
225 | |
226 void RenderViewDevToolsAgentHost::OnClientDetached() { | |
227 #if defined(OS_ANDROID) | |
228 power_save_blocker_.reset(); | |
229 #endif | |
230 page_handler_->Detached(); | |
231 power_handler_->Detached(); | |
232 tracing_handler_->Detached(); | |
233 ClientDetachedFromRenderer(); | |
234 | |
235 // TODO(kaznacheev): Move this call back to DevToolsManager when | |
236 // extensions::ProcessManager no longer relies on this notification. | |
237 if (!reattaching_) | |
238 DevToolsAgentHostImpl::NotifyCallbacks(this, false); | |
239 } | |
240 | |
241 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() { | |
242 if (!render_frame_host_) | |
243 return; | |
244 | |
245 InnerClientDetachedFromRenderer(); | |
246 } | |
247 | |
248 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() { | |
249 bool process_has_agents = false; | |
250 RenderProcessHost* render_process_host = render_frame_host_->GetProcess(); | |
251 for (Instances::iterator it = g_instances.Get().begin(); | |
252 it != g_instances.Get().end(); ++it) { | |
253 if (*it == this || !(*it)->IsAttached()) | |
254 continue; | |
255 RenderFrameHost* rfh = (*it)->render_frame_host_; | |
256 if (rfh && rfh->GetProcess() == render_process_host) | |
257 process_has_agents = true; | |
258 } | |
259 | |
260 // We are the last to disconnect from the renderer -> revoke permissions. | |
261 if (!process_has_agents) { | |
262 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies( | |
263 render_process_host->GetID()); | |
264 } | |
265 } | |
266 | |
267 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() { | |
268 Instances::iterator it = std::find(g_instances.Get().begin(), | |
269 g_instances.Get().end(), | |
270 this); | |
271 if (it != g_instances.Get().end()) | |
272 g_instances.Get().erase(it); | |
273 } | |
274 | |
275 // TODO(creis): Consider removing this in favor of RenderFrameHostChanged. | |
276 void RenderViewDevToolsAgentHost::AboutToNavigateRenderFrame( | |
277 RenderFrameHost* old_host, | |
278 RenderFrameHost* new_host) { | |
279 if (render_frame_host_ != old_host) | |
280 return; | |
281 | |
282 // TODO(creis): This will need to be updated for --site-per-process, since | |
283 // RenderViewHost is going away and navigations could happen in any frame. | |
284 if (render_frame_host_ == new_host) { | |
285 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( | |
286 render_frame_host_->GetRenderViewHost()); | |
287 if (rvh->render_view_termination_status() == | |
288 base::TERMINATION_STATUS_STILL_RUNNING) | |
289 return; | |
290 } | |
291 ReattachToRenderFrameHost(new_host); | |
292 } | |
293 | |
294 void RenderViewDevToolsAgentHost::RenderFrameHostChanged( | |
295 RenderFrameHost* old_host, | |
296 RenderFrameHost* new_host) { | |
297 if (old_host == render_frame_host_ && new_host != render_frame_host_) { | |
298 // AboutToNavigateRenderFrame was not called for renderer-initiated | |
299 // navigation. | |
300 ReattachToRenderFrameHost(new_host); | |
301 } | |
302 } | |
303 | |
304 void | |
305 RenderViewDevToolsAgentHost::ReattachToRenderFrameHost(RenderFrameHost* rfh) { | |
306 DCHECK(!reattaching_); | |
307 reattaching_ = true; | |
308 DisconnectRenderFrameHost(); | |
309 ConnectRenderFrameHost(rfh); | |
310 reattaching_ = false; | |
311 } | |
312 | |
313 void RenderViewDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost* rfh) { | |
314 if (rfh != render_frame_host_) | |
315 return; | |
316 | |
317 DCHECK(render_frame_host_); | |
318 scoped_refptr<RenderViewDevToolsAgentHost> protect(this); | |
319 HostClosed(); | |
320 ClearRenderFrameHost(); | |
321 DevToolsManager::GetInstance()->AgentHostChanged(this); | |
322 Release(); | |
323 } | |
324 | |
325 void RenderViewDevToolsAgentHost::RenderProcessGone( | |
326 base::TerminationStatus status) { | |
327 switch(status) { | |
328 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: | |
329 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: | |
330 case base::TERMINATION_STATUS_PROCESS_CRASHED: | |
331 #if defined(OS_ANDROID) | |
332 case base::TERMINATION_STATUS_OOM_PROTECTED: | |
333 #endif | |
334 RenderFrameCrashed(); | |
335 break; | |
336 default: | |
337 break; | |
338 } | |
339 } | |
340 | |
341 bool RenderViewDevToolsAgentHost::OnMessageReceived( | |
342 const IPC::Message& message) { | |
343 if (!render_frame_host_) | |
344 return false; | |
345 if (message.type() == ViewHostMsg_SwapCompositorFrame::ID) | |
346 OnSwapCompositorFrame(message); | |
347 return false; | |
348 } | |
349 | |
350 bool RenderViewDevToolsAgentHost::OnMessageReceived( | |
351 const IPC::Message& message, | |
352 RenderFrameHost* render_frame_host) { | |
353 if (!render_frame_host_ || render_frame_host != render_frame_host_) | |
354 return false; | |
355 | |
356 bool handled = true; | |
357 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, message) | |
358 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, | |
359 OnDispatchOnInspectorFrontend) | |
360 IPC_MESSAGE_UNHANDLED(handled = false) | |
361 IPC_END_MESSAGE_MAP() | |
362 return handled; | |
363 } | |
364 | |
365 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() { | |
366 page_handler_->DidAttachInterstitialPage(); | |
367 | |
368 if (!render_frame_host_) | |
369 return; | |
370 // The rvh set in AboutToNavigateRenderFrame turned out to be interstitial. | |
371 // Connect back to the real one. | |
372 WebContents* web_contents = | |
373 WebContents::FromRenderFrameHost(render_frame_host_); | |
374 if (!web_contents) | |
375 return; | |
376 DisconnectRenderFrameHost(); | |
377 ConnectRenderFrameHost(web_contents->GetMainFrame()); | |
378 } | |
379 | |
380 void RenderViewDevToolsAgentHost::DidDetachInterstitialPage() { | |
381 page_handler_->DidDetachInterstitialPage(); | |
382 } | |
383 | |
384 void RenderViewDevToolsAgentHost::TitleWasSet( | |
385 NavigationEntry* entry, bool explicit_set) { | |
386 DevToolsManager::GetInstance()->AgentHostChanged(this); | |
387 } | |
388 | |
389 void RenderViewDevToolsAgentHost::NavigationEntryCommitted( | |
390 const LoadCommittedDetails& load_details) { | |
391 DevToolsManager::GetInstance()->AgentHostChanged(this); | |
392 } | |
393 | |
394 void RenderViewDevToolsAgentHost::Observe(int type, | |
395 const NotificationSource& source, | |
396 const NotificationDetails& details) { | |
397 if (type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) { | |
398 bool visible = *Details<bool>(details).ptr(); | |
399 page_handler_->OnVisibilityChanged(visible); | |
400 } | |
401 } | |
402 | |
403 void RenderViewDevToolsAgentHost::SetRenderFrameHost(RenderFrameHost* rfh) { | |
404 DCHECK(!render_frame_host_); | |
405 render_frame_host_ = static_cast<RenderFrameHostImpl*>(rfh); | |
406 | |
407 WebContentsObserver::Observe(WebContents::FromRenderFrameHost(rfh)); | |
408 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( | |
409 rfh->GetRenderViewHost()); | |
410 dom_handler_->SetRenderViewHost(rvh); | |
411 input_handler_->SetRenderViewHost(rvh); | |
412 network_handler_->SetRenderViewHost(rvh); | |
413 page_handler_->SetRenderViewHost(rvh); | |
414 | |
415 registrar_.Add( | |
416 this, | |
417 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | |
418 content::Source<RenderWidgetHost>(rvh)); | |
419 } | |
420 | |
421 void RenderViewDevToolsAgentHost::ClearRenderFrameHost() { | |
422 DCHECK(render_frame_host_); | |
423 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( | |
424 render_frame_host_->GetRenderViewHost()); | |
425 registrar_.Remove( | |
426 this, | |
427 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | |
428 content::Source<RenderWidgetHost>(rvh)); | |
429 render_frame_host_ = nullptr; | |
430 dom_handler_->SetRenderViewHost(nullptr); | |
431 input_handler_->SetRenderViewHost(nullptr); | |
432 network_handler_->SetRenderViewHost(nullptr); | |
433 page_handler_->SetRenderViewHost(nullptr); | |
434 } | |
435 | |
436 void RenderViewDevToolsAgentHost::DisconnectWebContents() { | |
437 DisconnectRenderFrameHost(); | |
438 } | |
439 | |
440 void RenderViewDevToolsAgentHost::ConnectWebContents(WebContents* wc) { | |
441 ConnectRenderFrameHost(wc->GetMainFrame()); | |
442 } | |
443 | |
444 DevToolsAgentHost::Type RenderViewDevToolsAgentHost::GetType() { | |
445 return TYPE_WEB_CONTENTS; | |
446 } | |
447 | |
448 std::string RenderViewDevToolsAgentHost::GetTitle() { | |
449 if (WebContents* web_contents = GetWebContents()) | |
450 return base::UTF16ToUTF8(web_contents->GetTitle()); | |
451 return ""; | |
452 } | |
453 | |
454 GURL RenderViewDevToolsAgentHost::GetURL() { | |
455 if (WebContents* web_contents = GetWebContents()) | |
456 return web_contents->GetVisibleURL(); | |
457 return render_frame_host_ ? | |
458 render_frame_host_->GetLastCommittedURL() : GURL(); | |
459 } | |
460 | |
461 bool RenderViewDevToolsAgentHost::Activate() { | |
462 if (render_frame_host_) { | |
463 render_frame_host_->GetRenderViewHost()->GetDelegate()->Activate(); | |
464 return true; | |
465 } | |
466 return false; | |
467 } | |
468 | |
469 bool RenderViewDevToolsAgentHost::Close() { | |
470 if (render_frame_host_) { | |
471 render_frame_host_->GetRenderViewHost()->ClosePage(); | |
472 return true; | |
473 } | |
474 return false; | |
475 } | |
476 | |
477 void RenderViewDevToolsAgentHost::ConnectRenderFrameHost(RenderFrameHost* rfh) { | |
478 SetRenderFrameHost(rfh); | |
479 if (IsAttached()) | |
480 Reattach(); | |
481 } | |
482 | |
483 void RenderViewDevToolsAgentHost::DisconnectRenderFrameHost() { | |
484 ClientDetachedFromRenderer(); | |
485 ClearRenderFrameHost(); | |
486 } | |
487 | |
488 void RenderViewDevToolsAgentHost::RenderFrameCrashed() { | |
489 inspector_handler_->TargetCrashed(); | |
490 } | |
491 | |
492 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame( | |
493 const IPC::Message& message) { | |
494 ViewHostMsg_SwapCompositorFrame::Param param; | |
495 if (!ViewHostMsg_SwapCompositorFrame::Read(&message, ¶m)) | |
496 return; | |
497 page_handler_->OnSwapCompositorFrame(get<1>(param).metadata); | |
498 } | |
499 | |
500 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame( | |
501 const cc::CompositorFrameMetadata& frame_metadata) { | |
502 if (!render_frame_host_) | |
503 return; | |
504 page_handler_->OnSwapCompositorFrame(frame_metadata); | |
505 } | |
506 | |
507 bool RenderViewDevToolsAgentHost::HasRenderFrameHost( | |
508 RenderFrameHost* host) { | |
509 return host == render_frame_host_; | |
510 } | |
511 | |
512 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend( | |
513 const DevToolsMessageChunk& message) { | |
514 if (!IsAttached() || !render_frame_host_) | |
515 return; | |
516 ProcessChunkedMessageFromAgent(message); | |
517 } | |
518 | |
519 void RenderViewDevToolsAgentHost::DispatchOnInspectorFrontend( | |
520 const std::string& message) { | |
521 if (!IsAttached() || !render_frame_host_) | |
522 return; | |
523 SendMessageToClient(message); | |
524 } | |
525 | |
526 } // namespace content | |
OLD | NEW |