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