| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2006-2008 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/dom_ui/history_ui.h" |
| 6 |
| 7 #include "base/message_loop.h" |
| 8 #include "base/string_piece.h" |
| 9 #include "base/thread.h" |
| 10 #include "base/time.h" |
| 11 #include "base/time_format.h" |
| 12 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/browser_resources.h" |
| 14 #include "chrome/browser/history/history_types.h" |
| 15 #include "chrome/browser/profile.h" |
| 16 #include "chrome/browser/user_metrics.h" |
| 17 #include "chrome/common/jstemplate_builder.h" |
| 18 #include "chrome/common/l10n_util.h" |
| 19 #include "chrome/common/resource_bundle.h" |
| 20 #include "chrome/common/time_format.h" |
| 21 |
| 22 #include "chromium_strings.h" |
| 23 #include "generated_resources.h" |
| 24 |
| 25 using base::Time; |
| 26 |
| 27 // HistoryUI is accessible from chrome://history, and the raw HTML is |
| 28 // accessed from chrome://history. |
| 29 static const std::string kHistoryHost = "history"; |
| 30 |
| 31 // Maximum number of search results to return in a given search. We should |
| 32 // eventually remove this. |
| 33 static const int kMaxSearchResults = 100; |
| 34 |
| 35 //////////////////////////////////////////////////////////////////////////////// |
| 36 // |
| 37 // HistoryHTMLSource |
| 38 // |
| 39 //////////////////////////////////////////////////////////////////////////////// |
| 40 |
| 41 HistoryUIHTMLSource::HistoryUIHTMLSource() |
| 42 : DataSource(kHistoryHost, MessageLoop::current()) { |
| 43 } |
| 44 |
| 45 void HistoryUIHTMLSource::StartDataRequest(const std::string& path, |
| 46 int request_id) { |
| 47 DictionaryValue localized_strings; |
| 48 localized_strings.SetString(L"title", |
| 49 l10n_util::GetString(IDS_HISTORY_TITLE)); |
| 50 localized_strings.SetString(L"loading", |
| 51 l10n_util::GetString(IDS_HISTORY_LOADING)); |
| 52 localized_strings.SetString(L"newest", |
| 53 l10n_util::GetString(IDS_HISTORY_NEWEST)); |
| 54 localized_strings.SetString(L"newer", |
| 55 l10n_util::GetString(IDS_HISTORY_NEWER)); |
| 56 localized_strings.SetString(L"older", |
| 57 l10n_util::GetString(IDS_HISTORY_OLDER)); |
| 58 localized_strings.SetString(L"searchresultsfor", |
| 59 l10n_util::GetString(IDS_HISTORY_SEARCHRESULTSFOR)); |
| 60 localized_strings.SetString(L"history", |
| 61 l10n_util::GetString(IDS_HISTORY_BROWSERESULTS)); |
| 62 localized_strings.SetString(L"cont", |
| 63 l10n_util::GetString(IDS_HISTORY_CONTINUED)); |
| 64 localized_strings.SetString(L"searchbutton", |
| 65 l10n_util::GetString(IDS_HISTORY_SEARCH_BUTTON)); |
| 66 localized_strings.SetString(L"noresults", |
| 67 l10n_util::GetString(IDS_HISTORY_NO_RESULTS)); |
| 68 localized_strings.SetString(L"noitems", |
| 69 l10n_util::GetString(IDS_HISTORY_NO_ITEMS)); |
| 70 localized_strings.SetString(L"delete", |
| 71 l10n_util::GetString(IDS_HISTORY_DELETE)); |
| 72 |
| 73 static const StringPiece history_html( |
| 74 ResourceBundle::GetSharedInstance().GetRawDataResource( |
| 75 IDR_HISTORY_HTML)); |
| 76 const std::string full_html = jstemplate_builder::GetTemplateHtml( |
| 77 history_html, &localized_strings, "t"); |
| 78 |
| 79 scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); |
| 80 html_bytes->data.resize(full_html.size()); |
| 81 std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); |
| 82 |
| 83 SendResponse(request_id, html_bytes); |
| 84 } |
| 85 |
| 86 //////////////////////////////////////////////////////////////////////////////// |
| 87 // |
| 88 // HistoryHandler |
| 89 // |
| 90 //////////////////////////////////////////////////////////////////////////////// |
| 91 BrowsingHistoryHandler::BrowsingHistoryHandler(DOMUI* dom_ui) |
| 92 : DOMMessageHandler(dom_ui), |
| 93 search_text_() { |
| 94 dom_ui_->RegisterMessageCallback("getHistory", |
| 95 NewCallback(this, &BrowsingHistoryHandler::HandleGetHistory)); |
| 96 |
| 97 // Create our favicon data source. |
| 98 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, |
| 99 NewRunnableMethod(&chrome_url_data_manager, |
| 100 &ChromeURLDataManager::AddDataSource, |
| 101 new FavIconSource(dom_ui_->get_profile()))); |
| 102 |
| 103 // Get notifications when history is cleared. |
| 104 NotificationService* service = NotificationService::current(); |
| 105 service->AddObserver(this, NOTIFY_HISTORY_URLS_DELETED, |
| 106 Source<Profile>(dom_ui_->get_profile())); |
| 107 } |
| 108 |
| 109 BrowsingHistoryHandler::~BrowsingHistoryHandler() { |
| 110 NotificationService* service = NotificationService::current(); |
| 111 service->RemoveObserver(this, NOTIFY_HISTORY_URLS_DELETED, |
| 112 Source<Profile>(dom_ui_->get_profile())); |
| 113 } |
| 114 |
| 115 void BrowsingHistoryHandler::HandleGetHistory(const Value* value) { |
| 116 // Anything in-flight is invalid. |
| 117 cancelable_consumer_.CancelAllRequests(); |
| 118 |
| 119 // Get arguments (if any). |
| 120 int month; |
| 121 std::wstring query; |
| 122 ExtractGetHistoryArguments(value, &month, &query); |
| 123 |
| 124 // Set our query options. |
| 125 history::QueryOptions options = CreateQueryOptions(month, query); |
| 126 |
| 127 // Need to remember the query string for our results. |
| 128 search_text_ = query; |
| 129 HistoryService* hs = |
| 130 dom_ui_->get_profile()->GetHistoryService(Profile::EXPLICIT_ACCESS); |
| 131 hs->QueryHistory(search_text_, |
| 132 options, |
| 133 &cancelable_consumer_, |
| 134 NewCallback(this, &BrowsingHistoryHandler::QueryComplete)); |
| 135 } |
| 136 |
| 137 void BrowsingHistoryHandler::QueryComplete( |
| 138 HistoryService::Handle request_handle, |
| 139 history::QueryResults* results) { |
| 140 |
| 141 ListValue results_value; |
| 142 Time midnight_today = Time::Now().LocalMidnight(); |
| 143 |
| 144 for (size_t i = 0; i < results->size(); ++i) { |
| 145 history::URLResult const &page = (*results)[i]; |
| 146 DictionaryValue* page_value = new DictionaryValue(); |
| 147 SetURLAndTitle(page_value, page.title(), page.url()); |
| 148 |
| 149 // Need to pass the time in epoch time (fastest JS conversion). |
| 150 page_value->SetInteger(L"time", |
| 151 static_cast<int>(page.visit_time().ToTimeT())); |
| 152 |
| 153 // Until we get some JS i18n infrastructure, we also need to |
| 154 // pass the dates in as strings. This could use some |
| 155 // optimization. |
| 156 |
| 157 // Only pass in the strings we need (search results need a shortdate |
| 158 // and snippet, browse results need day and time information). |
| 159 if (search_text_.empty()) { |
| 160 // Figure out the relative date string. |
| 161 std::wstring date_str = TimeFormat::RelativeDate(page.visit_time(), |
| 162 &midnight_today); |
| 163 if (date_str.empty()) { |
| 164 date_str = base::TimeFormatFriendlyDate(page.visit_time()); |
| 165 } else { |
| 166 date_str = l10n_util::GetStringF( |
| 167 IDS_HISTORY_DATE_WITH_RELATIVE_TIME, |
| 168 date_str, base::TimeFormatFriendlyDate(page.visit_time())); |
| 169 } |
| 170 page_value->SetString(L"dateRelativeDay", date_str); |
| 171 page_value->SetString(L"dateTimeOfDay", |
| 172 base::TimeFormatTimeOfDay(page.visit_time())); |
| 173 } else { |
| 174 page_value->SetString(L"dateShort", |
| 175 base::TimeFormatShortDate(page.visit_time())); |
| 176 page_value->SetString(L"snippet", page.snippet().text()); |
| 177 } |
| 178 |
| 179 results_value.Append(page_value); |
| 180 } |
| 181 |
| 182 dom_ui_->CallJavascriptFunction(L"historyResult", |
| 183 StringValue(search_text_), results_value); |
| 184 } |
| 185 |
| 186 void BrowsingHistoryHandler::ExtractGetHistoryArguments(const Value* value, |
| 187 int* month, std::wstring* query) { |
| 188 *month = 0; |
| 189 |
| 190 if (value && value->GetType() == Value::TYPE_LIST) { |
| 191 const ListValue* list_value = static_cast<const ListValue*>(value); |
| 192 Value* list_member; |
| 193 |
| 194 // Get search string. |
| 195 if (list_value->Get(0, &list_member) && |
| 196 list_member->GetType() == Value::TYPE_STRING) { |
| 197 const StringValue* string_value = |
| 198 static_cast<const StringValue*>(list_member); |
| 199 string_value->GetAsString(query); |
| 200 } |
| 201 |
| 202 // Get search month. |
| 203 if (list_value->Get(1, &list_member) && |
| 204 list_member->GetType() == Value::TYPE_STRING) { |
| 205 const StringValue* string_value = |
| 206 static_cast<const StringValue*>(list_member); |
| 207 std::wstring wstring_value; |
| 208 string_value->GetAsString(&wstring_value); |
| 209 *month = _wtoi(wstring_value.c_str()); |
| 210 } |
| 211 } |
| 212 } |
| 213 |
| 214 history::QueryOptions BrowsingHistoryHandler::CreateQueryOptions(int month, |
| 215 const std::wstring& query) { |
| 216 history::QueryOptions options; |
| 217 |
| 218 // Configure the begin point of the search to the start of the |
| 219 // current month. |
| 220 Time::Exploded exploded; |
| 221 Time::Now().LocalMidnight().LocalExplode(&exploded); |
| 222 exploded.day_of_month = 1; |
| 223 |
| 224 if (month == 0) { |
| 225 options.begin_time = Time::FromLocalExploded(exploded); |
| 226 |
| 227 // Set the end time of this first search to null (which will |
| 228 // show results from the future, should the user's clock have |
| 229 // been set incorrectly). |
| 230 options.end_time = Time(); |
| 231 } else { |
| 232 // Set the end-time of this search to the end of the month that is |
| 233 // |depth| months before the search end point. The end time is not |
| 234 // inclusive, so we should feel free to set it to midnight on the |
| 235 // first day of the following month. |
| 236 exploded.month -= month - 1; |
| 237 while (exploded.month < 1) { |
| 238 exploded.month += 12; |
| 239 exploded.year--; |
| 240 } |
| 241 options.end_time = Time::FromLocalExploded(exploded); |
| 242 |
| 243 // Set the begin-time of the search to the start of the month |
| 244 // that is |depth| months prior to search_start_. |
| 245 if (exploded.month > 1) { |
| 246 exploded.month--; |
| 247 } else { |
| 248 exploded.month = 12; |
| 249 exploded.year--; |
| 250 } |
| 251 options.begin_time = Time::FromLocalExploded(exploded); |
| 252 } |
| 253 |
| 254 // If searching, only show the most recent entry and limit the number of |
| 255 // results returned. |
| 256 if (!query.empty()) { |
| 257 options.max_count = kMaxSearchResults; |
| 258 options.most_recent_visit_only = true; |
| 259 } |
| 260 |
| 261 return options; |
| 262 } |
| 263 |
| 264 void BrowsingHistoryHandler::Observe(NotificationType type, |
| 265 const NotificationSource& source, |
| 266 const NotificationDetails& details) { |
| 267 if (type != NOTIFY_HISTORY_URLS_DELETED) { |
| 268 NOTREACHED(); |
| 269 return; |
| 270 } |
| 271 |
| 272 // Some URLs were deleted from history. Reload the most visited list. |
| 273 HandleGetHistory(NULL); |
| 274 } |
| 275 |
| 276 //////////////////////////////////////////////////////////////////////////////// |
| 277 // |
| 278 // HistoryUIContents |
| 279 // |
| 280 //////////////////////////////////////////////////////////////////////////////// |
| 281 |
| 282 HistoryUI::HistoryUI(DOMUIContents* contents) : DOMUI(contents) { |
| 283 } |
| 284 |
| 285 void HistoryUI::Init() { |
| 286 AddMessageHandler(new BrowsingHistoryHandler(this)); |
| 287 |
| 288 HistoryUIHTMLSource* html_source = new HistoryUIHTMLSource(); |
| 289 |
| 290 // Set up the chrome://history/ source. |
| 291 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, |
| 292 NewRunnableMethod(&chrome_url_data_manager, |
| 293 &ChromeURLDataManager::AddDataSource, |
| 294 html_source)); |
| 295 } |
| 296 |
| 297 // static |
| 298 GURL HistoryUI::GetBaseURL() { |
| 299 std::string url = DOMUIContents::GetScheme(); |
| 300 url += "://"; |
| 301 url += kHistoryHost; |
| 302 return GURL(url); |
| 303 } |
| OLD | NEW |