blob: 4fb558d88d81b302a222a9701704ef28037febf6 [file] [log] [blame]
// Copyright 2017 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 "components/offline_items_collection/core/throttled_offline_content_provider.h"
#include <utility>
#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/offline_items_collection/core/offline_item.h"
namespace offline_items_collection {
namespace {
const int kDelayBetweenUpdatesMs = 1000;
} // namespace
ThrottledOfflineContentProvider::ThrottledOfflineContentProvider(
OfflineContentProvider* provider)
: ThrottledOfflineContentProvider(
base::TimeDelta::FromMilliseconds(kDelayBetweenUpdatesMs),
provider) {}
ThrottledOfflineContentProvider::ThrottledOfflineContentProvider(
const base::TimeDelta& delay_between_updates,
OfflineContentProvider* provider)
: delay_between_updates_(delay_between_updates),
last_update_time_(base::TimeTicks::Now()),
update_queued_(false),
wrapped_provider_(provider),
weak_ptr_factory_(this) {
DCHECK(wrapped_provider_);
wrapped_provider_->AddObserver(this);
}
ThrottledOfflineContentProvider::~ThrottledOfflineContentProvider() {
wrapped_provider_->RemoveObserver(this);
}
bool ThrottledOfflineContentProvider::AreItemsAvailable() {
return wrapped_provider_->AreItemsAvailable();
}
void ThrottledOfflineContentProvider::OpenItem(const ContentId& id) {
wrapped_provider_->OpenItem(id);
FlushUpdates();
}
void ThrottledOfflineContentProvider::RemoveItem(const ContentId& id) {
wrapped_provider_->RemoveItem(id);
FlushUpdates();
}
void ThrottledOfflineContentProvider::CancelDownload(const ContentId& id) {
wrapped_provider_->CancelDownload(id);
FlushUpdates();
}
void ThrottledOfflineContentProvider::PauseDownload(const ContentId& id) {
wrapped_provider_->PauseDownload(id);
FlushUpdates();
}
void ThrottledOfflineContentProvider::ResumeDownload(const ContentId& id,
bool has_user_gesture) {
wrapped_provider_->ResumeDownload(id, has_user_gesture);
FlushUpdates();
}
void ThrottledOfflineContentProvider::GetItemById(const ContentId& id,
SingleItemCallback callback) {
wrapped_provider_->GetItemById(
id, base::BindOnce(&ThrottledOfflineContentProvider::OnGetItemByIdDone,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ThrottledOfflineContentProvider::GetAllItems(
MultipleItemCallback callback) {
wrapped_provider_->GetAllItems(
base::BindOnce(&ThrottledOfflineContentProvider::OnGetAllItemsDone,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ThrottledOfflineContentProvider::OnGetAllItemsDone(
MultipleItemCallback callback,
const OfflineItemList& items) {
for (const auto item : items)
UpdateItemIfPresent(item);
std::move(callback).Run(items);
}
void ThrottledOfflineContentProvider::OnGetItemByIdDone(
SingleItemCallback callback,
const base::Optional<OfflineItem>& item) {
if (item.has_value())
UpdateItemIfPresent(item.value());
std::move(callback).Run(item);
}
void ThrottledOfflineContentProvider::GetVisualsForItem(
const ContentId& id,
const VisualsCallback& callback) {
wrapped_provider_->GetVisualsForItem(id, callback);
}
void ThrottledOfflineContentProvider::AddObserver(
OfflineContentProvider::Observer* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
if (!wrapped_provider_->AreItemsAvailable())
return;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ThrottledOfflineContentProvider::NotifyItemsAvailable,
weak_ptr_factory_.GetWeakPtr(), base::Unretained(observer)));
}
void ThrottledOfflineContentProvider::RemoveObserver(
OfflineContentProvider::Observer* observer) {
observers_.RemoveObserver(observer);
}
void ThrottledOfflineContentProvider::OnItemsAvailable(
OfflineContentProvider* provider) {
DCHECK_EQ(provider, wrapped_provider_);
for (auto& observer : observers_)
observer.OnItemsAvailable(this);
}
void ThrottledOfflineContentProvider::OnItemsAdded(
const OfflineItemList& items) {
for (auto& observer : observers_)
observer.OnItemsAdded(items);
}
void ThrottledOfflineContentProvider::OnItemRemoved(const ContentId& id) {
updates_.erase(id);
for (auto& observer : observers_)
observer.OnItemRemoved(id);
}
void ThrottledOfflineContentProvider::OnItemUpdated(const OfflineItem& item) {
updates_[item.id] = item;
// If we already queued an update, we're throttling, just wait until the
// update passes through.
if (update_queued_)
return;
// If we haven't sent an update recently, let the update go through.
base::TimeDelta current_delay = base::TimeTicks::Now() - last_update_time_;
if (current_delay >= delay_between_updates_) {
FlushUpdates();
return;
}
// Queue the update so we wait for the proper amount of time before notifying
// observers.
update_queued_ = true;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&ThrottledOfflineContentProvider::FlushUpdates,
weak_ptr_factory_.GetWeakPtr()),
delay_between_updates_ - current_delay);
}
void ThrottledOfflineContentProvider::NotifyItemsAvailable(
OfflineContentProvider::Observer* observer) {
if (!observers_.HasObserver(observer))
return;
observer->OnItemsAvailable(this);
}
void ThrottledOfflineContentProvider::UpdateItemIfPresent(
const OfflineItem& item) {
OfflineItemMap::iterator it = updates_.find(item.id);
if (it != updates_.end())
it->second = item;
}
void ThrottledOfflineContentProvider::FlushUpdates() {
last_update_time_ = base::TimeTicks::Now();
update_queued_ = false;
OfflineItemMap updates = std::move(updates_);
for (auto item_pair : updates) {
for (auto& observer : observers_)
observer.OnItemUpdated(item_pair.second);
}
}
} // namespace offline_items_collection