• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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