1 // Copyright (c) 2011 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 // -> TextDatabaseManager 18 // -> SQLite connection to one month's data 19 // -> SQLite connection to one month's data 20 // ... 21 // -> ThumbnailDatabase 22 // -> SQLite connection to Thumbnails 23 // (and favicons) 24 25 #include "chrome/browser/history/history.h" 26 27 #include "base/callback.h" 28 #include "base/memory/ref_counted.h" 29 #include "base/message_loop.h" 30 #include "base/path_service.h" 31 #include "base/string_util.h" 32 #include "base/task.h" 33 #include "chrome/browser/autocomplete/history_url_provider.h" 34 #include "chrome/browser/browser_process.h" 35 #include "chrome/browser/history/download_create_info.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_database.h" 40 #include "chrome/browser/history/in_memory_history_backend.h" 41 #include "chrome/browser/history/top_sites.h" 42 #include "chrome/browser/prefs/pref_service.h" 43 #include "chrome/browser/profiles/profile.h" 44 #include "chrome/browser/ui/profile_error_dialog.h" 45 #include "chrome/browser/visitedlink/visitedlink_master.h" 46 #include "chrome/common/chrome_constants.h" 47 #include "chrome/common/pref_names.h" 48 #include "chrome/common/thumbnail_score.h" 49 #include "chrome/common/url_constants.h" 50 #include "content/browser/browser_thread.h" 51 #include "content/common/notification_service.h" 52 #include "grit/chromium_strings.h" 53 #include "grit/generated_resources.h" 54 #include "third_party/skia/include/core/SkBitmap.h" 55 56 using base::Time; 57 using history::HistoryBackend; 58 59 namespace { 60 61 static const char* kHistoryThreadName = "Chrome_HistoryThread"; 62 63 } // namespace 64 65 // Sends messages from the backend to us on the main thread. This must be a 66 // separate class from the history service so that it can hold a reference to 67 // the history service (otherwise we would have to manually AddRef and 68 // Release when the Backend has a reference to us). 69 class HistoryService::BackendDelegate : public HistoryBackend::Delegate { 70 public: 71 explicit BackendDelegate(HistoryService* history_service) 72 : history_service_(history_service), 73 message_loop_(MessageLoop::current()) { 74 } 75 76 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE { 77 // Send to the history service on the main thread. 78 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(), 79 &HistoryService::NotifyProfileError, init_status)); 80 } 81 82 virtual void SetInMemoryBackend( 83 history::InMemoryHistoryBackend* backend) OVERRIDE { 84 // Send the backend to the history service on the main thread. 85 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(), 86 &HistoryService::SetInMemoryBackend, backend)); 87 } 88 89 virtual void BroadcastNotifications( 90 NotificationType type, 91 history::HistoryDetails* details) OVERRIDE { 92 // Send the notification on the history thread. 93 if (NotificationService::current()) { 94 Details<history::HistoryDetails> det(details); 95 NotificationService::current()->Notify(type, 96 NotificationService::AllSources(), 97 det); 98 } 99 // Send the notification to the history service on the main thread. 100 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(), 101 &HistoryService::BroadcastNotifications, type, details)); 102 } 103 104 virtual void DBLoaded() OVERRIDE { 105 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(), 106 &HistoryService::OnDBLoaded)); 107 } 108 109 virtual void StartTopSitesMigration() OVERRIDE { 110 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(), 111 &HistoryService::StartTopSitesMigration)); 112 } 113 114 private: 115 scoped_refptr<HistoryService> history_service_; 116 MessageLoop* message_loop_; 117 }; 118 119 // static 120 const history::StarID HistoryService::kBookmarkBarID = 1; 121 122 // The history thread is intentionally not a BrowserThread because the 123 // sync integration unit tests depend on being able to create more than one 124 // history thread. 125 HistoryService::HistoryService() 126 : thread_(new base::Thread(kHistoryThreadName)), 127 profile_(NULL), 128 backend_loaded_(false), 129 bookmark_service_(NULL), 130 no_db_(false), 131 needs_top_sites_migration_(false) { 132 } 133 134 HistoryService::HistoryService(Profile* profile) 135 : thread_(new base::Thread(kHistoryThreadName)), 136 profile_(profile), 137 backend_loaded_(false), 138 bookmark_service_(NULL), 139 no_db_(false), 140 needs_top_sites_migration_(false) { 141 DCHECK(profile_); 142 registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, 143 Source<Profile>(profile_)); 144 registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, 145 Source<Profile>(profile_)); 146 } 147 148 HistoryService::~HistoryService() { 149 // Shutdown the backend. This does nothing if Cleanup was already invoked. 150 Cleanup(); 151 } 152 153 bool HistoryService::BackendLoaded() { 154 // NOTE: We start the backend loading even though it completes asynchronously 155 // and thus won't affect the return value of this function. This is because 156 // callers of this assume that if the backend isn't yet loaded it will be 157 // soon, so they will either listen for notifications or just retry this call 158 // later. If we've purged the backend, we haven't necessarily restarted it 159 // loading by now, so we need to trigger the load in order to maintain that 160 // expectation. 161 LoadBackendIfNecessary(); 162 return backend_loaded_; 163 } 164 165 void HistoryService::UnloadBackend() { 166 if (!history_backend_) 167 return; // Already unloaded. 168 169 // Get rid of the in-memory backend. 170 in_memory_backend_.reset(); 171 172 // The backend's destructor must run on the history thread since it is not 173 // threadsafe. So this thread must not be the last thread holding a reference 174 // to the backend, or a crash could happen. 175 // 176 // We have a reference to the history backend. There is also an extra 177 // reference held by our delegate installed in the backend, which 178 // HistoryBackend::Closing will release. This means if we scheduled a call 179 // to HistoryBackend::Closing and *then* released our backend reference, there 180 // will be a race between us and the backend's Closing function to see who is 181 // the last holder of a reference. If the backend thread's Closing manages to 182 // run before we release our backend refptr, the last reference will be held 183 // by this thread and the destructor will be called from here. 184 // 185 // Therefore, we create a task to run the Closing operation first. This holds 186 // a reference to the backend. Then we release our reference, then we schedule 187 // the task to run. After the task runs, it will delete its reference from 188 // the history thread, ensuring everything works properly. 189 Task* closing_task = 190 NewRunnableMethod(history_backend_.get(), &HistoryBackend::Closing); 191 history_backend_ = NULL; 192 ScheduleTask(PRIORITY_NORMAL, closing_task); 193 } 194 195 void HistoryService::Cleanup() { 196 if (!thread_) { 197 // We've already cleaned up. 198 return; 199 } 200 201 // Unload the backend. 202 UnloadBackend(); 203 204 // Delete the thread, which joins with the background thread. We defensively 205 // NULL the pointer before deleting it in case somebody tries to use it 206 // during shutdown, but this shouldn't happen. 207 base::Thread* thread = thread_; 208 thread_ = NULL; 209 delete thread; 210 } 211 212 void HistoryService::NotifyRenderProcessHostDestruction(const void* host) { 213 ScheduleAndForget(PRIORITY_NORMAL, 214 &HistoryBackend::NotifyRenderProcessHostDestruction, host); 215 } 216 217 history::URLDatabase* HistoryService::InMemoryDatabase() { 218 // NOTE: See comments in BackendLoaded() as to why we call 219 // LoadBackendIfNecessary() here even though it won't affect the return value 220 // for this call. 221 LoadBackendIfNecessary(); 222 if (in_memory_backend_.get()) 223 return in_memory_backend_->db(); 224 return NULL; 225 } 226 227 history::InMemoryURLIndex* HistoryService::InMemoryIndex() { 228 // NOTE: See comments in BackendLoaded() as to why we call 229 // LoadBackendIfNecessary() here even though it won't affect the return value 230 // for this call. 231 LoadBackendIfNecessary(); 232 if (in_memory_backend_.get()) 233 return in_memory_backend_->InMemoryIndex(); 234 return NULL; 235 } 236 237 void HistoryService::SetSegmentPresentationIndex(int64 segment_id, int index) { 238 ScheduleAndForget(PRIORITY_UI, 239 &HistoryBackend::SetSegmentPresentationIndex, 240 segment_id, index); 241 } 242 243 void HistoryService::SetKeywordSearchTermsForURL(const GURL& url, 244 TemplateURLID keyword_id, 245 const string16& term) { 246 ScheduleAndForget(PRIORITY_UI, 247 &HistoryBackend::SetKeywordSearchTermsForURL, 248 url, keyword_id, term); 249 } 250 251 void HistoryService::DeleteAllSearchTermsForKeyword( 252 TemplateURLID keyword_id) { 253 ScheduleAndForget(PRIORITY_UI, 254 &HistoryBackend::DeleteAllSearchTermsForKeyword, 255 keyword_id); 256 } 257 258 HistoryService::Handle HistoryService::GetMostRecentKeywordSearchTerms( 259 TemplateURLID keyword_id, 260 const string16& prefix, 261 int max_count, 262 CancelableRequestConsumerBase* consumer, 263 GetMostRecentKeywordSearchTermsCallback* callback) { 264 return Schedule(PRIORITY_UI, &HistoryBackend::GetMostRecentKeywordSearchTerms, 265 consumer, 266 new history::GetMostRecentKeywordSearchTermsRequest(callback), 267 keyword_id, prefix, max_count); 268 } 269 270 void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) { 271 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::URLsNoLongerBookmarked, 272 urls); 273 } 274 275 HistoryService::Handle HistoryService::ScheduleDBTask( 276 HistoryDBTask* task, 277 CancelableRequestConsumerBase* consumer) { 278 history::HistoryDBTaskRequest* request = new history::HistoryDBTaskRequest( 279 NewCallback(task, &HistoryDBTask::DoneRunOnMainThread)); 280 request->value = task; // The value is the task to execute. 281 return Schedule(PRIORITY_UI, &HistoryBackend::ProcessDBTask, consumer, 282 request); 283 } 284 285 HistoryService::Handle HistoryService::QuerySegmentUsageSince( 286 CancelableRequestConsumerBase* consumer, 287 const Time from_time, 288 int max_result_count, 289 SegmentQueryCallback* callback) { 290 return Schedule(PRIORITY_UI, &HistoryBackend::QuerySegmentUsage, 291 consumer, new history::QuerySegmentUsageRequest(callback), 292 from_time, max_result_count); 293 } 294 295 void HistoryService::SetOnBackendDestroyTask(Task* task) { 296 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetOnBackendDestroyTask, 297 MessageLoop::current(), task); 298 } 299 300 void HistoryService::AddPage(const GURL& url, 301 const void* id_scope, 302 int32 page_id, 303 const GURL& referrer, 304 PageTransition::Type transition, 305 const history::RedirectList& redirects, 306 history::VisitSource visit_source, 307 bool did_replace_entry) { 308 AddPage(url, Time::Now(), id_scope, page_id, referrer, transition, redirects, 309 visit_source, did_replace_entry); 310 } 311 312 void HistoryService::AddPage(const GURL& url, 313 Time time, 314 const void* id_scope, 315 int32 page_id, 316 const GURL& referrer, 317 PageTransition::Type transition, 318 const history::RedirectList& redirects, 319 history::VisitSource visit_source, 320 bool did_replace_entry) { 321 scoped_refptr<history::HistoryAddPageArgs> request( 322 new history::HistoryAddPageArgs(url, time, id_scope, page_id, referrer, 323 redirects, transition, visit_source, 324 did_replace_entry)); 325 AddPage(*request); 326 } 327 328 void HistoryService::AddPage(const history::HistoryAddPageArgs& add_page_args) { 329 DCHECK(thread_) << "History service being called after cleanup"; 330 331 // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a 332 // large part of history (think iframes for ads) and we never display them in 333 // history UI. We will still add manual subframes, which are ones the user 334 // has clicked on to get. 335 if (!CanAddURL(add_page_args.url)) 336 return; 337 338 // Add link & all redirects to visited link list. 339 VisitedLinkMaster* visited_links; 340 if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) { 341 visited_links->AddURL(add_page_args.url); 342 343 if (!add_page_args.redirects.empty()) { 344 // We should not be asked to add a page in the middle of a redirect chain. 345 DCHECK_EQ(add_page_args.url, 346 add_page_args.redirects[add_page_args.redirects.size() - 1]); 347 348 // We need the !redirects.empty() condition above since size_t is unsigned 349 // and will wrap around when we subtract one from a 0 size. 350 for (size_t i = 0; i < add_page_args.redirects.size() - 1; i++) 351 visited_links->AddURL(add_page_args.redirects[i]); 352 } 353 } 354 355 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::AddPage, 356 scoped_refptr<history::HistoryAddPageArgs>( 357 add_page_args.Clone())); 358 } 359 360 void HistoryService::AddPageNoVisitForBookmark(const GURL& url) { 361 if (!CanAddURL(url)) 362 return; 363 364 ScheduleAndForget(PRIORITY_NORMAL, 365 &HistoryBackend::AddPageNoVisitForBookmark, url); 366 } 367 368 void HistoryService::SetPageTitle(const GURL& url, 369 const string16& title) { 370 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageTitle, url, title); 371 } 372 373 void HistoryService::AddPageWithDetails(const GURL& url, 374 const string16& title, 375 int visit_count, 376 int typed_count, 377 Time last_visit, 378 bool hidden, 379 history::VisitSource visit_source) { 380 // Filter out unwanted URLs. 381 if (!CanAddURL(url)) 382 return; 383 384 // Add to the visited links system. 385 VisitedLinkMaster* visited_links; 386 if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) 387 visited_links->AddURL(url); 388 389 history::URLRow row(url); 390 row.set_title(title); 391 row.set_visit_count(visit_count); 392 row.set_typed_count(typed_count); 393 row.set_last_visit(last_visit); 394 row.set_hidden(hidden); 395 396 std::vector<history::URLRow> rows; 397 rows.push_back(row); 398 399 ScheduleAndForget(PRIORITY_NORMAL, 400 &HistoryBackend::AddPagesWithDetails, rows, visit_source); 401 } 402 403 void HistoryService::AddPagesWithDetails( 404 const std::vector<history::URLRow>& info, 405 history::VisitSource visit_source) { 406 407 // Add to the visited links system. 408 VisitedLinkMaster* visited_links; 409 if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) { 410 std::vector<GURL> urls; 411 urls.reserve(info.size()); 412 for (std::vector<history::URLRow>::const_iterator i = info.begin(); 413 i != info.end(); 414 ++i) 415 urls.push_back(i->url()); 416 417 visited_links->AddURLs(urls); 418 } 419 420 ScheduleAndForget(PRIORITY_NORMAL, 421 &HistoryBackend::AddPagesWithDetails, info, visit_source); 422 } 423 424 void HistoryService::SetPageContents(const GURL& url, 425 const string16& contents) { 426 if (!CanAddURL(url)) 427 return; 428 429 ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents, 430 url, contents); 431 } 432 433 void HistoryService::SetPageThumbnail(const GURL& page_url, 434 const SkBitmap& thumbnail, 435 const ThumbnailScore& score) { 436 if (!CanAddURL(page_url)) 437 return; 438 439 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageThumbnail, 440 page_url, thumbnail, score); 441 } 442 443 HistoryService::Handle HistoryService::GetPageThumbnail( 444 const GURL& page_url, 445 CancelableRequestConsumerBase* consumer, 446 ThumbnailDataCallback* callback) { 447 return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer, 448 new history::GetPageThumbnailRequest(callback), page_url); 449 } 450 451 void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request, 452 const GURL& icon_url, 453 history::IconType icon_type) { 454 Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavicon, NULL, request, 455 icon_url, icon_type); 456 } 457 458 void HistoryService::UpdateFaviconMappingAndFetch( 459 FaviconService::GetFaviconRequest* request, 460 const GURL& page_url, 461 const GURL& icon_url, 462 history::IconType icon_type) { 463 Schedule(PRIORITY_NORMAL, &HistoryBackend::UpdateFaviconMappingAndFetch, NULL, 464 request, page_url, icon_url, history::FAVICON); 465 } 466 467 void HistoryService::GetFaviconForURL( 468 FaviconService::GetFaviconRequest* request, 469 const GURL& page_url, 470 int icon_types) { 471 Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFaviconForURL, NULL, request, 472 page_url, icon_types); 473 } 474 475 void HistoryService::SetFavicon(const GURL& page_url, 476 const GURL& icon_url, 477 const std::vector<unsigned char>& image_data, 478 history::IconType icon_type) { 479 if (!CanAddURL(page_url)) 480 return; 481 482 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavicon, 483 page_url, icon_url, 484 scoped_refptr<RefCountedMemory>(new RefCountedBytes(image_data)), 485 icon_type); 486 } 487 488 void HistoryService::SetFaviconOutOfDateForPage(const GURL& page_url) { 489 ScheduleAndForget(PRIORITY_NORMAL, 490 &HistoryBackend::SetFaviconOutOfDateForPage, page_url); 491 } 492 493 void HistoryService::SetImportedFavicons( 494 const std::vector<history::ImportedFaviconUsage>& favicon_usage) { 495 ScheduleAndForget(PRIORITY_NORMAL, 496 &HistoryBackend::SetImportedFavicons, favicon_usage); 497 } 498 499 void HistoryService::IterateURLs(URLEnumerator* enumerator) { 500 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator); 501 } 502 503 HistoryService::Handle HistoryService::QueryURL( 504 const GURL& url, 505 bool want_visits, 506 CancelableRequestConsumerBase* consumer, 507 QueryURLCallback* callback) { 508 return Schedule(PRIORITY_UI, &HistoryBackend::QueryURL, consumer, 509 new history::QueryURLRequest(callback), url, want_visits); 510 } 511 512 // Downloads ------------------------------------------------------------------- 513 514 // Handle creation of a download by creating an entry in the history service's 515 // 'downloads' table. 516 HistoryService::Handle HistoryService::CreateDownload( 517 const DownloadCreateInfo& create_info, 518 CancelableRequestConsumerBase* consumer, 519 HistoryService::DownloadCreateCallback* callback) { 520 return Schedule(PRIORITY_NORMAL, &HistoryBackend::CreateDownload, consumer, 521 new history::DownloadCreateRequest(callback), create_info); 522 } 523 524 // Handle queries for a list of all downloads in the history database's 525 // 'downloads' table. 526 HistoryService::Handle HistoryService::QueryDownloads( 527 CancelableRequestConsumerBase* consumer, 528 DownloadQueryCallback* callback) { 529 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryDownloads, consumer, 530 new history::DownloadQueryRequest(callback)); 531 } 532 533 // Changes all IN_PROGRESS in the database entries to CANCELED. 534 // IN_PROGRESS entries are the corrupted entries, not updated by next function 535 // because of the crash or some other extremal exit. 536 void HistoryService::CleanUpInProgressEntries() { 537 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::CleanUpInProgressEntries); 538 } 539 540 // Handle updates for a particular download. This is a 'fire and forget' 541 // operation, so we don't need to be called back. 542 void HistoryService::UpdateDownload(int64 received_bytes, 543 int32 state, 544 int64 db_handle) { 545 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownload, 546 received_bytes, state, db_handle); 547 } 548 549 void HistoryService::UpdateDownloadPath(const FilePath& path, 550 int64 db_handle) { 551 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownloadPath, 552 path, db_handle); 553 } 554 555 void HistoryService::RemoveDownload(int64 db_handle) { 556 ScheduleAndForget(PRIORITY_NORMAL, 557 &HistoryBackend::RemoveDownload, db_handle); 558 } 559 560 void HistoryService::RemoveDownloadsBetween(Time remove_begin, 561 Time remove_end) { 562 ScheduleAndForget(PRIORITY_NORMAL, 563 &HistoryBackend::RemoveDownloadsBetween, 564 remove_begin, 565 remove_end); 566 } 567 568 HistoryService::Handle HistoryService::QueryHistory( 569 const string16& text_query, 570 const history::QueryOptions& options, 571 CancelableRequestConsumerBase* consumer, 572 QueryHistoryCallback* callback) { 573 return Schedule(PRIORITY_UI, &HistoryBackend::QueryHistory, consumer, 574 new history::QueryHistoryRequest(callback), 575 text_query, options); 576 } 577 578 HistoryService::Handle HistoryService::QueryRedirectsFrom( 579 const GURL& from_url, 580 CancelableRequestConsumerBase* consumer, 581 QueryRedirectsCallback* callback) { 582 return Schedule(PRIORITY_UI, &HistoryBackend::QueryRedirectsFrom, consumer, 583 new history::QueryRedirectsRequest(callback), from_url); 584 } 585 586 HistoryService::Handle HistoryService::QueryRedirectsTo( 587 const GURL& to_url, 588 CancelableRequestConsumerBase* consumer, 589 QueryRedirectsCallback* callback) { 590 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryRedirectsTo, consumer, 591 new history::QueryRedirectsRequest(callback), to_url); 592 } 593 594 HistoryService::Handle HistoryService::GetVisitCountToHost( 595 const GURL& url, 596 CancelableRequestConsumerBase* consumer, 597 GetVisitCountToHostCallback* callback) { 598 return Schedule(PRIORITY_UI, &HistoryBackend::GetVisitCountToHost, consumer, 599 new history::GetVisitCountToHostRequest(callback), url); 600 } 601 602 HistoryService::Handle HistoryService::QueryTopURLsAndRedirects( 603 int result_count, 604 CancelableRequestConsumerBase* consumer, 605 QueryTopURLsAndRedirectsCallback* callback) { 606 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryTopURLsAndRedirects, 607 consumer, new history::QueryTopURLsAndRedirectsRequest(callback), 608 result_count); 609 } 610 611 HistoryService::Handle HistoryService::QueryMostVisitedURLs( 612 int result_count, 613 int days_back, 614 CancelableRequestConsumerBase* consumer, 615 QueryMostVisitedURLsCallback* callback) { 616 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryMostVisitedURLs, 617 consumer, 618 new history::QueryMostVisitedURLsRequest(callback), 619 result_count, days_back); 620 } 621 622 void HistoryService::Observe(NotificationType type, 623 const NotificationSource& source, 624 const NotificationDetails& details) { 625 if (!thread_) 626 return; 627 628 switch (type.value) { 629 case NotificationType::HISTORY_URLS_DELETED: { 630 // Update the visited link system for deleted URLs. We will update the 631 // visited link system for added URLs as soon as we get the add 632 // notification (we don't have to wait for the backend, which allows us to 633 // be faster to update the state). 634 // 635 // For deleted URLs, we don't typically know what will be deleted since 636 // delete notifications are by time. We would also like to be more 637 // respectful of privacy and never tell the user something is gone when it 638 // isn't. Therefore, we update the delete URLs after the fact. 639 if (!profile_) 640 return; // No profile, probably unit testing. 641 Details<history::URLsDeletedDetails> deleted_details(details); 642 VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster(); 643 if (!visited_links) 644 return; // Nobody to update. 645 if (deleted_details->all_history) 646 visited_links->DeleteAllURLs(); 647 else // Delete individual ones. 648 visited_links->DeleteURLs(deleted_details->urls); 649 break; 650 } 651 652 case NotificationType::TEMPLATE_URL_REMOVED: 653 DeleteAllSearchTermsForKeyword(*(Details<TemplateURLID>(details).ptr())); 654 break; 655 656 default: 657 NOTREACHED(); 658 } 659 } 660 661 bool HistoryService::Init(const FilePath& history_dir, 662 BookmarkService* bookmark_service, 663 bool no_db) { 664 if (!thread_->Start()) { 665 Cleanup(); 666 return false; 667 } 668 669 history_dir_ = history_dir; 670 bookmark_service_ = bookmark_service; 671 no_db_ = no_db; 672 673 // Create the history backend. 674 LoadBackendIfNecessary(); 675 return true; 676 } 677 678 void HistoryService::ScheduleAutocomplete(HistoryURLProvider* provider, 679 HistoryURLProviderParams* params) { 680 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::ScheduleAutocomplete, 681 scoped_refptr<HistoryURLProvider>(provider), params); 682 } 683 684 void HistoryService::ScheduleTask(SchedulePriority priority, 685 Task* task) { 686 // TODO(brettw): do prioritization. 687 thread_->message_loop()->PostTask(FROM_HERE, task); 688 } 689 690 // static 691 bool HistoryService::CanAddURL(const GURL& url) { 692 if (!url.is_valid()) 693 return false; 694 695 // TODO: We should allow kChromeUIScheme URLs if they have been explicitly 696 // typed. Right now, however, these are marked as typed even when triggered 697 // by a shortcut or menu action. 698 if (url.SchemeIs(chrome::kJavaScriptScheme) || 699 url.SchemeIs(chrome::kChromeDevToolsScheme) || 700 url.SchemeIs(chrome::kChromeUIScheme) || 701 url.SchemeIs(chrome::kViewSourceScheme) || 702 url.SchemeIs(chrome::kChromeInternalScheme)) 703 return false; 704 705 if (url.SchemeIs(chrome::kAboutScheme)) { 706 if (LowerCaseEqualsASCII(url.path(), "blank")) 707 return false; 708 // We allow all other about URLs since the user may like to see things 709 // like "about:memory" or "about:histograms" in their history and 710 // autocomplete. 711 } 712 713 return true; 714 } 715 716 void HistoryService::SetInMemoryBackend( 717 history::InMemoryHistoryBackend* mem_backend) { 718 DCHECK(!in_memory_backend_.get()) << "Setting mem DB twice"; 719 in_memory_backend_.reset(mem_backend); 720 721 // The database requires additional initialization once we own it. 722 in_memory_backend_->AttachToHistoryService(profile_); 723 } 724 725 void HistoryService::NotifyProfileError(sql::InitStatus init_status) { 726 ShowProfileErrorDialog( 727 (init_status == sql::INIT_FAILURE) ? 728 IDS_COULDNT_OPEN_PROFILE_ERROR : IDS_PROFILE_TOO_NEW_ERROR); 729 } 730 731 void HistoryService::DeleteURL(const GURL& url) { 732 // We will update the visited links when we observe the delete notifications. 733 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURL, url); 734 } 735 736 void HistoryService::ExpireHistoryBetween( 737 const std::set<GURL>& restrict_urls, 738 Time begin_time, Time end_time, 739 CancelableRequestConsumerBase* consumer, 740 ExpireHistoryCallback* callback) { 741 742 // We will update the visited links when we observe the delete notifications. 743 Schedule(PRIORITY_UI, &HistoryBackend::ExpireHistoryBetween, consumer, 744 new history::ExpireHistoryRequest(callback), 745 restrict_urls, begin_time, end_time); 746 } 747 748 void HistoryService::BroadcastNotifications( 749 NotificationType type, 750 history::HistoryDetails* details_deleted) { 751 // We take ownership of the passed-in pointer and delete it. It was made for 752 // us on another thread, so the caller doesn't know when we will handle it. 753 scoped_ptr<history::HistoryDetails> details(details_deleted); 754 // TODO(evanm): this is currently necessitated by generate_profile, which 755 // runs without a browser process. generate_profile should really create 756 // a browser process, at which point this check can then be nuked. 757 if (!g_browser_process) 758 return; 759 760 if (!thread_) 761 return; 762 763 // The source of all of our notifications is the profile. Note that this 764 // pointer is NULL in unit tests. 765 Source<Profile> source(profile_); 766 767 // The details object just contains the pointer to the object that the 768 // backend has allocated for us. The receiver of the notification will cast 769 // this to the proper type. 770 Details<history::HistoryDetails> det(details_deleted); 771 772 NotificationService::current()->Notify(type, source, det); 773 } 774 775 void HistoryService::LoadBackendIfNecessary() { 776 if (!thread_ || history_backend_) 777 return; // Failed to init, or already started loading. 778 779 scoped_refptr<HistoryBackend> backend( 780 new HistoryBackend(history_dir_, 781 new BackendDelegate(this), 782 bookmark_service_)); 783 history_backend_.swap(backend); 784 785 // There may not be a profile when unit testing. 786 std::string languages; 787 if (profile_) { 788 PrefService* prefs = profile_->GetPrefs(); 789 languages = prefs->GetString(prefs::kAcceptLanguages); 790 } 791 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init, languages, no_db_); 792 } 793 794 void HistoryService::OnDBLoaded() { 795 backend_loaded_ = true; 796 NotificationService::current()->Notify(NotificationType::HISTORY_LOADED, 797 Source<Profile>(profile_), 798 Details<HistoryService>(this)); 799 if (thread_ && profile_) { 800 // We don't want to force creation of TopSites. 801 history::TopSites* ts = profile_->GetTopSitesWithoutCreating(); 802 if (ts) 803 ts->HistoryLoaded(); 804 } 805 } 806 807 void HistoryService::StartTopSitesMigration() { 808 needs_top_sites_migration_ = true; 809 if (thread_ && profile_) { 810 // We don't want to force creation of TopSites. 811 history::TopSites* ts = profile_->GetTopSitesWithoutCreating(); 812 if (ts) 813 ts->MigrateFromHistory(); 814 } 815 } 816 817 void HistoryService::OnTopSitesReady() { 818 ScheduleAndForget(PRIORITY_NORMAL, 819 &HistoryBackend::MigrateThumbnailsDatabase); 820 } 821