Commit 29349eee authored by Dan Harrington's avatar Dan Harrington Committed by Commit Bot

Provide thumbnails in AvailableOfflineContentProvider

Shows thumbnails on the net error page, but UI in this CL is a placeholder.

Bug: 852872
Change-Id: I4710058e835318fca3f84eb52752c261ba2fcc36
Reviewed-on: https://chromium-review.googlesource.com/1157265Reviewed-by: default avatarMin Qin <qinmin@chromium.org>
Reviewed-by: default avatarCarlos Knippschild <carlosk@chromium.org>
Reviewed-by: default avatarEdward Jung <edwardjung@chromium.org>
Commit-Queue: Dan H <harringtond@google.com>
Cr-Commit-Position: refs/heads/master@{#584509}
parent 3033eaec
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "chrome/browser/android/download/available_offline_content_provider.h" #include "chrome/browser/android/download/available_offline_content_provider.h"
#include "base/base64.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/android/chrome_feature_list.h" #include "chrome/browser/android/chrome_feature_list.h"
...@@ -65,9 +67,110 @@ bool CompareItemsByUsefulness(const OfflineItem& a, const OfflineItem& b) { ...@@ -65,9 +67,110 @@ bool CompareItemsByUsefulness(const OfflineItem& a, const OfflineItem& b) {
return a.id < b.id; return a.id < b.id;
} }
class ThumbnailFetch {
public:
// Gets visuals for a list of thumbnails. Calls |complete_callback| with
// a list of data URIs containing the thumbnails for |content_ids|, in the
// same order. If no thumbnail is available, the corresponding result
// string is left empty.
static void Start(
offline_items_collection::OfflineContentAggregator* aggregator,
std::vector<offline_items_collection::ContentId> content_ids,
base::OnceCallback<void(std::vector<GURL>)> complete_callback) {
// ThumbnailFetch instances are self-deleting.
ThumbnailFetch* fetch = new ThumbnailFetch(std::move(content_ids),
std::move(complete_callback));
fetch->Start(aggregator);
}
private:
ThumbnailFetch(std::vector<offline_items_collection::ContentId> content_ids,
base::OnceCallback<void(std::vector<GURL>)> complete_callback)
: content_ids_(std::move(content_ids)),
complete_callback_(std::move(complete_callback)) {
thumbnails_.resize(content_ids_.size());
}
void Start(offline_items_collection::OfflineContentAggregator* aggregator) {
if (content_ids_.empty()) {
Complete();
return;
}
auto callback = base::BindRepeating(&ThumbnailFetch::VisualsReceived,
base::Unretained(this));
for (offline_items_collection::ContentId id : content_ids_) {
aggregator->GetVisualsForItem(id, callback);
}
}
void VisualsReceived(
const offline_items_collection::ContentId& id,
std::unique_ptr<offline_items_collection::OfflineItemVisuals> visuals) {
DCHECK(callback_count_ < content_ids_.size());
AddVisual(id, std::move(visuals));
if (++callback_count_ == content_ids_.size())
Complete();
}
void Complete() {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(complete_callback_), std::move(thumbnails_)));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
[](ThumbnailFetch* thumbnail_fetch) { delete thumbnail_fetch; },
this));
}
void AddVisual(
const offline_items_collection::ContentId& id,
std::unique_ptr<offline_items_collection::OfflineItemVisuals> visuals) {
if (!visuals)
return;
scoped_refptr<base::RefCountedMemory> data = visuals->icon.As1xPNGBytes();
if (!data || data->size() == 0)
return;
std::string content_base64;
base::Base64Encode(base::StringPiece(data->front_as<char>(), data->size()),
&content_base64);
for (size_t i = 0; i < content_ids_.size(); ++i) {
if (content_ids_[i] == id) {
thumbnails_[i] =
GURL(base::StrCat({"data:image/png;base64,", content_base64}));
break;
}
}
}
// The list of item IDs for which to fetch visuals.
std::vector<offline_items_collection::ContentId> content_ids_;
// The thumbnail data URIs to be returned. |thumbnails_| size is equal to
// |content_ids_| size.
std::vector<GURL> thumbnails_;
base::OnceCallback<void(std::vector<GURL>)> complete_callback_;
size_t callback_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(ThumbnailFetch);
};
chrome::mojom::AvailableOfflineContentPtr CreateAvailableOfflineContent(
const OfflineItem& item,
const GURL& thumbnail_url) {
return chrome::mojom::AvailableOfflineContent::New(
item.id.id, item.id.name_space, item.title, item.description,
base::UTF16ToASCII(ui::TimeFormat::Simple(
ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
base::Time::Now() - item.creation_time)),
"", // TODO(crbug.com/852872): Add attribution
std::move(thumbnail_url));
}
// Picks the best available offline content items, and passes them to callback. // Picks the best available offline content items, and passes them to callback.
void ListFinalize(AvailableOfflineContentProvider::ListCallback callback, void ListFinalize(
const std::vector<OfflineItem>& all_items) { AvailableOfflineContentProvider::ListCallback callback,
offline_items_collection::OfflineContentAggregator* aggregator,
const std::vector<OfflineItem>& all_items) {
// Save the best 3 or fewer times to |selected|. // Save the best 3 or fewer times to |selected|.
const int kMaxItemsToReturn = 3; const int kMaxItemsToReturn = 3;
std::vector<OfflineItem> selected(kMaxItemsToReturn); std::vector<OfflineItem> selected(kMaxItemsToReturn);
...@@ -76,22 +179,29 @@ void ListFinalize(AvailableOfflineContentProvider::ListCallback callback, ...@@ -76,22 +179,29 @@ void ListFinalize(AvailableOfflineContentProvider::ListCallback callback,
CompareItemsByUsefulness); CompareItemsByUsefulness);
selected.resize(end - selected.begin()); selected.resize(end - selected.begin());
// Translate OfflineItem to AvailableOfflineContentPtr, and filter out while (!selected.empty() &&
// items that should not be shown. ContentTypePriority(selected.back()) >= ContentPriority::kDoNotShow)
std::vector<chrome::mojom::AvailableOfflineContentPtr> result; selected.pop_back();
for (const OfflineItem& item : selected) {
if (ContentTypePriority(item) >= ContentPriority::kDoNotShow) std::vector<offline_items_collection::ContentId> selected_ids;
break; for (const OfflineItem& item : selected)
result.push_back(chrome::mojom::AvailableOfflineContent::New( selected_ids.push_back(item.id);
item.id.id, item.id.name_space, item.title, item.description,
base::UTF16ToASCII(ui::TimeFormat::Simple( auto complete = [](AvailableOfflineContentProvider::ListCallback callback,
ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT, std::vector<OfflineItem> selected,
base::Time::Now() - item.creation_time)), std::vector<GURL> thumbnail_data_uris) {
"", // TODO(crbug.com/852872): Add attribution // Translate OfflineItem to AvailableOfflineContentPtr.
GURL() // TODO(crbug.com/852872): Add thumbnail data URL std::vector<chrome::mojom::AvailableOfflineContentPtr> result;
)); for (size_t i = 0; i < selected.size(); ++i) {
} result.push_back(
std::move(callback).Run(std::move(result)); CreateAvailableOfflineContent(selected[i], thumbnail_data_uris[i]));
}
std::move(callback).Run(std::move(result));
};
ThumbnailFetch::Start(
aggregator, selected_ids,
base::BindOnce(complete, std::move(callback), std::move(selected)));
} }
} // namespace } // namespace
...@@ -109,7 +219,8 @@ void AvailableOfflineContentProvider::List(ListCallback callback) { ...@@ -109,7 +219,8 @@ void AvailableOfflineContentProvider::List(ListCallback callback) {
} }
offline_items_collection::OfflineContentAggregator* aggregator = offline_items_collection::OfflineContentAggregator* aggregator =
OfflineContentAggregatorFactory::GetForBrowserContext(browser_context_); OfflineContentAggregatorFactory::GetForBrowserContext(browser_context_);
aggregator->GetAllItems(base::BindOnce(ListFinalize, std::move(callback))); aggregator->GetAllItems(base::BindOnce(ListFinalize, std::move(callback),
base::Unretained(aggregator)));
} }
void AvailableOfflineContentProvider::Create( void AvailableOfflineContentProvider::Create(
......
...@@ -4,15 +4,18 @@ ...@@ -4,15 +4,18 @@
#include "chrome/browser/android/download/available_offline_content_provider.h" #include "chrome/browser/android/download/available_offline_content_provider.h"
#include "base/strings/string_util.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "chrome/browser/android/chrome_feature_list.h" #include "chrome/browser/android/chrome_feature_list.h"
#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h" #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "components/offline_items_collection/core/offline_content_aggregator.h" #include "components/offline_items_collection/core/offline_content_aggregator.h"
#include "components/offline_items_collection/core/offline_item.h"
#include "components/offline_items_collection/core/test_support/mock_offline_content_provider.h" #include "components/offline_items_collection/core/test_support/mock_offline_content_provider.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace android { namespace android {
...@@ -20,6 +23,7 @@ namespace { ...@@ -20,6 +23,7 @@ namespace {
using offline_items_collection::OfflineContentAggregator; using offline_items_collection::OfflineContentAggregator;
using testing::_; using testing::_;
const char kProviderNamespace[] = "offline_pages";
std::unique_ptr<KeyedService> BuildOfflineContentAggregator( std::unique_ptr<KeyedService> BuildOfflineContentAggregator(
content::BrowserContext* context) { content::BrowserContext* context) {
...@@ -28,9 +32,10 @@ std::unique_ptr<KeyedService> BuildOfflineContentAggregator( ...@@ -28,9 +32,10 @@ std::unique_ptr<KeyedService> BuildOfflineContentAggregator(
offline_items_collection::OfflineItem UselessItem() { offline_items_collection::OfflineItem UselessItem() {
offline_items_collection::OfflineItem item; offline_items_collection::OfflineItem item;
item.original_url = GURL("https://uesless"); item.original_url = GURL("https://useless");
item.filter = offline_items_collection::FILTER_IMAGE; item.filter = offline_items_collection::FILTER_IMAGE;
item.id.id = "Useless"; item.id.id = "Useless";
item.id.name_space = kProviderNamespace;
return item; return item;
} }
...@@ -39,6 +44,7 @@ offline_items_collection::OfflineItem OldOfflinePage() { ...@@ -39,6 +44,7 @@ offline_items_collection::OfflineItem OldOfflinePage() {
item.original_url = GURL("https://already_read"); item.original_url = GURL("https://already_read");
item.filter = offline_items_collection::FILTER_PAGE; item.filter = offline_items_collection::FILTER_PAGE;
item.id.id = "AlreadyRead"; item.id.id = "AlreadyRead";
item.id.name_space = kProviderNamespace;
item.is_suggested = true; item.is_suggested = true;
item.last_accessed_time = base::Time::Now(); item.last_accessed_time = base::Time::Now();
return item; return item;
...@@ -49,7 +55,7 @@ offline_items_collection::OfflineItem SuggestedOfflinePageItem() { ...@@ -49,7 +55,7 @@ offline_items_collection::OfflineItem SuggestedOfflinePageItem() {
item.original_url = GURL("https://page"); item.original_url = GURL("https://page");
item.filter = offline_items_collection::FILTER_PAGE; item.filter = offline_items_collection::FILTER_PAGE;
item.id.id = "SuggestedPage"; item.id.id = "SuggestedPage";
item.id.name_space = "testnamespace"; item.id.name_space = kProviderNamespace;
item.is_suggested = true; item.is_suggested = true;
item.title = "Page Title"; item.title = "Page Title";
item.description = "snippet"; item.description = "snippet";
...@@ -65,6 +71,7 @@ offline_items_collection::OfflineItem VideoItem() { ...@@ -65,6 +71,7 @@ offline_items_collection::OfflineItem VideoItem() {
item.original_url = GURL("https://video"); item.original_url = GURL("https://video");
item.filter = offline_items_collection::FILTER_VIDEO; item.filter = offline_items_collection::FILTER_VIDEO;
item.id.id = "VideoItem"; item.id.id = "VideoItem";
item.id.name_space = kProviderNamespace;
return item; return item;
} }
...@@ -73,39 +80,47 @@ offline_items_collection::OfflineItem AudioItem() { ...@@ -73,39 +80,47 @@ offline_items_collection::OfflineItem AudioItem() {
item.original_url = GURL("https://audio"); item.original_url = GURL("https://audio");
item.filter = offline_items_collection::FILTER_AUDIO; item.filter = offline_items_collection::FILTER_AUDIO;
item.id.id = "AudioItem"; item.id.id = "AudioItem";
item.id.name_space = kProviderNamespace;
return item; return item;
} }
offline_items_collection::OfflineItemVisuals TestThumbnail() {
offline_items_collection::OfflineItemVisuals visuals;
visuals.icon = gfx::test::CreateImage(2, 4);
return visuals;
}
class AvailableOfflineContentTest : public testing::Test { class AvailableOfflineContentTest : public testing::Test {
protected: protected:
void SetUp() override { void SetUp() override {
// To control the items in the aggregator, we create it and register a // To control the items in the aggregator, we create it and register a
// single MockOfflineContentProvider. // single MockOfflineContentProvider.
aggregator = static_cast<OfflineContentAggregator*>( aggregator_ = static_cast<OfflineContentAggregator*>(
OfflineContentAggregatorFactory::GetInstance()->SetTestingFactoryAndUse( OfflineContentAggregatorFactory::GetInstance()->SetTestingFactoryAndUse(
&profile, &BuildOfflineContentAggregator)); &profile_, &BuildOfflineContentAggregator));
aggregator->RegisterProvider("offline_pages", &content_provider); aggregator_->RegisterProvider(kProviderNamespace, &content_provider_);
content_provider_.SetVisuals({});
} }
std::vector<chrome::mojom::AvailableOfflineContentPtr> ListAndWait() { std::vector<chrome::mojom::AvailableOfflineContentPtr> ListAndWait() {
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions; std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions;
bool received = false; bool received = false;
provider.List(base::BindLambdaForTesting( provider_.List(base::BindLambdaForTesting(
[&](std::vector<chrome::mojom::AvailableOfflineContentPtr> result) { [&](std::vector<chrome::mojom::AvailableOfflineContentPtr> result) {
received = true; received = true;
suggestions = std::move(result); suggestions = std::move(result);
})); }));
thread_bundle.RunUntilIdle(); thread_bundle_.RunUntilIdle();
EXPECT_TRUE(received); EXPECT_TRUE(received);
return suggestions; return suggestions;
} }
content::TestBrowserThreadBundle thread_bundle; content::TestBrowserThreadBundle thread_bundle_;
TestingProfile profile; TestingProfile profile_;
base::test::ScopedFeatureList scoped_feature_list; base::test::ScopedFeatureList scoped_feature_list_;
OfflineContentAggregator* aggregator; OfflineContentAggregator* aggregator_;
offline_items_collection::MockOfflineContentProvider content_provider; offline_items_collection::MockOfflineContentProvider content_provider_;
AvailableOfflineContentProvider provider{&profile}; AvailableOfflineContentProvider provider_{&profile_};
}; };
TEST_F(AvailableOfflineContentTest, NoContent) { TEST_F(AvailableOfflineContentTest, NoContent) {
...@@ -116,8 +131,9 @@ TEST_F(AvailableOfflineContentTest, NoContent) { ...@@ -116,8 +131,9 @@ TEST_F(AvailableOfflineContentTest, NoContent) {
} }
TEST_F(AvailableOfflineContentTest, AllContentFilteredOut) { TEST_F(AvailableOfflineContentTest, AllContentFilteredOut) {
scoped_feature_list.InitAndEnableFeature(chrome::android::kNewNetErrorPageUI); scoped_feature_list_.InitAndEnableFeature(
content_provider.SetItems({UselessItem(), OldOfflinePage()}); chrome::android::kNewNetErrorPageUI);
content_provider_.SetItems({UselessItem(), OldOfflinePage()});
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions = std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions =
ListAndWait(); ListAndWait();
...@@ -126,11 +142,15 @@ TEST_F(AvailableOfflineContentTest, AllContentFilteredOut) { ...@@ -126,11 +142,15 @@ TEST_F(AvailableOfflineContentTest, AllContentFilteredOut) {
} }
TEST_F(AvailableOfflineContentTest, ThreeItems) { TEST_F(AvailableOfflineContentTest, ThreeItems) {
scoped_feature_list.InitAndEnableFeature(chrome::android::kNewNetErrorPageUI); scoped_feature_list_.InitAndEnableFeature(
content_provider.SetItems({ chrome::android::kNewNetErrorPageUI);
content_provider_.SetItems({
UselessItem(), VideoItem(), SuggestedOfflinePageItem(), AudioItem(), UselessItem(), VideoItem(), SuggestedOfflinePageItem(), AudioItem(),
}); });
content_provider_.SetVisuals(
{{SuggestedOfflinePageItem().id, TestThumbnail()}});
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions = std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions =
ListAndWait(); ListAndWait();
...@@ -150,15 +170,22 @@ TEST_F(AvailableOfflineContentTest, ThreeItems) { ...@@ -150,15 +170,22 @@ TEST_F(AvailableOfflineContentTest, ThreeItems) {
EXPECT_EQ(page_item.title, first->title); EXPECT_EQ(page_item.title, first->title);
EXPECT_EQ(page_item.description, first->snippet); EXPECT_EQ(page_item.description, first->snippet);
EXPECT_EQ("4 hours ago", first->date_modified); EXPECT_EQ("4 hours ago", first->date_modified);
// attribution and thumbnail_data_uri not yet implemented. // At the time of writing this test, the output was:
// data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAAECAYAAACk7+45AAAAFk
// lEQVQYlWNk+M/wn4GBgYGJAQowGQBCcgIG00vTRwAAAABJRU5ErkJggg==
// Since other encodings are possible, just check the prefix. PNGs all have
// the same 8 byte header.
EXPECT_TRUE(base::StartsWith(first->thumbnail_data_uri.spec(),
"data:image/png;base64,iVBORw0K",
base::CompareCase::SENSITIVE));
// TODO(crbug.com/852872): Add attribution.
EXPECT_EQ("", first->attribution); EXPECT_EQ("", first->attribution);
EXPECT_EQ("", first->thumbnail_data_uri);
} }
TEST_F(AvailableOfflineContentTest, NotEnabled) { TEST_F(AvailableOfflineContentTest, NotEnabled) {
scoped_feature_list.InitAndDisableFeature( scoped_feature_list_.InitAndDisableFeature(
chrome::android::kNewNetErrorPageUI); chrome::android::kNewNetErrorPageUI);
content_provider.SetItems({SuggestedOfflinePageItem()}); content_provider_.SetItems({SuggestedOfflinePageItem()});
std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions = std::vector<chrome::mojom::AvailableOfflineContentPtr> suggestions =
ListAndWait(); ListAndWait();
......
...@@ -367,6 +367,14 @@ html[subframe] body { ...@@ -367,6 +367,14 @@ html[subframe] body {
color: rgb(255, 255, 255); color: rgb(255, 255, 255);
} }
/*
TODO(https://crbug.com/852872): UI for offline suggested content is incomplete.
*/
.suggested-thumbnail {
width: 25vw;
height: 25vw;
}
/* Alternate dino page button styles */ /* Alternate dino page button styles */
#control-buttons .reload-button-alternate:disabled { #control-buttons .reload-button-alternate:disabled {
background: #ccc; background: #ccc;
......
...@@ -160,8 +160,16 @@ function offlineContentAvailable(content) { ...@@ -160,8 +160,16 @@ function offlineContentAvailable(content) {
var div = document.getElementById('offline-suggestions'); var div = document.getElementById('offline-suggestions');
var suggestionText = []; var suggestionText = [];
for (var c of content) { for (var c of content) {
var visual = '';
if (c.thumbnail_data_uri) {
// html_inline.py will try to replace src attributes with data URIs using
// a simple regex. The following is obfuscated slightly to avoid that.
var src = 'src';
visual = `<img ${src}="${c.thumbnail_data_uri}"` +
' class="suggested-thumbnail"></img>';
}
suggestionText.push( suggestionText.push(
`<li>${c.title} ${c.date_modified} ${c.attribution}</li>`); `<li>${visual} ${c.title} ${c.date_modified} ${c.attribution}</li>`);
} }
var htmlList = document.getElementById('offline-content-list'); var htmlList = document.getElementById('offline-content-list');
htmlList.innerHTML = suggestionText.join('\n'); htmlList.innerHTML = suggestionText.join('\n');
......
...@@ -185,8 +185,8 @@ TEST_F(OfflineContentAggregatorTest, ActionPropagatesToRightProvider) { ...@@ -185,8 +185,8 @@ TEST_F(OfflineContentAggregatorTest, ActionPropagatesToRightProvider) {
EXPECT_CALL(provider2, ResumeDownload(id2, true)).Times(1); EXPECT_CALL(provider2, ResumeDownload(id2, true)).Times(1);
EXPECT_CALL(provider1, PauseDownload(id1)).Times(1); EXPECT_CALL(provider1, PauseDownload(id1)).Times(1);
EXPECT_CALL(provider2, PauseDownload(id2)).Times(1); EXPECT_CALL(provider2, PauseDownload(id2)).Times(1);
EXPECT_CALL(provider1, GetVisualsForItem(id1, _)).Times(1); EXPECT_CALL(provider1, GetVisualsForItem_(id1, _)).Times(1);
EXPECT_CALL(provider2, GetVisualsForItem(id2, _)).Times(1); EXPECT_CALL(provider2, GetVisualsForItem_(id2, _)).Times(1);
EXPECT_CALL(provider1, GetShareInfoForItem(id1, _)).Times(1); EXPECT_CALL(provider1, GetShareInfoForItem(id1, _)).Times(1);
EXPECT_CALL(provider2, GetShareInfoForItem(id2, _)).Times(1); EXPECT_CALL(provider2, GetShareInfoForItem(id2, _)).Times(1);
aggregator_.OpenItem(LaunchLocation::DOWNLOAD_HOME, id1); aggregator_.OpenItem(LaunchLocation::DOWNLOAD_HOME, id1);
......
...@@ -23,6 +23,12 @@ void MockOfflineContentProvider::SetItems(const OfflineItemList& items) { ...@@ -23,6 +23,12 @@ void MockOfflineContentProvider::SetItems(const OfflineItemList& items) {
items_ = items; items_ = items;
} }
void MockOfflineContentProvider::SetVisuals(
std::map<ContentId, OfflineItemVisuals> visuals) {
override_visuals_ = true;
visuals_ = std::move(visuals);
}
void MockOfflineContentProvider::NotifyOnItemsAdded( void MockOfflineContentProvider::NotifyOnItemsAdded(
const OfflineItemList& items) { const OfflineItemList& items) {
for (auto& observer : observers_) for (auto& observer : observers_)
...@@ -39,6 +45,21 @@ void MockOfflineContentProvider::NotifyOnItemUpdated(const OfflineItem& item) { ...@@ -39,6 +45,21 @@ void MockOfflineContentProvider::NotifyOnItemUpdated(const OfflineItem& item) {
observer.OnItemUpdated(item); observer.OnItemUpdated(item);
} }
void MockOfflineContentProvider::GetVisualsForItem(const ContentId& id,
VisualsCallback callback) {
if (!override_visuals_) {
GetVisualsForItem_(id, std::move(callback));
} else {
std::unique_ptr<OfflineItemVisuals> visuals;
auto iter = visuals_.find(id);
if (iter != visuals_.end()) {
visuals = std::make_unique<OfflineItemVisuals>(iter->second);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), id, std::move(visuals)));
}
}
void MockOfflineContentProvider::GetAllItems(MultipleItemCallback callback) { void MockOfflineContentProvider::GetAllItems(MultipleItemCallback callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), items_)); FROM_HERE, base::BindOnce(std::move(callback), items_));
......
...@@ -31,6 +31,9 @@ class MockOfflineContentProvider : public OfflineContentProvider { ...@@ -31,6 +31,9 @@ class MockOfflineContentProvider : public OfflineContentProvider {
bool HasObserver(Observer* observer); bool HasObserver(Observer* observer);
void SetItems(const OfflineItemList& items); void SetItems(const OfflineItemList& items);
// Sets visuals returned by |GetVisualsForItem()|. If this is not called,
// then the mocked method |GetVisualsForItem_()| is called instead.
void SetVisuals(std::map<ContentId, OfflineItemVisuals> visuals);
void NotifyOnItemsAdded(const OfflineItemList& items); void NotifyOnItemsAdded(const OfflineItemList& items);
void NotifyOnItemRemoved(const ContentId& id); void NotifyOnItemRemoved(const ContentId& id);
void NotifyOnItemUpdated(const OfflineItem& item); void NotifyOnItemUpdated(const OfflineItem& item);
...@@ -41,7 +44,10 @@ class MockOfflineContentProvider : public OfflineContentProvider { ...@@ -41,7 +44,10 @@ class MockOfflineContentProvider : public OfflineContentProvider {
MOCK_METHOD1(CancelDownload, void(const ContentId&)); MOCK_METHOD1(CancelDownload, void(const ContentId&));
MOCK_METHOD1(PauseDownload, void(const ContentId&)); MOCK_METHOD1(PauseDownload, void(const ContentId&));
MOCK_METHOD2(ResumeDownload, void(const ContentId&, bool)); MOCK_METHOD2(ResumeDownload, void(const ContentId&, bool));
MOCK_METHOD2(GetVisualsForItem, void(const ContentId&, VisualsCallback)); MOCK_METHOD2(GetVisualsForItem_,
void(const ContentId&, const VisualsCallback&));
void GetVisualsForItem(const ContentId& id,
VisualsCallback callback) override;
MOCK_METHOD2(GetShareInfoForItem, void(const ContentId&, ShareCallback)); MOCK_METHOD2(GetShareInfoForItem, void(const ContentId&, ShareCallback));
void GetAllItems(MultipleItemCallback callback) override; void GetAllItems(MultipleItemCallback callback) override;
void GetItemById(const ContentId& id, SingleItemCallback callback) override; void GetItemById(const ContentId& id, SingleItemCallback callback) override;
...@@ -51,6 +57,8 @@ class MockOfflineContentProvider : public OfflineContentProvider { ...@@ -51,6 +57,8 @@ class MockOfflineContentProvider : public OfflineContentProvider {
private: private:
base::ObserverList<Observer>::Unchecked observers_; base::ObserverList<Observer>::Unchecked observers_;
OfflineItemList items_; OfflineItemList items_;
std::map<ContentId, OfflineItemVisuals> visuals_;
bool override_visuals_ = false;
}; };
} // namespace offline_items_collection } // namespace offline_items_collection
......
...@@ -97,7 +97,7 @@ TEST_F(ThrottledOfflineContentProviderTest, TestBasicPassthrough) { ...@@ -97,7 +97,7 @@ TEST_F(ThrottledOfflineContentProviderTest, TestBasicPassthrough) {
EXPECT_CALL(wrapped_provider_, CancelDownload(id)); EXPECT_CALL(wrapped_provider_, CancelDownload(id));
EXPECT_CALL(wrapped_provider_, PauseDownload(id)); EXPECT_CALL(wrapped_provider_, PauseDownload(id));
EXPECT_CALL(wrapped_provider_, ResumeDownload(id, true)); EXPECT_CALL(wrapped_provider_, ResumeDownload(id, true));
EXPECT_CALL(wrapped_provider_, GetVisualsForItem(id, _)); EXPECT_CALL(wrapped_provider_, GetVisualsForItem_(id, _));
wrapped_provider_.SetItems(items); wrapped_provider_.SetItems(items);
provider_.OpenItem(LaunchLocation::DOWNLOAD_HOME, id); provider_.OpenItem(LaunchLocation::DOWNLOAD_HOME, id);
provider_.RemoveItem(id); provider_.RemoveItem(id);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment