history_service.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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// The history system runs on a background thread so that potentially slow 6// database operations don't delay the browser. This backend processing is 7// represented by HistoryBackend. The HistoryService's job is to dispatch to 8// that thread. 9// 10// Main thread History thread 11// ----------- -------------- 12// HistoryService <----------------> HistoryBackend 13// -> HistoryDatabase 14// -> SQLite connection to History 15// -> ArchivedDatabase 16// -> SQLite connection to Archived History 17// -> ThumbnailDatabase 18// -> SQLite connection to Thumbnails 19// (and favicons) 20 21#include "chrome/browser/history/history_service.h" 22 23#include "base/bind_helpers.h" 24#include "base/callback.h" 25#include "base/command_line.h" 26#include "base/compiler_specific.h" 27#include "base/location.h" 28#include "base/memory/ref_counted.h" 29#include "base/message_loop/message_loop.h" 30#include "base/path_service.h" 31#include "base/prefs/pref_service.h" 32#include "base/thread_task_runner_handle.h" 33#include "base/threading/thread.h" 34#include "base/time/time.h" 35#include "chrome/browser/autocomplete/history_url_provider.h" 36#include "chrome/browser/bookmarks/bookmark_model.h" 37#include "chrome/browser/bookmarks/bookmark_model_factory.h" 38#include "chrome/browser/browser_process.h" 39#include "chrome/browser/chrome_notification_types.h" 40#include "chrome/browser/history/download_row.h" 41#include "chrome/browser/history/history_backend.h" 42#include "chrome/browser/history/history_notifications.h" 43#include "chrome/browser/history/history_types.h" 44#include "chrome/browser/history/in_memory_database.h" 45#include "chrome/browser/history/in_memory_history_backend.h" 46#include "chrome/browser/history/in_memory_url_index.h" 47#include "chrome/browser/history/top_sites.h" 48#include "chrome/browser/history/visit_database.h" 49#include "chrome/browser/history/visit_filter.h" 50#include "chrome/browser/history/web_history_service.h" 51#include "chrome/browser/history/web_history_service_factory.h" 52#include "chrome/browser/profiles/profile.h" 53#include "chrome/browser/ui/profile_error_dialog.h" 54#include "chrome/common/chrome_constants.h" 55#include "chrome/common/chrome_switches.h" 56#include "chrome/common/importer/imported_favicon_usage.h" 57#include "chrome/common/pref_names.h" 58#include "chrome/common/thumbnail_score.h" 59#include "chrome/common/url_constants.h" 60#include "components/visitedlink/browser/visitedlink_master.h" 61#include "content/public/browser/browser_thread.h" 62#include "content/public/browser/download_item.h" 63#include "content/public/browser/notification_service.h" 64#include "grit/chromium_strings.h" 65#include "grit/generated_resources.h" 66#include "sync/api/sync_error_factory.h" 67#include "third_party/skia/include/core/SkBitmap.h" 68 69using base::Time; 70using history::HistoryBackend; 71 72namespace { 73 74static const char* kHistoryThreadName = "Chrome_HistoryThread"; 75 76void RunWithFaviconResults( 77 const FaviconService::FaviconResultsCallback& callback, 78 std::vector<favicon_base::FaviconBitmapResult>* bitmap_results) { 79 callback.Run(*bitmap_results); 80} 81 82void RunWithFaviconResult(const FaviconService::FaviconRawCallback& callback, 83 favicon_base::FaviconBitmapResult* bitmap_result) { 84 callback.Run(*bitmap_result); 85} 86 87// Extract history::URLRows into GURLs for VisitedLinkMaster. 88class URLIteratorFromURLRows 89 : public visitedlink::VisitedLinkMaster::URLIterator { 90 public: 91 explicit URLIteratorFromURLRows(const history::URLRows& url_rows) 92 : itr_(url_rows.begin()), 93 end_(url_rows.end()) { 94 } 95 96 virtual const GURL& NextURL() OVERRIDE { 97 return (itr_++)->url(); 98 } 99 100 virtual bool HasNextURL() const OVERRIDE { 101 return itr_ != end_; 102 } 103 104 private: 105 history::URLRows::const_iterator itr_; 106 history::URLRows::const_iterator end_; 107 108 DISALLOW_COPY_AND_ASSIGN(URLIteratorFromURLRows); 109}; 110 111// Callback from WebHistoryService::ExpireWebHistory(). 112void ExpireWebHistoryComplete(bool success) { 113 // Ignore the result. 114 // 115 // TODO(davidben): ExpireLocalAndRemoteHistoryBetween callback should not fire 116 // until this completes. 117} 118 119} // namespace 120 121// Sends messages from the backend to us on the main thread. This must be a 122// separate class from the history service so that it can hold a reference to 123// the history service (otherwise we would have to manually AddRef and 124// Release when the Backend has a reference to us). 125class HistoryService::BackendDelegate : public HistoryBackend::Delegate { 126 public: 127 BackendDelegate( 128 const base::WeakPtr<HistoryService>& history_service, 129 const scoped_refptr<base::SequencedTaskRunner>& service_task_runner, 130 Profile* profile) 131 : history_service_(history_service), 132 service_task_runner_(service_task_runner), 133 profile_(profile) { 134 } 135 136 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE { 137 // Send to the history service on the main thread. 138 service_task_runner_->PostTask( 139 FROM_HERE, 140 base::Bind(&HistoryService::NotifyProfileError, history_service_, 141 init_status)); 142 } 143 144 virtual void SetInMemoryBackend( 145 scoped_ptr<history::InMemoryHistoryBackend> backend) OVERRIDE { 146 // Send the backend to the history service on the main thread. 147 service_task_runner_->PostTask( 148 FROM_HERE, 149 base::Bind(&HistoryService::SetInMemoryBackend, history_service_, 150 base::Passed(&backend))); 151 } 152 153 virtual void BroadcastNotifications( 154 int type, 155 scoped_ptr<history::HistoryDetails> details) OVERRIDE { 156 // Send the notification on the history thread. 157 if (content::NotificationService::current()) { 158 content::Details<history::HistoryDetails> det(details.get()); 159 content::NotificationService::current()->Notify( 160 type, content::Source<Profile>(profile_), det); 161 } 162 // Send the notification to the history service on the main thread. 163 service_task_runner_->PostTask( 164 FROM_HERE, 165 base::Bind(&HistoryService::BroadcastNotificationsHelper, 166 history_service_, type, base::Passed(&details))); 167 } 168 169 virtual void DBLoaded() OVERRIDE { 170 service_task_runner_->PostTask( 171 FROM_HERE, 172 base::Bind(&HistoryService::OnDBLoaded, history_service_)); 173 } 174 175 virtual void NotifyVisitDBObserversOnAddVisit( 176 const history::BriefVisitInfo& info) OVERRIDE { 177 service_task_runner_->PostTask( 178 FROM_HERE, 179 base::Bind(&HistoryService::NotifyVisitDBObserversOnAddVisit, 180 history_service_, info)); 181 } 182 183 private: 184 const base::WeakPtr<HistoryService> history_service_; 185 const scoped_refptr<base::SequencedTaskRunner> service_task_runner_; 186 Profile* const profile_; 187}; 188 189// The history thread is intentionally not a BrowserThread because the 190// sync integration unit tests depend on being able to create more than one 191// history thread. 192HistoryService::HistoryService() 193 : weak_ptr_factory_(this), 194 thread_(new base::Thread(kHistoryThreadName)), 195 profile_(NULL), 196 backend_loaded_(false), 197 bookmark_service_(NULL), 198 no_db_(false) { 199} 200 201HistoryService::HistoryService(Profile* profile) 202 : weak_ptr_factory_(this), 203 thread_(new base::Thread(kHistoryThreadName)), 204 profile_(profile), 205 visitedlink_master_(new visitedlink::VisitedLinkMaster( 206 profile, this, true)), 207 backend_loaded_(false), 208 bookmark_service_(NULL), 209 no_db_(false) { 210 DCHECK(profile_); 211 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, 212 content::Source<Profile>(profile_)); 213 registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, 214 content::Source<Profile>(profile_)); 215} 216 217HistoryService::~HistoryService() { 218 DCHECK(thread_checker_.CalledOnValidThread()); 219 // Shutdown the backend. This does nothing if Cleanup was already invoked. 220 Cleanup(); 221} 222 223bool HistoryService::BackendLoaded() { 224 DCHECK(thread_checker_.CalledOnValidThread()); 225 return backend_loaded_; 226} 227 228void HistoryService::Cleanup() { 229 DCHECK(thread_checker_.CalledOnValidThread()); 230 if (!thread_) { 231 // We've already cleaned up. 232 return; 233 } 234 235 weak_ptr_factory_.InvalidateWeakPtrs(); 236 237 // Unload the backend. 238 if (history_backend_) { 239 // Get rid of the in-memory backend. 240 in_memory_backend_.reset(); 241 242 // Give the InMemoryURLIndex a chance to shutdown. 243 // NOTE: In tests, there may be no index. 244 if (in_memory_url_index_) 245 in_memory_url_index_->ShutDown(); 246 247 // The backend's destructor must run on the history thread since it is not 248 // threadsafe. So this thread must not be the last thread holding a 249 // reference to the backend, or a crash could happen. 250 // 251 // We have a reference to the history backend. There is also an extra 252 // reference held by our delegate installed in the backend, which 253 // HistoryBackend::Closing will release. This means if we scheduled a call 254 // to HistoryBackend::Closing and *then* released our backend reference, 255 // there will be a race between us and the backend's Closing function to see 256 // who is the last holder of a reference. If the backend thread's Closing 257 // manages to run before we release our backend refptr, the last reference 258 // will be held by this thread and the destructor will be called from here. 259 // 260 // Therefore, we create a closure to run the Closing operation first. This 261 // holds a reference to the backend. Then we release our reference, then we 262 // schedule the task to run. After the task runs, it will delete its 263 // reference from the history thread, ensuring everything works properly. 264 // 265 // TODO(ajwong): Cleanup HistoryBackend lifetime issues. 266 // See http://crbug.com/99767. 267 history_backend_->AddRef(); 268 base::Closure closing_task = 269 base::Bind(&HistoryBackend::Closing, history_backend_.get()); 270 ScheduleTask(PRIORITY_NORMAL, closing_task); 271 closing_task.Reset(); 272 HistoryBackend* raw_ptr = history_backend_.get(); 273 history_backend_ = NULL; 274 thread_->message_loop()->ReleaseSoon(FROM_HERE, raw_ptr); 275 } 276 277 // Delete the thread, which joins with the background thread. We defensively 278 // NULL the pointer before deleting it in case somebody tries to use it 279 // during shutdown, but this shouldn't happen. 280 base::Thread* thread = thread_; 281 thread_ = NULL; 282 delete thread; 283} 284 285void HistoryService::NotifyRenderProcessHostDestruction(const void* host) { 286 DCHECK(thread_checker_.CalledOnValidThread()); 287 ScheduleAndForget(PRIORITY_NORMAL, 288 &HistoryBackend::NotifyRenderProcessHostDestruction, host); 289} 290 291history::URLDatabase* HistoryService::InMemoryDatabase() { 292 DCHECK(thread_checker_.CalledOnValidThread()); 293 return in_memory_backend_ ? in_memory_backend_->db() : NULL; 294} 295 296bool HistoryService::GetTypedCountForURL(const GURL& url, int* typed_count) { 297 DCHECK(thread_checker_.CalledOnValidThread()); 298 history::URLRow url_row; 299 if (!GetRowForURL(url, &url_row)) 300 return false; 301 *typed_count = url_row.typed_count(); 302 return true; 303} 304 305bool HistoryService::GetLastVisitTimeForURL(const GURL& url, 306 base::Time* last_visit) { 307 DCHECK(thread_checker_.CalledOnValidThread()); 308 history::URLRow url_row; 309 if (!GetRowForURL(url, &url_row)) 310 return false; 311 *last_visit = url_row.last_visit(); 312 return true; 313} 314 315bool HistoryService::GetVisitCountForURL(const GURL& url, int* visit_count) { 316 DCHECK(thread_checker_.CalledOnValidThread()); 317 history::URLRow url_row; 318 if (!GetRowForURL(url, &url_row)) 319 return false; 320 *visit_count = url_row.visit_count(); 321 return true; 322} 323 324history::TypedUrlSyncableService* HistoryService::GetTypedUrlSyncableService() 325 const { 326 return history_backend_->GetTypedUrlSyncableService(); 327} 328 329void HistoryService::Shutdown() { 330 DCHECK(thread_checker_.CalledOnValidThread()); 331 // It's possible that bookmarks haven't loaded and history is waiting for 332 // bookmarks to complete loading. In such a situation history can't shutdown 333 // (meaning if we invoked history_service_->Cleanup now, we would 334 // deadlock). To break the deadlock we tell BookmarkModel it's about to be 335 // deleted so that it can release the signal history is waiting on, allowing 336 // history to shutdown (history_service_->Cleanup to complete). In such a 337 // scenario history sees an incorrect view of bookmarks, but it's better 338 // than a deadlock. 339 BookmarkModel* bookmark_model = static_cast<BookmarkModel*>( 340 BookmarkModelFactory::GetForProfileIfExists(profile_)); 341 if (bookmark_model) 342 bookmark_model->Shutdown(); 343 344 Cleanup(); 345} 346 347void HistoryService::SetKeywordSearchTermsForURL(const GURL& url, 348 TemplateURLID keyword_id, 349 const base::string16& term) { 350 DCHECK(thread_checker_.CalledOnValidThread()); 351 ScheduleAndForget(PRIORITY_UI, 352 &HistoryBackend::SetKeywordSearchTermsForURL, 353 url, keyword_id, term); 354} 355 356void HistoryService::DeleteAllSearchTermsForKeyword( 357 TemplateURLID keyword_id) { 358 DCHECK(thread_checker_.CalledOnValidThread()); 359 ScheduleAndForget(PRIORITY_UI, 360 &HistoryBackend::DeleteAllSearchTermsForKeyword, 361 keyword_id); 362} 363 364HistoryService::Handle HistoryService::GetMostRecentKeywordSearchTerms( 365 TemplateURLID keyword_id, 366 const base::string16& prefix, 367 int max_count, 368 CancelableRequestConsumerBase* consumer, 369 const GetMostRecentKeywordSearchTermsCallback& callback) { 370 DCHECK(thread_checker_.CalledOnValidThread()); 371 return Schedule(PRIORITY_UI, &HistoryBackend::GetMostRecentKeywordSearchTerms, 372 consumer, 373 new history::GetMostRecentKeywordSearchTermsRequest(callback), 374 keyword_id, prefix, max_count); 375} 376 377void HistoryService::DeleteKeywordSearchTermForURL(const GURL& url) { 378 DCHECK(thread_checker_.CalledOnValidThread()); 379 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::DeleteKeywordSearchTermForURL, 380 url); 381} 382 383void HistoryService::DeleteMatchingURLsForKeyword(TemplateURLID keyword_id, 384 const base::string16& term) { 385 DCHECK(thread_checker_.CalledOnValidThread()); 386 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::DeleteMatchingURLsForKeyword, 387 keyword_id, term); 388} 389 390void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) { 391 DCHECK(thread_checker_.CalledOnValidThread()); 392 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::URLsNoLongerBookmarked, 393 urls); 394} 395 396void HistoryService::ScheduleDBTask(history::HistoryDBTask* task, 397 CancelableRequestConsumerBase* consumer) { 398 DCHECK(thread_checker_.CalledOnValidThread()); 399 history::HistoryDBTaskRequest* request = new history::HistoryDBTaskRequest( 400 base::Bind(&history::HistoryDBTask::DoneRunOnMainThread, task)); 401 request->value = task; // The value is the task to execute. 402 Schedule(PRIORITY_UI, &HistoryBackend::ProcessDBTask, consumer, request); 403} 404 405HistoryService::Handle HistoryService::QuerySegmentUsageSince( 406 CancelableRequestConsumerBase* consumer, 407 const Time from_time, 408 int max_result_count, 409 const SegmentQueryCallback& callback) { 410 DCHECK(thread_checker_.CalledOnValidThread()); 411 return Schedule(PRIORITY_UI, &HistoryBackend::QuerySegmentUsage, 412 consumer, new history::QuerySegmentUsageRequest(callback), 413 from_time, max_result_count); 414} 415 416void HistoryService::FlushForTest(const base::Closure& flushed) { 417 thread_->message_loop_proxy()->PostTaskAndReply( 418 FROM_HERE, base::Bind(&base::DoNothing), flushed); 419} 420 421void HistoryService::SetOnBackendDestroyTask(const base::Closure& task) { 422 DCHECK(thread_checker_.CalledOnValidThread()); 423 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetOnBackendDestroyTask, 424 base::MessageLoop::current(), task); 425} 426 427void HistoryService::AddPage(const GURL& url, 428 Time time, 429 const void* id_scope, 430 int32 page_id, 431 const GURL& referrer, 432 const history::RedirectList& redirects, 433 content::PageTransition transition, 434 history::VisitSource visit_source, 435 bool did_replace_entry) { 436 DCHECK(thread_checker_.CalledOnValidThread()); 437 AddPage( 438 history::HistoryAddPageArgs(url, time, id_scope, page_id, referrer, 439 redirects, transition, visit_source, 440 did_replace_entry)); 441} 442 443void HistoryService::AddPage(const GURL& url, 444 base::Time time, 445 history::VisitSource visit_source) { 446 DCHECK(thread_checker_.CalledOnValidThread()); 447 AddPage( 448 history::HistoryAddPageArgs(url, time, NULL, 0, GURL(), 449 history::RedirectList(), 450 content::PAGE_TRANSITION_LINK, 451 visit_source, false)); 452} 453 454void HistoryService::AddPage(const history::HistoryAddPageArgs& add_page_args) { 455 DCHECK(thread_checker_.CalledOnValidThread()); 456 DCHECK(thread_) << "History service being called after cleanup"; 457 458 // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a 459 // large part of history (think iframes for ads) and we never display them in 460 // history UI. We will still add manual subframes, which are ones the user 461 // has clicked on to get. 462 if (!CanAddURL(add_page_args.url)) 463 return; 464 465 // Add link & all redirects to visited link list. 466 if (visitedlink_master_) { 467 visitedlink_master_->AddURL(add_page_args.url); 468 469 if (!add_page_args.redirects.empty()) { 470 // We should not be asked to add a page in the middle of a redirect chain. 471 DCHECK_EQ(add_page_args.url, 472 add_page_args.redirects[add_page_args.redirects.size() - 1]); 473 474 // We need the !redirects.empty() condition above since size_t is unsigned 475 // and will wrap around when we subtract one from a 0 size. 476 for (size_t i = 0; i < add_page_args.redirects.size() - 1; i++) 477 visitedlink_master_->AddURL(add_page_args.redirects[i]); 478 } 479 } 480 481 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::AddPage, add_page_args); 482} 483 484void HistoryService::AddPageNoVisitForBookmark(const GURL& url, 485 const base::string16& title) { 486 DCHECK(thread_checker_.CalledOnValidThread()); 487 if (!CanAddURL(url)) 488 return; 489 490 ScheduleAndForget(PRIORITY_NORMAL, 491 &HistoryBackend::AddPageNoVisitForBookmark, url, title); 492} 493 494void HistoryService::SetPageTitle(const GURL& url, 495 const base::string16& title) { 496 DCHECK(thread_checker_.CalledOnValidThread()); 497 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageTitle, url, title); 498} 499 500void HistoryService::UpdateWithPageEndTime(const void* host, 501 int32 page_id, 502 const GURL& url, 503 Time end_ts) { 504 DCHECK(thread_checker_.CalledOnValidThread()); 505 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateWithPageEndTime, 506 host, page_id, url, end_ts); 507} 508 509void HistoryService::AddPageWithDetails(const GURL& url, 510 const base::string16& title, 511 int visit_count, 512 int typed_count, 513 Time last_visit, 514 bool hidden, 515 history::VisitSource visit_source) { 516 DCHECK(thread_checker_.CalledOnValidThread()); 517 // Filter out unwanted URLs. 518 if (!CanAddURL(url)) 519 return; 520 521 // Add to the visited links system. 522 if (visitedlink_master_) 523 visitedlink_master_->AddURL(url); 524 525 history::URLRow row(url); 526 row.set_title(title); 527 row.set_visit_count(visit_count); 528 row.set_typed_count(typed_count); 529 row.set_last_visit(last_visit); 530 row.set_hidden(hidden); 531 532 history::URLRows rows; 533 rows.push_back(row); 534 535 ScheduleAndForget(PRIORITY_NORMAL, 536 &HistoryBackend::AddPagesWithDetails, rows, visit_source); 537} 538 539void HistoryService::AddPagesWithDetails(const history::URLRows& info, 540 history::VisitSource visit_source) { 541 DCHECK(thread_checker_.CalledOnValidThread()); 542 // Add to the visited links system. 543 if (visitedlink_master_) { 544 std::vector<GURL> urls; 545 urls.reserve(info.size()); 546 for (history::URLRows::const_iterator i = info.begin(); i != info.end(); 547 ++i) 548 urls.push_back(i->url()); 549 550 visitedlink_master_->AddURLs(urls); 551 } 552 553 ScheduleAndForget(PRIORITY_NORMAL, 554 &HistoryBackend::AddPagesWithDetails, info, visit_source); 555} 556 557base::CancelableTaskTracker::TaskId HistoryService::GetFavicons( 558 const std::vector<GURL>& icon_urls, 559 int icon_types, 560 int desired_size_in_dip, 561 const std::vector<ui::ScaleFactor>& desired_scale_factors, 562 const FaviconService::FaviconResultsCallback& callback, 563 base::CancelableTaskTracker* tracker) { 564 DCHECK(thread_checker_.CalledOnValidThread()); 565 566 std::vector<favicon_base::FaviconBitmapResult>* results = 567 new std::vector<favicon_base::FaviconBitmapResult>(); 568 return tracker->PostTaskAndReply( 569 thread_->message_loop_proxy().get(), 570 FROM_HERE, 571 base::Bind(&HistoryBackend::GetFavicons, 572 history_backend_.get(), 573 icon_urls, 574 icon_types, 575 desired_size_in_dip, 576 desired_scale_factors, 577 results), 578 base::Bind(&RunWithFaviconResults, callback, base::Owned(results))); 579} 580 581base::CancelableTaskTracker::TaskId HistoryService::GetFaviconsForURL( 582 const GURL& page_url, 583 int icon_types, 584 int desired_size_in_dip, 585 const std::vector<ui::ScaleFactor>& desired_scale_factors, 586 const FaviconService::FaviconResultsCallback& callback, 587 base::CancelableTaskTracker* tracker) { 588 DCHECK(thread_checker_.CalledOnValidThread()); 589 590 std::vector<favicon_base::FaviconBitmapResult>* results = 591 new std::vector<favicon_base::FaviconBitmapResult>(); 592 return tracker->PostTaskAndReply( 593 thread_->message_loop_proxy().get(), 594 FROM_HERE, 595 base::Bind(&HistoryBackend::GetFaviconsForURL, 596 history_backend_.get(), 597 page_url, 598 icon_types, 599 desired_size_in_dip, 600 desired_scale_factors, 601 results), 602 base::Bind(&RunWithFaviconResults, callback, base::Owned(results))); 603} 604 605base::CancelableTaskTracker::TaskId HistoryService::GetLargestFaviconForURL( 606 const GURL& page_url, 607 const std::vector<int>& icon_types, 608 int minimum_size_in_pixels, 609 const FaviconService::FaviconRawCallback& callback, 610 base::CancelableTaskTracker* tracker) { 611 DCHECK(thread_checker_.CalledOnValidThread()); 612 613 favicon_base::FaviconBitmapResult* result = 614 new favicon_base::FaviconBitmapResult(); 615 return tracker->PostTaskAndReply( 616 thread_->message_loop_proxy().get(), 617 FROM_HERE, 618 base::Bind(&HistoryBackend::GetLargestFaviconForURL, 619 history_backend_.get(), 620 page_url, 621 icon_types, 622 minimum_size_in_pixels, 623 result), 624 base::Bind(&RunWithFaviconResult, callback, base::Owned(result))); 625} 626 627base::CancelableTaskTracker::TaskId HistoryService::GetFaviconForID( 628 favicon_base::FaviconID favicon_id, 629 int desired_size_in_dip, 630 ui::ScaleFactor desired_scale_factor, 631 const FaviconService::FaviconResultsCallback& callback, 632 base::CancelableTaskTracker* tracker) { 633 DCHECK(thread_checker_.CalledOnValidThread()); 634 635 std::vector<favicon_base::FaviconBitmapResult>* results = 636 new std::vector<favicon_base::FaviconBitmapResult>(); 637 return tracker->PostTaskAndReply( 638 thread_->message_loop_proxy().get(), 639 FROM_HERE, 640 base::Bind(&HistoryBackend::GetFaviconForID, 641 history_backend_.get(), 642 favicon_id, 643 desired_size_in_dip, 644 desired_scale_factor, 645 results), 646 base::Bind(&RunWithFaviconResults, callback, base::Owned(results))); 647} 648 649base::CancelableTaskTracker::TaskId 650HistoryService::UpdateFaviconMappingsAndFetch( 651 const GURL& page_url, 652 const std::vector<GURL>& icon_urls, 653 int icon_types, 654 int desired_size_in_dip, 655 const std::vector<ui::ScaleFactor>& desired_scale_factors, 656 const FaviconService::FaviconResultsCallback& callback, 657 base::CancelableTaskTracker* tracker) { 658 DCHECK(thread_checker_.CalledOnValidThread()); 659 660 std::vector<favicon_base::FaviconBitmapResult>* results = 661 new std::vector<favicon_base::FaviconBitmapResult>(); 662 return tracker->PostTaskAndReply( 663 thread_->message_loop_proxy().get(), 664 FROM_HERE, 665 base::Bind(&HistoryBackend::UpdateFaviconMappingsAndFetch, 666 history_backend_.get(), 667 page_url, 668 icon_urls, 669 icon_types, 670 desired_size_in_dip, 671 desired_scale_factors, 672 results), 673 base::Bind(&RunWithFaviconResults, callback, base::Owned(results))); 674} 675 676void HistoryService::MergeFavicon( 677 const GURL& page_url, 678 const GURL& icon_url, 679 favicon_base::IconType icon_type, 680 scoped_refptr<base::RefCountedMemory> bitmap_data, 681 const gfx::Size& pixel_size) { 682 DCHECK(thread_checker_.CalledOnValidThread()); 683 if (!CanAddURL(page_url)) 684 return; 685 686 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::MergeFavicon, page_url, 687 icon_url, icon_type, bitmap_data, pixel_size); 688} 689 690void HistoryService::SetFavicons( 691 const GURL& page_url, 692 favicon_base::IconType icon_type, 693 const std::vector<favicon_base::FaviconBitmapData>& favicon_bitmap_data) { 694 DCHECK(thread_checker_.CalledOnValidThread()); 695 if (!CanAddURL(page_url)) 696 return; 697 698 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavicons, page_url, 699 icon_type, favicon_bitmap_data); 700} 701 702void HistoryService::SetFaviconsOutOfDateForPage(const GURL& page_url) { 703 DCHECK(thread_checker_.CalledOnValidThread()); 704 ScheduleAndForget(PRIORITY_NORMAL, 705 &HistoryBackend::SetFaviconsOutOfDateForPage, page_url); 706} 707 708void HistoryService::CloneFavicons(const GURL& old_page_url, 709 const GURL& new_page_url) { 710 DCHECK(thread_checker_.CalledOnValidThread()); 711 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::CloneFavicons, 712 old_page_url, new_page_url); 713} 714 715void HistoryService::SetImportedFavicons( 716 const std::vector<ImportedFaviconUsage>& favicon_usage) { 717 DCHECK(thread_checker_.CalledOnValidThread()); 718 ScheduleAndForget(PRIORITY_NORMAL, 719 &HistoryBackend::SetImportedFavicons, favicon_usage); 720} 721 722HistoryService::Handle HistoryService::QueryURL( 723 const GURL& url, 724 bool want_visits, 725 CancelableRequestConsumerBase* consumer, 726 const QueryURLCallback& callback) { 727 DCHECK(thread_checker_.CalledOnValidThread()); 728 return Schedule(PRIORITY_UI, &HistoryBackend::QueryURL, consumer, 729 new history::QueryURLRequest(callback), url, want_visits); 730} 731 732// Downloads ------------------------------------------------------------------- 733 734// Handle creation of a download by creating an entry in the history service's 735// 'downloads' table. 736void HistoryService::CreateDownload( 737 const history::DownloadRow& create_info, 738 const HistoryService::DownloadCreateCallback& callback) { 739 DCHECK(thread_) << "History service being called after cleanup"; 740 DCHECK(thread_checker_.CalledOnValidThread()); 741 PostTaskAndReplyWithResult( 742 thread_->message_loop_proxy(), FROM_HERE, 743 base::Bind(&HistoryBackend::CreateDownload, history_backend_.get(), 744 create_info), 745 callback); 746} 747 748void HistoryService::GetNextDownloadId( 749 const content::DownloadIdCallback& callback) { 750 DCHECK(thread_) << "History service being called after cleanup"; 751 DCHECK(thread_checker_.CalledOnValidThread()); 752 PostTaskAndReplyWithResult( 753 thread_->message_loop_proxy(), FROM_HERE, 754 base::Bind(&HistoryBackend::GetNextDownloadId, history_backend_.get()), 755 callback); 756} 757 758// Handle queries for a list of all downloads in the history database's 759// 'downloads' table. 760void HistoryService::QueryDownloads( 761 const DownloadQueryCallback& callback) { 762 DCHECK(thread_) << "History service being called after cleanup"; 763 DCHECK(thread_checker_.CalledOnValidThread()); 764 std::vector<history::DownloadRow>* rows = 765 new std::vector<history::DownloadRow>(); 766 scoped_ptr<std::vector<history::DownloadRow> > scoped_rows(rows); 767 // Beware! The first Bind() does not simply |scoped_rows.get()| because 768 // base::Passed(&scoped_rows) nullifies |scoped_rows|, and compilers do not 769 // guarantee that the first Bind's arguments are evaluated before the second 770 // Bind's arguments. 771 thread_->message_loop_proxy()->PostTaskAndReply( 772 FROM_HERE, 773 base::Bind(&HistoryBackend::QueryDownloads, history_backend_.get(), rows), 774 base::Bind(callback, base::Passed(&scoped_rows))); 775} 776 777// Handle updates for a particular download. This is a 'fire and forget' 778// operation, so we don't need to be called back. 779void HistoryService::UpdateDownload(const history::DownloadRow& data) { 780 DCHECK(thread_checker_.CalledOnValidThread()); 781 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownload, data); 782} 783 784void HistoryService::RemoveDownloads(const std::set<uint32>& ids) { 785 DCHECK(thread_checker_.CalledOnValidThread()); 786 ScheduleAndForget(PRIORITY_NORMAL, 787 &HistoryBackend::RemoveDownloads, ids); 788} 789 790HistoryService::Handle HistoryService::QueryHistory( 791 const base::string16& text_query, 792 const history::QueryOptions& options, 793 CancelableRequestConsumerBase* consumer, 794 const QueryHistoryCallback& callback) { 795 DCHECK(thread_checker_.CalledOnValidThread()); 796 return Schedule(PRIORITY_UI, &HistoryBackend::QueryHistory, consumer, 797 new history::QueryHistoryRequest(callback), 798 text_query, options); 799} 800 801HistoryService::Handle HistoryService::QueryRedirectsFrom( 802 const GURL& from_url, 803 CancelableRequestConsumerBase* consumer, 804 const QueryRedirectsCallback& callback) { 805 DCHECK(thread_checker_.CalledOnValidThread()); 806 return Schedule(PRIORITY_UI, &HistoryBackend::QueryRedirectsFrom, consumer, 807 new history::QueryRedirectsRequest(callback), from_url); 808} 809 810HistoryService::Handle HistoryService::QueryRedirectsTo( 811 const GURL& to_url, 812 CancelableRequestConsumerBase* consumer, 813 const QueryRedirectsCallback& callback) { 814 DCHECK(thread_checker_.CalledOnValidThread()); 815 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryRedirectsTo, consumer, 816 new history::QueryRedirectsRequest(callback), to_url); 817} 818 819HistoryService::Handle HistoryService::GetVisibleVisitCountToHost( 820 const GURL& url, 821 CancelableRequestConsumerBase* consumer, 822 const GetVisibleVisitCountToHostCallback& callback) { 823 DCHECK(thread_checker_.CalledOnValidThread()); 824 return Schedule(PRIORITY_UI, &HistoryBackend::GetVisibleVisitCountToHost, 825 consumer, new history::GetVisibleVisitCountToHostRequest(callback), url); 826} 827 828HistoryService::Handle HistoryService::QueryTopURLsAndRedirects( 829 int result_count, 830 CancelableRequestConsumerBase* consumer, 831 const QueryTopURLsAndRedirectsCallback& callback) { 832 DCHECK(thread_checker_.CalledOnValidThread()); 833 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryTopURLsAndRedirects, 834 consumer, new history::QueryTopURLsAndRedirectsRequest(callback), 835 result_count); 836} 837 838HistoryService::Handle HistoryService::QueryMostVisitedURLs( 839 int result_count, 840 int days_back, 841 CancelableRequestConsumerBase* consumer, 842 const QueryMostVisitedURLsCallback& callback) { 843 DCHECK(thread_checker_.CalledOnValidThread()); 844 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryMostVisitedURLs, 845 consumer, 846 new history::QueryMostVisitedURLsRequest(callback), 847 result_count, days_back); 848} 849 850HistoryService::Handle HistoryService::QueryFilteredURLs( 851 int result_count, 852 const history::VisitFilter& filter, 853 bool extended_info, 854 CancelableRequestConsumerBase* consumer, 855 const QueryFilteredURLsCallback& callback) { 856 DCHECK(thread_checker_.CalledOnValidThread()); 857 return Schedule(PRIORITY_NORMAL, 858 &HistoryBackend::QueryFilteredURLs, 859 consumer, 860 new history::QueryFilteredURLsRequest(callback), 861 result_count, filter, extended_info); 862} 863 864void HistoryService::Observe(int type, 865 const content::NotificationSource& source, 866 const content::NotificationDetails& details) { 867 DCHECK(thread_checker_.CalledOnValidThread()); 868 if (!thread_) 869 return; 870 871 switch (type) { 872 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: { 873 // Update the visited link system for deleted URLs. We will update the 874 // visited link system for added URLs as soon as we get the add 875 // notification (we don't have to wait for the backend, which allows us to 876 // be faster to update the state). 877 // 878 // For deleted URLs, we don't typically know what will be deleted since 879 // delete notifications are by time. We would also like to be more 880 // respectful of privacy and never tell the user something is gone when it 881 // isn't. Therefore, we update the delete URLs after the fact. 882 if (visitedlink_master_) { 883 content::Details<history::URLsDeletedDetails> deleted_details(details); 884 885 if (deleted_details->all_history) { 886 visitedlink_master_->DeleteAllURLs(); 887 } else { 888 URLIteratorFromURLRows iterator(deleted_details->rows); 889 visitedlink_master_->DeleteURLs(&iterator); 890 } 891 } 892 break; 893 } 894 895 case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED: 896 DeleteAllSearchTermsForKeyword( 897 *(content::Details<TemplateURLID>(details).ptr())); 898 break; 899 900 default: 901 NOTREACHED(); 902 } 903} 904 905void HistoryService::RebuildTable( 906 const scoped_refptr<URLEnumerator>& enumerator) { 907 DCHECK(thread_checker_.CalledOnValidThread()); 908 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator); 909} 910 911bool HistoryService::Init(const base::FilePath& history_dir, 912 BookmarkService* bookmark_service, 913 bool no_db) { 914 DCHECK(thread_checker_.CalledOnValidThread()); 915 if (!thread_->Start()) { 916 Cleanup(); 917 return false; 918 } 919 920 history_dir_ = history_dir; 921 bookmark_service_ = bookmark_service; 922 no_db_ = no_db; 923 924 if (profile_) { 925 std::string languages = 926 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); 927 in_memory_url_index_.reset( 928 new history::InMemoryURLIndex(profile_, history_dir_, languages)); 929 in_memory_url_index_->Init(); 930 } 931 932 // Create the history backend. 933 scoped_refptr<HistoryBackend> backend( 934 new HistoryBackend(history_dir_, 935 new BackendDelegate( 936 weak_ptr_factory_.GetWeakPtr(), 937 base::ThreadTaskRunnerHandle::Get(), 938 profile_), 939 bookmark_service_)); 940 history_backend_.swap(backend); 941 942 // There may not be a profile when unit testing. 943 std::string languages; 944 if (profile_) { 945 PrefService* prefs = profile_->GetPrefs(); 946 languages = prefs->GetString(prefs::kAcceptLanguages); 947 } 948 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init, languages, no_db_); 949 950 if (visitedlink_master_) { 951 bool result = visitedlink_master_->Init(); 952 DCHECK(result); 953 } 954 955 return true; 956} 957 958void HistoryService::ScheduleAutocomplete(HistoryURLProvider* provider, 959 HistoryURLProviderParams* params) { 960 DCHECK(thread_checker_.CalledOnValidThread()); 961 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::ScheduleAutocomplete, 962 scoped_refptr<HistoryURLProvider>(provider), params); 963} 964 965void HistoryService::ScheduleTask(SchedulePriority priority, 966 const base::Closure& task) { 967 DCHECK(thread_checker_.CalledOnValidThread()); 968 CHECK(thread_); 969 CHECK(thread_->message_loop()); 970 // TODO(brettw): Do prioritization. 971 thread_->message_loop()->PostTask(FROM_HERE, task); 972} 973 974// static 975bool HistoryService::CanAddURL(const GURL& url) { 976 if (!url.is_valid()) 977 return false; 978 979 // TODO: We should allow kChromeUIScheme URLs if they have been explicitly 980 // typed. Right now, however, these are marked as typed even when triggered 981 // by a shortcut or menu action. 982 if (url.SchemeIs(content::kJavaScriptScheme) || 983 url.SchemeIs(content::kChromeDevToolsScheme) || 984 url.SchemeIs(content::kChromeUIScheme) || 985 url.SchemeIs(content::kViewSourceScheme) || 986 url.SchemeIs(chrome::kChromeNativeScheme) || 987 url.SchemeIs(chrome::kChromeSearchScheme) || 988 url.SchemeIs(chrome::kDomDistillerScheme)) 989 return false; 990 991 // Allow all about: and chrome: URLs except about:blank, since the user may 992 // like to see "chrome://memory/", etc. in their history and autocomplete. 993 if (url == GURL(content::kAboutBlankURL)) 994 return false; 995 996 return true; 997} 998 999base::WeakPtr<HistoryService> HistoryService::AsWeakPtr() { 1000 DCHECK(thread_checker_.CalledOnValidThread()); 1001 return weak_ptr_factory_.GetWeakPtr(); 1002} 1003 1004syncer::SyncMergeResult HistoryService::MergeDataAndStartSyncing( 1005 syncer::ModelType type, 1006 const syncer::SyncDataList& initial_sync_data, 1007 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 1008 scoped_ptr<syncer::SyncErrorFactory> error_handler) { 1009 DCHECK(thread_checker_.CalledOnValidThread()); 1010 DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES); 1011 delete_directive_handler_.Start(this, initial_sync_data, 1012 sync_processor.Pass()); 1013 return syncer::SyncMergeResult(type); 1014} 1015 1016void HistoryService::StopSyncing(syncer::ModelType type) { 1017 DCHECK(thread_checker_.CalledOnValidThread()); 1018 DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES); 1019 delete_directive_handler_.Stop(); 1020} 1021 1022syncer::SyncDataList HistoryService::GetAllSyncData( 1023 syncer::ModelType type) const { 1024 DCHECK(thread_checker_.CalledOnValidThread()); 1025 DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES); 1026 // TODO(akalin): Keep track of existing delete directives. 1027 return syncer::SyncDataList(); 1028} 1029 1030syncer::SyncError HistoryService::ProcessSyncChanges( 1031 const tracked_objects::Location& from_here, 1032 const syncer::SyncChangeList& change_list) { 1033 delete_directive_handler_.ProcessSyncChanges(this, change_list); 1034 return syncer::SyncError(); 1035} 1036 1037syncer::SyncError HistoryService::ProcessLocalDeleteDirective( 1038 const sync_pb::HistoryDeleteDirectiveSpecifics& delete_directive) { 1039 DCHECK(thread_checker_.CalledOnValidThread()); 1040 return delete_directive_handler_.ProcessLocalDeleteDirective( 1041 delete_directive); 1042} 1043 1044void HistoryService::SetInMemoryBackend( 1045 scoped_ptr<history::InMemoryHistoryBackend> mem_backend) { 1046 DCHECK(thread_checker_.CalledOnValidThread()); 1047 DCHECK(!in_memory_backend_) << "Setting mem DB twice"; 1048 in_memory_backend_.reset(mem_backend.release()); 1049 1050 // The database requires additional initialization once we own it. 1051 in_memory_backend_->AttachToHistoryService(profile_); 1052} 1053 1054void HistoryService::NotifyProfileError(sql::InitStatus init_status) { 1055 DCHECK(thread_checker_.CalledOnValidThread()); 1056 ShowProfileErrorDialog( 1057 PROFILE_ERROR_HISTORY, 1058 (init_status == sql::INIT_FAILURE) ? 1059 IDS_COULDNT_OPEN_PROFILE_ERROR : IDS_PROFILE_TOO_NEW_ERROR); 1060} 1061 1062void HistoryService::DeleteURL(const GURL& url) { 1063 DCHECK(thread_checker_.CalledOnValidThread()); 1064 // We will update the visited links when we observe the delete notifications. 1065 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURL, url); 1066} 1067 1068void HistoryService::DeleteURLsForTest(const std::vector<GURL>& urls) { 1069 DCHECK(thread_checker_.CalledOnValidThread()); 1070 // We will update the visited links when we observe the delete 1071 // notifications. 1072 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURLs, urls); 1073} 1074 1075void HistoryService::ExpireHistoryBetween( 1076 const std::set<GURL>& restrict_urls, 1077 Time begin_time, 1078 Time end_time, 1079 const base::Closure& callback, 1080 base::CancelableTaskTracker* tracker) { 1081 DCHECK(thread_); 1082 DCHECK(thread_checker_.CalledOnValidThread()); 1083 DCHECK(history_backend_.get()); 1084 tracker->PostTaskAndReply(thread_->message_loop_proxy().get(), 1085 FROM_HERE, 1086 base::Bind(&HistoryBackend::ExpireHistoryBetween, 1087 history_backend_, 1088 restrict_urls, 1089 begin_time, 1090 end_time), 1091 callback); 1092} 1093 1094void HistoryService::ExpireHistory( 1095 const std::vector<history::ExpireHistoryArgs>& expire_list, 1096 const base::Closure& callback, 1097 base::CancelableTaskTracker* tracker) { 1098 DCHECK(thread_); 1099 DCHECK(thread_checker_.CalledOnValidThread()); 1100 DCHECK(history_backend_.get()); 1101 tracker->PostTaskAndReply( 1102 thread_->message_loop_proxy().get(), 1103 FROM_HERE, 1104 base::Bind(&HistoryBackend::ExpireHistory, history_backend_, expire_list), 1105 callback); 1106} 1107 1108void HistoryService::ExpireLocalAndRemoteHistoryBetween( 1109 const std::set<GURL>& restrict_urls, 1110 Time begin_time, 1111 Time end_time, 1112 const base::Closure& callback, 1113 base::CancelableTaskTracker* tracker) { 1114 // TODO(dubroy): This should be factored out into a separate class that 1115 // dispatches deletions to the proper places. 1116 1117 history::WebHistoryService* web_history = 1118 WebHistoryServiceFactory::GetForProfile(profile_); 1119 if (web_history) { 1120 // TODO(dubroy): This API does not yet support deletion of specific URLs. 1121 DCHECK(restrict_urls.empty()); 1122 1123 delete_directive_handler_.CreateDeleteDirectives( 1124 std::set<int64>(), begin_time, end_time); 1125 1126 // Attempt online deletion from the history server, but ignore the result. 1127 // Deletion directives ensure that the results will eventually be deleted. 1128 // 1129 // TODO(davidben): |callback| should not run until this operation completes 1130 // too. 1131 web_history->ExpireHistoryBetween( 1132 restrict_urls, begin_time, end_time, 1133 base::Bind(&ExpireWebHistoryComplete)); 1134 } 1135 ExpireHistoryBetween(restrict_urls, begin_time, end_time, callback, tracker); 1136} 1137 1138void HistoryService::BroadcastNotificationsHelper( 1139 int type, 1140 scoped_ptr<history::HistoryDetails> details) { 1141 DCHECK(thread_checker_.CalledOnValidThread()); 1142 // TODO(evanm): this is currently necessitated by generate_profile, which 1143 // runs without a browser process. generate_profile should really create 1144 // a browser process, at which point this check can then be nuked. 1145 if (!g_browser_process) 1146 return; 1147 1148 if (!thread_) 1149 return; 1150 1151 // The source of all of our notifications is the profile. Note that this 1152 // pointer is NULL in unit tests. 1153 content::Source<Profile> source(profile_); 1154 1155 // The details object just contains the pointer to the object that the 1156 // backend has allocated for us. The receiver of the notification will cast 1157 // this to the proper type. 1158 content::Details<history::HistoryDetails> det(details.get()); 1159 1160 content::NotificationService::current()->Notify(type, source, det); 1161} 1162 1163void HistoryService::OnDBLoaded() { 1164 DCHECK(thread_checker_.CalledOnValidThread()); 1165 backend_loaded_ = true; 1166 content::NotificationService::current()->Notify( 1167 chrome::NOTIFICATION_HISTORY_LOADED, 1168 content::Source<Profile>(profile_), 1169 content::Details<HistoryService>(this)); 1170} 1171 1172bool HistoryService::GetRowForURL(const GURL& url, history::URLRow* url_row) { 1173 DCHECK(thread_checker_.CalledOnValidThread()); 1174 history::URLDatabase* db = InMemoryDatabase(); 1175 return db && (db->GetRowForURL(url, url_row) != 0); 1176} 1177 1178void HistoryService::AddVisitDatabaseObserver( 1179 history::VisitDatabaseObserver* observer) { 1180 DCHECK(thread_checker_.CalledOnValidThread()); 1181 visit_database_observers_.AddObserver(observer); 1182} 1183 1184void HistoryService::RemoveVisitDatabaseObserver( 1185 history::VisitDatabaseObserver* observer) { 1186 DCHECK(thread_checker_.CalledOnValidThread()); 1187 visit_database_observers_.RemoveObserver(observer); 1188} 1189 1190void HistoryService::NotifyVisitDBObserversOnAddVisit( 1191 const history::BriefVisitInfo& info) { 1192 DCHECK(thread_checker_.CalledOnValidThread()); 1193 FOR_EACH_OBSERVER(history::VisitDatabaseObserver, visit_database_observers_, 1194 OnAddVisit(info)); 1195} 1196