1 // Copyright (c) 2010 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/possible_url_model.h"
6
7 #include "base/callback.h"
8 #include "base/i18n/rtl.h"
9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/favicon_service.h"
12 #include "chrome/browser/prefs/pref_service.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/common/pref_names.h"
15 #include "content/browser/cancelable_request.h"
16 #include "grit/app_resources.h"
17 #include "grit/generated_resources.h"
18 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "ui/base/models/table_model_observer.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/base/text/text_elider.h"
22 #include "ui/gfx/codec/png_codec.h"
23
24 using base::Time;
25 using base::TimeDelta;
26
27 namespace {
28
29 // The default favicon.
30 SkBitmap* default_favicon = NULL;
31
32 // How long we query entry points for.
33 const int kPossibleURLTimeScope = 30;
34
35 } // anonymous namespace
36
37 // Contains the data needed to show a result.
38 struct PossibleURLModel::Result {
ResultPossibleURLModel::Result39 Result() : index(0) {}
40
41 GURL url;
42 // Index of this Result in results_. This is used as the key into
43 // favicon_map_ to lookup the favicon for the url, as well as the index
44 // into results_ when the favicon is received.
45 size_t index;
46 ui::SortedDisplayURL display_url;
47 std::wstring title;
48 };
49
50
PossibleURLModel()51 PossibleURLModel::PossibleURLModel()
52 : profile_(NULL),
53 observer_(NULL) {
54 if (!default_favicon) {
55 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
56 default_favicon = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
57 }
58 }
59
~PossibleURLModel()60 PossibleURLModel::~PossibleURLModel() {
61 }
62
Reload(Profile * profile)63 void PossibleURLModel::Reload(Profile *profile) {
64 profile_ = profile;
65 consumer_.CancelAllRequests();
66 HistoryService* hs =
67 profile->GetHistoryService(Profile::EXPLICIT_ACCESS);
68 if (hs) {
69 history::QueryOptions options;
70 options.end_time = Time::Now();
71 options.begin_time =
72 options.end_time - TimeDelta::FromDays(kPossibleURLTimeScope);
73 options.max_count = 50;
74
75 hs->QueryHistory(string16(), options, &consumer_,
76 NewCallback(this, &PossibleURLModel::OnHistoryQueryComplete));
77 }
78 }
79
OnHistoryQueryComplete(HistoryService::Handle h,history::QueryResults * result)80 void PossibleURLModel::OnHistoryQueryComplete(HistoryService::Handle h,
81 history::QueryResults* result) {
82 results_.resize(result->size());
83 std::string languages = profile_ ?
84 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages) : std::string();
85 for (size_t i = 0; i < result->size(); ++i) {
86 results_[i].url = (*result)[i].url();
87 results_[i].index = i;
88 results_[i].display_url =
89 ui::SortedDisplayURL((*result)[i].url(), languages);
90 results_[i].title = UTF16ToWide((*result)[i].title());
91 }
92
93 // The old version of this code would filter out all but the most recent
94 // visit to each host, plus all typed URLs and AUTO_BOOKMARK transitions. I
95 // think this dialog has a lot of work, and I'm not sure those old
96 // conditions are correct (the results look about equal quality for my
97 // history with and without those conditions), so I'm not spending time
98 // re-implementing them here. They used to be implemented in the history
99 // service, but I think they should be implemented here because that was
100 // pretty specific behavior that shouldn't be generally exposed.
101
102 favicon_map_.clear();
103 if (observer_)
104 observer_->OnModelChanged();
105 }
106
RowCount()107 int PossibleURLModel::RowCount() {
108 return static_cast<int>(results_.size());
109 }
110
GetURL(int row)111 const GURL& PossibleURLModel::GetURL(int row) {
112 if (row < 0 || row >= RowCount()) {
113 NOTREACHED();
114 return GURL::EmptyGURL();
115 }
116 return results_[row].url;
117 }
118
GetTitle(int row)119 const std::wstring& PossibleURLModel::GetTitle(int row) {
120 if (row < 0 || row >= RowCount()) {
121 NOTREACHED();
122 return EmptyWString();
123 }
124 return results_[row].title;
125 }
126
GetText(int row,int col_id)127 string16 PossibleURLModel::GetText(int row, int col_id) {
128 if (row < 0 || row >= RowCount()) {
129 NOTREACHED();
130 return string16();
131 }
132
133 if (col_id == IDS_ASI_PAGE_COLUMN) {
134 string16 title = WideToUTF16Hack(GetTitle(row));
135 // TODO(xji): Consider adding a special case if the title text is a URL,
136 // since those should always have LTR directionality. Please refer to
137 // http://crbug.com/6726 for more information.
138 base::i18n::AdjustStringForLocaleDirection(&title);
139 return title;
140 }
141
142 // TODO(brettw): this should probably pass the GURL up so the URL elider
143 // can be used at a higher level when we know the width.
144 string16 url = results_[row].display_url.display_url();
145 return base::i18n::GetDisplayStringInLTRDirectionality(url);
146 }
147
GetIcon(int row)148 SkBitmap PossibleURLModel::GetIcon(int row) {
149 if (row < 0 || row >= RowCount()) {
150 NOTREACHED();
151 return *default_favicon;
152 }
153
154 Result& result = results_[row];
155 FaviconMap::iterator i = favicon_map_.find(result.index);
156 if (i != favicon_map_.end()) {
157 // We already requested the favicon, return it.
158 if (!i->second.isNull())
159 return i->second;
160 } else if (profile_) {
161 FaviconService* favicon_service =
162 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
163 if (favicon_service) {
164 CancelableRequestProvider::Handle h =
165 favicon_service->GetFaviconForURL(
166 result.url, history::FAVICON, &consumer_,
167 NewCallback(this, &PossibleURLModel::OnFaviconAvailable));
168 consumer_.SetClientData(favicon_service, h, result.index);
169 // Add an entry to the map so that we don't attempt to request the
170 // favicon again.
171 favicon_map_[result.index] = SkBitmap();
172 }
173 }
174 return *default_favicon;
175 }
176
CompareValues(int row1,int row2,int column_id)177 int PossibleURLModel::CompareValues(int row1, int row2, int column_id) {
178 if (column_id == IDS_ASI_URL_COLUMN) {
179 return results_[row1].display_url.Compare(
180 results_[row2].display_url, GetCollator());
181 }
182 return ui::TableModel::CompareValues(row1, row2, column_id);
183 }
184
OnFaviconAvailable(FaviconService::Handle h,history::FaviconData favicon)185 void PossibleURLModel::OnFaviconAvailable(
186 FaviconService::Handle h,
187 history::FaviconData favicon) {
188 if (profile_) {
189 FaviconService* favicon_service =
190 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
191 size_t index = consumer_.GetClientData(favicon_service, h);
192 if (favicon.is_valid()) {
193 // The decoder will leave our bitmap empty on error.
194 gfx::PNGCodec::Decode(favicon.image_data->front(),
195 favicon.image_data->size(),
196 &(favicon_map_[index]));
197
198 // Notify the observer.
199 if (!favicon_map_[index].isNull() && observer_)
200 observer_->OnItemsChanged(static_cast<int>(index), 1);
201 }
202 }
203 }
204
SetObserver(ui::TableModelObserver * observer)205 void PossibleURLModel::SetObserver(ui::TableModelObserver* observer) {
206 observer_ = observer;
207 }
208