Home | History | Annotate | Download | only in browser
      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 {
     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 
     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 
     53 CustomHomePagesTableModel::~CustomHomePagesTableModel() {
     54 }
     55 
     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 
     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 
     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 
    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 
    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 
    130 int CustomHomePagesTableModel::RowCount() {
    131   return static_cast<int>(entries_.size());
    132 }
    133 
    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 
    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 
    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 
    151 void CustomHomePagesTableModel::SetObserver(ui::TableModelObserver* observer) {
    152   observer_ = observer;
    153 }
    154 
    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 
    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 
    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*
    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 
    233 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