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 26struct CustomHomePagesTableModel::Entry { 27 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 45CustomHomePagesTableModel::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 53CustomHomePagesTableModel::~CustomHomePagesTableModel() { 54} 55 56void 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 69void 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 78void 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 100void 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 123std::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 130int CustomHomePagesTableModel::RowCount() { 131 return static_cast<int>(entries_.size()); 132} 133 134string16 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 140SkBitmap CustomHomePagesTableModel::GetIcon(int row) { 141 DCHECK(row >= 0 && row < RowCount()); 142 return entries_[row].icon.isNull() ? *default_favicon_ : entries_[row].icon; 143} 144 145string16 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 151void CustomHomePagesTableModel::SetObserver(ui::TableModelObserver* observer) { 152 observer_ = observer; 153} 154 155void 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 172void 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 191void 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 219CustomHomePagesTableModel::Entry* 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 233string16 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