Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2010 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/possible_url_model.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/i18n/rtl.h"
      9 #include "base/string_util.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "chrome/browser/favicon_service.h"
     12 #include "chrome/browser/prefs/pref_service.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/common/pref_names.h"
     15 #include "content/browser/cancelable_request.h"
     16 #include "grit/app_resources.h"
     17 #include "grit/generated_resources.h"
     18 #include "third_party/skia/include/core/SkBitmap.h"
     19 #include "ui/base/models/table_model_observer.h"
     20 #include "ui/base/resource/resource_bundle.h"
     21 #include "ui/base/text/text_elider.h"
     22 #include "ui/gfx/codec/png_codec.h"
     23 
     24 using base::Time;
     25 using base::TimeDelta;
     26 
     27 namespace {
     28 
     29 // The default favicon.
     30 SkBitmap* default_favicon = NULL;
     31 
     32 // How long we query entry points for.
     33 const int kPossibleURLTimeScope = 30;
     34 
     35 }  // anonymous namespace
     36 
     37 // Contains the data needed to show a result.
     38 struct PossibleURLModel::Result {
     39   Result() : index(0) {}
     40 
     41   GURL url;
     42   // Index of this Result in results_. This is used as the key into
     43   // favicon_map_ to lookup the favicon for the url, as well as the index
     44   // into results_ when the favicon is received.
     45   size_t index;
     46   ui::SortedDisplayURL display_url;
     47   std::wstring title;
     48 };
     49 
     50 
     51 PossibleURLModel::PossibleURLModel()
     52     : profile_(NULL),
     53       observer_(NULL) {
     54   if (!default_favicon) {
     55     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
     56     default_favicon = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
     57   }
     58 }
     59 
     60 PossibleURLModel::~PossibleURLModel() {
     61 }
     62 
     63 void PossibleURLModel::Reload(Profile *profile) {
     64   profile_ = profile;
     65   consumer_.CancelAllRequests();
     66   HistoryService* hs =
     67       profile->GetHistoryService(Profile::EXPLICIT_ACCESS);
     68   if (hs) {
     69     history::QueryOptions options;
     70     options.end_time = Time::Now();
     71     options.begin_time =
     72         options.end_time - TimeDelta::FromDays(kPossibleURLTimeScope);
     73     options.max_count = 50;
     74 
     75     hs->QueryHistory(string16(), options, &consumer_,
     76         NewCallback(this, &PossibleURLModel::OnHistoryQueryComplete));
     77   }
     78 }
     79 
     80 void PossibleURLModel::OnHistoryQueryComplete(HistoryService::Handle h,
     81                                               history::QueryResults* result) {
     82   results_.resize(result->size());
     83   std::string languages = profile_ ?
     84       profile_->GetPrefs()->GetString(prefs::kAcceptLanguages) : std::string();
     85   for (size_t i = 0; i < result->size(); ++i) {
     86     results_[i].url = (*result)[i].url();
     87     results_[i].index = i;
     88     results_[i].display_url =
     89         ui::SortedDisplayURL((*result)[i].url(), languages);
     90     results_[i].title = UTF16ToWide((*result)[i].title());
     91   }
     92 
     93   // The old version of this code would filter out all but the most recent
     94   // visit to each host, plus all typed URLs and AUTO_BOOKMARK transitions. I
     95   // think this dialog has a lot of work, and I'm not sure those old
     96   // conditions are correct (the results look about equal quality for my
     97   // history with and without those conditions), so I'm not spending time
     98   // re-implementing them here. They used to be implemented in the history
     99   // service, but I think they should be implemented here because that was
    100   // pretty specific behavior that shouldn't be generally exposed.
    101 
    102   favicon_map_.clear();
    103   if (observer_)
    104     observer_->OnModelChanged();
    105 }
    106 
    107 int PossibleURLModel::RowCount() {
    108   return static_cast<int>(results_.size());
    109 }
    110 
    111 const GURL& PossibleURLModel::GetURL(int row) {
    112   if (row < 0 || row >= RowCount()) {
    113     NOTREACHED();
    114     return GURL::EmptyGURL();
    115   }
    116   return results_[row].url;
    117 }
    118 
    119 const std::wstring& PossibleURLModel::GetTitle(int row) {
    120   if (row < 0 || row >= RowCount()) {
    121     NOTREACHED();
    122     return EmptyWString();
    123   }
    124   return results_[row].title;
    125 }
    126 
    127 string16 PossibleURLModel::GetText(int row, int col_id) {
    128   if (row < 0 || row >= RowCount()) {
    129     NOTREACHED();
    130     return string16();
    131   }
    132 
    133   if (col_id == IDS_ASI_PAGE_COLUMN) {
    134     string16 title = WideToUTF16Hack(GetTitle(row));
    135     // TODO(xji): Consider adding a special case if the title text is a URL,
    136     // since those should always have LTR directionality. Please refer to
    137     // http://crbug.com/6726 for more information.
    138     base::i18n::AdjustStringForLocaleDirection(&title);
    139     return title;
    140   }
    141 
    142   // TODO(brettw): this should probably pass the GURL up so the URL elider
    143   // can be used at a higher level when we know the width.
    144   string16 url = results_[row].display_url.display_url();
    145   return base::i18n::GetDisplayStringInLTRDirectionality(url);
    146 }
    147 
    148 SkBitmap PossibleURLModel::GetIcon(int row) {
    149   if (row < 0 || row >= RowCount()) {
    150     NOTREACHED();
    151     return *default_favicon;
    152   }
    153 
    154   Result& result = results_[row];
    155   FaviconMap::iterator i = favicon_map_.find(result.index);
    156   if (i != favicon_map_.end()) {
    157     // We already requested the favicon, return it.
    158     if (!i->second.isNull())
    159       return i->second;
    160   } else if (profile_) {
    161     FaviconService* favicon_service =
    162         profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
    163     if (favicon_service) {
    164       CancelableRequestProvider::Handle h =
    165           favicon_service->GetFaviconForURL(
    166               result.url, history::FAVICON, &consumer_,
    167               NewCallback(this, &PossibleURLModel::OnFaviconAvailable));
    168       consumer_.SetClientData(favicon_service, h, result.index);
    169       // Add an entry to the map so that we don't attempt to request the
    170       // favicon again.
    171       favicon_map_[result.index] = SkBitmap();
    172     }
    173   }
    174   return *default_favicon;
    175 }
    176 
    177 int PossibleURLModel::CompareValues(int row1, int row2, int column_id) {
    178   if (column_id == IDS_ASI_URL_COLUMN) {
    179     return results_[row1].display_url.Compare(
    180         results_[row2].display_url, GetCollator());
    181   }
    182   return ui::TableModel::CompareValues(row1, row2, column_id);
    183 }
    184 
    185 void PossibleURLModel::OnFaviconAvailable(
    186     FaviconService::Handle h,
    187     history::FaviconData favicon) {
    188   if (profile_) {
    189     FaviconService* favicon_service =
    190         profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
    191     size_t index = consumer_.GetClientData(favicon_service, h);
    192     if (favicon.is_valid()) {
    193       // The decoder will leave our bitmap empty on error.
    194       gfx::PNGCodec::Decode(favicon.image_data->front(),
    195                             favicon.image_data->size(),
    196                             &(favicon_map_[index]));
    197 
    198       // Notify the observer.
    199       if (!favicon_map_[index].isNull() && observer_)
    200         observer_->OnItemsChanged(static_cast<int>(index), 1);
    201     }
    202   }
    203 }
    204 
    205 void PossibleURLModel::SetObserver(ui::TableModelObserver* observer) {
    206   observer_ = observer;
    207 }
    208