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/devtools_manager_impl.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/message_loop/message_loop.h"
11#include "content/browser/devtools/devtools_netlog_observer.h"
12#include "content/browser/devtools/render_view_devtools_agent_host.h"
13#include "content/browser/renderer_host/render_view_host_impl.h"
14#include "content/browser/web_contents/web_contents_impl.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/browser/devtools_client_host.h"
17
18namespace content {
19
20// static
21DevToolsManager* DevToolsManager::GetInstance() {
22  return DevToolsManagerImpl::GetInstance();
23}
24
25// static
26DevToolsManagerImpl* DevToolsManagerImpl::GetInstance() {
27  return Singleton<DevToolsManagerImpl>::get();
28}
29
30DevToolsManagerImpl::DevToolsManagerImpl() {
31}
32
33DevToolsManagerImpl::~DevToolsManagerImpl() {
34  DCHECK(agent_to_client_host_.empty());
35  DCHECK(client_to_agent_host_.empty());
36}
37
38DevToolsClientHost* DevToolsManagerImpl::GetDevToolsClientHostFor(
39    DevToolsAgentHostImpl* agent_host_impl) {
40  AgentToClientHostMap::iterator it =
41      agent_to_client_host_.find(agent_host_impl);
42  if (it != agent_to_client_host_.end())
43    return it->second;
44  return NULL;
45}
46
47DevToolsAgentHost* DevToolsManagerImpl::GetDevToolsAgentHostFor(
48    DevToolsClientHost* client_host) {
49  ClientToAgentHostMap::iterator it = client_to_agent_host_.find(client_host);
50  if (it != client_to_agent_host_.end())
51    return it->second.get();
52  return NULL;
53}
54
55void DevToolsManagerImpl::RegisterDevToolsClientHostFor(
56    DevToolsAgentHost* agent_host,
57    DevToolsClientHost* client_host) {
58  DevToolsAgentHostImpl* agent_host_impl =
59      static_cast<DevToolsAgentHostImpl*>(agent_host);
60  DevToolsClientHost* old_client_host =
61      GetDevToolsClientHostFor(agent_host_impl);
62  if (old_client_host) {
63    old_client_host->ReplacedWithAnotherClient();
64    UnregisterDevToolsClientHostFor(agent_host_impl);
65  }
66  BindClientHost(agent_host_impl, client_host);
67  agent_host_impl->Attach();
68}
69
70bool DevToolsManagerImpl::DispatchOnInspectorBackend(
71    DevToolsClientHost* from,
72    const std::string& message) {
73  DevToolsAgentHost* agent_host = GetDevToolsAgentHostFor(from);
74  if (!agent_host)
75    return false;
76  DevToolsAgentHostImpl* agent_host_impl =
77      static_cast<DevToolsAgentHostImpl*>(agent_host);
78  agent_host_impl->DispatchOnInspectorBackend(message);
79  return true;
80}
81
82void DevToolsManagerImpl::DispatchOnInspectorFrontend(
83    DevToolsAgentHost* agent_host,
84    const std::string& message) {
85  DevToolsAgentHostImpl* agent_host_impl =
86      static_cast<DevToolsAgentHostImpl*>(agent_host);
87  DevToolsClientHost* client_host = GetDevToolsClientHostFor(agent_host_impl);
88  if (!client_host) {
89    // Client window was closed while there were messages
90    // being sent to it.
91    return;
92  }
93  client_host->DispatchOnInspectorFrontend(message);
94}
95
96void DevToolsManagerImpl::ClientHostClosing(DevToolsClientHost* client_host) {
97  DevToolsAgentHost* agent_host = GetDevToolsAgentHostFor(client_host);
98  if (!agent_host)
99    return;
100  DevToolsAgentHostImpl* agent_host_impl =
101      static_cast<DevToolsAgentHostImpl*>(agent_host);
102  UnbindClientHost(agent_host_impl, client_host);
103}
104
105void DevToolsManagerImpl::AgentHostClosing(DevToolsAgentHostImpl* agent_host) {
106  UnregisterDevToolsClientHostFor(agent_host);
107}
108
109void DevToolsManagerImpl::UnregisterDevToolsClientHostFor(
110    DevToolsAgentHostImpl* agent_host_impl) {
111  DevToolsClientHost* client_host = GetDevToolsClientHostFor(agent_host_impl);
112  if (!client_host)
113    return;
114  UnbindClientHost(agent_host_impl, client_host);
115  client_host->InspectedContentsClosing();
116}
117
118void DevToolsManagerImpl::BindClientHost(
119    DevToolsAgentHostImpl* agent_host,
120    DevToolsClientHost* client_host) {
121  DCHECK(agent_to_client_host_.find(agent_host) ==
122      agent_to_client_host_.end());
123  DCHECK(client_to_agent_host_.find(client_host) ==
124      client_to_agent_host_.end());
125
126  if (client_to_agent_host_.empty()) {
127    BrowserThread::PostTask(
128        BrowserThread::IO,
129        FROM_HERE,
130        base::Bind(&DevToolsNetLogObserver::Attach));
131  }
132  agent_to_client_host_[agent_host] = client_host;
133  client_to_agent_host_[client_host] = agent_host;
134  agent_host->set_close_listener(this);
135}
136
137void DevToolsManagerImpl::UnbindClientHost(DevToolsAgentHostImpl* agent_host,
138                                           DevToolsClientHost* client_host) {
139  DCHECK(agent_host);
140  scoped_refptr<DevToolsAgentHostImpl> protect(agent_host);
141  DCHECK(agent_to_client_host_.find(agent_host)->second ==
142      client_host);
143  DCHECK(client_to_agent_host_.find(client_host)->second.get() == agent_host);
144  agent_host->set_close_listener(NULL);
145
146  agent_to_client_host_.erase(agent_host);
147  client_to_agent_host_.erase(client_host);
148
149  if (client_to_agent_host_.empty()) {
150    BrowserThread::PostTask(
151        BrowserThread::IO,
152        FROM_HERE,
153        base::Bind(&DevToolsNetLogObserver::Detach));
154  }
155  // Lazy agent hosts can be deleted from within detach.
156  // Do not access agent_host below this line.
157  agent_host->Detach();
158}
159
160void DevToolsManagerImpl::CloseAllClientHosts() {
161  std::vector<DevToolsAgentHostImpl*> agents;
162  for (AgentToClientHostMap::iterator it =
163           agent_to_client_host_.begin();
164       it != agent_to_client_host_.end(); ++it) {
165    agents.push_back(it->first);
166  }
167  for (std::vector<DevToolsAgentHostImpl*>::iterator it = agents.begin();
168       it != agents.end(); ++it) {
169    UnregisterDevToolsClientHostFor(*it);
170  }
171}
172
173void DevToolsManagerImpl::AddAgentStateCallback(const Callback& callback) {
174  callbacks_.push_back(&callback);
175}
176
177void DevToolsManagerImpl::RemoveAgentStateCallback(const Callback& callback) {
178  CallbackContainer::iterator it =
179      std::find(callbacks_.begin(), callbacks_.end(), &callback);
180  DCHECK(it != callbacks_.end());
181  callbacks_.erase(it);
182}
183
184void DevToolsManagerImpl::NotifyObservers(DevToolsAgentHost* agent_host,
185                                          bool attached) {
186  CallbackContainer copy(callbacks_);
187  for (CallbackContainer::iterator it = copy.begin(); it != copy.end(); ++it)
188     (*it)->Run(agent_host, attached);
189}
190
191}  // namespace content
192