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