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