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/history2_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 HistoryUIHTMLSource2::HistoryUIHTMLSource2()
     54     : DataSource(chrome::kChromeUIHistory2Host, MessageLoop::current()) {
     55 }
     56 
     57 void HistoryUIHTMLSource2::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_HISTORY2_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 HistoryUIHTMLSource2::GetMimeType(const std::string&) const {
    112   return "text/html";
    113 }
    114 
    115 ////////////////////////////////////////////////////////////////////////////////
    116 //
    117 // HistoryHandler
    118 //
    119 ////////////////////////////////////////////////////////////////////////////////
    120 BrowsingHistoryHandler2::BrowsingHistoryHandler2()
    121     : search_text_() {
    122 }
    123 
    124 BrowsingHistoryHandler2::~BrowsingHistoryHandler2() {
    125   cancelable_search_consumer_.CancelAllRequests();
    126   cancelable_delete_consumer_.CancelAllRequests();
    127 }
    128 
    129 WebUIMessageHandler* BrowsingHistoryHandler2::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 BrowsingHistoryHandler2::RegisterMessages() {
    139   web_ui_->RegisterMessageCallback("getHistory",
    140       NewCallback(this, &BrowsingHistoryHandler2::HandleGetHistory));
    141   web_ui_->RegisterMessageCallback("searchHistory",
    142       NewCallback(this, &BrowsingHistoryHandler2::HandleSearchHistory));
    143   web_ui_->RegisterMessageCallback("removeURLsOnOneDay",
    144       NewCallback(this, &BrowsingHistoryHandler2::HandleRemoveURLsOnOneDay));
    145   web_ui_->RegisterMessageCallback("clearBrowsingData",
    146       NewCallback(this, &BrowsingHistoryHandler2::HandleClearBrowsingData));
    147 }
    148 
    149 void BrowsingHistoryHandler2::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, &BrowsingHistoryHandler2::QueryComplete));
    173 }
    174 
    175 void BrowsingHistoryHandler2::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, &BrowsingHistoryHandler2::QueryComplete));
    198 }
    199 
    200 void BrowsingHistoryHandler2::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, &BrowsingHistoryHandler2::RemoveComplete));
    234 }
    235 
    236 void BrowsingHistoryHandler2::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 BrowsingHistoryHandler2::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 BrowsingHistoryHandler2::RemoveComplete() {
    299   // Some Visits were deleted from history. Reload the list.
    300   web_ui_->CallJavascriptFunction("deleteComplete");
    301 }
    302 
    303 void BrowsingHistoryHandler2::ExtractSearchHistoryArguments(
    304     const ListValue* args,
    305     int* month,
    306     string16* query) {
    307   *month = 0;
    308   Value* list_member;
    309 
    310   // Get search string.
    311   if (args->Get(0, &list_member) &&
    312       list_member->GetType() == Value::TYPE_STRING) {
    313     const StringValue* string_value =
    314       static_cast<const StringValue*>(list_member);
    315     string_value->GetAsString(query);
    316   }
    317 
    318   // Get search month.
    319   if (args->Get(1, &list_member) &&
    320       list_member->GetType() == Value::TYPE_STRING) {
    321     const StringValue* string_value =
    322       static_cast<const StringValue*>(list_member);
    323     string16 string16_value;
    324     string_value->GetAsString(&string16_value);
    325     base::StringToInt(string16_value, month);
    326   }
    327 }
    328 
    329 history::QueryOptions BrowsingHistoryHandler2::CreateMonthQueryOptions(
    330     int month) {
    331   history::QueryOptions options;
    332 
    333   // Configure the begin point of the search to the start of the
    334   // current month.
    335   base::Time::Exploded exploded;
    336   base::Time::Now().LocalMidnight().LocalExplode(&exploded);
    337   exploded.day_of_month = 1;
    338 
    339   if (month == 0) {
    340     options.begin_time = base::Time::FromLocalExploded(exploded);
    341 
    342     // Set the end time of this first search to null (which will
    343     // show results from the future, should the user's clock have
    344     // been set incorrectly).
    345     options.end_time = base::Time();
    346   } else {
    347     // Set the end-time of this search to the end of the month that is
    348     // |depth| months before the search end point. The end time is not
    349     // inclusive, so we should feel free to set it to midnight on the
    350     // first day of the following month.
    351     exploded.month -= month - 1;
    352     while (exploded.month < 1) {
    353       exploded.month += 12;
    354       exploded.year--;
    355     }
    356     options.end_time = base::Time::FromLocalExploded(exploded);
    357 
    358     // Set the begin-time of the search to the start of the month
    359     // that is |depth| months prior to search_start_.
    360     if (exploded.month > 1) {
    361       exploded.month--;
    362     } else {
    363       exploded.month = 12;
    364       exploded.year--;
    365     }
    366     options.begin_time = base::Time::FromLocalExploded(exploded);
    367   }
    368 
    369   return options;
    370 }
    371 
    372 ////////////////////////////////////////////////////////////////////////////////
    373 //
    374 // HistoryUIContents
    375 //
    376 ////////////////////////////////////////////////////////////////////////////////
    377 
    378 HistoryUI2::HistoryUI2(TabContents* contents) : WebUI(contents) {
    379   AddMessageHandler((new BrowsingHistoryHandler2())->Attach(this));
    380 
    381   HistoryUIHTMLSource2* html_source = new HistoryUIHTMLSource2();
    382 
    383   // Set up the chrome://history2/ source.
    384   contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
    385 }
    386 
    387 // static
    388 const GURL HistoryUI2::GetHistoryURLWithSearchText(const string16& text) {
    389   return GURL(std::string(chrome::kChromeUIHistory2URL) + "#q=" +
    390               EscapeQueryParamValue(UTF16ToUTF8(text), true));
    391 }
    392 
    393 // static
    394 RefCountedMemory* HistoryUI2::GetFaviconResourceBytes() {
    395   return ResourceBundle::GetSharedInstance().
    396       LoadDataResourceBytes(IDR_HISTORY_FAVICON);
    397 }
    398