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/custom_home_pages_table_model.h"
6
7 #include "base/i18n/rtl.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/prefs/pref_service.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_list.h"
13 #include "chrome/common/pref_names.h"
14 #include "chrome/common/url_constants.h"
15 #include "content/browser/tab_contents/tab_contents.h"
16 #include "googleurl/src/gurl.h"
17 #include "grit/app_resources.h"
18 #include "grit/generated_resources.h"
19 #include "net/base/net_util.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/models/table_model_observer.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/codec/png_codec.h"
25
26 struct CustomHomePagesTableModel::Entry {
EntryCustomHomePagesTableModel::Entry27 Entry() : title_handle(0), favicon_handle(0) {}
28
29 // URL of the page.
30 GURL url;
31
32 // Page title. If this is empty, we'll display the URL as the entry.
33 string16 title;
34
35 // Icon for the page.
36 SkBitmap icon;
37
38 // If non-zero, indicates we're loading the title for the page.
39 HistoryService::Handle title_handle;
40
41 // If non-zero, indicates we're loading the favicon for the page.
42 FaviconService::Handle favicon_handle;
43 };
44
CustomHomePagesTableModel(Profile * profile)45 CustomHomePagesTableModel::CustomHomePagesTableModel(Profile* profile)
46 : default_favicon_(NULL),
47 profile_(profile),
48 observer_(NULL) {
49 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
50 default_favicon_ = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
51 }
52
~CustomHomePagesTableModel()53 CustomHomePagesTableModel::~CustomHomePagesTableModel() {
54 }
55
SetURLs(const std::vector<GURL> & urls)56 void CustomHomePagesTableModel::SetURLs(const std::vector<GURL>& urls) {
57 entries_.resize(urls.size());
58 for (size_t i = 0; i < urls.size(); ++i) {
59 entries_[i].url = urls[i];
60 entries_[i].title.erase();
61 entries_[i].icon.reset();
62 LoadTitleAndFavicon(&(entries_[i]));
63 }
64 // Complete change, so tell the view to just rebuild itself.
65 if (observer_)
66 observer_->OnModelChanged();
67 }
68
Add(int index,const GURL & url)69 void CustomHomePagesTableModel::Add(int index, const GURL& url) {
70 DCHECK(index >= 0 && index <= RowCount());
71 entries_.insert(entries_.begin() + static_cast<size_t>(index), Entry());
72 entries_[index].url = url;
73 LoadTitleAndFavicon(&(entries_[index]));
74 if (observer_)
75 observer_->OnItemsAdded(index, 1);
76 }
77
Remove(int index)78 void CustomHomePagesTableModel::Remove(int index) {
79 DCHECK(index >= 0 && index < RowCount());
80 Entry* entry = &(entries_[index]);
81 // Cancel any pending load requests now so we don't deref a bogus pointer when
82 // we get the loaded notification.
83 if (entry->title_handle) {
84 HistoryService* history_service =
85 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
86 if (history_service)
87 history_service->CancelRequest(entry->title_handle);
88 }
89 if (entry->favicon_handle) {
90 FaviconService* favicon_service =
91 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
92 if (favicon_service)
93 favicon_service->CancelRequest(entry->favicon_handle);
94 }
95 entries_.erase(entries_.begin() + static_cast<size_t>(index));
96 if (observer_)
97 observer_->OnItemsRemoved(index, 1);
98 }
99
SetToCurrentlyOpenPages()100 void CustomHomePagesTableModel::SetToCurrentlyOpenPages() {
101 // Remove the current entries.
102 while (RowCount())
103 Remove(0);
104
105 // And add all tabs for all open browsers with our profile.
106 int add_index = 0;
107 for (BrowserList::const_iterator browser_i = BrowserList::begin();
108 browser_i != BrowserList::end(); ++browser_i) {
109 Browser* browser = *browser_i;
110 if (browser->profile() != profile_)
111 continue; // Skip incognito browsers.
112
113 for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
114 const GURL url = browser->GetTabContentsAt(tab_index)->GetURL();
115 if (!url.is_empty() &&
116 !(url.SchemeIs(chrome::kChromeUIScheme) &&
117 url.host() == chrome::kChromeUISettingsHost))
118 Add(add_index++, url);
119 }
120 }
121 }
122
GetURLs()123 std::vector<GURL> CustomHomePagesTableModel::GetURLs() {
124 std::vector<GURL> urls(entries_.size());
125 for (size_t i = 0; i < entries_.size(); ++i)
126 urls[i] = entries_[i].url;
127 return urls;
128 }
129
RowCount()130 int CustomHomePagesTableModel::RowCount() {
131 return static_cast<int>(entries_.size());
132 }
133
GetText(int row,int column_id)134 string16 CustomHomePagesTableModel::GetText(int row, int column_id) {
135 DCHECK(column_id == 0);
136 DCHECK(row >= 0 && row < RowCount());
137 return entries_[row].title.empty() ? FormattedURL(row) : entries_[row].title;
138 }
139
GetIcon(int row)140 SkBitmap CustomHomePagesTableModel::GetIcon(int row) {
141 DCHECK(row >= 0 && row < RowCount());
142 return entries_[row].icon.isNull() ? *default_favicon_ : entries_[row].icon;
143 }
144
GetTooltip(int row)145 string16 CustomHomePagesTableModel::GetTooltip(int row) {
146 return entries_[row].title.empty() ? string16() :
147 l10n_util::GetStringFUTF16(IDS_OPTIONS_STARTUP_PAGE_TOOLTIP,
148 entries_[row].title, FormattedURL(row));
149 }
150
SetObserver(ui::TableModelObserver * observer)151 void CustomHomePagesTableModel::SetObserver(ui::TableModelObserver* observer) {
152 observer_ = observer;
153 }
154
LoadTitleAndFavicon(Entry * entry)155 void CustomHomePagesTableModel::LoadTitleAndFavicon(Entry* entry) {
156 HistoryService* history_service =
157 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
158 if (history_service) {
159 entry->title_handle = history_service->QueryURL(entry->url, false,
160 &query_consumer_,
161 NewCallback(this, &CustomHomePagesTableModel::OnGotTitle));
162 }
163 FaviconService* favicon_service =
164 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
165 if (favicon_service) {
166 entry->favicon_handle = favicon_service->GetFaviconForURL(entry->url,
167 history::FAVICON, &query_consumer_,
168 NewCallback(this, &CustomHomePagesTableModel::OnGotFavicon));
169 }
170 }
171
OnGotTitle(HistoryService::Handle handle,bool found_url,const history::URLRow * row,history::VisitVector * visits)172 void CustomHomePagesTableModel::OnGotTitle(HistoryService::Handle handle,
173 bool found_url,
174 const history::URLRow* row,
175 history::VisitVector* visits) {
176 int entry_index;
177 Entry* entry =
178 GetEntryByLoadHandle(&Entry::title_handle, handle, &entry_index);
179 if (!entry) {
180 // The URLs changed before we were called back.
181 return;
182 }
183 entry->title_handle = 0;
184 if (found_url && !row->title().empty()) {
185 entry->title = row->title();
186 if (observer_)
187 observer_->OnItemsChanged(static_cast<int>(entry_index), 1);
188 }
189 }
190
OnGotFavicon(FaviconService::Handle handle,history::FaviconData favicon)191 void CustomHomePagesTableModel::OnGotFavicon(
192 FaviconService::Handle handle,
193 history::FaviconData favicon) {
194 int entry_index;
195 Entry* entry =
196 GetEntryByLoadHandle(&Entry::favicon_handle, handle, &entry_index);
197 if (!entry) {
198 // The URLs changed before we were called back.
199 return;
200 }
201 entry->favicon_handle = 0;
202 if (favicon.is_valid()) {
203 int width, height;
204 std::vector<unsigned char> decoded_data;
205 if (gfx::PNGCodec::Decode(favicon.image_data->front(),
206 favicon.image_data->size(),
207 gfx::PNGCodec::FORMAT_BGRA, &decoded_data,
208 &width, &height)) {
209 entry->icon.setConfig(SkBitmap::kARGB_8888_Config, width, height);
210 entry->icon.allocPixels();
211 memcpy(entry->icon.getPixels(), &decoded_data.front(),
212 width * height * 4);
213 if (observer_)
214 observer_->OnItemsChanged(static_cast<int>(entry_index), 1);
215 }
216 }
217 }
218
219 CustomHomePagesTableModel::Entry*
GetEntryByLoadHandle(CancelableRequestProvider::Handle Entry::* member,CancelableRequestProvider::Handle handle,int * index)220 CustomHomePagesTableModel::GetEntryByLoadHandle(
221 CancelableRequestProvider::Handle Entry::* member,
222 CancelableRequestProvider::Handle handle,
223 int* index) {
224 for (size_t i = 0; i < entries_.size(); ++i) {
225 if (entries_[i].*member == handle) {
226 *index = static_cast<int>(i);
227 return &entries_[i];
228 }
229 }
230 return NULL;
231 }
232
FormattedURL(int row) const233 string16 CustomHomePagesTableModel::FormattedURL(int row) const {
234 std::string languages =
235 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages);
236 string16 url = net::FormatUrl(entries_[row].url, languages);
237 url = base::i18n::GetDisplayStringInLTRDirectionality(url);
238 return url;
239 }
240