• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/search_engines/template_url_table_model.h"
6 
7 #include "base/callback.h"
8 #include "base/i18n/rtl.h"
9 #include "base/stl_util-inl.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/favicon_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/search_engines/template_url.h"
14 #include "chrome/browser/search_engines/template_url_model.h"
15 #include "grit/app_resources.h"
16 #include "grit/generated_resources.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/base/models/table_model_observer.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/gfx/codec/png_codec.h"
22 
23 // Group IDs used by TemplateURLTableModel.
24 static const int kMainGroupID = 0;
25 static const int kOtherGroupID = 1;
26 
27 // ModelEntry ----------------------------------------------------
28 
29 // ModelEntry wraps a TemplateURL as returned from the TemplateURL.
30 // ModelEntry also tracks state information about the URL.
31 
32 // Icon used while loading, or if a specific favicon can't be found.
33 static SkBitmap* default_icon = NULL;
34 
35 class ModelEntry {
36  public:
ModelEntry(TemplateURLTableModel * model,const TemplateURL & template_url)37   explicit ModelEntry(TemplateURLTableModel* model,
38                       const TemplateURL& template_url)
39       : template_url_(template_url),
40         load_state_(NOT_LOADED),
41         model_(model) {
42     if (!default_icon) {
43       default_icon = ResourceBundle::GetSharedInstance().
44           GetBitmapNamed(IDR_DEFAULT_FAVICON);
45     }
46   }
47 
template_url()48   const TemplateURL& template_url() {
49     return template_url_;
50   }
51 
GetIcon()52   SkBitmap GetIcon() {
53     if (load_state_ == NOT_LOADED)
54       LoadFavicon();
55     if (!favicon_.isNull())
56       return favicon_;
57     return *default_icon;
58   }
59 
60   // Resets internal status so that the next time the icon is asked for its
61   // fetched again. This should be invoked if the url is modified.
ResetIcon()62   void ResetIcon() {
63     load_state_ = NOT_LOADED;
64     favicon_ = SkBitmap();
65   }
66 
67  private:
68   // State of the favicon.
69   enum LoadState {
70     NOT_LOADED,
71     LOADING,
72     LOADED
73   };
74 
LoadFavicon()75   void LoadFavicon() {
76     load_state_ = LOADED;
77     FaviconService* favicon_service =
78         model_->template_url_model()->profile()->GetFaviconService(
79             Profile::EXPLICIT_ACCESS);
80     if (!favicon_service)
81       return;
82     GURL favicon_url = template_url().GetFaviconURL();
83     if (!favicon_url.is_valid()) {
84       // The favicon url isn't always set. Guess at one here.
85       if (template_url_.url() && template_url_.url()->IsValid()) {
86         GURL url = GURL(template_url_.url()->url());
87         if (url.is_valid())
88           favicon_url = TemplateURL::GenerateFaviconURL(url);
89       }
90       if (!favicon_url.is_valid())
91         return;
92     }
93     load_state_ = LOADING;
94     favicon_service->GetFavicon(favicon_url, history::FAVICON,
95         &request_consumer_,
96         NewCallback(this, &ModelEntry::OnFaviconDataAvailable));
97   }
98 
OnFaviconDataAvailable(FaviconService::Handle handle,history::FaviconData favicon)99   void OnFaviconDataAvailable(
100       FaviconService::Handle handle,
101       history::FaviconData favicon) {
102     load_state_ = LOADED;
103     if (favicon.is_valid() && gfx::PNGCodec::Decode(favicon.image_data->front(),
104                                                     favicon.image_data->size(),
105                                                     &favicon_)) {
106       model_->FaviconAvailable(this);
107     }
108   }
109 
110   const TemplateURL& template_url_;
111   SkBitmap favicon_;
112   LoadState load_state_;
113   TemplateURLTableModel* model_;
114   CancelableRequestConsumer request_consumer_;
115 
116   DISALLOW_COPY_AND_ASSIGN(ModelEntry);
117 };
118 
119 // TemplateURLTableModel -----------------------------------------
120 
TemplateURLTableModel(TemplateURLModel * template_url_model)121 TemplateURLTableModel::TemplateURLTableModel(
122     TemplateURLModel* template_url_model)
123     : observer_(NULL),
124       template_url_model_(template_url_model) {
125   DCHECK(template_url_model);
126   template_url_model_->Load();
127   template_url_model_->AddObserver(this);
128   Reload();
129 }
130 
~TemplateURLTableModel()131 TemplateURLTableModel::~TemplateURLTableModel() {
132   template_url_model_->RemoveObserver(this);
133   STLDeleteElements(&entries_);
134   entries_.clear();
135 }
136 
Reload()137 void TemplateURLTableModel::Reload() {
138   STLDeleteElements(&entries_);
139   entries_.clear();
140 
141   std::vector<const TemplateURL*> urls = template_url_model_->GetTemplateURLs();
142 
143   // Keywords that can be made the default first.
144   for (std::vector<const TemplateURL*>::iterator i = urls.begin();
145        i != urls.end(); ++i) {
146     const TemplateURL& template_url = *(*i);
147     // NOTE: we don't use ShowInDefaultList here to avoid items bouncing around
148     // the lists while editing.
149     if (template_url.show_in_default_list())
150       entries_.push_back(new ModelEntry(this, template_url));
151   }
152 
153   last_search_engine_index_ = static_cast<int>(entries_.size());
154 
155   // Then the rest.
156   for (std::vector<const TemplateURL*>::iterator i = urls.begin();
157        i != urls.end(); ++i) {
158     const TemplateURL* template_url = *i;
159     // NOTE: we don't use ShowInDefaultList here to avoid things bouncing
160     // the lists while editing.
161     if (!template_url->show_in_default_list() &&
162         !template_url->IsExtensionKeyword()) {
163       entries_.push_back(new ModelEntry(this, *template_url));
164     }
165   }
166 
167   if (observer_)
168     observer_->OnModelChanged();
169 }
170 
RowCount()171 int TemplateURLTableModel::RowCount() {
172   return static_cast<int>(entries_.size());
173 }
174 
GetText(int row,int col_id)175 string16 TemplateURLTableModel::GetText(int row, int col_id) {
176   DCHECK(row >= 0 && row < RowCount());
177   const TemplateURL& url = entries_[row]->template_url();
178   if (col_id == IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN) {
179     string16 url_short_name = url.short_name();
180     // TODO(xji): Consider adding a special case if the short name is a URL,
181     // since those should always be displayed LTR. Please refer to
182     // http://crbug.com/6726 for more information.
183     base::i18n::AdjustStringForLocaleDirection(&url_short_name);
184     if (template_url_model_->GetDefaultSearchProvider() == &url) {
185       return l10n_util::GetStringFUTF16(
186           IDS_SEARCH_ENGINES_EDITOR_DEFAULT_ENGINE,
187           url_short_name);
188     }
189     return url_short_name;
190   } else if (col_id == IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN) {
191     // Keyword should be domain name. Force it to have LTR directionality.
192     string16 keyword = url.keyword();
193     keyword = base::i18n::GetDisplayStringInLTRDirectionality(keyword);
194     return keyword;
195   } else {
196     NOTREACHED();
197     return string16();
198   }
199 }
200 
GetIcon(int row)201 SkBitmap TemplateURLTableModel::GetIcon(int row) {
202   DCHECK(row >= 0 && row < RowCount());
203   return entries_[row]->GetIcon();
204 }
205 
SetObserver(ui::TableModelObserver * observer)206 void TemplateURLTableModel::SetObserver(ui::TableModelObserver* observer) {
207   observer_ = observer;
208 }
209 
HasGroups()210 bool TemplateURLTableModel::HasGroups() {
211   return true;
212 }
213 
GetGroups()214 TemplateURLTableModel::Groups TemplateURLTableModel::GetGroups() {
215   Groups groups;
216 
217   Group search_engine_group;
218   search_engine_group.title =
219       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAIN_SEPARATOR);
220   search_engine_group.id = kMainGroupID;
221   groups.push_back(search_engine_group);
222 
223   Group other_group;
224   other_group.title =
225       l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_OTHER_SEPARATOR);
226   other_group.id = kOtherGroupID;
227   groups.push_back(other_group);
228 
229   return groups;
230 }
231 
GetGroupID(int row)232 int TemplateURLTableModel::GetGroupID(int row) {
233   DCHECK(row >= 0 && row < RowCount());
234   return row < last_search_engine_index_ ? kMainGroupID : kOtherGroupID;
235 }
236 
Remove(int index)237 void TemplateURLTableModel::Remove(int index) {
238   // Remove the observer while we modify the model, that way we don't need to
239   // worry about the model calling us back when we mutate it.
240   template_url_model_->RemoveObserver(this);
241   const TemplateURL* template_url = &GetTemplateURL(index);
242 
243   scoped_ptr<ModelEntry> entry(entries_[index]);
244   entries_.erase(entries_.begin() + index);
245   if (index < last_search_engine_index_)
246     last_search_engine_index_--;
247   if (observer_)
248     observer_->OnItemsRemoved(index, 1);
249 
250   // Make sure to remove from the table model first, otherwise the
251   // TemplateURL would be freed.
252   template_url_model_->Remove(template_url);
253   template_url_model_->AddObserver(this);
254 }
255 
Add(int index,TemplateURL * template_url)256 void TemplateURLTableModel::Add(int index, TemplateURL* template_url) {
257   DCHECK(index >= 0 && index <= RowCount());
258   ModelEntry* entry = new ModelEntry(this, *template_url);
259   entries_.insert(entries_.begin() + index, entry);
260   if (observer_)
261     observer_->OnItemsAdded(index, 1);
262   template_url_model_->RemoveObserver(this);
263   template_url_model_->Add(template_url);
264   template_url_model_->AddObserver(this);
265 }
266 
ModifyTemplateURL(int index,const string16 & title,const string16 & keyword,const std::string & url)267 void TemplateURLTableModel::ModifyTemplateURL(int index,
268                                               const string16& title,
269                                               const string16& keyword,
270                                               const std::string& url) {
271   DCHECK(index >= 0 && index <= RowCount());
272   const TemplateURL* template_url = &GetTemplateURL(index);
273   template_url_model_->RemoveObserver(this);
274   template_url_model_->ResetTemplateURL(template_url, title, keyword, url);
275   if (template_url_model_->GetDefaultSearchProvider() == template_url &&
276       !TemplateURL::SupportsReplacement(template_url)) {
277     // The entry was the default search provider, but the url has been modified
278     // so that it no longer supports replacement. Reset the default search
279     // provider so that it doesn't point to a bogus entry.
280     template_url_model_->SetDefaultSearchProvider(NULL);
281   }
282   template_url_model_->AddObserver(this);
283   ReloadIcon(index);  // Also calls NotifyChanged().
284 }
285 
ReloadIcon(int index)286 void TemplateURLTableModel::ReloadIcon(int index) {
287   DCHECK(index >= 0 && index < RowCount());
288 
289   entries_[index]->ResetIcon();
290 
291   NotifyChanged(index);
292 }
293 
GetTemplateURL(int index)294 const TemplateURL& TemplateURLTableModel::GetTemplateURL(int index) {
295   return entries_[index]->template_url();
296 }
297 
IndexOfTemplateURL(const TemplateURL * template_url)298 int TemplateURLTableModel::IndexOfTemplateURL(
299     const TemplateURL* template_url) {
300   for (std::vector<ModelEntry*>::iterator i = entries_.begin();
301        i != entries_.end(); ++i) {
302     ModelEntry* entry = *i;
303     if (&(entry->template_url()) == template_url)
304       return static_cast<int>(i - entries_.begin());
305   }
306   return -1;
307 }
308 
MoveToMainGroup(int index)309 int TemplateURLTableModel::MoveToMainGroup(int index) {
310   if (index < last_search_engine_index_)
311     return index;  // Already in the main group.
312 
313   ModelEntry* current_entry = entries_[index];
314   entries_.erase(index + entries_.begin());
315   if (observer_)
316     observer_->OnItemsRemoved(index, 1);
317 
318   const int new_index = last_search_engine_index_++;
319   entries_.insert(entries_.begin() + new_index, current_entry);
320   if (observer_)
321     observer_->OnItemsAdded(new_index, 1);
322   return new_index;
323 }
324 
MakeDefaultTemplateURL(int index)325 int TemplateURLTableModel::MakeDefaultTemplateURL(int index) {
326   if (index < 0 || index >= RowCount()) {
327     NOTREACHED();
328     return -1;
329   }
330 
331   const TemplateURL* keyword = &GetTemplateURL(index);
332   const TemplateURL* current_default =
333       template_url_model_->GetDefaultSearchProvider();
334   if (current_default == keyword)
335     return -1;
336 
337   template_url_model_->RemoveObserver(this);
338   template_url_model_->SetDefaultSearchProvider(keyword);
339   template_url_model_->AddObserver(this);
340 
341   // The formatting of the default engine is different; notify the table that
342   // both old and new entries have changed.
343   if (current_default != NULL) {
344     int old_index = IndexOfTemplateURL(current_default);
345     // current_default may not be in the list of TemplateURLs if the database is
346     // corrupt and the default TemplateURL is used from preferences
347     if (old_index >= 0)
348       NotifyChanged(old_index);
349   }
350   const int new_index = IndexOfTemplateURL(keyword);
351   NotifyChanged(new_index);
352 
353   // Make sure the new default is in the main group.
354   return MoveToMainGroup(index);
355 }
356 
NotifyChanged(int index)357 void TemplateURLTableModel::NotifyChanged(int index) {
358   if (observer_) {
359     DCHECK_GE(index, 0);
360     observer_->OnItemsChanged(index, 1);
361   }
362 }
363 
FaviconAvailable(ModelEntry * entry)364 void TemplateURLTableModel::FaviconAvailable(ModelEntry* entry) {
365   std::vector<ModelEntry*>::iterator i =
366       find(entries_.begin(), entries_.end(), entry);
367   DCHECK(i != entries_.end());
368   NotifyChanged(static_cast<int>(i - entries_.begin()));
369 }
370 
OnTemplateURLModelChanged()371 void TemplateURLTableModel::OnTemplateURLModelChanged() {
372   Reload();
373 }
374