blob: 4174e5539f6a0902ec628bb989ce82eeac654615 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The history system runs on a background thread so that potentially slow
// database operations don't delay the browser. This backend processing is
// represented by HistoryBackend. The HistoryService's job is to dispatch to
// that thread.
//
// Main thread History thread
// ----------- --------------
// HistoryService <----------------> HistoryBackend
// -> HistoryDatabase
// -> SQLite connection to History
// -> ThumbnailDatabase
// -> SQLite connection to Thumbnails
// (and favicons)
#include "components/history/core/browser/history_service.h"
#include <utility>
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/history/core/browser/download_row.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_backend_client.h"
#include "components/history/core/browser/history_client.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/in_memory_database.h"
#include "components/history/core/browser/in_memory_history_backend.h"
#include "components/history/core/browser/keyword_search_term.h"
#include "components/history/core/browser/visit_database.h"
#include "components/history/core/browser/visit_delegate.h"
#include "components/history/core/browser/web_history_service.h"
#include "components/history/core/common/thumbnail_score.h"
#include "components/sync/model/sync_error_factory.h"
#include "components/sync/model_impl/proxy_model_type_controller_delegate.h"
#include "components/variations/variations_associated_data.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/page_transition_types.h"
#if defined(OS_IOS)
#include "base/critical_closure.h"
#endif
using base::Time;
namespace history {
namespace {
static const char* kHistoryThreadName = "Chrome_HistoryThread";
void RunWithFaviconResults(
const favicon_base::FaviconResultsCallback& callback,
std::vector<favicon_base::FaviconRawBitmapResult>* bitmap_results) {
TRACE_EVENT0("browser", "RunWithFaviconResults");
callback.Run(*bitmap_results);
}
void RunWithFaviconResult(
const favicon_base::FaviconRawBitmapCallback& callback,
favicon_base::FaviconRawBitmapResult* bitmap_result) {
callback.Run(*bitmap_result);
}
void RunWithQueryURLResult(HistoryService::QueryURLCallback callback,
const QueryURLResult* result) {
std::move(callback).Run(result->success, result->row, result->visits);
}
void RunWithVisibleVisitCountToHostResult(
const HistoryService::GetVisibleVisitCountToHostCallback& callback,
const VisibleVisitCountToHostResult* result) {
callback.Run(result->success, result->count, result->first_visit);
}
// Callback from WebHistoryService::ExpireWebHistory().
void ExpireWebHistoryComplete(bool success) {
// Ignore the result.
//
// TODO(davidben): ExpireLocalAndRemoteHistoryBetween callback should not fire
// until this completes.
}
// Used for debugging 807009. This keeps a static of the number of instances of
// AddPageMemoryDebuggingTask that have been created but not destroyed. This
// count corresponds to the number of calls to AddPage() that have not yet been
// processed by the HistoryBackend.
class AddPageMemoryDebuggingTask {
public:
// |real_task| is the task to actually call to HistoryBackend.
explicit AddPageMemoryDebuggingTask(base::OnceClosure real_task)
: real_task_(std::move(real_task)) {
base::AutoLock auto_lock(GetLock());
++live_count_;
}
~AddPageMemoryDebuggingTask() {
base::AutoLock auto_lock(GetLock());
DCHECK_GT(live_count_, 0);
--live_count_;
}
// Called on the background/db-thread to run the closure supplied to the
// constructor. The instance is deleted right after this call.
void RunOnDbThread() { std::move(real_task_).Run(); }
static int live_count() {
base::AutoLock auto_lock(GetLock());
return live_count_;
}
private:
static base::Lock& GetLock() {
static base::NoDestructor<base::Lock> lock;
return *lock;
}
// The number of instances waiting to run.
static int live_count_; // Guarded by getLock()
base::OnceClosure real_task_;
DISALLOW_COPY_AND_ASSIGN(AddPageMemoryDebuggingTask);
};
// static
int AddPageMemoryDebuggingTask::live_count_ = 0;
class HistoryMemoryDumpProvider : public base::trace_event::MemoryDumpProvider {
public:
static HistoryMemoryDumpProvider* Get() {
if (!instance_)
instance_ = new HistoryMemoryDumpProvider();
return instance_;
}
// base::trace_event::MemoryDumpProvider:
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override {
// NOTE: this may be called on any thread, see constructor.
base::trace_event::MemoryAllocatorDump* dump =
pmd->CreateAllocatorDump("history/history_service");
dump->AddScalar("pending_add_pages",
base::trace_event::MemoryAllocatorDump::kUnitsObjects,
AddPageMemoryDebuggingTask::live_count());
return true;
}
private:
HistoryMemoryDumpProvider() {
// NOTE: the nullptr arg means OnMemoryDump() may be called from any
// thread.
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "HistoryService", nullptr);
}
~HistoryMemoryDumpProvider() override {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
}
static HistoryMemoryDumpProvider* instance_;
DISALLOW_COPY_AND_ASSIGN(HistoryMemoryDumpProvider);
};
// static
HistoryMemoryDumpProvider* HistoryMemoryDumpProvider::instance_ = nullptr;
} // namespace
// Sends messages from the backend to us on the main thread. This must be a
// separate class from the history service so that it can hold a reference to
// the history service (otherwise we would have to manually AddRef and
// Release when the Backend has a reference to us).
class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
public:
BackendDelegate(
const base::WeakPtr<HistoryService>& history_service,
const scoped_refptr<base::SequencedTaskRunner>& service_task_runner)
: history_service_(history_service),
service_task_runner_(service_task_runner) {}
void NotifyProfileError(sql::InitStatus init_status,
const std::string& diagnostics) override {
// Send to the history service on the main thread.
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HistoryService::NotifyProfileError,
history_service_, init_status, diagnostics));
}
void SetInMemoryBackend(
std::unique_ptr<InMemoryHistoryBackend> backend) override {
// Send the backend to the history service on the main thread.
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HistoryService::SetInMemoryBackend,
history_service_, std::move(backend)));
}
void NotifyFaviconsChanged(const std::set<GURL>& page_urls,
const GURL& icon_url) override {
// Send the notification to the history service on the main thread.
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HistoryService::NotifyFaviconsChanged,
history_service_, page_urls, icon_url));
}
void NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
const RedirectList& redirects,
base::Time visit_time) override {
service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HistoryService::NotifyURLVisited, history_service_,
transition, row, redirects, visit_time));
}
void NotifyURLsModified(const URLRows& changed_urls) override {
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HistoryService::NotifyURLsModified,
history_service_, changed_urls));
}
void NotifyURLsDeleted(DeletionInfo deletion_info) override {
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&HistoryService::NotifyURLsDeleted,
history_service_, std::move(deletion_info)));
}
void NotifyKeywordSearchTermUpdated(const URLRow& row,
KeywordID keyword_id,
const base::string16& term) override {
service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HistoryService::NotifyKeywordSearchTermUpdated,
history_service_, row, keyword_id, term));
}
void NotifyKeywordSearchTermDeleted(URLID url_id) override {
service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HistoryService::NotifyKeywordSearchTermDeleted,
history_service_, url_id));
}
void DBLoaded() override {
service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&HistoryService::OnDBLoaded, history_service_));
}
private:
const base::WeakPtr<HistoryService> history_service_;
const scoped_refptr<base::SequencedTaskRunner> service_task_runner_;
};
HistoryService::HistoryService() : HistoryService(nullptr, nullptr) {}
HistoryService::HistoryService(std::unique_ptr<HistoryClient> history_client,
std::unique_ptr<VisitDelegate> visit_delegate)
: thread_(variations::GetVariationParamValue("BrowserScheduler",
"RedirectHistoryService") ==
"true"
? nullptr
: new base::Thread(kHistoryThreadName)),
history_client_(std::move(history_client)),
visit_delegate_(std::move(visit_delegate)),
backend_loaded_(false),
weak_ptr_factory_(this) {
// Make sure the HistoryMemoryDumpProvider is created and registered.
HistoryMemoryDumpProvider::Get();
}
HistoryService::~HistoryService() {
DCHECK(thread_checker_.CalledOnValidThread());
// Shutdown the backend. This does nothing if Cleanup was already invoked.
Cleanup();
}
bool HistoryService::BackendLoaded() {
DCHECK(thread_checker_.CalledOnValidThread());
return backend_loaded_;
}
#if defined(OS_IOS)
void HistoryService::HandleBackgrounding() {
if (!backend_task_runner_ || !history_backend_.get())
return;
ScheduleTask(PRIORITY_NORMAL,
base::MakeCriticalClosure(base::Bind(
&HistoryBackend::PersistState, history_backend_.get())));
}
#endif
void HistoryService::ClearCachedDataForContextID(ContextID context_id) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::ClearCachedDataForContextID,
history_backend_, context_id));
}
URLDatabase* HistoryService::InMemoryDatabase() {
DCHECK(thread_checker_.CalledOnValidThread());
return in_memory_backend_ ? in_memory_backend_->db() : nullptr;
}
void HistoryService::Shutdown() {
DCHECK(thread_checker_.CalledOnValidThread());
Cleanup();
}
void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
KeywordID keyword_id,
const base::string16& term) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_UI,
base::BindOnce(&HistoryBackend::SetKeywordSearchTermsForURL,
history_backend_, url, keyword_id, term));
}
void HistoryService::DeleteAllSearchTermsForKeyword(KeywordID keyword_id) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (in_memory_backend_)
in_memory_backend_->DeleteAllSearchTermsForKeyword(keyword_id);
ScheduleTask(PRIORITY_UI,
base::BindOnce(&HistoryBackend::DeleteAllSearchTermsForKeyword,
history_backend_, keyword_id));
}
void HistoryService::DeleteKeywordSearchTermForURL(const GURL& url) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_UI,
base::BindOnce(&HistoryBackend::DeleteKeywordSearchTermForURL,
history_backend_, url));
}
void HistoryService::DeleteMatchingURLsForKeyword(KeywordID keyword_id,
const base::string16& term) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_UI,
base::BindOnce(&HistoryBackend::DeleteMatchingURLsForKeyword,
history_backend_, keyword_id, term));
}
void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::URLsNoLongerBookmarked,
history_backend_, urls));
}
void HistoryService::AddObserver(HistoryServiceObserver* observer) {
DCHECK(thread_checker_.CalledOnValidThread());
observers_.AddObserver(observer);
}
void HistoryService::RemoveObserver(HistoryServiceObserver* observer) {
DCHECK(thread_checker_.CalledOnValidThread());
observers_.RemoveObserver(observer);
}
base::CancelableTaskTracker::TaskId HistoryService::ScheduleDBTask(
const base::Location& from_here,
std::unique_ptr<HistoryDBTask> task,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
base::CancelableTaskTracker::IsCanceledCallback is_canceled;
base::CancelableTaskTracker::TaskId task_id =
tracker->NewTrackedTaskId(&is_canceled);
// Use base::ThreadTaskRunnerHandler::Get() to get a task runner for
// the current message loop so that we can forward the call to the method
// HistoryDBTask::DoneRunOnMainThread() in the correct thread.
backend_task_runner_->PostTask(
from_here,
base::BindOnce(&HistoryBackend::ProcessDBTask, history_backend_,
std::move(task), base::ThreadTaskRunnerHandle::Get(),
is_canceled));
return task_id;
}
void HistoryService::FlushForTest(const base::Closure& flushed) {
backend_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(), flushed);
}
void HistoryService::SetOnBackendDestroyTask(const base::Closure& task) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(
PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::SetOnBackendDestroyTask, history_backend_,
base::ThreadTaskRunnerHandle::Get(), task));
}
void HistoryService::TopHosts(size_t num_hosts,
const TopHostsCallback& callback) const {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::TopHosts, history_backend_, num_hosts),
callback);
}
void HistoryService::GetCountsAndLastVisitForOriginsForTesting(
const std::set<GURL>& origins,
const GetCountsAndLastVisitForOriginsCallback& callback) const {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::GetCountsAndLastVisitForOrigins,
history_backend_, origins),
callback);
}
void HistoryService::HostRankIfAvailable(
const GURL& url,
const base::Callback<void(int)>& callback) const {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::HostRankIfAvailable, history_backend_, url),
callback);
}
void HistoryService::AddPage(const GURL& url,
Time time,
ContextID context_id,
int nav_entry_id,
const GURL& referrer,
const RedirectList& redirects,
ui::PageTransition transition,
VisitSource visit_source,
bool did_replace_entry) {
DCHECK(thread_checker_.CalledOnValidThread());
AddPage(HistoryAddPageArgs(url, time, context_id, nav_entry_id, referrer,
redirects, transition,
!ui::PageTransitionIsMainFrame(transition),
visit_source, did_replace_entry, true));
}
void HistoryService::AddPage(const GURL& url,
base::Time time,
VisitSource visit_source) {
DCHECK(thread_checker_.CalledOnValidThread());
AddPage(HistoryAddPageArgs(url, time, nullptr, 0, GURL(), RedirectList(),
ui::PAGE_TRANSITION_LINK, false, visit_source,
false, true));
}
void HistoryService::AddPage(const HistoryAddPageArgs& add_page_args) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(add_page_args.url))
return;
// Inform VisitedDelegate of all links and redirects.
if (visit_delegate_) {
if (!add_page_args.redirects.empty()) {
// We should not be asked to add a page in the middle of a redirect chain,
// and thus add_page_args.url should be the last element in the array
// add_page_args.redirects which mean we can use VisitDelegate::AddURLs()
// with the whole array.
DCHECK_EQ(add_page_args.url, add_page_args.redirects.back());
visit_delegate_->AddURLs(add_page_args.redirects);
} else {
visit_delegate_->AddURL(add_page_args.url);
}
}
auto real_task =
base::BindOnce(&HistoryBackend::AddPage, history_backend_, add_page_args);
std::unique_ptr<AddPageMemoryDebuggingTask> debug_task =
std::make_unique<AddPageMemoryDebuggingTask>(std::move(real_task));
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&AddPageMemoryDebuggingTask::RunOnDbThread,
std::move(debug_task)));
}
void HistoryService::AddPageNoVisitForBookmark(const GURL& url,
const base::string16& title) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(url))
return;
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::AddPageNoVisitForBookmark,
history_backend_, url, title));
}
void HistoryService::SetPageTitle(const GURL& url,
const base::string16& title) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL, base::BindOnce(&HistoryBackend::SetPageTitle,
history_backend_, url, title));
}
void HistoryService::UpdateWithPageEndTime(ContextID context_id,
int nav_entry_id,
const GURL& url,
Time end_ts) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(
PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::UpdateWithPageEndTime, history_backend_,
context_id, nav_entry_id, url, end_ts));
}
void HistoryService::AddPageWithDetails(const GURL& url,
const base::string16& title,
int visit_count,
int typed_count,
Time last_visit,
bool hidden,
VisitSource visit_source) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// Filter out unwanted URLs.
if (history_client_ && !history_client_->CanAddURL(url))
return;
// Inform VisitDelegate of the URL.
if (visit_delegate_)
visit_delegate_->AddURL(url);
URLRow row(url);
row.set_title(title);
row.set_visit_count(visit_count);
row.set_typed_count(typed_count);
row.set_last_visit(last_visit);
row.set_hidden(hidden);
URLRows rows;
rows.push_back(row);
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::AddPagesWithDetails,
history_backend_, rows, visit_source));
}
void HistoryService::AddPagesWithDetails(const URLRows& info,
VisitSource visit_source) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// Inform the VisitDelegate of the URLs
if (!info.empty() && visit_delegate_) {
std::vector<GURL> urls;
urls.reserve(info.size());
for (const auto& row : info)
urls.push_back(row.url());
visit_delegate_->AddURLs(urls);
}
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::AddPagesWithDetails,
history_backend_, info, visit_source));
}
base::CancelableTaskTracker::TaskId HistoryService::GetFavicon(
const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes,
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::GetFavicons");
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::GetFavicon, history_backend_, icon_url,
icon_type, desired_sizes, results),
base::BindOnce(&RunWithFaviconResults, callback, base::Owned(results)));
}
base::CancelableTaskTracker::TaskId HistoryService::GetFaviconsForURL(
const GURL& page_url,
const favicon_base::IconTypeSet& icon_types,
const std::vector<int>& desired_sizes,
bool fallback_to_host,
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::GetFaviconsForURL");
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::GetFaviconsForURL, history_backend_,
page_url, icon_types, desired_sizes, fallback_to_host,
results),
base::BindOnce(&RunWithFaviconResults, callback, base::Owned(results)));
}
base::CancelableTaskTracker::TaskId HistoryService::GetLargestFaviconForURL(
const GURL& page_url,
const std::vector<favicon_base::IconTypeSet>& icon_types,
int minimum_size_in_pixels,
const favicon_base::FaviconRawBitmapCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
favicon_base::FaviconRawBitmapResult* result =
new favicon_base::FaviconRawBitmapResult();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::GetLargestFaviconForURL, history_backend_,
page_url, icon_types, minimum_size_in_pixels, result),
base::BindOnce(&RunWithFaviconResult, callback, base::Owned(result)));
}
base::CancelableTaskTracker::TaskId HistoryService::GetFaviconForID(
favicon_base::FaviconID favicon_id,
int desired_size,
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::GetFaviconForID");
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::GetFaviconForID, history_backend_,
favicon_id, desired_size, results),
base::BindOnce(&RunWithFaviconResults, callback, base::Owned(results)));
}
base::CancelableTaskTracker::TaskId
HistoryService::UpdateFaviconMappingsAndFetch(
const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes,
const favicon_base::FaviconResultsCallback& callback,
base::CancelableTaskTracker* tracker) {
TRACE_EVENT0("browser", "HistoryService::UpdateFaviconMappingsAndFetch");
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<favicon_base::FaviconRawBitmapResult>* results =
new std::vector<favicon_base::FaviconRawBitmapResult>();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::UpdateFaviconMappingsAndFetch,
history_backend_, page_urls, icon_url, icon_type,
desired_sizes, results),
base::BindOnce(&RunWithFaviconResults, callback, base::Owned(results)));
}
void HistoryService::DeleteFaviconMappings(
const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type) {
TRACE_EVENT0("browser", "HistoryService::DeleteFaviconMappings");
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::DeleteFaviconMappings,
history_backend_, page_urls, icon_type));
}
void HistoryService::MergeFavicon(
const GURL& page_url,
const GURL& icon_url,
favicon_base::IconType icon_type,
scoped_refptr<base::RefCountedMemory> bitmap_data,
const gfx::Size& pixel_size) {
TRACE_EVENT0("browser", "HistoryService::MergeFavicon");
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(page_url))
return;
ScheduleTask(
PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::MergeFavicon, history_backend_, page_url,
icon_url, icon_type, bitmap_data, pixel_size));
}
void HistoryService::SetFavicons(const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
base::flat_set<GURL> page_urls_to_save;
page_urls_to_save.reserve(page_urls.capacity());
for (const GURL& page_url : page_urls) {
if (!history_client_ || history_client_->CanAddURL(page_url))
page_urls_to_save.insert(page_url);
}
if (page_urls_to_save.empty())
return;
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::SetFavicons, history_backend_,
page_urls_to_save, icon_type, icon_url, bitmaps));
}
void HistoryService::CloneFaviconMappingsForPages(
const GURL& page_url_to_read,
const favicon_base::IconTypeSet& icon_types,
const base::flat_set<GURL>& page_urls_to_write) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::CloneFaviconMappingsForPages,
history_backend_, page_url_to_read, icon_types,
page_urls_to_write));
}
void HistoryService::CanSetOnDemandFavicons(
const GURL& page_url,
favicon_base::IconType icon_type,
base::OnceCallback<void(bool)> callback) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(page_url)) {
std::move(callback).Run(false);
return;
}
PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::CanSetOnDemandFavicons, history_backend_,
page_url, icon_type),
std::move(callback));
}
void HistoryService::SetOnDemandFavicons(
const GURL& page_url,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps,
base::OnceCallback<void(bool)> callback) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_ && !history_client_->CanAddURL(page_url)) {
std::move(callback).Run(false);
return;
}
PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::SetOnDemandFavicons, history_backend_,
page_url, icon_type, icon_url, bitmaps),
std::move(callback));
}
void HistoryService::SetFaviconsOutOfDateForPage(const GURL& page_url) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::SetFaviconsOutOfDateForPage,
history_backend_, page_url));
}
void HistoryService::TouchOnDemandFavicon(const GURL& icon_url) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::TouchOnDemandFavicon,
history_backend_, icon_url));
}
void HistoryService::SetImportedFavicons(
const favicon_base::FaviconUsageDataList& favicon_usage) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::SetImportedFavicons,
history_backend_, favicon_usage));
}
base::CancelableTaskTracker::TaskId HistoryService::QueryURL(
const GURL& url,
bool want_visits,
QueryURLCallback callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
QueryURLResult* query_url_result = new QueryURLResult();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::QueryURL, history_backend_, url,
want_visits, base::Unretained(query_url_result)),
base::BindOnce(&RunWithQueryURLResult, std::move(callback),
base::Owned(query_url_result)));
}
// Statistics ------------------------------------------------------------------
base::CancelableTaskTracker::TaskId HistoryService::GetHistoryCount(
const Time& begin_time,
const Time& end_time,
const GetHistoryCountCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
return tracker->PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::GetHistoryCount, history_backend_, begin_time,
end_time),
callback);
}
void HistoryService::CountUniqueHostsVisitedLastMonth(
const GetHistoryCountCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
tracker->PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindRepeating(&HistoryBackend::CountUniqueHostsVisitedLastMonth,
history_backend_),
callback);
}
// Downloads -------------------------------------------------------------------
// Handle creation of a download by creating an entry in the history service's
// 'downloads' table.
void HistoryService::CreateDownload(
const DownloadRow& create_info,
const HistoryService::DownloadCreateCallback& callback) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::CreateDownload,
history_backend_, create_info),
callback);
}
void HistoryService::GetNextDownloadId(const DownloadIdCallback& callback) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::Bind(&HistoryBackend::GetNextDownloadId, history_backend_),
callback);
}
// Handle queries for a list of all downloads in the history database's
// 'downloads' table.
void HistoryService::QueryDownloads(const DownloadQueryCallback& callback) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<DownloadRow>* rows = new std::vector<DownloadRow>();
std::unique_ptr<std::vector<DownloadRow>> scoped_rows(rows);
// Beware! The first Bind() does not simply |scoped_rows.get()| because
// std::move(scoped_rows) nullifies |scoped_rows|, and compilers do not
// guarantee that the first Bind's arguments are evaluated before the second
// Bind's arguments.
backend_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&HistoryBackend::QueryDownloads, history_backend_, rows),
base::BindOnce(callback, std::move(scoped_rows)));
}
// Handle updates for a particular download. This is a 'fire and forget'
// operation, so we don't need to be called back.
void HistoryService::UpdateDownload(
const DownloadRow& data,
bool should_commit_immediately) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL,
base::BindOnce(&HistoryBackend::UpdateDownload, history_backend_,
data, should_commit_immediately));
}
void HistoryService::RemoveDownloads(const std::set<uint32_t>& ids) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_NORMAL, base::BindOnce(&HistoryBackend::RemoveDownloads,
history_backend_, ids));
}
base::CancelableTaskTracker::TaskId HistoryService::QueryHistory(
const base::string16& text_query,
const QueryOptions& options,
const QueryHistoryCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
QueryResults* query_results = new QueryResults();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::QueryHistory, history_backend_,
text_query, options, base::Unretained(query_results)),
base::BindOnce(callback, base::Owned(query_results)));
}
base::CancelableTaskTracker::TaskId HistoryService::QueryRedirectsFrom(
const GURL& from_url,
const QueryRedirectsCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
RedirectList* result = new RedirectList();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::QueryRedirectsFrom, history_backend_,
from_url, base::Unretained(result)),
base::BindOnce(callback, base::Owned(result)));
}
base::CancelableTaskTracker::TaskId HistoryService::QueryRedirectsTo(
const GURL& to_url,
const QueryRedirectsCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
RedirectList* result = new RedirectList();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::QueryRedirectsTo, history_backend_,
to_url, base::Unretained(result)),
base::BindOnce(callback, base::Owned(result)));
}
base::CancelableTaskTracker::TaskId HistoryService::GetVisibleVisitCountToHost(
const GURL& url,
const GetVisibleVisitCountToHostCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
VisibleVisitCountToHostResult* result = new VisibleVisitCountToHostResult();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::GetVisibleVisitCountToHost,
history_backend_, url, base::Unretained(result)),
base::BindOnce(&RunWithVisibleVisitCountToHostResult, callback,
base::Owned(result)));
}
base::CancelableTaskTracker::TaskId HistoryService::QueryMostVisitedURLs(
int result_count,
int days_back,
const QueryMostVisitedURLsCallback& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
MostVisitedURLList* result = new MostVisitedURLList();
return tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::QueryMostVisitedURLs, history_backend_,
result_count, days_back, base::Unretained(result)),
base::BindOnce(callback, base::Owned(result)));
}
void HistoryService::Cleanup() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!backend_task_runner_) {
// We've already cleaned up.
return;
}
NotifyHistoryServiceBeingDeleted();
weak_ptr_factory_.InvalidateWeakPtrs();
// Inform the HistoryClient that we are shuting down.
if (history_client_)
history_client_->Shutdown();
// Unload the backend.
if (history_backend_) {
// Get rid of the in-memory backend.
in_memory_backend_.reset();
// The backend's destructor must run on the history thread since it is not
// threadsafe. So this thread must not be the last thread holding a
// reference to the backend, or a crash could happen.
//
// We have a reference to the history backend. There is also an extra
// reference held by our delegate installed in the backend, which
// HistoryBackend::Closing will release. This means if we scheduled a call
// to HistoryBackend::Closing and *then* released our backend reference,
// there will be a race between us and the backend's Closing function to see
// who is the last holder of a reference. If the backend thread's Closing
// manages to run before we release our backend refptr, the last reference
// will be held by this thread and the destructor will be called from here.
//
// Therefore, we create a closure to run the Closing operation first. This
// holds a reference to the backend. Then we release our reference, then we
// schedule the task to run. After the task runs, it will delete its
// reference from the history thread, ensuring everything works properly.
//
// TODO(ajwong): Cleanup HistoryBackend lifetime issues.
// See http://crbug.com/99767.
history_backend_->AddRef();
base::Closure closing_task =
base::Bind(&HistoryBackend::Closing, history_backend_);
ScheduleTask(PRIORITY_NORMAL, closing_task);
closing_task.Reset();
HistoryBackend* raw_ptr = history_backend_.get();
history_backend_ = nullptr;
backend_task_runner_->ReleaseSoon(FROM_HERE, raw_ptr);
}
// Clear |backend_task_runner_| to make sure it's not used after Cleanup().
backend_task_runner_ = nullptr;
// Join the background thread, if any.
thread_.reset();
}
bool HistoryService::Init(
bool no_db,
const HistoryDatabaseParams& history_database_params) {
TRACE_EVENT0("browser,startup", "HistoryService::Init")
SCOPED_UMA_HISTOGRAM_TIMER("History.HistoryServiceInitTime");
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!backend_task_runner_);
if (thread_) {
base::Thread::Options options;
options.timer_slack = base::TIMER_SLACK_MAXIMUM;
if (!thread_->StartWithOptions(options)) {
Cleanup();
return false;
}
backend_task_runner_ = thread_->task_runner();
} else {
backend_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::WithBaseSyncPrimitives(),
base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
}
// Create the history backend.
scoped_refptr<HistoryBackend> backend(new HistoryBackend(
new BackendDelegate(weak_ptr_factory_.GetWeakPtr(),
base::ThreadTaskRunnerHandle::Get()),
history_client_ ? history_client_->CreateBackendClient() : nullptr,
backend_task_runner_));
history_backend_.swap(backend);
ScheduleTask(PRIORITY_UI,
base::BindOnce(&HistoryBackend::Init, history_backend_, no_db,
history_database_params));
if (visit_delegate_ && !visit_delegate_->Init(this))
return false;
if (history_client_)
history_client_->OnHistoryServiceCreated(this);
return true;
}
void HistoryService::ScheduleAutocomplete(
const base::Callback<void(HistoryBackend*, URLDatabase*)>& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
ScheduleTask(PRIORITY_UI,
base::BindOnce(&HistoryBackend::ScheduleAutocomplete,
history_backend_, callback));
}
void HistoryService::ScheduleTask(SchedulePriority priority,
base::OnceClosure task) {
DCHECK(thread_checker_.CalledOnValidThread());
CHECK(backend_task_runner_);
// TODO(brettw): Do prioritization.
// NOTE(mastiz): If this implementation changes, be cautious with implications
// for sync, because a) the sync engine (sync thread) post tasks directly to
// the task runner via ModelTypeProcessorProxy (which is subtle); and b)
// ProfileSyncService (UI thread) does the same via
// ProxyModelTypeControllerDelegate.
backend_task_runner_->PostTask(FROM_HERE, std::move(task));
}
base::WeakPtr<HistoryService> HistoryService::AsWeakPtr() {
DCHECK(thread_checker_.CalledOnValidThread());
return weak_ptr_factory_.GetWeakPtr();
}
syncer::SyncMergeResult HistoryService::MergeDataAndStartSyncing(
syncer::ModelType type,
const syncer::SyncDataList& initial_sync_data,
std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
std::unique_ptr<syncer::SyncErrorFactory> error_handler) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES);
delete_directive_handler_.Start(this, initial_sync_data,
std::move(sync_processor));
return syncer::SyncMergeResult(type);
}
void HistoryService::StopSyncing(syncer::ModelType type) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES);
delete_directive_handler_.Stop();
}
syncer::SyncDataList HistoryService::GetAllSyncData(
syncer::ModelType type) const {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES);
// TODO(akalin): Keep track of existing delete directives.
return syncer::SyncDataList();
}
syncer::SyncError HistoryService::ProcessSyncChanges(
const base::Location& from_here,
const syncer::SyncChangeList& change_list) {
delete_directive_handler_.ProcessSyncChanges(this, change_list);
return syncer::SyncError();
}
std::unique_ptr<syncer::ModelTypeControllerDelegate>
HistoryService::GetTypedURLSyncControllerDelegate() {
DCHECK(thread_checker_.CalledOnValidThread());
// Note that a callback is bound for GetTypedURLSyncControllerDelegate()
// because this getter itself must also run in the backend sequence, and the
// proxy object below will take care of that.
return std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
backend_task_runner_,
base::BindRepeating(&HistoryBackend::GetTypedURLSyncControllerDelegate,
base::Unretained(history_backend_.get())));
}
syncer::SyncError HistoryService::ProcessLocalDeleteDirective(
const sync_pb::HistoryDeleteDirectiveSpecifics& delete_directive) {
DCHECK(thread_checker_.CalledOnValidThread());
return delete_directive_handler_.ProcessLocalDeleteDirective(
delete_directive);
}
void HistoryService::SetInMemoryBackend(
std::unique_ptr<InMemoryHistoryBackend> mem_backend) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!in_memory_backend_) << "Setting mem DB twice";
in_memory_backend_ = std::move(mem_backend);
// The database requires additional initialization once we own it.
in_memory_backend_->AttachToHistoryService(this);
}
void HistoryService::NotifyProfileError(sql::InitStatus init_status,
const std::string& diagnostics) {
DCHECK(thread_checker_.CalledOnValidThread());
if (history_client_)
history_client_->NotifyProfileError(init_status, diagnostics);
}
void HistoryService::DeleteURL(const GURL& url) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// We will update the visited links when we observe the delete notifications.
ScheduleTask(PRIORITY_NORMAL, base::BindOnce(&HistoryBackend::DeleteURL,
history_backend_, url));
}
void HistoryService::DeleteURLsForTest(const std::vector<GURL>& urls) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
// We will update the visited links when we observe the delete
// notifications.
ScheduleTask(PRIORITY_NORMAL, base::BindOnce(&HistoryBackend::DeleteURLs,
history_backend_, urls));
}
void HistoryService::ExpireHistoryBetween(
const std::set<GURL>& restrict_urls,
Time begin_time,
Time end_time,
const base::Closure& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::ExpireHistoryBetween, history_backend_,
restrict_urls, begin_time, end_time),
callback);
}
void HistoryService::ExpireHistory(
const std::vector<ExpireHistoryArgs>& expire_list,
const base::Closure& callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
tracker->PostTaskAndReply(backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::ExpireHistory,
history_backend_, expire_list),
callback);
}
void HistoryService::ExpireHistoryBeforeForTesting(
base::Time end_time,
base::OnceClosure callback,
base::CancelableTaskTracker* tracker) {
DCHECK(backend_task_runner_) << "History service being called after cleanup";
DCHECK(thread_checker_.CalledOnValidThread());
tracker->PostTaskAndReply(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&HistoryBackend::ExpireHistoryBeforeForTesting,
history_backend_, end_time),
std::move(callback));
}
void HistoryService::ExpireLocalAndRemoteHistoryBetween(
WebHistoryService* web_history,
const std::set<GURL>& restrict_urls,
Time begin_time,
Time end_time,
const base::Closure& callback,
base::CancelableTaskTracker* tracker) {
// TODO(dubroy): This should be factored out into a separate class that
// dispatches deletions to the proper places.
if (web_history) {
// TODO(dubroy): This API does not yet support deletion of specific URLs.
DCHECK(restrict_urls.empty());
delete_directive_handler_.CreateDeleteDirectives(std::set<int64_t>(),
begin_time, end_time);
// Attempt online deletion from the history server, but ignore the result.
// Deletion directives ensure that the results will eventually be deleted.
//
// TODO(davidben): |callback| should not run until this operation completes
// too.
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"web_history_expire_between_dates", "web_history_service", R"(
semantics {
description:
"If a user who syncs their browsing history deletes history "
"items for a time range, Chrome sends a request to a google.com "
"host to execute the corresponding deletion serverside."
trigger:
"Deleting browsing history for a given time range, e.g. from the "
"Clear Browsing Data dialog, by an extension, or the "
"Clear-Site-Data header."
data:
"The begin and end timestamps of the selected time range, a "
"version info token to resolve transaction conflicts, and an "
"OAuth2 token authenticating the user."
}
policy {
chrome_policy {
AllowDeletingBrowserHistory {
AllowDeletingBrowserHistory: false
}
}
})");
web_history->ExpireHistoryBetween(restrict_urls, begin_time, end_time,
base::Bind(&ExpireWebHistoryComplete),
partial_traffic_annotation);
}
ExpireHistoryBetween(restrict_urls, begin_time, end_time, callback, tracker);
}
void HistoryService::OnDBLoaded() {
DCHECK(thread_checker_.CalledOnValidThread());
backend_loaded_ = true;
NotifyHistoryServiceLoaded();
}
void HistoryService::NotifyURLVisited(ui::PageTransition transition,
const URLRow& row,
const RedirectList& redirects,
base::Time visit_time) {
DCHECK(thread_checker_.CalledOnValidThread());
for (HistoryServiceObserver& observer : observers_)
observer.OnURLVisited(this, transition, row, redirects, visit_time);
}
void HistoryService::NotifyURLsModified(const URLRows& changed_urls) {
DCHECK(thread_checker_.CalledOnValidThread());
for (HistoryServiceObserver& observer : observers_)
observer.OnURLsModified(this, changed_urls);
}
void HistoryService::NotifyURLsDeleted(const DeletionInfo& deletion_info) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!backend_task_runner_)
return;
// Inform the VisitDelegate of the deleted URLs. We will inform the delegate
// of added URLs as soon as we get the add notification (we don't have to wait
// for the backend, which allows us to be faster to update the state).
//
// For deleted URLs, we don't typically know what will be deleted since
// delete notifications are by time. We would also like to be more
// respectful of privacy and never tell the user something is gone when it
// isn't. Therefore, we update the delete URLs after the fact.
if (visit_delegate_) {
if (deletion_info.IsAllHistory()) {
visit_delegate_->DeleteAllURLs();
} else {
std::vector<GURL> urls;
urls.reserve(deletion_info.deleted_rows().size());
for (const auto& row : deletion_info.deleted_rows())
urls.push_back(row.url());
visit_delegate_->DeleteURLs(urls);
}
}
for (HistoryServiceObserver& observer : observers_)
observer.OnURLsDeleted(this, deletion_info);
}
void HistoryService::NotifyHistoryServiceLoaded() {
DCHECK(thread_checker_.CalledOnValidThread());
for (HistoryServiceObserver& observer : observers_)
observer.OnHistoryServiceLoaded(this);
}
void HistoryService::NotifyHistoryServiceBeingDeleted() {
DCHECK(thread_checker_.CalledOnValidThread());
for (HistoryServiceObserver& observer : observers_)
observer.HistoryServiceBeingDeleted(this);
}
void HistoryService::NotifyKeywordSearchTermUpdated(
const URLRow& row,
KeywordID keyword_id,
const base::string16& term) {
DCHECK(thread_checker_.CalledOnValidThread());
for (HistoryServiceObserver& observer : observers_)
observer.OnKeywordSearchTermUpdated(this, row, keyword_id, term);
}
void HistoryService::NotifyKeywordSearchTermDeleted(URLID url_id) {
DCHECK(thread_checker_.CalledOnValidThread());
for (HistoryServiceObserver& observer : observers_)
observer.OnKeywordSearchTermDeleted(this, url_id);
}
std::unique_ptr<
base::CallbackList<void(const std::set<GURL>&, const GURL&)>::Subscription>
HistoryService::AddFaviconsChangedCallback(
const HistoryService::OnFaviconsChangedCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
return favicon_changed_callback_list_.Add(callback);
}
void HistoryService::NotifyFaviconsChanged(const std::set<GURL>& page_urls,
const GURL& icon_url) {
DCHECK(thread_checker_.CalledOnValidThread());
favicon_changed_callback_list_.Notify(page_urls, icon_url);
}
} // namespace history