1 // Copyright (c) 2012 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/extensions/api/history/history_api.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/callback.h" 10 #include "base/json/json_writer.h" 11 #include "base/lazy_instance.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/prefs/pref_service.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/time/time.h" 18 #include "base/values.h" 19 #include "chrome/browser/chrome_notification_types.h" 20 #include "chrome/browser/extensions/event_router.h" 21 #include "chrome/browser/extensions/extension_system.h" 22 #include "chrome/browser/history/history_service.h" 23 #include "chrome/browser/history/history_service_factory.h" 24 #include "chrome/browser/history/history_types.h" 25 #include "chrome/browser/history/visit_filter.h" 26 #include "chrome/browser/profiles/profile.h" 27 #include "chrome/common/cancelable_task_tracker.h" 28 #include "chrome/common/extensions/api/experimental_history.h" 29 #include "chrome/common/pref_names.h" 30 #include "content/public/browser/notification_details.h" 31 #include "content/public/browser/notification_source.h" 32 33 namespace extensions { 34 35 using api::experimental_history::MostVisitedItem; 36 using api::history::HistoryItem; 37 using api::history::VisitItem; 38 39 typedef std::vector<linked_ptr<api::history::HistoryItem> > 40 HistoryItemList; 41 typedef std::vector<linked_ptr<api::history::VisitItem> > 42 VisitItemList; 43 44 namespace AddUrl = api::history::AddUrl; 45 namespace DeleteUrl = api::history::DeleteUrl; 46 namespace DeleteRange = api::history::DeleteRange; 47 namespace GetMostVisited = api::experimental_history::GetMostVisited; 48 namespace GetVisits = api::history::GetVisits; 49 namespace OnVisited = api::history::OnVisited; 50 namespace OnVisitRemoved = api::history::OnVisitRemoved; 51 namespace Search = api::history::Search; 52 53 namespace { 54 55 const char kOnVisited[] = "history.onVisited"; 56 const char kOnVisitRemoved[] = "history.onVisitRemoved"; 57 58 const char kInvalidIdError[] = "History item id is invalid."; 59 const char kInvalidUrlError[] = "Url is invalid."; 60 const char kDeleteProhibitedError[] = "Browsing history is not allowed to be " 61 "deleted."; 62 63 double MilliSecondsFromTime(const base::Time& time) { 64 return 1000 * time.ToDoubleT(); 65 } 66 67 scoped_ptr<HistoryItem> GetHistoryItem(const history::URLRow& row) { 68 scoped_ptr<HistoryItem> history_item(new HistoryItem()); 69 70 history_item->id = base::Int64ToString(row.id()); 71 history_item->url.reset(new std::string(row.url().spec())); 72 history_item->title.reset(new std::string(UTF16ToUTF8(row.title()))); 73 history_item->last_visit_time.reset( 74 new double(MilliSecondsFromTime(row.last_visit()))); 75 history_item->typed_count.reset(new int(row.typed_count())); 76 history_item->visit_count.reset(new int(row.visit_count())); 77 78 return history_item.Pass(); 79 } 80 81 scoped_ptr<VisitItem> GetVisitItem(const history::VisitRow& row) { 82 scoped_ptr<VisitItem> visit_item(new VisitItem()); 83 84 visit_item->id = base::Int64ToString(row.url_id); 85 visit_item->visit_id = base::Int64ToString(row.visit_id); 86 visit_item->visit_time.reset( 87 new double(MilliSecondsFromTime(row.visit_time))); 88 visit_item->referring_visit_id = base::Int64ToString(row.referring_visit); 89 90 VisitItem::Transition transition = VisitItem::TRANSITION_LINK; 91 switch (row.transition & content::PAGE_TRANSITION_CORE_MASK) { 92 case content::PAGE_TRANSITION_LINK: 93 transition = VisitItem::TRANSITION_LINK; 94 break; 95 case content::PAGE_TRANSITION_TYPED: 96 transition = VisitItem::TRANSITION_TYPED; 97 break; 98 case content::PAGE_TRANSITION_AUTO_BOOKMARK: 99 transition = VisitItem::TRANSITION_AUTO_BOOKMARK; 100 break; 101 case content::PAGE_TRANSITION_AUTO_SUBFRAME: 102 transition = VisitItem::TRANSITION_AUTO_SUBFRAME; 103 break; 104 case content::PAGE_TRANSITION_MANUAL_SUBFRAME: 105 transition = VisitItem::TRANSITION_MANUAL_SUBFRAME; 106 break; 107 case content::PAGE_TRANSITION_GENERATED: 108 transition = VisitItem::TRANSITION_GENERATED; 109 break; 110 case content::PAGE_TRANSITION_AUTO_TOPLEVEL: 111 transition = VisitItem::TRANSITION_AUTO_TOPLEVEL; 112 break; 113 case content::PAGE_TRANSITION_FORM_SUBMIT: 114 transition = VisitItem::TRANSITION_FORM_SUBMIT; 115 break; 116 case content::PAGE_TRANSITION_RELOAD: 117 transition = VisitItem::TRANSITION_RELOAD; 118 break; 119 case content::PAGE_TRANSITION_KEYWORD: 120 transition = VisitItem::TRANSITION_KEYWORD; 121 break; 122 case content::PAGE_TRANSITION_KEYWORD_GENERATED: 123 transition = VisitItem::TRANSITION_KEYWORD_GENERATED; 124 break; 125 default: 126 DCHECK(false); 127 } 128 129 visit_item->transition = transition; 130 131 return visit_item.Pass(); 132 } 133 134 } // namespace 135 136 HistoryEventRouter::HistoryEventRouter(Profile* profile) { 137 const content::Source<Profile> source = content::Source<Profile>(profile); 138 registrar_.Add(this, 139 chrome::NOTIFICATION_HISTORY_URL_VISITED, 140 source); 141 registrar_.Add(this, 142 chrome::NOTIFICATION_HISTORY_URLS_DELETED, 143 source); 144 } 145 146 HistoryEventRouter::~HistoryEventRouter() {} 147 148 void HistoryEventRouter::Observe(int type, 149 const content::NotificationSource& source, 150 const content::NotificationDetails& details) { 151 switch (type) { 152 case chrome::NOTIFICATION_HISTORY_URL_VISITED: 153 HistoryUrlVisited( 154 content::Source<Profile>(source).ptr(), 155 content::Details<const history::URLVisitedDetails>(details).ptr()); 156 break; 157 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: 158 HistoryUrlsRemoved( 159 content::Source<Profile>(source).ptr(), 160 content::Details<const history::URLsDeletedDetails>(details).ptr()); 161 break; 162 default: 163 NOTREACHED(); 164 } 165 } 166 167 void HistoryEventRouter::HistoryUrlVisited( 168 Profile* profile, 169 const history::URLVisitedDetails* details) { 170 scoped_ptr<HistoryItem> history_item = GetHistoryItem(details->row); 171 scoped_ptr<base::ListValue> args = OnVisited::Create(*history_item); 172 173 DispatchEvent(profile, kOnVisited, args.Pass()); 174 } 175 176 void HistoryEventRouter::HistoryUrlsRemoved( 177 Profile* profile, 178 const history::URLsDeletedDetails* details) { 179 OnVisitRemoved::Removed removed; 180 removed.all_history = details->all_history; 181 182 std::vector<std::string>* urls = new std::vector<std::string>(); 183 for (history::URLRows::const_iterator iterator = details->rows.begin(); 184 iterator != details->rows.end(); ++iterator) { 185 urls->push_back(iterator->url().spec()); 186 } 187 removed.urls.reset(urls); 188 189 scoped_ptr<base::ListValue> args = OnVisitRemoved::Create(removed); 190 DispatchEvent(profile, kOnVisitRemoved, args.Pass()); 191 } 192 193 void HistoryEventRouter::DispatchEvent( 194 Profile* profile, 195 const char* event_name, 196 scoped_ptr<base::ListValue> event_args) { 197 if (profile && extensions::ExtensionSystem::Get(profile)->event_router()) { 198 scoped_ptr<extensions::Event> event(new extensions::Event( 199 event_name, event_args.Pass())); 200 event->restrict_to_profile = profile; 201 extensions::ExtensionSystem::Get(profile)->event_router()-> 202 BroadcastEvent(event.Pass()); 203 } 204 } 205 206 HistoryAPI::HistoryAPI(Profile* profile) : profile_(profile) { 207 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 208 this, kOnVisited); 209 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver( 210 this, kOnVisitRemoved); 211 } 212 213 HistoryAPI::~HistoryAPI() { 214 } 215 216 void HistoryAPI::Shutdown() { 217 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); 218 } 219 220 static base::LazyInstance<ProfileKeyedAPIFactory<HistoryAPI> > 221 g_factory = LAZY_INSTANCE_INITIALIZER; 222 223 // static 224 ProfileKeyedAPIFactory<HistoryAPI>* HistoryAPI::GetFactoryInstance() { 225 return &g_factory.Get(); 226 } 227 228 void HistoryAPI::OnListenerAdded(const EventListenerInfo& details) { 229 history_event_router_.reset(new HistoryEventRouter(profile_)); 230 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this); 231 } 232 233 void HistoryFunction::Run() { 234 if (!RunImpl()) { 235 SendResponse(false); 236 } 237 } 238 239 bool HistoryFunction::ValidateUrl(const std::string& url_string, GURL* url) { 240 GURL temp_url(url_string); 241 if (!temp_url.is_valid()) { 242 error_ = kInvalidUrlError; 243 return false; 244 } 245 url->Swap(&temp_url); 246 return true; 247 } 248 249 bool HistoryFunction::VerifyDeleteAllowed() { 250 PrefService* prefs = profile()->GetPrefs(); 251 if (!prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory)) { 252 error_ = kDeleteProhibitedError; 253 return false; 254 } 255 return true; 256 } 257 258 base::Time HistoryFunction::GetTime(double ms_from_epoch) { 259 // The history service has seconds resolution, while javascript Date() has 260 // milliseconds resolution. 261 double seconds_from_epoch = ms_from_epoch / 1000.0; 262 // Time::FromDoubleT converts double time 0 to empty Time object. So we need 263 // to do special handling here. 264 return (seconds_from_epoch == 0) ? 265 base::Time::UnixEpoch() : base::Time::FromDoubleT(seconds_from_epoch); 266 } 267 268 HistoryFunctionWithCallback::HistoryFunctionWithCallback() { 269 } 270 271 HistoryFunctionWithCallback::~HistoryFunctionWithCallback() { 272 } 273 274 bool HistoryFunctionWithCallback::RunImpl() { 275 AddRef(); // Balanced in SendAysncRepose() and below. 276 bool retval = RunAsyncImpl(); 277 if (false == retval) 278 Release(); 279 return retval; 280 } 281 282 void HistoryFunctionWithCallback::SendAsyncResponse() { 283 base::MessageLoop::current()->PostTask( 284 FROM_HERE, 285 base::Bind(&HistoryFunctionWithCallback::SendResponseToCallback, this)); 286 } 287 288 void HistoryFunctionWithCallback::SendResponseToCallback() { 289 SendResponse(true); 290 Release(); // Balanced in RunImpl(). 291 } 292 293 bool HistoryGetMostVisitedFunction::RunAsyncImpl() { 294 scoped_ptr<GetMostVisited::Params> params = 295 GetMostVisited::Params::Create(*args_); 296 EXTENSION_FUNCTION_VALIDATE(params.get()); 297 298 history::VisitFilter filter; 299 if (params->details.filter_time.get()) 300 filter.SetFilterTime(GetTime(*params->details.filter_time)); 301 if (params->details.filter_width.get()) { 302 filter.SetFilterWidth(base::TimeDelta::FromMilliseconds( 303 static_cast<int64>(*params->details.filter_width))); 304 } 305 if (params->details.day_of_the_week.get()) 306 filter.SetDayOfTheWeekFilter(*params->details.day_of_the_week); 307 int max_results = 100; 308 if (params->details.max_results.get()) 309 max_results = *params->details.max_results; 310 HistoryService* hs = 311 HistoryServiceFactory::GetForProfile(profile(), Profile::EXPLICIT_ACCESS); 312 hs->QueryFilteredURLs(max_results, filter, false, &cancelable_consumer_, 313 base::Bind(&HistoryGetMostVisitedFunction::QueryComplete, 314 base::Unretained(this))); 315 return true; 316 } 317 318 void HistoryGetMostVisitedFunction::QueryComplete( 319 CancelableRequestProvider::Handle handle, 320 const history::FilteredURLList& data) { 321 std::vector<linked_ptr<MostVisitedItem> > results; 322 results.reserve(data.size()); 323 for (size_t i = 0; i < data.size(); i++) { 324 linked_ptr<MostVisitedItem> item(new MostVisitedItem); 325 item->url = data[i].url.spec(); 326 item->title = UTF16ToUTF8(data[i].title); 327 results.push_back(item); 328 } 329 results_ = GetMostVisited::Results::Create(results); 330 SendAsyncResponse(); 331 } 332 333 bool HistoryGetVisitsFunction::RunAsyncImpl() { 334 scoped_ptr<GetVisits::Params> params(GetVisits::Params::Create(*args_)); 335 EXTENSION_FUNCTION_VALIDATE(params.get()); 336 337 GURL url; 338 if (!ValidateUrl(params->details.url, &url)) 339 return false; 340 341 HistoryService* hs = 342 HistoryServiceFactory::GetForProfile(profile(), 343 Profile::EXPLICIT_ACCESS); 344 hs->QueryURL(url, 345 true, // Retrieve full history of a URL. 346 &cancelable_consumer_, 347 base::Bind(&HistoryGetVisitsFunction::QueryComplete, 348 base::Unretained(this))); 349 350 return true; 351 } 352 353 void HistoryGetVisitsFunction::QueryComplete( 354 HistoryService::Handle request_service, 355 bool success, 356 const history::URLRow* url_row, 357 history::VisitVector* visits) { 358 VisitItemList visit_item_vec; 359 if (visits && !visits->empty()) { 360 for (history::VisitVector::iterator iterator = visits->begin(); 361 iterator != visits->end(); 362 ++iterator) { 363 visit_item_vec.push_back(make_linked_ptr( 364 GetVisitItem(*iterator).release())); 365 } 366 } 367 368 results_ = GetVisits::Results::Create(visit_item_vec); 369 SendAsyncResponse(); 370 } 371 372 bool HistorySearchFunction::RunAsyncImpl() { 373 scoped_ptr<Search::Params> params(Search::Params::Create(*args_)); 374 EXTENSION_FUNCTION_VALIDATE(params.get()); 375 376 string16 search_text = UTF8ToUTF16(params->query.text); 377 378 history::QueryOptions options; 379 options.SetRecentDayRange(1); 380 options.max_count = 100; 381 382 if (params->query.start_time.get()) 383 options.begin_time = GetTime(*params->query.start_time); 384 if (params->query.end_time.get()) 385 options.end_time = GetTime(*params->query.end_time); 386 if (params->query.max_results.get()) 387 options.max_count = *params->query.max_results; 388 389 HistoryService* hs = 390 HistoryServiceFactory::GetForProfile(profile(), 391 Profile::EXPLICIT_ACCESS); 392 hs->QueryHistory(search_text, options, &cancelable_consumer_, 393 base::Bind(&HistorySearchFunction::SearchComplete, 394 base::Unretained(this))); 395 396 return true; 397 } 398 399 void HistorySearchFunction::SearchComplete( 400 HistoryService::Handle request_handle, 401 history::QueryResults* results) { 402 HistoryItemList history_item_vec; 403 if (results && !results->empty()) { 404 for (history::QueryResults::URLResultVector::const_iterator iterator = 405 results->begin(); 406 iterator != results->end(); 407 ++iterator) { 408 history_item_vec.push_back(make_linked_ptr( 409 GetHistoryItem(**iterator).release())); 410 } 411 } 412 results_ = Search::Results::Create(history_item_vec); 413 SendAsyncResponse(); 414 } 415 416 bool HistoryAddUrlFunction::RunImpl() { 417 scoped_ptr<AddUrl::Params> params(AddUrl::Params::Create(*args_)); 418 EXTENSION_FUNCTION_VALIDATE(params.get()); 419 420 GURL url; 421 if (!ValidateUrl(params->details.url, &url)) 422 return false; 423 424 HistoryService* hs = 425 HistoryServiceFactory::GetForProfile(profile(), 426 Profile::EXPLICIT_ACCESS); 427 hs->AddPage(url, base::Time::Now(), history::SOURCE_EXTENSION); 428 429 SendResponse(true); 430 return true; 431 } 432 433 bool HistoryDeleteUrlFunction::RunImpl() { 434 scoped_ptr<DeleteUrl::Params> params(DeleteUrl::Params::Create(*args_)); 435 EXTENSION_FUNCTION_VALIDATE(params.get()); 436 437 if (!VerifyDeleteAllowed()) 438 return false; 439 440 GURL url; 441 if (!ValidateUrl(params->details.url, &url)) 442 return false; 443 444 HistoryService* hs = 445 HistoryServiceFactory::GetForProfile(profile(), 446 Profile::EXPLICIT_ACCESS); 447 hs->DeleteURL(url); 448 449 SendResponse(true); 450 return true; 451 } 452 453 bool HistoryDeleteRangeFunction::RunAsyncImpl() { 454 scoped_ptr<DeleteRange::Params> params(DeleteRange::Params::Create(*args_)); 455 EXTENSION_FUNCTION_VALIDATE(params.get()); 456 457 if (!VerifyDeleteAllowed()) 458 return false; 459 460 base::Time start_time = GetTime(params->range.start_time); 461 base::Time end_time = GetTime(params->range.end_time); 462 463 std::set<GURL> restrict_urls; 464 HistoryService* hs = 465 HistoryServiceFactory::GetForProfile(profile(), 466 Profile::EXPLICIT_ACCESS); 467 hs->ExpireHistoryBetween( 468 restrict_urls, 469 start_time, 470 end_time, 471 base::Bind(&HistoryDeleteRangeFunction::DeleteComplete, 472 base::Unretained(this)), 473 &task_tracker_); 474 475 return true; 476 } 477 478 void HistoryDeleteRangeFunction::DeleteComplete() { 479 SendAsyncResponse(); 480 } 481 482 bool HistoryDeleteAllFunction::RunAsyncImpl() { 483 if (!VerifyDeleteAllowed()) 484 return false; 485 486 std::set<GURL> restrict_urls; 487 HistoryService* hs = 488 HistoryServiceFactory::GetForProfile(profile(), 489 Profile::EXPLICIT_ACCESS); 490 hs->ExpireHistoryBetween( 491 restrict_urls, 492 base::Time(), // Unbounded beginning... 493 base::Time(), // ...and the end. 494 base::Bind(&HistoryDeleteAllFunction::DeleteComplete, 495 base::Unretained(this)), 496 &task_tracker_); 497 498 return true; 499 } 500 501 void HistoryDeleteAllFunction::DeleteComplete() { 502 SendAsyncResponse(); 503 } 504 505 } // namespace extensions 506