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 "chrome/browser/ui/webui/favicon_source.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/strings/string_number_conversions.h"
10#include "chrome/browser/favicon/favicon_service_factory.h"
11#include "chrome/browser/history/top_sites.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/search/instant_io_context.h"
14#include "chrome/browser/sync/glue/session_model_associator.h"
15#include "chrome/browser/sync/profile_sync_service.h"
16#include "chrome/browser/sync/profile_sync_service_factory.h"
17#include "chrome/common/favicon/favicon_url_parser.h"
18#include "chrome/common/url_constants.h"
19#include "grit/locale_settings.h"
20#include "grit/ui_resources.h"
21#include "net/url_request/url_request.h"
22#include "ui/base/l10n/l10n_util.h"
23#include "ui/base/layout.h"
24#include "ui/base/resource/resource_bundle.h"
25#include "ui/webui/web_ui_util.h"
26
27FaviconSource::IconRequest::IconRequest()
28    : size_in_dip(gfx::kFaviconSize),
29      scale_factor(ui::SCALE_FACTOR_NONE) {
30}
31
32FaviconSource::IconRequest::IconRequest(
33    const content::URLDataSource::GotDataCallback& cb,
34    const GURL& path,
35    int size,
36    ui::ScaleFactor scale)
37    : callback(cb),
38      request_path(path),
39      size_in_dip(size),
40      scale_factor(scale) {
41}
42
43FaviconSource::IconRequest::~IconRequest() {
44}
45
46FaviconSource::FaviconSource(Profile* profile, IconType type)
47    : profile_(profile->GetOriginalProfile()),
48      icon_types_(type == FAVICON ? chrome::FAVICON :
49          chrome::TOUCH_PRECOMPOSED_ICON | chrome::TOUCH_ICON |
50          chrome::FAVICON) {
51}
52
53FaviconSource::~FaviconSource() {
54}
55
56std::string FaviconSource::GetSource() const {
57  return icon_types_ == chrome::FAVICON ?
58      chrome::kChromeUIFaviconHost : chrome::kChromeUITouchIconHost;
59}
60
61void FaviconSource::StartDataRequest(
62    const std::string& path,
63    int render_process_id,
64    int render_view_id,
65    const content::URLDataSource::GotDataCallback& callback) {
66  FaviconService* favicon_service =
67      FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
68  if (!favicon_service) {
69    SendDefaultResponse(callback);
70    return;
71  }
72
73  chrome::ParsedFaviconPath parsed;
74  bool success = chrome::ParseFaviconPath(path, icon_types_, &parsed);
75  if (!success) {
76    SendDefaultResponse(callback);
77    return;
78  }
79
80  GURL url(parsed.url);
81
82  if (parsed.is_icon_url) {
83    // TODO(michaelbai): Change GetRawFavicon to support combination of
84    // IconType.
85    favicon_service->GetRawFavicon(
86        url,
87        chrome::FAVICON,
88        parsed.size_in_dip,
89        parsed.scale_factor,
90        base::Bind(&FaviconSource::OnFaviconDataAvailable,
91                   base::Unretained(this),
92                   IconRequest(callback,
93                               url,
94                               parsed.size_in_dip,
95                               parsed.scale_factor)),
96        &cancelable_task_tracker_);
97  } else {
98    // Intercept requests for prepopulated pages.
99    for (size_t i = 0; i < arraysize(history::kPrepopulatedPages); i++) {
100      if (url.spec() ==
101          l10n_util::GetStringUTF8(history::kPrepopulatedPages[i].url_id)) {
102        callback.Run(
103            ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
104                history::kPrepopulatedPages[i].favicon_id,
105                parsed.scale_factor));
106        return;
107      }
108    }
109
110    favicon_service->GetRawFaviconForURL(
111        FaviconService::FaviconForURLParams(
112            profile_, url, icon_types_, parsed.size_in_dip),
113        parsed.scale_factor,
114        base::Bind(&FaviconSource::OnFaviconDataAvailable,
115                   base::Unretained(this),
116                   IconRequest(callback,
117                               url,
118                               parsed.size_in_dip,
119                               parsed.scale_factor)),
120        &cancelable_task_tracker_);
121  }
122}
123
124std::string FaviconSource::GetMimeType(const std::string&) const {
125  // We need to explicitly return a mime type, otherwise if the user tries to
126  // drag the image they get no extension.
127  return "image/png";
128}
129
130bool FaviconSource::ShouldReplaceExistingSource() const {
131  // Leave the existing DataSource in place, otherwise we'll drop any pending
132  // requests on the floor.
133  return false;
134}
135
136bool FaviconSource::ShouldServiceRequest(const net::URLRequest* request) const {
137  if (request->url().SchemeIs(chrome::kChromeSearchScheme))
138    return InstantIOContext::ShouldServiceRequest(request);
139  return URLDataSource::ShouldServiceRequest(request);
140}
141
142bool FaviconSource::HandleMissingResource(const IconRequest& request) {
143  // If the favicon is not available, try to use the synced favicon.
144  ProfileSyncService* sync_service =
145      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
146  browser_sync::SessionModelAssociator* associator = sync_service ?
147      sync_service->GetSessionModelAssociator() : NULL;
148
149  scoped_refptr<base::RefCountedMemory> response;
150  if (associator &&
151      associator->GetSyncedFaviconForPageURL(request.request_path.spec(),
152                                             &response)) {
153    request.callback.Run(response.get());
154    return true;
155  }
156  return false;
157}
158
159void FaviconSource::OnFaviconDataAvailable(
160    const IconRequest& request,
161    const chrome::FaviconBitmapResult& bitmap_result) {
162  if (bitmap_result.is_valid()) {
163    // Forward the data along to the networking system.
164    request.callback.Run(bitmap_result.bitmap_data.get());
165  } else if (!HandleMissingResource(request)) {
166    SendDefaultResponse(request);
167  }
168}
169
170void FaviconSource::SendDefaultResponse(
171    const content::URLDataSource::GotDataCallback& callback) {
172  SendDefaultResponse(
173      IconRequest(callback, GURL(), 16, ui::SCALE_FACTOR_100P));
174}
175
176void FaviconSource::SendDefaultResponse(const IconRequest& icon_request) {
177  int favicon_index;
178  int resource_id;
179  switch (icon_request.size_in_dip) {
180    case 64:
181      favicon_index = SIZE_64;
182      resource_id = IDR_DEFAULT_FAVICON_64;
183      break;
184    case 32:
185      favicon_index = SIZE_32;
186      resource_id = IDR_DEFAULT_FAVICON_32;
187      break;
188    default:
189      favicon_index = SIZE_16;
190      resource_id = IDR_DEFAULT_FAVICON;
191      break;
192  }
193  base::RefCountedMemory* default_favicon =
194      default_favicons_[favicon_index].get();
195
196  if (!default_favicon) {
197    ui::ScaleFactor scale_factor = icon_request.scale_factor;
198    default_favicon = ResourceBundle::GetSharedInstance()
199        .LoadDataResourceBytesForScale(resource_id, scale_factor);
200    default_favicons_[favicon_index] = default_favicon;
201  }
202
203  icon_request.callback.Run(default_favicon);
204}
205