Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / devtools / render_view_devtools_agent_host.cc
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/notification_service.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/render_widget_host_iterator.h"
26
27 #if defined(OS_ANDROID)
28 #include "content/browser/power_save_blocker_impl.h"
29 #include "content/public/browser/render_widget_host_view.h"
30 #endif
31
32 namespace content {
33
34 typedef std::vector<RenderViewDevToolsAgentHost*> Instances;
35
36 namespace {
37 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
38
39 //Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
40 //instances associated with |web_contents|
41 static RenderViewDevToolsAgentHost* FindAgentHost(WebContents* web_contents) {
42   if (g_instances == NULL)
43     return NULL;
44   RenderViewHostDelegate* delegate =
45       static_cast<WebContentsImpl*>(web_contents);
46   for (Instances::iterator it = g_instances.Get().begin();
47        it != g_instances.Get().end(); ++it) {
48     RenderViewHost* rvh = (*it)->render_view_host();
49     if (rvh && rvh->GetDelegate() == delegate)
50       return *it;
51   }
52   return NULL;
53 }
54
55 static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) {
56   if (g_instances == NULL)
57     return NULL;
58   for (Instances::iterator it = g_instances.Get().begin();
59        it != g_instances.Get().end(); ++it) {
60     if (rvh == (*it)->render_view_host())
61       return *it;
62   }
63   return NULL;
64 }
65
66 }  // namespace
67
68 scoped_refptr<DevToolsAgentHost>
69 DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
70   RenderViewDevToolsAgentHost* result = FindAgentHost(web_contents);
71   if (!result)
72     result = new RenderViewDevToolsAgentHost(web_contents->GetRenderViewHost());
73   return result;
74 }
75
76 // static
77 scoped_refptr<DevToolsAgentHost>
78 DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) {
79   RenderViewDevToolsAgentHost* result = FindAgentHost(rvh);
80   if (!result)
81     result = new RenderViewDevToolsAgentHost(rvh);
82   return result;
83 }
84
85 // static
86 bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) {
87   return FindAgentHost(rvh) != NULL;
88 }
89
90 // static
91 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
92   if (g_instances == NULL)
93     return false;
94   DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
95   if (!devtools_manager)
96     return false;
97   RenderViewHostDelegate* delegate =
98       static_cast<WebContentsImpl*>(web_contents);
99   for (Instances::iterator it = g_instances.Get().begin();
100        it != g_instances.Get().end(); ++it) {
101     RenderViewHost* rvh = (*it)->render_view_host_;
102     if (rvh && rvh->GetDelegate() != delegate)
103       continue;
104     if ((*it)->IsAttached())
105       return true;
106   }
107   return false;
108 }
109
110 //static
111 std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() {
112   std::vector<RenderViewHost*> result;
113   scoped_ptr<RenderWidgetHostIterator> widgets(
114       RenderWidgetHost::GetRenderWidgetHosts());
115   while (RenderWidgetHost* widget = widgets->GetNextHost()) {
116     // Ignore processes that don't have a connection, such as crashed contents.
117     if (!widget->GetProcess()->HasConnection())
118       continue;
119     if (!widget->IsRenderView())
120       continue;
121
122     RenderViewHost* rvh = RenderViewHost::From(widget);
123     WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
124     if (!web_contents)
125       continue;
126
127     // Don't report a RenderViewHost if it is not the current RenderViewHost
128     // for some WebContents (this filters out pre-render RVHs and similar).
129     // However report a RenderViewHost created for an out of process iframe.
130     // TODO (kaznacheev): Revisit this when it is clear how OOP iframes
131     // interact with pre-rendering.
132     // TODO (kaznacheev): GetMainFrame() call is a temporary hack. Iterate over
133     // all RenderFrameHost instances when multiple OOP frames are supported.
134     if (rvh != web_contents->GetRenderViewHost() &&
135         !rvh->GetMainFrame()->IsCrossProcessSubframe()) {
136       continue;
137     }
138
139     result.push_back(rvh);
140   }
141   return result;
142 }
143
144 // static
145 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
146     RenderViewHost* pending,
147     RenderViewHost* current) {
148   RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending);
149   if (!agent_host)
150     return;
151   agent_host->DisconnectRenderViewHost();
152   agent_host->ConnectRenderViewHost(current);
153 }
154
155 // static
156 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
157     RenderViewHost* source,
158     const IPC::Message& message) {
159   RenderViewDevToolsAgentHost* agent_host = FindAgentHost(source);
160   return agent_host && agent_host->DispatchIPCMessage(message);
161 }
162
163 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(
164     RenderViewHost* rvh)
165     : render_view_host_(NULL),
166       overrides_handler_(new RendererOverridesHandler(this)),
167       tracing_handler_(new DevToolsTracingHandler()),
168       power_handler_(new DevToolsPowerHandler())
169  {
170   SetRenderViewHost(rvh);
171   DevToolsProtocol::Notifier notifier(base::Bind(
172       &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend,
173       base::Unretained(this)));
174   overrides_handler_->SetNotifier(notifier);
175   tracing_handler_->SetNotifier(notifier);
176   power_handler_->SetNotifier(notifier);
177   g_instances.Get().push_back(this);
178   AddRef();  // Balanced in RenderViewHostDestroyed.
179 }
180
181 RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() {
182   return render_view_host_;
183 }
184
185 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
186     const std::string& message) {
187   std::string error_message;
188   scoped_refptr<DevToolsProtocol::Command> command =
189       DevToolsProtocol::ParseCommand(message, &error_message);
190
191   if (command) {
192     scoped_refptr<DevToolsProtocol::Response> overridden_response =
193         overrides_handler_->HandleCommand(command);
194     if (!overridden_response)
195       overridden_response = tracing_handler_->HandleCommand(command);
196     if (!overridden_response)
197       overridden_response = power_handler_->HandleCommand(command);
198     if (overridden_response) {
199       if (!overridden_response->is_async_promise())
200         OnDispatchOnInspectorFrontend(overridden_response->Serialize());
201       return;
202     }
203   }
204
205   IPCDevToolsAgentHost::DispatchOnInspectorBackend(message);
206 }
207
208 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) {
209   if (!render_view_host_)
210     return;
211   msg->set_routing_id(render_view_host_->GetRoutingID());
212   render_view_host_->Send(msg);
213 }
214
215 void RenderViewDevToolsAgentHost::OnClientAttached() {
216   if (!render_view_host_)
217     return;
218
219   ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
220       render_view_host_->GetProcess()->GetID());
221
222   // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
223   // extensions::ProcessManager no longer relies on this notification.
224   DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
225
226 #if defined(OS_ANDROID)
227   power_save_blocker_.reset(
228       static_cast<PowerSaveBlockerImpl*>(
229           PowerSaveBlocker::Create(
230               PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
231               "DevTools").release()));
232   if (render_view_host_->GetView()) {
233     power_save_blocker_.get()->
234         InitDisplaySleepBlocker(render_view_host_->GetView()->GetNativeView());
235   }
236 #endif
237 }
238
239 void RenderViewDevToolsAgentHost::OnClientDetached() {
240 #if defined(OS_ANDROID)
241   power_save_blocker_.reset();
242 #endif
243   overrides_handler_->OnClientDetached();
244   ClientDetachedFromRenderer();
245 }
246
247 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
248   if (!render_view_host_)
249     return;
250
251   bool process_has_agents = false;
252   RenderProcessHost* render_process_host = render_view_host_->GetProcess();
253   for (Instances::iterator it = g_instances.Get().begin();
254        it != g_instances.Get().end(); ++it) {
255     if (*it == this || !(*it)->IsAttached())
256       continue;
257     RenderViewHost* rvh = (*it)->render_view_host();
258     if (rvh && rvh->GetProcess() == render_process_host)
259       process_has_agents = true;
260   }
261
262   // We are the last to disconnect from the renderer -> revoke permissions.
263   if (!process_has_agents) {
264     ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
265         render_process_host->GetID());
266   }
267
268   // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
269   // extensions::ProcessManager no longer relies on this notification.
270   DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
271 }
272
273 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
274   Instances::iterator it = std::find(g_instances.Get().begin(),
275                                      g_instances.Get().end(),
276                                      this);
277   if (it != g_instances.Get().end())
278     g_instances.Get().erase(it);
279 }
280
281 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
282     RenderViewHost* dest_rvh) {
283   if (!render_view_host_)
284     return;
285
286   if (render_view_host_ == dest_rvh && static_cast<RenderViewHostImpl*>(
287           render_view_host_)->render_view_termination_status() ==
288               base::TERMINATION_STATUS_STILL_RUNNING)
289     return;
290   DisconnectRenderViewHost();
291   ConnectRenderViewHost(dest_rvh);
292 }
293
294 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
295     RenderViewHost* old_host,
296     RenderViewHost* new_host) {
297   if (new_host != render_view_host_) {
298     // AboutToNavigateRenderView was not called for renderer-initiated
299     // navigation.
300     DisconnectRenderViewHost();
301     ConnectRenderViewHost(new_host);
302   }
303 }
304
305 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost* rvh) {
306   if (rvh != render_view_host_)
307     return;
308
309   DCHECK(render_view_host_);
310   scoped_refptr<RenderViewDevToolsAgentHost> protect(this);
311   NotifyCloseListener();
312   ClearRenderViewHost();
313   Release();
314 }
315
316 void RenderViewDevToolsAgentHost::RenderProcessGone(
317     base::TerminationStatus status) {
318   switch(status) {
319     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
320     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
321     case base::TERMINATION_STATUS_PROCESS_CRASHED:
322 #if defined(OS_ANDROID)
323     case base::TERMINATION_STATUS_OOM_PROTECTED:
324 #endif
325       RenderViewCrashed();
326       break;
327     default:
328       break;
329   }
330 }
331
332 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
333   if (!render_view_host_)
334     return;
335   // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
336   // Connect back to the real one.
337   WebContents* web_contents =
338     WebContents::FromRenderViewHost(render_view_host_);
339   if (!web_contents)
340     return;
341   DisconnectRenderViewHost();
342   ConnectRenderViewHost(web_contents->GetRenderViewHost());
343 }
344
345 void RenderViewDevToolsAgentHost::Observe(int type,
346                                           const NotificationSource& source,
347                                           const NotificationDetails& details) {
348   if (type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) {
349     bool visible = *Details<bool>(details).ptr();
350     overrides_handler_->OnVisibilityChanged(visible);
351   }
352 }
353
354 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) {
355   DCHECK(!render_view_host_);
356   render_view_host_ = rvh;
357
358   WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh));
359
360   registrar_.Add(
361       this,
362       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
363       content::Source<RenderWidgetHost>(render_view_host_));
364 }
365
366 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
367   DCHECK(render_view_host_);
368   registrar_.Remove(
369       this,
370       content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
371       content::Source<RenderWidgetHost>(render_view_host_));
372   render_view_host_ = NULL;
373 }
374
375 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
376   SetRenderViewHost(rvh);
377   if (IsAttached())
378     Reattach(state_);
379 }
380
381 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
382   ClientDetachedFromRenderer();
383   ClearRenderViewHost();
384 }
385
386 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
387   scoped_refptr<DevToolsProtocol::Notification> notification =
388       DevToolsProtocol::CreateNotification(
389           devtools::Inspector::targetCrashed::kName, NULL);
390   DevToolsManagerImpl::GetInstance()->
391       DispatchOnInspectorFrontend(this, notification->Serialize());
392 }
393
394 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
395     const IPC::Message& msg) {
396   if (!render_view_host_)
397     return false;
398
399   bool handled = true;
400   IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, msg)
401     IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
402                         OnDispatchOnInspectorFrontend)
403     IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
404                         OnSaveAgentRuntimeState)
405     IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
406                                 handled = false; OnSwapCompositorFrame(msg))
407     IPC_MESSAGE_UNHANDLED(handled = false)
408   IPC_END_MESSAGE_MAP()
409   return handled;
410 }
411
412 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
413     const IPC::Message& message) {
414   ViewHostMsg_SwapCompositorFrame::Param param;
415   if (!ViewHostMsg_SwapCompositorFrame::Read(&message, &param))
416     return;
417   overrides_handler_->OnSwapCompositorFrame(param.b.metadata);
418 }
419
420 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
421     const cc::CompositorFrameMetadata& frame_metadata) {
422   if (!render_view_host_)
423     return;
424   overrides_handler_->OnSwapCompositorFrame(frame_metadata);
425 }
426
427 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
428     const std::string& state) {
429   if (!render_view_host_)
430     return;
431   state_ = state;
432 }
433
434 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
435     const std::string& message) {
436   if (!render_view_host_)
437     return;
438   DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
439       this, message);
440 }
441
442 }  // namespace content