Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / custom_home_pages_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/custom_home_pages_table_model.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/i18n/rtl.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/history/history_service_factory.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_iterator.h"
16 #include "chrome/browser/ui/browser_list.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/common/url_constants.h"
20 #include "content/public/browser/web_contents.h"
21 #include "grit/generated_resources.h"
22 #include "grit/ui_resources.h"
23 #include "net/base/net_util.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/base/models/table_model_observer.h"
26 #include "ui/gfx/codec/png_codec.h"
27 #include "url/gurl.h"
28
29 namespace {
30
31 // Checks whether the given URL should count as one of the "current" pages.
32 // Returns true for all pages except dev tools and settings.
33 bool ShouldAddPage(const GURL& url) {
34   if (url.is_empty())
35     return false;
36
37   if (url.SchemeIs(content::kChromeDevToolsScheme))
38     return false;
39
40   if (url.SchemeIs(content::kChromeUIScheme)) {
41     if (url.host() == chrome::kChromeUISettingsHost)
42       return false;
43
44     // For a settings page, the path will start with "/settings" not "settings"
45     // so find() will return 1, not 0.
46     if (url.host() == chrome::kChromeUIUberHost &&
47         url.path().find(chrome::kChromeUISettingsHost) == 1) {
48       return false;
49     }
50   }
51
52   return true;
53 }
54
55 }  // namespace
56
57 struct CustomHomePagesTableModel::Entry {
58   Entry() : title_handle(0) {}
59
60   // URL of the page.
61   GURL url;
62
63   // Page title.  If this is empty, we'll display the URL as the entry.
64   base::string16 title;
65
66   // If non-zero, indicates we're loading the title for the page.
67   HistoryService::Handle title_handle;
68 };
69
70 CustomHomePagesTableModel::CustomHomePagesTableModel(Profile* profile)
71     : profile_(profile),
72       observer_(NULL) {
73 }
74
75 CustomHomePagesTableModel::~CustomHomePagesTableModel() {
76 }
77
78 void CustomHomePagesTableModel::SetURLs(const std::vector<GURL>& urls) {
79   entries_.resize(urls.size());
80   for (size_t i = 0; i < urls.size(); ++i) {
81     entries_[i].url = urls[i];
82     entries_[i].title.erase();
83     LoadTitle(&(entries_[i]));
84   }
85   // Complete change, so tell the view to just rebuild itself.
86   if (observer_)
87     observer_->OnModelChanged();
88 }
89
90 /**
91  * Move a number of existing entries to a new position, reordering the table.
92  *
93  * We determine the range of elements affected by the move, save the moved
94  * elements, compact the remaining ones, and re-insert moved elements.
95  * Expects |index_list| to be ordered ascending.
96  */
97 void CustomHomePagesTableModel::MoveURLs(int insert_before,
98                                          const std::vector<int>& index_list) {
99   if (index_list.empty()) return;
100   DCHECK(insert_before >= 0 && insert_before <= RowCount());
101
102   // The range of elements that needs to be reshuffled is [ |first|, |last| ).
103   int first = std::min(insert_before, index_list.front());
104   int last = std::max(insert_before, index_list.back() + 1);
105
106   // Save the dragged elements. Also, adjust insertion point if it is before a
107   // dragged element.
108   std::vector<Entry> moved_entries;
109   for (size_t i = 0; i < index_list.size(); ++i) {
110     moved_entries.push_back(entries_[index_list[i]]);
111     if (index_list[i] == insert_before)
112       insert_before++;
113   }
114
115   // Compact the range between beginning and insertion point, moving downwards.
116   size_t skip_count = 0;
117   for (int i = first; i < insert_before; ++i) {
118     if (skip_count < index_list.size() && index_list[skip_count] == i)
119       skip_count++;
120     else
121       entries_[i - skip_count] = entries_[i];
122   }
123
124   // Moving items down created a gap. We start compacting up after it.
125   first = insert_before;
126   insert_before -= skip_count;
127
128   // Now compact up for elements after the insertion point.
129   skip_count = 0;
130   for (int i = last - 1; i >= first; --i) {
131     if (skip_count < index_list.size() &&
132         index_list[index_list.size() - skip_count - 1] == i) {
133       skip_count++;
134     } else {
135       entries_[i + skip_count] = entries_[i];
136     }
137   }
138
139   // Insert moved elements.
140   std::copy(moved_entries.begin(), moved_entries.end(),
141       entries_.begin() + insert_before);
142
143   // Possibly large change, so tell the view to just rebuild itself.
144   if (observer_)
145     observer_->OnModelChanged();
146 }
147
148 void CustomHomePagesTableModel::Add(int index, const GURL& url) {
149   DCHECK(index >= 0 && index <= RowCount());
150   entries_.insert(entries_.begin() + static_cast<size_t>(index), Entry());
151   entries_[index].url = url;
152   LoadTitle(&(entries_[index]));
153   if (observer_)
154     observer_->OnItemsAdded(index, 1);
155 }
156
157 void CustomHomePagesTableModel::Remove(int index) {
158   DCHECK(index >= 0 && index < RowCount());
159   Entry* entry = &(entries_[index]);
160   // Cancel any pending load requests now so we don't deref a bogus pointer when
161   // we get the loaded notification.
162   if (entry->title_handle) {
163     HistoryService* history_service = HistoryServiceFactory::GetForProfile(
164         profile_, Profile::EXPLICIT_ACCESS);
165     if (history_service)
166       history_service->CancelRequest(entry->title_handle);
167   }
168   entries_.erase(entries_.begin() + static_cast<size_t>(index));
169   if (observer_)
170     observer_->OnItemsRemoved(index, 1);
171 }
172
173 void CustomHomePagesTableModel::SetToCurrentlyOpenPages() {
174   // Remove the current entries.
175   while (RowCount())
176     Remove(0);
177
178   // And add all tabs for all open browsers with our profile.
179   int add_index = 0;
180   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
181     Browser* browser = *it;
182     if (browser->profile() != profile_)
183       continue;  // Skip incognito browsers.
184
185     for (int tab_index = 0;
186          tab_index < browser->tab_strip_model()->count();
187          ++tab_index) {
188       const GURL url =
189           browser->tab_strip_model()->GetWebContentsAt(tab_index)->GetURL();
190       if (ShouldAddPage(url))
191         Add(add_index++, url);
192     }
193   }
194 }
195
196 std::vector<GURL> CustomHomePagesTableModel::GetURLs() {
197   std::vector<GURL> urls(entries_.size());
198   for (size_t i = 0; i < entries_.size(); ++i)
199     urls[i] = entries_[i].url;
200   return urls;
201 }
202
203 int CustomHomePagesTableModel::RowCount() {
204   return static_cast<int>(entries_.size());
205 }
206
207 base::string16 CustomHomePagesTableModel::GetText(int row, int column_id) {
208   DCHECK(column_id == 0);
209   DCHECK(row >= 0 && row < RowCount());
210   return entries_[row].title.empty() ? FormattedURL(row) : entries_[row].title;
211 }
212
213 base::string16 CustomHomePagesTableModel::GetTooltip(int row) {
214   return entries_[row].title.empty() ? base::string16() :
215       l10n_util::GetStringFUTF16(IDS_OPTIONS_STARTUP_PAGE_TOOLTIP,
216                                  entries_[row].title, FormattedURL(row));
217 }
218
219 void CustomHomePagesTableModel::SetObserver(ui::TableModelObserver* observer) {
220   observer_ = observer;
221 }
222
223 void CustomHomePagesTableModel::LoadTitle(Entry* entry) {
224     HistoryService* history_service = HistoryServiceFactory::GetForProfile(
225         profile_, Profile::EXPLICIT_ACCESS);
226   if (history_service) {
227     entry->title_handle = history_service->QueryURL(entry->url, false,
228         &history_query_consumer_,
229         base::Bind(&CustomHomePagesTableModel::OnGotTitle,
230                    base::Unretained(this)));
231   }
232 }
233
234 void CustomHomePagesTableModel::OnGotTitle(HistoryService::Handle handle,
235                                            bool found_url,
236                                            const history::URLRow* row,
237                                            history::VisitVector* visits) {
238   int entry_index;
239   Entry* entry =
240       GetEntryByLoadHandle(&Entry::title_handle, handle, &entry_index);
241   if (!entry) {
242     // The URLs changed before we were called back.
243     return;
244   }
245   entry->title_handle = 0;
246   if (found_url && !row->title().empty()) {
247     entry->title = row->title();
248     if (observer_)
249       observer_->OnItemsChanged(static_cast<int>(entry_index), 1);
250   }
251 }
252
253 CustomHomePagesTableModel::Entry*
254     CustomHomePagesTableModel::GetEntryByLoadHandle(
255     CancelableRequestProvider::Handle Entry::* member,
256     CancelableRequestProvider::Handle handle,
257     int* index) {
258   for (size_t i = 0; i < entries_.size(); ++i) {
259     if (entries_[i].*member == handle) {
260       *index = static_cast<int>(i);
261       return &entries_[i];
262     }
263   }
264   return NULL;
265 }
266
267 base::string16 CustomHomePagesTableModel::FormattedURL(int row) const {
268   std::string languages =
269       profile_->GetPrefs()->GetString(prefs::kAcceptLanguages);
270   base::string16 url = net::FormatUrl(entries_[row].url, languages);
271   url = base::i18n::GetDisplayStringInLTRDirectionality(url);
272   return url;
273 }