1// Copyright 2013 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/extensions/favicon_downloader.h"
6
7#include "base/bind.h"
8#include "chrome/browser/favicon/favicon_tab_helper.h"
9#include "content/public/browser/web_contents.h"
10#include "content/public/common/favicon_url.h"
11#include "third_party/skia/include/core/SkBitmap.h"
12#include "ui/gfx/size.h"
13
14FaviconDownloader::FaviconDownloader(
15  content::WebContents* web_contents,
16  const std::vector<GURL>& extra_favicon_urls,
17  FaviconDownloaderCallback callback)
18  : content::WebContentsObserver(web_contents),
19    got_favicon_urls_(false),
20    extra_favicon_urls_(extra_favicon_urls),
21    callback_(callback),
22    weak_ptr_factory_(this) {
23}
24
25FaviconDownloader::~FaviconDownloader() {
26}
27
28void FaviconDownloader::Start() {
29  FetchIcons(extra_favicon_urls_);
30  // If the candidates aren't loaded, icons will be fetched when
31  // DidUpdateFaviconURL() is called.
32  std::vector<content::FaviconURL> favicon_tab_helper_urls =
33      GetFaviconURLsFromWebContents();
34  if (!favicon_tab_helper_urls.empty()) {
35    got_favicon_urls_ = true;
36    FetchIcons(favicon_tab_helper_urls);
37  }
38}
39
40int FaviconDownloader::DownloadImage(const GURL& url) {
41  return web_contents()->DownloadImage(
42      url,
43      true,  // is_favicon
44      0,     // no max size
45      base::Bind(&FaviconDownloader::DidDownloadFavicon,
46                 weak_ptr_factory_.GetWeakPtr()));
47}
48
49std::vector<content::FaviconURL>
50    FaviconDownloader::GetFaviconURLsFromWebContents() {
51  FaviconTabHelper* favicon_tab_helper =
52      web_contents() ? FaviconTabHelper::FromWebContents(web_contents()) : NULL;
53  // If favicon_urls() is empty, we are guaranteed that DidUpdateFaviconURLs has
54  // not yet been called for the current page's navigation.
55  return favicon_tab_helper ? favicon_tab_helper->favicon_urls()
56                            : std::vector<content::FaviconURL>();
57}
58
59void FaviconDownloader::FetchIcons(
60    const std::vector<content::FaviconURL>& favicon_urls) {
61  std::vector<GURL> urls;
62  for (std::vector<content::FaviconURL>::const_iterator it =
63           favicon_urls.begin();
64       it != favicon_urls.end(); ++it) {
65    if (it->icon_type != content::FaviconURL::INVALID_ICON)
66      urls.push_back(it->icon_url);
67  }
68  FetchIcons(urls);
69}
70
71void FaviconDownloader::FetchIcons(const std::vector<GURL>& urls) {
72  // Download icons; put their download ids into |in_progress_requests_| and
73  // their urls into |processed_urls_|.
74  for (std::vector<GURL>::const_iterator it = urls.begin();
75       it != urls.end(); ++it) {
76    // Only start the download if the url hasn't been processed before.
77    if (processed_urls_.insert(*it).second)
78      in_progress_requests_.insert(DownloadImage(*it));
79  }
80
81  // If no downloads were initiated, we can proceed directly to running the
82  // callback.
83  if (in_progress_requests_.empty() && got_favicon_urls_)
84    callback_.Run(true, favicon_map_);
85}
86
87void FaviconDownloader::DidDownloadFavicon(
88    int id,
89    int http_status_code,
90    const GURL& image_url,
91    const std::vector<SkBitmap>& bitmaps,
92    const std::vector<gfx::Size>& original_bitmap_sizes) {
93  // Request may have been canceled by DidNavigateMainFrame().
94  if (in_progress_requests_.erase(id) == 0)
95    return;
96
97  favicon_map_[image_url] = bitmaps;
98
99  // Once all requests have been resolved, perform post-download tasks.
100  if (in_progress_requests_.empty() && got_favicon_urls_)
101    callback_.Run(true, favicon_map_);
102}
103
104// content::WebContentsObserver overrides:
105void FaviconDownloader::DidNavigateMainFrame(
106    const content::LoadCommittedDetails& details,
107    const content::FrameNavigateParams& params) {
108  // Clear all pending requests.
109  in_progress_requests_.clear();
110  favicon_map_.clear();
111  callback_.Run(false, favicon_map_);
112}
113
114void FaviconDownloader::DidUpdateFaviconURL(
115    const std::vector<content::FaviconURL>& candidates) {
116  // Only consider the first candidates we are given. This prevents pages that
117  // change their favicon from spamming us.
118  if (got_favicon_urls_)
119    return;
120
121  got_favicon_urls_ = true;
122  FetchIcons(candidates);
123}
124