• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HistoryUIHTMLSource2()53 HistoryUIHTMLSource2::HistoryUIHTMLSource2()
54     : DataSource(chrome::kChromeUIHistory2Host, MessageLoop::current()) {
55 }
56 
StartDataRequest(const std::string & path,bool is_incognito,int request_id)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 
GetMimeType(const std::string &) const111 std::string HistoryUIHTMLSource2::GetMimeType(const std::string&) const {
112   return "text/html";
113 }
114 
115 ////////////////////////////////////////////////////////////////////////////////
116 //
117 // HistoryHandler
118 //
119 ////////////////////////////////////////////////////////////////////////////////
BrowsingHistoryHandler2()120 BrowsingHistoryHandler2::BrowsingHistoryHandler2()
121     : search_text_() {
122 }
123 
~BrowsingHistoryHandler2()124 BrowsingHistoryHandler2::~BrowsingHistoryHandler2() {
125   cancelable_search_consumer_.CancelAllRequests();
126   cancelable_delete_consumer_.CancelAllRequests();
127 }
128 
Attach(WebUI * web_ui)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 
RegisterMessages()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 
HandleGetHistory(const ListValue * args)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 
HandleSearchHistory(const ListValue * args)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 
HandleRemoveURLsOnOneDay(const ListValue * args)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 
HandleClearBrowsingData(const ListValue * args)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 
QueryComplete(HistoryService::Handle request_handle,history::QueryResults * results)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 
RemoveComplete()298 void BrowsingHistoryHandler2::RemoveComplete() {
299   // Some Visits were deleted from history. Reload the list.
300   web_ui_->CallJavascriptFunction("deleteComplete");
301 }
302 
ExtractSearchHistoryArguments(const ListValue * args,int * month,string16 * query)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 
CreateMonthQueryOptions(int month)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 
HistoryUI2(TabContents * contents)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
GetHistoryURLWithSearchText(const string16 & text)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
GetFaviconResourceBytes()394 RefCountedMemory* HistoryUI2::GetFaviconResourceBytes() {
395   return ResourceBundle::GetSharedInstance().
396       LoadDataResourceBytes(IDR_HISTORY_FAVICON);
397 }
398