1// Copyright (c) 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 "android_webview/browser/icon_helper.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/hash.h"
10#include "base/logging.h"
11#include "content/public/browser/browser_thread.h"
12#include "content/public/browser/web_contents.h"
13#include "content/public/common/favicon_url.h"
14#include "third_party/skia/include/core/SkBitmap.h"
15#include "ui/gfx/size.h"
16
17using content::BrowserThread;
18using content::WebContents;
19
20namespace android_webview {
21
22IconHelper::IconHelper(WebContents* web_contents)
23    : WebContentsObserver(web_contents),
24      listener_(NULL),
25      missing_favicon_urls_() {
26}
27
28IconHelper::~IconHelper() {
29}
30
31void IconHelper::SetListener(Listener* listener) {
32  listener_ = listener;
33}
34
35void IconHelper::DownloadFaviconCallback(
36    int id,
37    int http_status_code,
38    const GURL& image_url,
39    const std::vector<SkBitmap>& bitmaps,
40    const std::vector<gfx::Size>& original_bitmap_sizes) {
41  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
42  if (http_status_code == 404) {
43    MarkUnableToDownloadFavicon(image_url);
44    return;
45  }
46
47  if (bitmaps.size() == 0) {
48    return;
49  }
50
51  // We can protentially have multiple frames of the icon
52  // in different sizes. We need more fine grain API spec
53  // to let clients pick out the frame they want.
54
55  // TODO(acleung): Pick the best icon to return based on size.
56  if (listener_)
57    listener_->OnReceivedIcon(image_url, bitmaps[0]);
58}
59
60void IconHelper::DidUpdateFaviconURL(
61    const std::vector<content::FaviconURL>& candidates) {
62  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
63  for (std::vector<content::FaviconURL>::const_iterator i = candidates.begin();
64       i != candidates.end(); ++i) {
65    if (!i->icon_url.is_valid())
66      continue;
67
68    switch(i->icon_type) {
69      case content::FaviconURL::FAVICON:
70        if ((listener_ && !listener_->ShouldDownloadFavicon(i->icon_url)) ||
71            WasUnableToDownloadFavicon(i->icon_url)) {
72          break;
73        }
74        web_contents()->DownloadImage(i->icon_url,
75            true,  // Is a favicon
76            0,  // No maximum size
77            base::Bind(
78                &IconHelper::DownloadFaviconCallback, base::Unretained(this)));
79        break;
80      case content::FaviconURL::TOUCH_ICON:
81        if (listener_)
82          listener_->OnReceivedTouchIconUrl(i->icon_url.spec(), false);
83        break;
84      case content::FaviconURL::TOUCH_PRECOMPOSED_ICON:
85        if (listener_)
86          listener_->OnReceivedTouchIconUrl(i->icon_url.spec(), true);
87        break;
88      case content::FaviconURL::INVALID_ICON:
89        // Silently ignore it. Only trigger a callback on valid icons.
90        break;
91      default:
92        NOTREACHED();
93        break;
94    }
95  }
96}
97
98void IconHelper::DidStartNavigationToPendingEntry(
99    const GURL& url,
100    content::NavigationController::ReloadType reload_type) {
101  if (reload_type == content::NavigationController::RELOAD_IGNORING_CACHE)
102    ClearUnableToDownloadFavicons();
103}
104
105void IconHelper::MarkUnableToDownloadFavicon(const GURL& icon_url) {
106  MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
107  missing_favicon_urls_.insert(url_hash);
108}
109
110bool IconHelper::WasUnableToDownloadFavicon(const GURL& icon_url) const {
111  MissingFaviconURLHash url_hash = base::Hash(icon_url.spec());
112  return missing_favicon_urls_.find(url_hash) != missing_favicon_urls_.end();
113}
114
115void IconHelper::ClearUnableToDownloadFavicons() {
116  missing_favicon_urls_.clear();
117}
118
119}  // namespace android_webview
120