Home | History | Annotate | Download | only in webui
      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/ui/webui/history_ui.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 
     10 #include "base/callback.h"
     11 #include "base/i18n/time_formatting.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/message_loop.h"
     14 #include "base/string16.h"
     15 #include "base/string_number_conversions.h"
     16 #include "base/string_piece.h"
     17 #include "base/threading/thread.h"
     18 #include "base/time.h"
     19 #include "base/utf_string_conversions.h"
     20 #include "base/values.h"
     21 #include "chrome/browser/bookmarks/bookmark_model.h"
     22 #include "chrome/browser/history/history_types.h"
     23 #include "chrome/browser/metrics/user_metrics.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/browser/ui/browser.h"
     26 #include "chrome/browser/ui/browser_list.h"
     27 #include "chrome/browser/ui/webui/favicon_source.h"
     28 #include "chrome/common/jstemplate_builder.h"
     29 #include "chrome/common/time_format.h"
     30 #include "chrome/common/url_constants.h"
     31 #include "content/browser/browser_thread.h"
     32 #include "content/browser/tab_contents/tab_contents.h"
     33 #include "content/browser/tab_contents/tab_contents_delegate.h"
     34 #include "grit/browser_resources.h"
     35 #include "grit/chromium_strings.h"
     36 #include "grit/generated_resources.h"
     37 #include "grit/locale_settings.h"
     38 #include "grit/theme_resources.h"
     39 #include "net/base/escape.h"
     40 #include "ui/base/l10n/l10n_util.h"
     41 #include "ui/base/resource/resource_bundle.h"
     42 
     43 // Maximum number of search results to return in a given search. We should
     44 // eventually remove this.
     45 static const int kMaxSearchResults = 100;
     46 
     47 ////////////////////////////////////////////////////////////////////////////////
     48 //
     49 // HistoryHTMLSource
     50 //
     51 ////////////////////////////////////////////////////////////////////////////////
     52 
     53 HistoryUIHTMLSource::HistoryUIHTMLSource()
     54     : DataSource(chrome::kChromeUIHistoryHost, MessageLoop::current()) {
     55 }
     56 
     57 void HistoryUIHTMLSource::StartDataRequest(const std::string& path,
     58                                            bool is_incognito,
     59                                            int request_id) {
     60   DictionaryValue localized_strings;
     61   localized_strings.SetString("loading",
     62       l10n_util::GetStringUTF16(IDS_HISTORY_LOADING));
     63   localized_strings.SetString("title",
     64       l10n_util::GetStringUTF16(IDS_HISTORY_TITLE));
     65   localized_strings.SetString("loading",
     66       l10n_util::GetStringUTF16(IDS_HISTORY_LOADING));
     67   localized_strings.SetString("newest",
     68       l10n_util::GetStringUTF16(IDS_HISTORY_NEWEST));
     69   localized_strings.SetString("newer",
     70       l10n_util::GetStringUTF16(IDS_HISTORY_NEWER));
     71   localized_strings.SetString("older",
     72       l10n_util::GetStringUTF16(IDS_HISTORY_OLDER));
     73   localized_strings.SetString("searchresultsfor",
     74       l10n_util::GetStringUTF16(IDS_HISTORY_SEARCHRESULTSFOR));
     75   localized_strings.SetString("history",
     76       l10n_util::GetStringUTF16(IDS_HISTORY_BROWSERESULTS));
     77   localized_strings.SetString("cont",
     78       l10n_util::GetStringUTF16(IDS_HISTORY_CONTINUED));
     79   localized_strings.SetString("searchbutton",
     80       l10n_util::GetStringUTF16(IDS_HISTORY_SEARCH_BUTTON));
     81   localized_strings.SetString("noresults",
     82       l10n_util::GetStringUTF16(IDS_HISTORY_NO_RESULTS));
     83   localized_strings.SetString("noitems",
     84       l10n_util::GetStringUTF16(IDS_HISTORY_NO_ITEMS));
     85   localized_strings.SetString("edithistory",
     86       l10n_util::GetStringUTF16(IDS_HISTORY_START_EDITING_HISTORY));
     87   localized_strings.SetString("doneediting",
     88       l10n_util::GetStringUTF16(IDS_HISTORY_STOP_EDITING_HISTORY));
     89   localized_strings.SetString("removeselected",
     90       l10n_util::GetStringUTF16(IDS_HISTORY_REMOVE_SELECTED_ITEMS));
     91   localized_strings.SetString("clearallhistory",
     92       l10n_util::GetStringUTF16(IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG));
     93   localized_strings.SetString("deletewarning",
     94       l10n_util::GetStringUTF16(IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING));
     95 
     96   SetFontAndTextDirection(&localized_strings);
     97 
     98   static const base::StringPiece history_html(
     99       ResourceBundle::GetSharedInstance().GetRawDataResource(
    100           IDR_HISTORY_HTML));
    101   const std::string full_html = jstemplate_builder::GetI18nTemplateHtml(
    102       history_html, &localized_strings);
    103 
    104   scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
    105   html_bytes->data.resize(full_html.size());
    106   std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
    107 
    108   SendResponse(request_id, html_bytes);
    109 }
    110 
    111 std::string HistoryUIHTMLSource::GetMimeType(const std::string&) const {
    112   return "text/html";
    113 }
    114 
    115 ////////////////////////////////////////////////////////////////////////////////
    116 //
    117 // HistoryHandler
    118 //
    119 ////////////////////////////////////////////////////////////////////////////////
    120 BrowsingHistoryHandler::BrowsingHistoryHandler()
    121     : search_text_() {
    122 }
    123 
    124 BrowsingHistoryHandler::~BrowsingHistoryHandler() {
    125   cancelable_search_consumer_.CancelAllRequests();
    126   cancelable_delete_consumer_.CancelAllRequests();
    127 }
    128 
    129 WebUIMessageHandler* BrowsingHistoryHandler::Attach(WebUI* web_ui) {
    130   // Create our favicon data source.
    131   Profile* profile = web_ui->GetProfile();
    132   profile->GetChromeURLDataManager()->AddDataSource(
    133       new FaviconSource(profile));
    134 
    135   return WebUIMessageHandler::Attach(web_ui);
    136 }
    137 
    138 void BrowsingHistoryHandler::RegisterMessages() {
    139   web_ui_->RegisterMessageCallback("getHistory",
    140       NewCallback(this, &BrowsingHistoryHandler::HandleGetHistory));
    141   web_ui_->RegisterMessageCallback("searchHistory",
    142       NewCallback(this, &BrowsingHistoryHandler::HandleSearchHistory));
    143   web_ui_->RegisterMessageCallback("removeURLsOnOneDay",
    144       NewCallback(this, &BrowsingHistoryHandler::HandleRemoveURLsOnOneDay));
    145   web_ui_->RegisterMessageCallback("clearBrowsingData",
    146       NewCallback(this, &BrowsingHistoryHandler::HandleClearBrowsingData));
    147 }
    148 
    149 void BrowsingHistoryHandler::HandleGetHistory(const ListValue* args) {
    150   // Anything in-flight is invalid.
    151   cancelable_search_consumer_.CancelAllRequests();
    152 
    153   // Get arguments (if any).
    154   int day = 0;
    155   ExtractIntegerValue(args, &day);
    156 
    157   // Set our query options.
    158   history::QueryOptions options;
    159   options.begin_time = base::Time::Now().LocalMidnight();
    160   options.begin_time -= base::TimeDelta::FromDays(day);
    161   options.end_time = base::Time::Now().LocalMidnight();
    162   options.end_time -= base::TimeDelta::FromDays(day - 1);
    163 
    164   // Need to remember the query string for our results.
    165   search_text_ = string16();
    166 
    167   HistoryService* hs =
    168       web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    169   hs->QueryHistory(search_text_,
    170       options,
    171       &cancelable_search_consumer_,
    172       NewCallback(this, &BrowsingHistoryHandler::QueryComplete));
    173 }
    174 
    175 void BrowsingHistoryHandler::HandleSearchHistory(const ListValue* args) {
    176   // Anything in-flight is invalid.
    177   cancelable_search_consumer_.CancelAllRequests();
    178 
    179   // Get arguments (if any).
    180   int month = 0;
    181   string16 query;
    182   ExtractSearchHistoryArguments(args, &month, &query);
    183 
    184   // Set the query ranges for the given month.
    185   history::QueryOptions options = CreateMonthQueryOptions(month);
    186 
    187   // When searching, limit the number of results returned.
    188   options.max_count = kMaxSearchResults;
    189 
    190   // Need to remember the query string for our results.
    191   search_text_ = query;
    192   HistoryService* hs =
    193       web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    194   hs->QueryHistory(search_text_,
    195       options,
    196       &cancelable_search_consumer_,
    197       NewCallback(this, &BrowsingHistoryHandler::QueryComplete));
    198 }
    199 
    200 void BrowsingHistoryHandler::HandleRemoveURLsOnOneDay(const ListValue* args) {
    201   if (cancelable_delete_consumer_.HasPendingRequests()) {
    202     web_ui_->CallJavascriptFunction("deleteFailed");
    203     return;
    204   }
    205 
    206   // Get day to delete data from.
    207   int visit_time = 0;
    208   ExtractIntegerValue(args, &visit_time);
    209   base::Time::Exploded exploded;
    210   base::Time::FromTimeT(
    211       static_cast<time_t>(visit_time)).LocalExplode(&exploded);
    212   exploded.hour = exploded.minute = exploded.second = exploded.millisecond = 0;
    213   base::Time begin_time = base::Time::FromLocalExploded(exploded);
    214   base::Time end_time = begin_time + base::TimeDelta::FromDays(1);
    215 
    216   // Get URLs.
    217   std::set<GURL> urls;
    218   for (ListValue::const_iterator v = args->begin() + 1;
    219        v != args->end(); ++v) {
    220     if ((*v)->GetType() != Value::TYPE_STRING)
    221       continue;
    222     const StringValue* string_value = static_cast<const StringValue*>(*v);
    223     string16 string16_value;
    224     if (!string_value->GetAsString(&string16_value))
    225       continue;
    226     urls.insert(GURL(string16_value));
    227   }
    228 
    229   HistoryService* hs =
    230       web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    231   hs->ExpireHistoryBetween(
    232       urls, begin_time, end_time, &cancelable_delete_consumer_,
    233       NewCallback(this, &BrowsingHistoryHandler::RemoveComplete));
    234 }
    235 
    236 void BrowsingHistoryHandler::HandleClearBrowsingData(const ListValue* args) {
    237   // TODO(beng): This is an improper direct dependency on Browser. Route this
    238   // through some sort of delegate.
    239   Browser* browser = BrowserList::FindBrowserWithProfile(web_ui_->GetProfile());
    240   if (browser)
    241     browser->OpenClearBrowsingDataDialog();
    242 }
    243 
    244 void BrowsingHistoryHandler::QueryComplete(
    245     HistoryService::Handle request_handle,
    246     history::QueryResults* results) {
    247 
    248   ListValue results_value;
    249   base::Time midnight_today = base::Time::Now().LocalMidnight();
    250 
    251   for (size_t i = 0; i < results->size(); ++i) {
    252     history::URLResult const &page = (*results)[i];
    253     DictionaryValue* page_value = new DictionaryValue();
    254     SetURLAndTitle(page_value, page.title(), page.url());
    255 
    256     // Need to pass the time in epoch time (fastest JS conversion).
    257     page_value->SetInteger("time",
    258         static_cast<int>(page.visit_time().ToTimeT()));
    259 
    260     // Until we get some JS i18n infrastructure, we also need to
    261     // pass the dates in as strings. This could use some
    262     // optimization.
    263 
    264     // Only pass in the strings we need (search results need a shortdate
    265     // and snippet, browse results need day and time information).
    266     if (search_text_.empty()) {
    267       // Figure out the relative date string.
    268       string16 date_str = TimeFormat::RelativeDate(page.visit_time(),
    269                                                    &midnight_today);
    270       if (date_str.empty()) {
    271         date_str = base::TimeFormatFriendlyDate(page.visit_time());
    272       } else {
    273         date_str = l10n_util::GetStringFUTF16(
    274             IDS_HISTORY_DATE_WITH_RELATIVE_TIME,
    275             date_str,
    276             base::TimeFormatFriendlyDate(page.visit_time()));
    277       }
    278       page_value->SetString("dateRelativeDay", date_str);
    279       page_value->SetString("dateTimeOfDay",
    280           base::TimeFormatTimeOfDay(page.visit_time()));
    281     } else {
    282       page_value->SetString("dateShort",
    283           base::TimeFormatShortDate(page.visit_time()));
    284       page_value->SetString("snippet", page.snippet().text());
    285     }
    286     page_value->SetBoolean("starred",
    287         web_ui_->GetProfile()->GetBookmarkModel()->IsBookmarked(page.url()));
    288     results_value.Append(page_value);
    289   }
    290 
    291   DictionaryValue info_value;
    292   info_value.SetString("term", search_text_);
    293   info_value.SetBoolean("finished", results->reached_beginning());
    294 
    295   web_ui_->CallJavascriptFunction("historyResult", info_value, results_value);
    296 }
    297 
    298 void BrowsingHistoryHandler::RemoveComplete() {
    299   // Some Visits were deleted from history. Reload the list.
    300   web_ui_->CallJavascriptFunction("deleteComplete");
    301 }
    302 
    303 void BrowsingHistoryHandler::ExtractSearchHistoryArguments(
    304       const ListValue* args,
    305       int* month,
    306       string16* query) {
    307   CHECK(args->GetSize() == 2);
    308   query->clear();
    309   CHECK(args->GetString(0, query));
    310 
    311   string16 string16_value;
    312   CHECK(args->GetString(1, &string16_value));
    313   *month = 0;
    314   base::StringToInt(string16_value, month);
    315 }
    316 
    317 history::QueryOptions BrowsingHistoryHandler::CreateMonthQueryOptions(
    318     int month) {
    319   history::QueryOptions options;
    320 
    321   // Configure the begin point of the search to the start of the
    322   // current month.
    323   base::Time::Exploded exploded;
    324   base::Time::Now().LocalMidnight().LocalExplode(&exploded);
    325   exploded.day_of_month = 1;
    326 
    327   if (month == 0) {
    328     options.begin_time = base::Time::FromLocalExploded(exploded);
    329 
    330     // Set the end time of this first search to null (which will
    331     // show results from the future, should the user's clock have
    332     // been set incorrectly).
    333     options.end_time = base::Time();
    334   } else {
    335     // Set the end-time of this search to the end of the month that is
    336     // |depth| months before the search end point. The end time is not
    337     // inclusive, so we should feel free to set it to midnight on the
    338     // first day of the following month.
    339     exploded.month -= month - 1;
    340     while (exploded.month < 1) {
    341       exploded.month += 12;
    342       exploded.year--;
    343     }
    344     options.end_time = base::Time::FromLocalExploded(exploded);
    345 
    346     // Set the begin-time of the search to the start of the month
    347     // that is |depth| months prior to search_start_.
    348     if (exploded.month > 1) {
    349       exploded.month--;
    350     } else {
    351       exploded.month = 12;
    352       exploded.year--;
    353     }
    354     options.begin_time = base::Time::FromLocalExploded(exploded);
    355   }
    356 
    357   return options;
    358 }
    359 
    360 ////////////////////////////////////////////////////////////////////////////////
    361 //
    362 // HistoryUIContents
    363 //
    364 ////////////////////////////////////////////////////////////////////////////////
    365 
    366 HistoryUI::HistoryUI(TabContents* contents) : WebUI(contents) {
    367   AddMessageHandler((new BrowsingHistoryHandler())->Attach(this));
    368 
    369   HistoryUIHTMLSource* html_source = new HistoryUIHTMLSource();
    370 
    371   // Set up the chrome://history/ source.
    372   contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
    373 }
    374 
    375 // static
    376 const GURL HistoryUI::GetHistoryURLWithSearchText(const string16& text) {
    377   return GURL(std::string(chrome::kChromeUIHistoryURL) + "#q=" +
    378               EscapeQueryParamValue(UTF16ToUTF8(text), true));
    379 }
    380 
    381 // static
    382 RefCountedMemory* HistoryUI::GetFaviconResourceBytes() {
    383   return ResourceBundle::GetSharedInstance().
    384       LoadDataResourceBytes(IDR_HISTORY_FAVICON);
    385 }
    386