blob: 95d30611a239dbf12bfdd130fc94c9cfadf47dd6 [file] [log] [blame]
// Copyright 2019 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.
#include "chrome/browser/media/history/media_history_keyed_service.h"
#include "base/feature_list.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/task_runner_util.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/media/history/media_history_keyed_service_factory.h"
#include "chrome/browser/media/history/media_history_store.h"
#include "chrome/browser/profiles/profile.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "media/base/media_switches.h"
namespace media_history {
// StoreHolder will in most cases hold a local MediaHistoryStore. However, for
// OTR profiles we hold a pointer to the original profile store. When accessing
// MediaHistoryStore you should use GetForRead for read operations,
// GetForWrite for write operations and GetForDelete for delete operations.
// These can be null if the store is read only or we disable storing browsing
// history.
class MediaHistoryKeyedService::StoreHolder {
public:
StoreHolder(Profile* profile,
scoped_refptr<base::UpdateableSequencedTaskRunner> db_task_runner,
const bool should_reset)
: profile_(profile),
local_(new MediaHistoryStore(profile, db_task_runner)),
db_task_runner_(db_task_runner) {
local_->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::Initialize,
base::RetainedRef(local_), should_reset));
}
explicit StoreHolder(Profile* profile, MediaHistoryKeyedService* remote)
: profile_(profile), remote_(remote) {}
~StoreHolder() = default;
StoreHolder(const StoreHolder& t) = delete;
StoreHolder& operator=(const StoreHolder&) = delete;
MediaHistoryStore* GetForRead() {
if (local_)
return local_.get();
return remote_->store_->GetForRead();
}
MediaHistoryStore* GetForWrite() {
if (profile_->GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled))
return nullptr;
if (local_)
return local_.get();
return nullptr;
}
MediaHistoryStore* GetForDelete() {
if (local_)
return local_.get();
return nullptr;
}
void Shutdown() {
if (!local_)
return;
local_->SetCancelled();
}
private:
Profile* profile_;
scoped_refptr<MediaHistoryStore> local_;
scoped_refptr<base::UpdateableSequencedTaskRunner> db_task_runner_;
MediaHistoryKeyedService* remote_ = nullptr;
};
MediaHistoryKeyedService::MediaHistoryKeyedService(Profile* profile)
: profile_(profile) {
// May be null in tests.
history::HistoryService* history = HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::IMPLICIT_ACCESS);
if (history)
history->AddObserver(this);
if (profile->IsOffTheRecord()) {
MediaHistoryKeyedService* original =
MediaHistoryKeyedService::Get(profile->GetOriginalProfile());
DCHECK(original);
store_ = std::make_unique<StoreHolder>(profile, original);
} else {
auto db_task_runner = base::ThreadPool::CreateUpdateableSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
store_ = std::make_unique<StoreHolder>(profile_, std::move(db_task_runner),
/* should_reset=*/false);
}
}
// static
MediaHistoryKeyedService* MediaHistoryKeyedService::Get(Profile* profile) {
return MediaHistoryKeyedServiceFactory::GetForProfile(profile);
}
MediaHistoryKeyedService::~MediaHistoryKeyedService() = default;
bool MediaHistoryKeyedService::IsEnabled() {
return base::FeatureList::IsEnabled(media::kUseMediaHistoryStore);
}
void MediaHistoryKeyedService::Shutdown() {
history::HistoryService* history = HistoryServiceFactory::GetForProfile(
profile_, ServiceAccessType::IMPLICIT_ACCESS);
if (history)
history->RemoveObserver(this);
store_->Shutdown();
}
void MediaHistoryKeyedService::OnURLsDeleted(
history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) {
// The store might not always be writable.
auto* store = store_->GetForDelete();
if (!store)
return;
if (deletion_info.IsAllHistory()) {
// Stop the old database and destroy the DB.
scoped_refptr<base::UpdateableSequencedTaskRunner> db_task_runner =
store->db_task_runner_;
store->SetCancelled();
store_.reset();
// Create a new internal store.
store_ = std::make_unique<StoreHolder>(profile_, std::move(db_task_runner),
/* should_reset= */ true);
return;
}
// Build a set of all urls and origins in |deleted_rows|.
std::set<url::Origin> origins;
for (const history::URLRow& row : deletion_info.deleted_rows()) {
origins.insert(url::Origin::Create(row.url()));
}
// Find any origins that do not have any more data in the history database.
std::set<url::Origin> deleted_origins;
for (const url::Origin& origin : origins) {
const auto& origin_count =
deletion_info.deleted_urls_origin_map().find(origin.GetURL());
if (origin_count == deletion_info.deleted_urls_origin_map().end() ||
origin_count->second.first > 0) {
continue;
}
deleted_origins.insert(origin);
}
if (!deleted_origins.empty()) {
store->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::DeleteAllOriginData,
store, deleted_origins));
}
// Build a set of all urls in |deleted_rows| that do not have their origin in
// |deleted_origins|.
std::set<GURL> deleted_urls;
for (const history::URLRow& row : deletion_info.deleted_rows()) {
auto origin = url::Origin::Create(row.url());
if (base::Contains(deleted_origins, origin))
continue;
deleted_urls.insert(row.url());
}
if (!deleted_urls.empty()) {
store->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::DeleteAllURLData, store,
deleted_urls));
}
}
void MediaHistoryKeyedService::SavePlayback(
const content::MediaPlayerWatchTime& watch_time) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&MediaHistoryStore::SavePlayback, store,
std::make_unique<content::MediaPlayerWatchTime>(
watch_time.url, watch_time.origin,
watch_time.cumulative_watch_time, watch_time.last_timestamp,
watch_time.has_video, watch_time.has_audio)));
}
}
void MediaHistoryKeyedService::GetMediaHistoryStats(
base::OnceCallback<void(mojom::MediaHistoryStatsPtr)> callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetMediaHistoryStats,
store_->GetForRead()),
std::move(callback));
}
void MediaHistoryKeyedService::GetOriginRowsForDebug(
base::OnceCallback<void(std::vector<mojom::MediaHistoryOriginRowPtr>)>
callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetOriginRowsForDebug,
store_->GetForRead()),
std::move(callback));
}
void MediaHistoryKeyedService::GetMediaHistoryPlaybackRowsForDebug(
base::OnceCallback<void(std::vector<mojom::MediaHistoryPlaybackRowPtr>)>
callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetMediaHistoryPlaybackRowsForDebug,
store_->GetForRead()),
std::move(callback));
}
void MediaHistoryKeyedService::GetPlaybackSessions(
base::Optional<unsigned int> num_sessions,
base::Optional<GetPlaybackSessionsFilter> filter,
base::OnceCallback<
void(std::vector<mojom::MediaHistoryPlaybackSessionRowPtr>)> callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetPlaybackSessions,
store_->GetForRead(), num_sessions, std::move(filter)),
std::move(callback));
}
void MediaHistoryKeyedService::SavePlaybackSession(
const GURL& url,
const media_session::MediaMetadata& metadata,
const base::Optional<media_session::MediaPosition>& position,
const std::vector<media_session::MediaImage>& artwork) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::SavePlaybackSession,
store, url, metadata, position, artwork));
}
}
void MediaHistoryKeyedService::GetHighWatchTimeOrigins(
const base::TimeDelta& audio_video_watchtime_min,
base::OnceCallback<void(const std::vector<url::Origin>&)> callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetHighWatchTimeOrigins,
store_->GetForRead(), audio_video_watchtime_min),
std::move(callback));
}
MediaHistoryKeyedService::GetMediaFeedItemsRequest
MediaHistoryKeyedService::GetMediaFeedItemsRequest::CreateItemsForDebug(
int64_t feed_id) {
GetMediaFeedItemsRequest request;
request.type = Type::kDebugAll;
request.feed_id = feed_id;
return request;
}
MediaHistoryKeyedService::GetMediaFeedItemsRequest
MediaHistoryKeyedService::GetMediaFeedItemsRequest::CreateItemsForFeed(
int64_t feed_id,
unsigned limit,
bool fetched_items_should_be_safe,
base::Optional<media_feeds::mojom::MediaFeedItemType> filter_by_type) {
GetMediaFeedItemsRequest request;
request.type = Type::kItemsForFeed;
request.limit = limit;
request.feed_id = feed_id;
request.fetched_items_should_be_safe = fetched_items_should_be_safe;
request.filter_by_type = filter_by_type;
return request;
}
MediaHistoryKeyedService::GetMediaFeedItemsRequest MediaHistoryKeyedService::
GetMediaFeedItemsRequest::CreateItemsForContinueWatching(
unsigned limit,
bool fetched_items_should_be_safe,
base::Optional<media_feeds::mojom::MediaFeedItemType> filter_by_type) {
GetMediaFeedItemsRequest request;
request.type = Type::kContinueWatching;
request.limit = limit;
request.fetched_items_should_be_safe = fetched_items_should_be_safe;
request.filter_by_type = filter_by_type;
return request;
}
MediaHistoryKeyedService::GetMediaFeedItemsRequest::GetMediaFeedItemsRequest() =
default;
MediaHistoryKeyedService::GetMediaFeedItemsRequest::GetMediaFeedItemsRequest(
const GetMediaFeedItemsRequest& t) = default;
void MediaHistoryKeyedService::GetMediaFeedItems(
const GetMediaFeedItemsRequest& request,
base::OnceCallback<void(std::vector<media_feeds::mojom::MediaFeedItemPtr>)>
callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetMediaFeedItems,
store_->GetForRead(), request),
std::move(callback));
}
MediaHistoryKeyedService::MediaFeedFetchResult::MediaFeedFetchResult() =
default;
MediaHistoryKeyedService::MediaFeedFetchResult::~MediaFeedFetchResult() =
default;
MediaHistoryKeyedService::MediaFeedFetchResult::MediaFeedFetchResult(
MediaFeedFetchResult&& t) = default;
void MediaHistoryKeyedService::StoreMediaFeedFetchResult(
MediaFeedFetchResult result,
base::OnceClosure callback) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::StoreMediaFeedFetchResult, store,
std::move(result)),
std::move(callback));
}
}
void MediaHistoryKeyedService::GetURLsInTableForTest(
const std::string& table,
base::OnceCallback<void(std::set<GURL>)> callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetURLsInTableForTest,
store_->GetForRead(), table),
std::move(callback));
}
void MediaHistoryKeyedService::DiscoverMediaFeed(
const GURL& url,
const base::Optional<GURL>& favicon,
base::OnceClosure callback) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::DiscoverMediaFeed, store, url,
favicon),
std::move(callback));
} else {
std::move(callback).Run();
}
}
MediaHistoryKeyedService::PendingSafeSearchCheck::PendingSafeSearchCheck(
SafeSearchCheckedType type,
int64_t id)
: id(std::make_pair(type, id)) {}
MediaHistoryKeyedService::PendingSafeSearchCheck::~PendingSafeSearchCheck() =
default;
void MediaHistoryKeyedService::GetPendingSafeSearchCheckMediaFeedItems(
base::OnceCallback<void(PendingSafeSearchCheckList)> callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(
&MediaHistoryStore::GetPendingSafeSearchCheckMediaFeedItems,
store_->GetForRead()),
std::move(callback));
}
void MediaHistoryKeyedService::StoreMediaFeedItemSafeSearchResults(
std::map<SafeSearchID, media_feeds::mojom::SafeSearchResult> results) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::StoreMediaFeedItemSafeSearchResults,
store, results));
}
}
void MediaHistoryKeyedService::PostTaskToDBForTest(base::OnceClosure callback) {
store_->GetForRead()->db_task_runner_->PostTaskAndReply(
FROM_HERE, base::DoNothing(), std::move(callback));
}
MediaHistoryKeyedService::GetMediaFeedsRequest
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateTopFeedsForFetch(
unsigned limit,
base::TimeDelta audio_video_watchtime_min) {
GetMediaFeedsRequest request;
request.type = Type::kTopFeedsForFetch;
request.limit = limit;
request.audio_video_watchtime_min = audio_video_watchtime_min;
return request;
}
MediaHistoryKeyedService::GetMediaFeedsRequest
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateTopFeedsForDisplay(
unsigned limit,
int fetched_items_min,
bool fetched_items_min_should_be_safe,
base::Optional<media_feeds::mojom::MediaFeedItemType> filter_by_type) {
GetMediaFeedsRequest request;
request.type = Type::kTopFeedsForDisplay;
request.limit = limit;
request.fetched_items_min = fetched_items_min;
request.fetched_items_min_should_be_safe = fetched_items_min_should_be_safe;
request.filter_by_type = filter_by_type;
return request;
}
MediaHistoryKeyedService::GetMediaFeedsRequest
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateSelectedFeedsForFetch() {
GetMediaFeedsRequest request;
request.type = Type::kSelectedFeedsForFetch;
return request;
}
MediaHistoryKeyedService::GetMediaFeedsRequest
MediaHistoryKeyedService::GetMediaFeedsRequest::CreateNewFeeds() {
GetMediaFeedsRequest request;
request.type = Type::kNewFeeds;
return request;
}
MediaHistoryKeyedService::GetMediaFeedsRequest::GetMediaFeedsRequest() =
default;
MediaHistoryKeyedService::GetMediaFeedsRequest::GetMediaFeedsRequest(
const GetMediaFeedsRequest& t) = default;
void MediaHistoryKeyedService::GetMediaFeeds(
const GetMediaFeedsRequest& request,
base::OnceCallback<void(std::vector<media_feeds::mojom::MediaFeedPtr>)>
callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetMediaFeeds, store_->GetForRead(),
request),
std::move(callback));
}
void MediaHistoryKeyedService::UpdateMediaFeedDisplayTime(
const int64_t feed_id) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::UpdateMediaFeedDisplayTime, store,
feed_id));
}
}
void MediaHistoryKeyedService::IncrementMediaFeedItemsShownCount(
const std::set<int64_t> feed_item_ids) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::IncrementMediaFeedItemsShownCount,
base::RetainedRef(store), feed_item_ids));
}
}
void MediaHistoryKeyedService::MarkMediaFeedItemAsClicked(
const int64_t& feed_item_id) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::MarkMediaFeedItemAsClicked,
base::RetainedRef(store), feed_item_id));
}
}
void MediaHistoryKeyedService::ResetMediaFeed(
const url::Origin& origin,
media_feeds::mojom::ResetReason reason) {
CHECK_NE(media_feeds::mojom::ResetReason::kNone, reason);
if (auto* store = store_->GetForDelete()) {
store->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::ResetMediaFeed, store,
origin, reason));
}
}
void MediaHistoryKeyedService::ResetMediaFeedDueToCookies(
const url::Origin& origin,
const bool include_subdomains,
const std::string& name,
const net::CookieChangeCause& cause) {
if (auto* store = store_->GetForDelete()) {
store->db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::ResetMediaFeedDueToCookies, store,
origin, include_subdomains, name, cause));
}
}
void MediaHistoryKeyedService::ResetMediaFeedDueToCacheClearing(
const base::Time& start_time,
const base::Time& end_time,
CacheClearingFilter filter,
base::OnceClosure callback) {
if (auto* store = store_->GetForDelete()) {
store->db_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::ResetMediaFeedDueToCacheClearing,
store, start_time, end_time, std::move(filter)),
std::move(callback));
} else {
std::move(callback).Run();
}
}
void MediaHistoryKeyedService::DeleteMediaFeed(const int64_t feed_id,
base::OnceClosure callback) {
if (auto* store = store_->GetForDelete()) {
store->db_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::DeleteMediaFeed, store, feed_id),
std::move(callback));
}
}
void MediaHistoryKeyedService::UpdateFeedUserStatus(
const int64_t feed_id,
media_feeds::mojom::FeedUserStatus status) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::UpdateFeedUserStatus,
store, feed_id, status));
}
}
MediaHistoryKeyedService::MediaFeedFetchDetails::MediaFeedFetchDetails() =
default;
MediaHistoryKeyedService::MediaFeedFetchDetails::~MediaFeedFetchDetails() =
default;
MediaHistoryKeyedService::MediaFeedFetchDetails::MediaFeedFetchDetails(
MediaFeedFetchDetails&& t) = default;
MediaHistoryKeyedService::MediaFeedFetchDetails&
MediaHistoryKeyedService::MediaFeedFetchDetails::operator=(
const MediaHistoryKeyedService::MediaFeedFetchDetails&) = default;
void MediaHistoryKeyedService::GetMediaFeedFetchDetails(
const int64_t feed_id,
GetMediaFeedFetchDetailsCallback callback) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetMediaFeedFetchDetails,
store_->GetForRead(), feed_id),
std::move(callback));
}
void MediaHistoryKeyedService::SetKaleidoscopeData(
media::mojom::GetCollectionsResponsePtr data,
const std::string& gaia_id) {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MediaHistoryStore::SetKaleidoscopeData,
store, std::move(data), gaia_id));
}
}
void MediaHistoryKeyedService::GetKaleidoscopeData(
const std::string& gaia_id,
GetKaleidoscopeDataCallback callback) {
if (auto* store = store_->GetForWrite()) {
base::PostTaskAndReplyWithResult(
store_->GetForRead()->db_task_runner_.get(), FROM_HERE,
base::BindOnce(&MediaHistoryStore::GetKaleidoscopeData, store, gaia_id),
std::move(callback));
} else {
std::move(callback).Run(nullptr);
}
}
void MediaHistoryKeyedService::DeleteKaleidoscopeData() {
if (auto* store = store_->GetForWrite()) {
store->db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MediaHistoryStore::DeleteKaleidoscopeData, store));
}
}
} // namespace media_history