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