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