blob: eac5366e94032f8ffd9a0a9b785c30b35df0f721 [file] [log] [blame]
// Copyright 2018 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/download/download_offline_content_provider.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/download/image_thumbnail_request.h"
#include "chrome/browser/download/offline_item_utils.h"
#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/download/public/common/download_item.h"
#include "components/offline_items_collection/core/offline_content_aggregator.h"
#include "components/offline_items_collection/core/offline_item.h"
#include "content/public/browser/browser_context.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#if defined(OS_ANDROID)
#include "chrome/browser/android/download/download_manager_bridge.h"
#include "chrome/browser/android/download/download_utils.h"
#endif
using OfflineItemFilter = offline_items_collection::OfflineItemFilter;
using OfflineItemState = offline_items_collection::OfflineItemState;
using OfflineItemProgressUnit =
offline_items_collection::OfflineItemProgressUnit;
using offline_items_collection::OfflineItemShareInfo;
using OfflineItemVisuals = offline_items_collection::OfflineItemVisuals;
namespace {
// Thumbnail size used for generating thumbnails for image files.
const int kThumbnailSizeInDP = 64;
bool ShouldShowDownloadItem(const DownloadItem* item) {
return !item->IsTemporary() && !item->IsTransient() && !item->IsDangerous() &&
!item->GetTargetFilePath().empty();
}
std::unique_ptr<OfflineItemShareInfo> CreateShareInfo(
const DownloadItem* item) {
auto share_info = std::make_unique<OfflineItemShareInfo>();
#if defined(OS_ANDROID)
if (item) {
share_info->uri =
DownloadUtils::GetUriStringForPath(item->GetTargetFilePath());
}
#else
NOTIMPLEMENTED();
#endif
return share_info;
}
} // namespace
DownloadOfflineContentProvider::DownloadOfflineContentProvider(
OfflineContentAggregator* aggregator,
const std::string& name_space)
: aggregator_(aggregator),
name_space_(name_space),
manager_(nullptr),
weak_ptr_factory_(this) {
aggregator_->RegisterProvider(name_space_, this);
}
DownloadOfflineContentProvider::~DownloadOfflineContentProvider() {
aggregator_->UnregisterProvider(name_space_);
if (manager_)
manager_->RemoveObserver(this);
}
void DownloadOfflineContentProvider::SetDownloadManager(
DownloadManager* manager) {
DCHECK(manager);
manager_ = manager;
manager_->AddObserver(this);
}
// TODO(shaktisahu) : Pass DownloadOpenSource.
void DownloadOfflineContentProvider::OpenItem(LaunchLocation location,
const ContentId& id) {
DownloadItem* item = GetDownload(id.id);
if (item)
item->OpenDownload();
}
void DownloadOfflineContentProvider::RemoveItem(const ContentId& id) {
DownloadItem* item = GetDownload(id.id);
if (item)
item->Remove();
}
void DownloadOfflineContentProvider::CancelDownload(const ContentId& id) {
DownloadItem* item = GetDownload(id.id);
if (item)
item->Cancel(true);
}
void DownloadOfflineContentProvider::PauseDownload(const ContentId& id) {
DownloadItem* item = GetDownload(id.id);
if (item)
item->Pause();
}
void DownloadOfflineContentProvider::ResumeDownload(const ContentId& id,
bool has_user_gesture) {
DownloadItem* item = GetDownload(id.id);
if (item)
item->Resume(has_user_gesture);
}
void DownloadOfflineContentProvider::GetItemById(
const ContentId& id,
OfflineContentProvider::SingleItemCallback callback) {
DownloadItem* item = GetDownload(id.id);
auto offline_item =
item && ShouldShowDownloadItem(item)
? base::make_optional(
OfflineItemUtils::CreateOfflineItem(name_space_, item))
: base::nullopt;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), offline_item));
}
void DownloadOfflineContentProvider::GetAllItems(
OfflineContentProvider::MultipleItemCallback callback) {
DownloadManager::DownloadVector all_items;
GetAllDownloads(&all_items);
std::vector<OfflineItem> items;
for (auto* item : all_items) {
if (!ShouldShowDownloadItem(item))
continue;
items.push_back(OfflineItemUtils::CreateOfflineItem(name_space_, item));
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), items));
}
void DownloadOfflineContentProvider::GetVisualsForItem(
const ContentId& id,
GetVisualsOptions options,
VisualsCallback callback) {
// TODO(crbug.com/855330) Supply thumbnail if item is visible.
DownloadItem* item = GetDownload(id.id);
if (!item || !options.get_icon) {
// No favicon is available; run the callback without visuals.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), id, nullptr));
return;
}
display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
int icon_size = kThumbnailSizeInDP * display.device_scale_factor();
auto request = std::make_unique<ImageThumbnailRequest>(
icon_size,
base::BindOnce(&DownloadOfflineContentProvider::OnThumbnailRetrieved,
weak_ptr_factory_.GetWeakPtr(), id, std::move(callback)));
request->Start(item->GetTargetFilePath());
// Dropping ownership of |request| here because it will clean itself up once
// the started request finishes.
request.release();
}
void DownloadOfflineContentProvider::GetShareInfoForItem(
const ContentId& id,
ShareCallback callback) {
DownloadItem* item = GetDownload(id.id);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), id, CreateShareInfo(item)));
}
void DownloadOfflineContentProvider::OnThumbnailRetrieved(
const ContentId& id,
VisualsCallback callback,
const SkBitmap& bitmap) {
auto visuals = std::make_unique<OfflineItemVisuals>();
visuals->icon = gfx::Image::CreateFrom1xBitmap(bitmap);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), id, std::move(visuals)));
}
void DownloadOfflineContentProvider::RenameItem(const ContentId& id,
const std::string& name,
RenameCallback callback) {
DownloadItem* item = GetDownload(id.id);
if (!item) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), RenameResult::FAILURE_UNAVAILABLE));
return;
}
download::DownloadItem::RenameDownloadCallback download_callback =
base::BindOnce(
[](RenameCallback callback,
download::DownloadItem::DownloadRenameResult result) {
std::move(callback).Run(
OfflineItemUtils::ConvertDownloadRenameResultToRenameResult(
result));
},
std::move(callback));
base::FilePath::StringType filename;
#if defined(OS_WIN)
filename = base::UTF8ToWide(name);
#else
filename = name;
#endif
item->Rename(base::FilePath(filename), std::move(download_callback));
}
void DownloadOfflineContentProvider::AddObserver(
OfflineContentProvider::Observer* observer) {
if (observers_.HasObserver(observer))
return;
observers_.AddObserver(observer);
}
void DownloadOfflineContentProvider::RemoveObserver(
OfflineContentProvider::Observer* observer) {
if (!observers_.HasObserver(observer))
return;
observers_.RemoveObserver(observer);
}
void DownloadOfflineContentProvider::ManagerGoingDown(
DownloadManager* manager) {
DownloadManager::DownloadVector all_items;
GetAllDownloads(&all_items);
for (auto* item : all_items) {
if (!ShouldShowDownloadItem(item))
continue;
for (auto& observer : observers_)
observer.OnItemRemoved(ContentId(name_space_, item->GetGuid()));
}
manager_ = nullptr;
}
void DownloadOfflineContentProvider::OnDownloadStarted(DownloadItem* item) {
item->RemoveObserver(this);
item->AddObserver(this);
OnDownloadUpdated(item);
}
void DownloadOfflineContentProvider::OnDownloadUpdated(DownloadItem* item) {
// Wait until the target path is determined or the download is canceled.
if (item->GetTargetFilePath().empty() &&
item->GetState() != DownloadItem::CANCELLED)
return;
if (!ShouldShowDownloadItem(item))
return;
if (item->GetState() == DownloadItem::COMPLETE) {
// TODO(crbug.com/938152): May be move this to DownloadItem.
if (completed_downloads_.find(item->GetGuid()) !=
completed_downloads_.end()) {
return;
}
completed_downloads_.insert(item->GetGuid());
AddCompletedDownload(item);
}
UpdateObservers(item);
}
void DownloadOfflineContentProvider::OnDownloadRemoved(DownloadItem* item) {
if (!ShouldShowDownloadItem(item))
return;
#if defined(OS_ANDROID)
DownloadManagerBridge::RemoveCompletedDownload(item);
#endif
ContentId contentId(name_space_, item->GetGuid());
for (auto& observer : observers_)
observer.OnItemRemoved(contentId);
}
void DownloadOfflineContentProvider::OnDownloadDestroyed(DownloadItem* item) {
completed_downloads_.erase(item->GetGuid());
}
void DownloadOfflineContentProvider::AddCompletedDownload(DownloadItem* item) {
#if defined(OS_ANDROID)
DownloadManagerBridge::AddCompletedDownload(
item,
base::BindOnce(&DownloadOfflineContentProvider::AddCompletedDownloadDone,
weak_ptr_factory_.GetWeakPtr(), item));
#endif
}
void DownloadOfflineContentProvider::AddCompletedDownloadDone(
DownloadItem* item,
int64_t system_download_id,
bool can_resolve) {
if (can_resolve && item->HasUserGesture())
item->OpenDownload();
}
DownloadItem* DownloadOfflineContentProvider::GetDownload(
const std::string& download_guid) {
return manager_ ? manager_->GetDownloadByGuid(download_guid) : nullptr;
}
void DownloadOfflineContentProvider::GetAllDownloads(
DownloadManager::DownloadVector* all_items) {
if (manager_)
manager_->GetAllDownloads(all_items);
}
void DownloadOfflineContentProvider::UpdateObservers(DownloadItem* item) {
for (auto& observer : observers_) {
observer.OnItemUpdated(
OfflineItemUtils::CreateOfflineItem(name_space_, item));
}
}