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