1// Copyright 2014 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 "chrome/browser/android/dev_tools_manager_delegate_android.h"
6
7#include "base/basictypes.h"
8#include "base/compiler_specific.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/android/tab_android.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/history/top_sites.h"
14#include "chrome/browser/profiles/profile_manager.h"
15#include "chrome/browser/ui/android/tab_model/tab_model.h"
16#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
17#include "content/public/browser/devtools_agent_host.h"
18#include "content/public/browser/devtools_target.h"
19#include "content/public/browser/favicon_status.h"
20#include "content/public/browser/navigation_entry.h"
21#include "content/public/browser/web_contents.h"
22
23using content::DevToolsAgentHost;
24using content::WebContents;
25
26namespace {
27
28const char kTargetTypePage[] = "page";
29const char kTargetTypeServiceWorker[] = "service_worker";
30const char kTargetTypeOther[] = "other";
31
32GURL GetFaviconURLForContents(WebContents* web_contents) {
33  content::NavigationController& controller = web_contents->GetController();
34  content::NavigationEntry* entry = controller.GetActiveEntry();
35  if (entry != NULL && entry->GetURL().is_valid())
36    return entry->GetFavicon().url;
37  return GURL();
38}
39
40GURL GetFaviconURLForAgentHost(
41    scoped_refptr<DevToolsAgentHost> agent_host) {
42  if (WebContents* web_contents = agent_host->GetWebContents())
43    return GetFaviconURLForContents(web_contents);
44  return GURL();
45}
46
47base::TimeTicks GetLastActiveTimeForAgentHost(
48    scoped_refptr<DevToolsAgentHost> agent_host) {
49  if (WebContents* web_contents = agent_host->GetWebContents())
50    return web_contents->GetLastActiveTime();
51  return base::TimeTicks();
52}
53
54class TargetBase : public content::DevToolsTarget {
55 public:
56  // content::DevToolsTarget implementation:
57  virtual std::string GetParentId() const OVERRIDE { return std::string(); }
58
59  virtual std::string GetTitle() const OVERRIDE { return title_; }
60
61  virtual std::string GetDescription() const OVERRIDE { return std::string(); }
62
63  virtual GURL GetURL() const OVERRIDE { return url_; }
64
65  virtual GURL GetFaviconURL() const OVERRIDE { return favicon_url_; }
66
67  virtual base::TimeTicks GetLastActivityTime() const OVERRIDE {
68    return last_activity_time_;
69  }
70
71 protected:
72  explicit TargetBase(WebContents* web_contents)
73      : title_(base::UTF16ToUTF8(web_contents->GetTitle())),
74        url_(web_contents->GetURL()),
75        favicon_url_(GetFaviconURLForContents(web_contents)),
76        last_activity_time_(web_contents->GetLastActiveTime()) {
77  }
78
79  explicit TargetBase(scoped_refptr<DevToolsAgentHost> agent_host)
80      : title_(agent_host->GetTitle()),
81        url_(agent_host->GetURL()),
82        favicon_url_(GetFaviconURLForAgentHost(agent_host)),
83        last_activity_time_(GetLastActiveTimeForAgentHost(agent_host)) {
84  }
85
86  TargetBase(const std::string& title, const GURL& url)
87      : title_(title),
88        url_(url) {
89  }
90
91 private:
92  const std::string title_;
93  const GURL url_;
94  const GURL favicon_url_;
95  const base::TimeTicks last_activity_time_;
96};
97
98class TabTarget : public TargetBase {
99 public:
100  static TabTarget* CreateForWebContents(int tab_id,
101                                         WebContents* web_contents) {
102    return new TabTarget(tab_id, web_contents);
103  }
104
105  static TabTarget* CreateForUnloadedTab(int tab_id,
106                                         const base::string16& title,
107                                         const GURL& url) {
108    return new TabTarget(tab_id, title, url);
109  }
110
111  // content::DevToolsTarget implementation:
112  virtual std::string GetId() const OVERRIDE {
113    return base::IntToString(tab_id_);
114  }
115
116  virtual std::string GetType() const OVERRIDE {
117    return kTargetTypePage;
118  }
119
120  virtual bool IsAttached() const OVERRIDE {
121    TabModel* model;
122    int index;
123    if (!FindTab(&model, &index))
124      return false;
125    WebContents* web_contents = model->GetWebContentsAt(index);
126    if (!web_contents)
127      return false;
128    return DevToolsAgentHost::IsDebuggerAttached(web_contents);
129  }
130
131  virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE {
132    TabModel* model;
133    int index;
134    if (!FindTab(&model, &index))
135      return NULL;
136    WebContents* web_contents = model->GetWebContentsAt(index);
137    if (!web_contents) {
138      // The tab has been pushed out of memory, pull it back.
139      TabAndroid* tab = model->GetTabAt(index);
140      if (!tab)
141        return NULL;
142
143      if (!tab->LoadIfNeeded())
144        return NULL;
145
146      web_contents = model->GetWebContentsAt(index);
147      if (!web_contents)
148        return NULL;
149    }
150    return DevToolsAgentHost::GetOrCreateFor(web_contents);
151  }
152
153  virtual bool Activate() const OVERRIDE {
154    TabModel* model;
155    int index;
156    if (!FindTab(&model, &index))
157      return false;
158    model->SetActiveIndex(index);
159    return true;
160  }
161
162  virtual bool Close() const OVERRIDE {
163    TabModel* model;
164    int index;
165    if (!FindTab(&model, &index))
166      return false;
167    model->CloseTabAt(index);
168    return true;
169  }
170
171 private:
172  TabTarget(int tab_id, WebContents* web_contents)
173      : TargetBase(web_contents),
174        tab_id_(tab_id) {
175  }
176
177  TabTarget(int tab_id, const base::string16& title, const GURL& url)
178      : TargetBase(base::UTF16ToUTF8(title), url),
179        tab_id_(tab_id) {
180  }
181
182  bool FindTab(TabModel** model_result, int* index_result) const {
183    for (TabModelList::const_iterator iter = TabModelList::begin();
184        iter != TabModelList::end(); ++iter) {
185      TabModel* model = *iter;
186      for (int i = 0; i < model->GetTabCount(); ++i) {
187        TabAndroid* tab = model->GetTabAt(i);
188        if (tab && tab->GetAndroidId() == tab_id_) {
189          *model_result = model;
190          *index_result = i;
191          return true;
192        }
193      }
194    }
195    return false;
196  }
197
198  const int tab_id_;
199};
200
201class NonTabTarget : public TargetBase {
202 public:
203  explicit NonTabTarget(scoped_refptr<DevToolsAgentHost> agent_host)
204      : TargetBase(agent_host),
205        agent_host_(agent_host) {
206  }
207
208  // content::DevToolsTarget implementation:
209  virtual std::string GetId() const OVERRIDE {
210    return agent_host_->GetId();
211  }
212
213  virtual std::string GetType() const OVERRIDE {
214    switch (agent_host_->GetType()) {
215      case DevToolsAgentHost::TYPE_WEB_CONTENTS:
216        if (TabModelList::begin() == TabModelList::end()) {
217          // If there are no tab models we must be running in ChromeShell.
218          // Return the 'page' target type for backwards compatibility.
219          return kTargetTypePage;
220        }
221        break;
222      case DevToolsAgentHost::TYPE_SERVICE_WORKER:
223        return kTargetTypeServiceWorker;
224      default:
225        break;
226    }
227    return kTargetTypeOther;
228  }
229
230  virtual bool IsAttached() const OVERRIDE {
231    return agent_host_->IsAttached();
232  }
233
234  virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE {
235    return agent_host_;
236  }
237
238  virtual bool Activate() const OVERRIDE {
239    return agent_host_->Activate();
240  }
241
242  virtual bool Close() const OVERRIDE {
243    return agent_host_->Close();
244  }
245
246 private:
247  scoped_refptr<DevToolsAgentHost> agent_host_;
248};
249
250}  // namespace
251
252DevToolsManagerDelegateAndroid::DevToolsManagerDelegateAndroid()
253    : network_protocol_handler_(new DevToolsNetworkProtocolHandler()) {
254}
255
256DevToolsManagerDelegateAndroid::~DevToolsManagerDelegateAndroid() {
257}
258
259void DevToolsManagerDelegateAndroid::Inspect(
260    content::BrowserContext* browser_context,
261    content::DevToolsAgentHost* agent_host) {
262}
263
264base::DictionaryValue* DevToolsManagerDelegateAndroid::HandleCommand(
265    content::DevToolsAgentHost* agent_host,
266    base::DictionaryValue* command_dict) {
267  return network_protocol_handler_->HandleCommand(agent_host, command_dict);
268}
269
270void DevToolsManagerDelegateAndroid::DevToolsAgentStateChanged(
271    content::DevToolsAgentHost* agent_host,
272    bool attached) {
273  network_protocol_handler_->DevToolsAgentStateChanged(agent_host, attached);
274}
275
276scoped_ptr<content::DevToolsTarget>
277    DevToolsManagerDelegateAndroid::CreateNewTarget(const GURL& url) {
278  if (TabModelList::empty())
279    return scoped_ptr<content::DevToolsTarget>();
280
281  TabModel* tab_model = TabModelList::get(0);
282  if (!tab_model)
283    return scoped_ptr<content::DevToolsTarget>();
284
285  WebContents* web_contents = tab_model->CreateNewTabForDevTools(url);
286  if (!web_contents)
287    return scoped_ptr<content::DevToolsTarget>();
288
289  TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
290  if (!tab)
291    return scoped_ptr<content::DevToolsTarget>();
292
293  return scoped_ptr<content::DevToolsTarget>(
294      TabTarget::CreateForWebContents(tab->GetAndroidId(), web_contents));
295}
296
297void DevToolsManagerDelegateAndroid::EnumerateTargets(TargetCallback callback) {
298  TargetList targets;
299
300  // Enumerate existing tabs, including the ones with no WebContents.
301  std::set<WebContents*> tab_web_contents;
302  for (TabModelList::const_iterator iter = TabModelList::begin();
303      iter != TabModelList::end(); ++iter) {
304    TabModel* model = *iter;
305    for (int i = 0; i < model->GetTabCount(); ++i) {
306      TabAndroid* tab = model->GetTabAt(i);
307      if (!tab)
308        continue;
309
310      WebContents* web_contents = model->GetWebContentsAt(i);
311      if (web_contents) {
312        tab_web_contents.insert(web_contents);
313        targets.push_back(TabTarget::CreateForWebContents(tab->GetAndroidId(),
314                                                          web_contents));
315      } else {
316        targets.push_back(TabTarget::CreateForUnloadedTab(tab->GetAndroidId(),
317                                                          tab->GetTitle(),
318                                                          tab->GetURL()));
319      }
320    }
321  }
322
323  // Add targets for WebContents not associated with any tabs.
324  DevToolsAgentHost::List agents = DevToolsAgentHost::GetOrCreateAll();
325  for (DevToolsAgentHost::List::iterator it = agents.begin();
326       it != agents.end(); ++it) {
327    if (WebContents* web_contents = (*it)->GetWebContents()) {
328      if (tab_web_contents.find(web_contents) != tab_web_contents.end())
329        continue;
330    }
331    targets.push_back(new NonTabTarget(*it));
332  }
333
334  callback.Run(targets);
335}
336
337std::string DevToolsManagerDelegateAndroid::GetPageThumbnailData(
338    const GURL& url) {
339  Profile* profile = ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
340  history::TopSites* top_sites = profile->GetTopSites();
341  if (top_sites) {
342    scoped_refptr<base::RefCountedMemory> data;
343    if (top_sites->GetPageThumbnail(url, false, &data))
344      return std::string(data->front_as<char>(), data->size());
345  }
346  return std::string();
347}
348