blob: 952d15b858d9cbb61b419eb41f85084529417327 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/search/background/ntp_custom_background_service.h"
#include <optional>
#include <vector>
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "base/token.h"
#include "build/build_config.h"
#include "chrome/browser/search/background/ntp_background_service_factory.h"
#include "chrome/browser/search/background/ntp_custom_background_service_observer.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/search/instant_types.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/testing_profile.h"
#include "components/search/ntp_features.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/mojom/themes.mojom.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
namespace {
using testing::SaveArg;
class MockNtpCustomBackgroundServiceObserver
: public NtpCustomBackgroundServiceObserver {
public:
MOCK_METHOD0(OnCustomBackgroundImageUpdated, void());
MOCK_METHOD0(OnNtpCustomBackgroundServiceShuttingDown, void());
};
class MockThemeService : public ThemeService {
public:
MockThemeService() : ThemeService(nullptr, theme_helper_) { set_ready(); }
MOCK_CONST_METHOD0(UsingExtensionTheme, bool());
MOCK_METHOD1(BuildAutogeneratedThemeFromColor, void(SkColor));
MOCK_METHOD2(SetUserColorAndBrowserColorVariant,
void(SkColor, ui::mojom::BrowserColorVariant));
private:
ThemeHelper theme_helper_;
};
class MockNtpBackgroundService : public NtpBackgroundService {
public:
explicit MockNtpBackgroundService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: NtpBackgroundService(url_loader_factory) {}
MOCK_CONST_METHOD1(IsValidBackdropCollection, bool(const std::string&));
MOCK_METHOD(void,
VerifyImageURL,
(const GURL&, base::OnceCallback<void(int)>),
(override));
};
std::unique_ptr<TestingProfile> MakeTestingProfile(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
{
TestingProfile::Builder profile_builder;
profile_builder.AddTestingFactory(
ThemeServiceFactory::GetInstance(),
base::BindRepeating([](content::BrowserContext* context)
-> std::unique_ptr<KeyedService> {
return std::make_unique<testing::NiceMock<MockThemeService>>();
}));
profile_builder.AddTestingFactory(
NtpBackgroundServiceFactory::GetInstance(),
base::BindRepeating(
[](scoped_refptr<network::SharedURLLoaderFactory>
url_loader_factory,
content::BrowserContext* context)
-> std::unique_ptr<KeyedService> {
return std::make_unique<
testing::NiceMock<MockNtpBackgroundService>>(
url_loader_factory);
},
url_loader_factory));
profile_builder.SetSharedURLLoaderFactory(url_loader_factory);
auto profile = profile_builder.Build();
TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile.get(),
base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor));
return profile;
}
}
base::Value::Dict GetBackgroundInfoAsDict(const GURL& background_url,
const GURL& thumbnail_url) {
base::Value::Dict background_info;
background_info.Set("background_url", base::Value(background_url.spec()));
background_info.Set("thumbnail_url", base::Value(thumbnail_url.spec()));
background_info.Set("attribution_line_1", base::Value(std::string()));
background_info.Set("attribution_line_2", base::Value(std::string()));
background_info.Set("attribution_action_url", base::Value(std::string()));
background_info.Set("collection_id", base::Value(std::string()));
background_info.Set("resume_token", base::Value(std::string()));
background_info.Set("refresh_timestamp", base::Value(0));
return background_info;
}
base::Time GetReferenceTime() {
static constexpr base::Time::Exploded kReferenceTime = {
.year = 2019, .month = 1, .day_of_week = 1, .day_of_month = 1};
base::Time out_time;
EXPECT_TRUE(base::Time::FromLocalExploded(kReferenceTime, &out_time));
return out_time;
}
} // namespace
class NtpCustomBackgroundServiceTest : public testing::Test {
public:
NtpCustomBackgroundServiceTest()
: profile_(
MakeTestingProfile(test_url_loader_factory_.GetSafeWeakWrapper())),
mock_theme_service_(static_cast<MockThemeService*>(
ThemeServiceFactory::GetForProfile(profile_.get()))),
mock_ntp_background_service_(static_cast<MockNtpBackgroundService*>(
NtpBackgroundServiceFactory::GetForProfile(profile_.get()))) {}
void SetUp() override {
custom_background_service_ =
std::make_unique<NtpCustomBackgroundService>(profile_.get());
custom_background_service_->AddObserver(&observer_);
}
void SetUpResponseWithNetworkError(const GURL& load_url) {
test_url_loader_factory_.AddResponse(load_url.spec(), std::string(),
net::HTTP_NOT_FOUND);
}
void SetUpResponseWithNetworkSuccess(const GURL& load_url,
const std::string& response = "") {
test_url_loader_factory_.AddResponse(load_url.spec(), response);
}
void SetUpResponseWithData(const GURL& load_url,
const std::string& response) {
test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
[&](const network::ResourceRequest& request) {}));
SetUpResponseWithNetworkSuccess(load_url, response);
}
TestingProfile& profile() { return *profile_; }
MockThemeService& mock_theme_service() { return *mock_theme_service_; }
MockNtpBackgroundService& mock_ntp_background_service() {
return *mock_ntp_background_service_;
}
protected:
// NOTE: The initialization order of these members matters.
content::BrowserTaskEnvironment task_environment_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<TestingProfile> profile_;
base::SimpleTestClock clock_;
MockNtpCustomBackgroundServiceObserver observer_;
raw_ptr<MockThemeService> mock_theme_service_;
raw_ptr<MockNtpBackgroundService> mock_ntp_background_service_;
base::HistogramTester histogram_tester_;
std::unique_ptr<NtpCustomBackgroundService> custom_background_service_;
};
TEST_F(NtpCustomBackgroundServiceTest, SetCustomBackgroundURL) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(1);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kUrl("https://www.foo.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kUrl);
custom_background_service_->SetCustomBackgroundInfo(kUrl, GURL(), "", "",
GURL(), "");
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kUrl, custom_background->custom_background_url);
EXPECT_FALSE(custom_background->is_uploaded_image);
EXPECT_FALSE(custom_background->daily_refresh_enabled);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, SetCustomBackgroundURLInvalidURL) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kInvalidUrl("foo");
const GURL kValidUrl("https://www.foo.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kValidUrl);
custom_background_service_->SetCustomBackgroundInfo(kValidUrl, GURL(), "", "",
GURL(), "");
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kValidUrl.spec(), custom_background->custom_background_url);
custom_background_service_->SetCustomBackgroundInfo(kInvalidUrl, GURL(), "",
"", GURL(), "");
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_FALSE(custom_background.has_value());
EXPECT_FALSE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, SetCustomBackgroundInfo) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(1);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kUrl("https://www.foo.com");
const std::string kAttributionLine1 = "foo";
const std::string kAttributionLine2 = "bar";
const GURL kActionUrl("https://www.bar.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kUrl);
custom_background_service_->SetCustomBackgroundInfo(
kUrl, GURL(), kAttributionLine1, kAttributionLine2, kActionUrl, "");
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kUrl, custom_background->custom_background_url);
EXPECT_EQ(false, custom_background->is_uploaded_image);
EXPECT_EQ(kAttributionLine1,
custom_background->custom_background_attribution_line_1);
EXPECT_EQ(kAttributionLine2,
custom_background->custom_background_attribution_line_2);
EXPECT_EQ(kActionUrl,
custom_background->custom_background_attribution_action_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, LocalBackgroundImageCopyCreated) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(1);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
base::FilePath profile_path = profile().GetPath();
base::FilePath path(profile_path.AppendASCII("test_file"));
base::FilePath copy_path(profile_path.AppendASCII(
chrome::kChromeUIUntrustedNewTabPageBackgroundFilename));
base::WriteFile(path, "background_image");
custom_background_service_->SelectLocalBackgroundImage(path);
task_environment_.RunUntilIdle();
bool file_exists = base::PathExists(copy_path);
EXPECT_EQ(true, file_exists);
EXPECT_EQ(true, profile().GetTestingPrefService()->GetBoolean(
prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(true, custom_background->is_uploaded_image);
}
TEST_F(NtpCustomBackgroundServiceTest,
SettingUrlRemovesLocalBackgroundImageCopy) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(1);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kUrl("https://www.foo.com");
base::FilePath profile_path = profile().GetPath();
base::FilePath path(profile_path.AppendASCII(
chrome::kChromeUIUntrustedNewTabPageBackgroundFilename));
base::WriteFile(path, "background_image");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kUrl);
custom_background_service_->SetCustomBackgroundInfo(kUrl, GURL(), "", "",
GURL(), "");
task_environment_.RunUntilIdle();
bool file_exists = base::PathExists(path);
EXPECT_EQ(false, file_exists);
EXPECT_EQ(false, profile().GetTestingPrefService()->GetBoolean(
prefs::kNtpCustomBackgroundLocalToDevice));
ASSERT_TRUE(custom_background_service_->IsCustomBackgroundSet());
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(false, custom_background->is_uploaded_image);
}
TEST_F(NtpCustomBackgroundServiceTest, UpdatingPrefUpdatesNtpTheme) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kUrlFoo("https://www.foo.com");
const GURL kUrlBar("https://www.bar.com");
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile().GetTestingPrefService();
pref_service->SetUserPref(prefs::kNtpCustomBackgroundDict,
GetBackgroundInfoAsDict(kUrlFoo, GURL()));
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kUrlFoo, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
pref_service->SetUserPref(prefs::kNtpCustomBackgroundDict,
GetBackgroundInfoAsDict(kUrlBar, GURL()));
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kUrlBar, custom_background->custom_background_url);
EXPECT_EQ(false, custom_background->is_uploaded_image);
EXPECT_EQ(false,
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, SetLocalImage) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(1);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile().GetTestingPrefService();
base::FilePath profile_path = profile().GetPath();
base::FilePath path(profile_path.AppendASCII(
chrome::kChromeUIUntrustedNewTabPageBackgroundFilename));
base::WriteFile(path, "background_image");
base::ThreadPoolInstance::Get()->FlushForTesting();
custom_background_service_->SelectLocalBackgroundImage(path);
task_environment_.RunUntilIdle();
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_TRUE(
base::StartsWith(custom_background->custom_background_url.spec(),
chrome::kChromeUIUntrustedNewTabPageBackgroundUrl,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
EXPECT_EQ(true, custom_background->is_uploaded_image);
}
TEST_F(NtpCustomBackgroundServiceTest, SyncPrefOverridesAndRemovesLocalImage) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kUrl("https://www.foo.com/");
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile().GetTestingPrefService();
base::FilePath profile_path = profile().GetPath();
base::FilePath path(profile_path.AppendASCII(
chrome::kChromeUIUntrustedNewTabPageBackgroundFilename));
base::WriteFile(path, "background_image");
base::ThreadPoolInstance::Get()->FlushForTesting();
custom_background_service_->SelectLocalBackgroundImage(path);
task_environment_.RunUntilIdle();
EXPECT_TRUE(
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_TRUE(base::PathExists(path));
// Update custom_background info via Sync.
pref_service->SetUserPref(prefs::kNtpCustomBackgroundDict,
GetBackgroundInfoAsDict(kUrl, GURL()));
task_environment_.RunUntilIdle();
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kUrl, custom_background->custom_background_url);
EXPECT_EQ(false, custom_background->is_uploaded_image);
EXPECT_FALSE(
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_FALSE(base::PathExists(path));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, ValidateBackdropUrls) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(4);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kBackdropUrl1("https://www.foo.com");
const GURL kBackdropUrl2("https://www.bar.com");
const GURL kNonBackdropUrl1("https://www.test.com");
const GURL kNonBackdropUrl2("https://www.foo.com/path");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl1);
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl2);
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl1, GURL(), "",
"", GURL(), "");
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kBackdropUrl1, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
custom_background_service_->SetCustomBackgroundInfo(kNonBackdropUrl1, GURL(),
"", "", GURL(), "");
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_FALSE(custom_background.has_value());
EXPECT_FALSE(custom_background_service_->IsCustomBackgroundSet());
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl2, GURL(), "",
"", GURL(), "");
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kBackdropUrl2, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
custom_background_service_->SetCustomBackgroundInfo(kNonBackdropUrl2, GURL(),
"", "", GURL(), "");
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_FALSE(custom_background.has_value());
EXPECT_FALSE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, LocalImageDoesNotHaveAttribution) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kUrl("https://www.foo.com");
const std::string kAttributionLine1 = "foo";
const std::string kAttributionLine2 = "bar";
const GURL kActionUrl("https://www.bar.com");
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile().GetTestingPrefService();
mock_ntp_background_service().AddValidBackdropUrlForTesting(kUrl);
custom_background_service_->SetCustomBackgroundInfo(
kUrl, GURL(), kAttributionLine1, kAttributionLine2, kActionUrl, "");
auto custom_background = custom_background_service_->GetCustomBackground();
ASSERT_EQ(kAttributionLine1,
custom_background->custom_background_attribution_line_1);
ASSERT_EQ(kAttributionLine2,
custom_background->custom_background_attribution_line_2);
ASSERT_EQ(kActionUrl,
custom_background->custom_background_attribution_action_url);
ASSERT_TRUE(custom_background_service_->IsCustomBackgroundSet());
base::FilePath profile_path = profile().GetPath();
base::FilePath path(profile_path.AppendASCII(
chrome::kChromeUIUntrustedNewTabPageBackgroundFilename));
base::WriteFile(path, "background_image");
base::ThreadPoolInstance::Get()->FlushForTesting();
custom_background_service_->SelectLocalBackgroundImage(path);
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_TRUE(
base::StartsWith(custom_background->custom_background_url.spec(),
chrome::kChromeUIUntrustedNewTabPageBackgroundUrl,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
EXPECT_EQ(true, custom_background->is_uploaded_image);
EXPECT_EQ("", custom_background->custom_background_attribution_line_1);
EXPECT_EQ("", custom_background->custom_background_attribution_line_2);
EXPECT_EQ(GURL(),
custom_background->custom_background_attribution_action_url);
}
TEST_F(NtpCustomBackgroundServiceTest, SetCustomBackgroundCollectionId) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kImageUrl("https://www.test.com/");
const std::string kInvalidId("aarrtt");
const std::string kValidId("art");
ntp::background::Image response_image;
response_image.set_image_url(kImageUrl.spec());
ntp::background::GetImagesInCollectionResponse response;
*response.add_images() = response_image;
std::string response_string;
response.SerializeToString(&response_string);
SetUpResponseWithData(
mock_ntp_background_service().GetNextImageURLForTesting(),
response_string);
// A valid id should update the pref/background.
CollectionImage image;
image.collection_id = kValidId;
image.image_url = kImageUrl;
mock_ntp_background_service().SetNextCollectionImageForTesting(image);
mock_ntp_background_service().AddValidBackdropCollectionForTesting(kValidId);
EXPECT_CALL(mock_ntp_background_service(), IsValidBackdropCollection)
.WillOnce(testing::Return(true));
custom_background_service_->SetCustomBackgroundInfo(GURL(), GURL(), "", "",
GURL(), kValidId);
task_environment_.RunUntilIdle();
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_TRUE(custom_background->daily_refresh_enabled);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
// An invalid id should clear the pref/background.
CollectionImage image2;
mock_ntp_background_service().SetNextCollectionImageForTesting(image2);
EXPECT_CALL(mock_ntp_background_service(), IsValidBackdropCollection)
.WillOnce(testing::Return(false));
custom_background_service_->SetCustomBackgroundInfo(GURL(), GURL(), "", "",
GURL(), kInvalidId);
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_FALSE(custom_background.has_value());
EXPECT_FALSE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, RefreshesBackgroundAfter24Hours) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const std::string kValidId("art");
const GURL kImageUrl1("https://www.test.com/1/");
const GURL kImageUrl2("https://www.test.com/2/");
ntp::background::Image response_image;
response_image.set_image_url(kImageUrl1.spec());
ntp::background::GetImagesInCollectionResponse response;
*response.add_images() = response_image;
std::string response_string;
response.SerializeToString(&response_string);
SetUpResponseWithData(
mock_ntp_background_service().GetNextImageURLForTesting(),
response_string);
custom_background_service_->SetClockForTesting(&clock_);
clock_.SetNow(GetReferenceTime());
// A valid id should update the pref/background.
CollectionImage image;
image.collection_id = kValidId;
image.image_url = kImageUrl1;
mock_ntp_background_service().SetNextCollectionImageForTesting(image);
mock_ntp_background_service().AddValidBackdropCollectionForTesting(kValidId);
EXPECT_CALL(mock_ntp_background_service(), IsValidBackdropCollection)
.WillOnce(testing::Return(true));
custom_background_service_->SetCustomBackgroundInfo(GURL(), GURL(), "", "",
GURL(), kValidId);
task_environment_.RunUntilIdle();
auto custom_background = custom_background_service_->GetCustomBackground();
const std::string image_options =
mock_ntp_background_service().GetImageOptionsForTesting();
EXPECT_EQ(GURL(kImageUrl1.spec() + image_options),
custom_background->custom_background_url);
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
CollectionImage image2;
image2.collection_id = kValidId;
image2.image_url = kImageUrl2;
mock_ntp_background_service().SetNextCollectionImageForTesting(image2);
ON_CALL(mock_theme_service(), UsingExtensionTheme())
.WillByDefault(testing::Return(false));
ntp::background::Image response_image_2;
response_image_2.set_image_url(kImageUrl2.spec());
ntp::background::GetImagesInCollectionResponse response_2;
*response_2.add_images() = response_image_2;
std::string response_string_2;
response_2.SerializeToString(&response_string_2);
SetUpResponseWithData(
mock_ntp_background_service().GetNextImageURLForTesting(),
response_string_2);
// Should not refresh background.
custom_background_service_->RefreshBackgroundIfNeeded();
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(GURL(kImageUrl1.spec() + image_options),
custom_background->custom_background_url);
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
clock_.Advance(base::Hours(25));
// Should refresh background after >24 hours.
custom_background_service_->RefreshBackgroundIfNeeded();
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_EQ(kImageUrl2.spec() + image_options,
custom_background->custom_background_url);
EXPECT_TRUE(custom_background->daily_refresh_enabled);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
// Should not refresh if there is an extension theme.
clock_.Advance(base::Hours(25));
ON_CALL(mock_theme_service(), UsingExtensionTheme())
.WillByDefault(testing::Return(true));
custom_background_service_->RefreshBackgroundIfNeeded();
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kImageUrl2.spec() + image_options,
custom_background->custom_background_url);
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_TRUE(custom_background->daily_refresh_enabled);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, RevertBackgroundChanges) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kBackdropUrl1("https://www.foo.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl1);
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl1, GURL(), "",
"", GURL(), "");
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kBackdropUrl1, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
// Revert from background set using |kBackdropUrl1| to the starting state (no
// background) since no background change was confirmed.
custom_background_service_->RevertBackgroundChanges();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_FALSE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest,
RevertBackgroundChangesWithMultipleSelections) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(3);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kBackdropUrl1("https://www.foo.com");
const GURL kBackdropUrl2("https://www.bar.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl1);
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl2);
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl1, GURL(), "",
"", GURL(), "");
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kBackdropUrl1, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl2, GURL(), "",
"", GURL(), "");
// Revert from background set using |kBackdropUrl2| to the starting state (no
// background) since no background change was confirmed.
custom_background_service_->RevertBackgroundChanges();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_FALSE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, ConfirmBackgroundChanges) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(3);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kBackdropUrl1("https://www.foo.com");
const GURL kBackdropUrl2("https://www.bar.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl1);
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl2);
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl1, GURL(), "",
"", GURL(), "");
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kBackdropUrl1, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
custom_background_service_->ConfirmBackgroundChanges();
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl2, GURL(), "",
"", GURL(), "");
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kBackdropUrl2, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
// Revert from background set using |kBackdropUrl2| to the starting state
// (background set using |kBackdropUrl1|) since it is the last confirmed
// background change.
custom_background_service_->RevertBackgroundChanges();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kBackdropUrl1, custom_background->custom_background_url);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
}
TEST_F(NtpCustomBackgroundServiceTest, TestUpdateCustomBackgroundColor) {
// TODO (crbug/1520873): Fix and re-enable or remove if no longer relevant.
if (features::IsChromeRefresh2023()) {
GTEST_SKIP();
}
// Turn on Color Extraction feature.
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
ntp_features::kCustomizeChromeColorExtraction);
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
EXPECT_CALL(mock_theme_service(), BuildAutogeneratedThemeFromColor).Times(1);
SkBitmap bitmap;
bitmap.allocN32Pixels(32, 32);
bitmap.eraseColor(SK_ColorRED);
gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
// Background color will not update if no background is set.
custom_background_service_->UpdateCustomBackgroundColorAsync(
GURL(), image, image_fetcher::RequestMetadata());
task_environment_.RunUntilIdle();
auto custom_background = custom_background_service_->GetCustomBackground();
auto custom_background_main_color =
custom_background ? custom_background->custom_background_main_color
: SK_ColorWHITE;
EXPECT_NE(SK_ColorRED, custom_background_main_color.value_or(SK_ColorWHITE));
const GURL kUrl("https://www.foo.com");
const GURL kThumbnailUrl("https://www.thumbnail.com");
const std::string kAttributionLine1 = "foo";
const std::string kAttributionLine2 = "bar";
const GURL kActionUrl("https://www.bar.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kUrl);
mock_ntp_background_service().AddValidBackdropUrlForTesting(kThumbnailUrl);
custom_background_service_->SetCustomBackgroundInfo(
kUrl, kThumbnailUrl, kAttributionLine1, kAttributionLine2, kActionUrl,
"");
image_fetcher::RequestMetadata metadata = image_fetcher::RequestMetadata();
// Background color will not update if metadata http code invalid.
metadata.http_response_code =
image_fetcher::RequestMetadata::ResponseCode::RESPONSE_CODE_INVALID;
custom_background_service_->UpdateCustomBackgroundColorAsync(kUrl, image,
metadata);
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_NE(
SK_ColorRED,
custom_background->custom_background_main_color.value_or(SK_ColorWHITE));
// Background color will not update if current background url changed.
metadata.http_response_code = 200;
custom_background_service_->UpdateCustomBackgroundColorAsync(
GURL("different_url"), image, metadata);
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_NE(
SK_ColorRED,
custom_background->custom_background_main_color.value_or(SK_ColorWHITE));
// Background color should update.
custom_background_service_->UpdateCustomBackgroundColorAsync(kUrl, image,
metadata);
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(
SK_ColorRED,
custom_background->custom_background_main_color.value_or(SK_ColorWHITE));
}
TEST_F(NtpCustomBackgroundServiceTest, TestUpdateCustomLocalBackgroundColor) {
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile().GetTestingPrefService();
SkColor color = SK_ColorBLUE;
EXPECT_CALL(mock_theme_service(), SetUserColorAndBrowserColorVariant)
.Times(1)
.WillOnce(SaveArg<0>(&color));
SkBitmap bitmap;
bitmap.allocN32Pixels(32, 32);
bitmap.eraseColor(SK_ColorRED);
gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
pref_service->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, false);
// Background color will not update if local background is not set.
// This is checked by not making another call to ThemeService.
custom_background_service_->UpdateCustomLocalBackgroundColorAsync(image);
task_environment_.RunUntilIdle();
pref_service->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, true);
// Background color should update.
custom_background_service_->UpdateCustomLocalBackgroundColorAsync(image);
task_environment_.RunUntilIdle();
EXPECT_EQ(SK_ColorRED, color);
}
// Most of the color extraction pipeline is tested above. The only thing tested
// here is that when kChromeWebuiRefresh2023 is enabled, we call
// SetUserColorAndBrowserColorVariant() instead of
// BuildAutogeneratedThemeFromColor().
TEST_F(NtpCustomBackgroundServiceTest, TestUpdateCustomBackgroundColorGM3) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kChromeRefresh2023, features::kChromeWebuiRefresh2023}, {});
// Create image that is one color so that we know what the extracted color
// will be.
SkBitmap bitmap;
bitmap.allocN32Pixels(32, 32);
bitmap.eraseColor(SK_ColorRED);
gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
// Set valid custom background info.
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const GURL kUrl("https://www.foo.com");
const GURL kThumbnailUrl("https://www.thumbnail.com");
const std::string kAttributionLine1 = "foo";
const std::string kAttributionLine2 = "bar";
const GURL kActionUrl("https://www.bar.com");
custom_background_service_->AddValidBackdropUrlForTesting(kUrl);
custom_background_service_->AddValidBackdropUrlForTesting(kThumbnailUrl);
custom_background_service_->SetCustomBackgroundInfo(
kUrl, kThumbnailUrl, kAttributionLine1, kAttributionLine2, kActionUrl,
"");
// Create fake metadata.
image_fetcher::RequestMetadata metadata = image_fetcher::RequestMetadata();
metadata.http_response_code = 200;
// Check that calling UpdateCustomBackgroundColorAsync() with correct data
// will call SetUserColorAndBrowserColorVariant instead of
// BuildAutogeneratedThemeFromColor() and sets the main color.
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(1);
EXPECT_CALL(mock_theme_service(), SetUserColorAndBrowserColorVariant)
.Times(1);
custom_background_service_->UpdateCustomBackgroundColorAsync(kUrl, image,
metadata);
task_environment_.RunUntilIdle();
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(
SK_ColorRED,
custom_background->custom_background_main_color.value_or(SK_ColorWHITE));
}
TEST_F(NtpCustomBackgroundServiceTest,
VerifyCustomBackgroundImageURLWithNetworkSuccess) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
const GURL kBackdropUrl("https://www.foo.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl);
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl, GURL(), "",
"", GURL(), "");
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
// Background should remain the same.
SetUpResponseWithNetworkSuccess(kBackdropUrl);
EXPECT_CALL(mock_ntp_background_service(),
VerifyImageURL(kBackdropUrl, testing::_))
.Times(1)
.WillOnce(testing::WithArg<1>([](auto callback) {
std::move(callback).Run(
/*headers_response_code=*/net::HTTP_OK);
}));
custom_background_service_->VerifyCustomBackgroundImageURL();
task_environment_.RunUntilIdle();
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
histogram_tester_.ExpectTotalCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
}
TEST_F(NtpCustomBackgroundServiceTest,
VerifyCustomBackgroundImageURLWithNetworkError) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
const GURL kBackdropUrl("https://www.foo.com");
mock_ntp_background_service().AddValidBackdropUrlForTesting(kBackdropUrl);
custom_background_service_->SetCustomBackgroundInfo(kBackdropUrl, GURL(), "",
"", GURL(), "");
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
// Background should be reset.
SetUpResponseWithNetworkError(kBackdropUrl);
EXPECT_CALL(mock_ntp_background_service(),
VerifyImageURL(kBackdropUrl, testing::_))
.Times(1)
.WillOnce(testing::WithArg<1>([](auto callback) {
std::move(callback).Run(
/*headers_response_code=*/net::HTTP_NOT_FOUND);
}));
histogram_tester_.ExpectTotalCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
custom_background_service_->VerifyCustomBackgroundImageURL();
task_environment_.RunUntilIdle();
EXPECT_FALSE(custom_background_service_->IsCustomBackgroundSet());
histogram_tester_.ExpectTotalCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 1);
ASSERT_EQ(1, histogram_tester_.GetBucketCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected",
NtpImageType::kBackgroundImage));
}
TEST_F(NtpCustomBackgroundServiceTest,
VerifyDailyRefreshBackgroundImageURLWithNetworkError) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(3);
ASSERT_FALSE(custom_background_service_->IsCustomBackgroundSet());
const std::string kValidId("art");
const std::string kImageUrl1("https://www.test.com/1/");
const std::string kImageUrl2("https://www.test.com/2/");
const std::string image_options(
mock_ntp_background_service().GetImageOptionsForTesting());
const GURL kImageUrl1WithOptions(kImageUrl1 + image_options);
const GURL kImageUrl2WithOptions(kImageUrl2 + image_options);
// A valid id should update the pref/background.
CollectionImage image;
image.collection_id = kValidId;
image.image_url = GURL(kImageUrl1);
mock_ntp_background_service().SetNextCollectionImageForTesting(image);
mock_ntp_background_service().AddValidBackdropCollectionForTesting(kValidId);
ntp::background::Image response_image;
response_image.set_image_url(kImageUrl1);
ntp::background::GetImagesInCollectionResponse response;
*response.add_images() = response_image;
std::string response_string;
response.SerializeToString(&response_string);
SetUpResponseWithData(
mock_ntp_background_service().GetNextImageURLForTesting(),
response_string);
EXPECT_CALL(mock_ntp_background_service(), IsValidBackdropCollection)
.WillOnce(testing::Return(true));
custom_background_service_->SetCustomBackgroundInfo(GURL(), GURL(), "", "",
GURL(), kValidId);
task_environment_.RunUntilIdle();
CollectionImage image2;
image2.collection_id = kValidId;
image2.image_url = GURL(kImageUrl2);
mock_ntp_background_service().SetNextCollectionImageForTesting(image2);
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kImageUrl1WithOptions, custom_background->custom_background_url);
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
ntp::background::Image response_image_2;
response_image_2.set_image_url(kImageUrl2);
ntp::background::GetImagesInCollectionResponse response_2;
*response_2.add_images() = response_image_2;
std::string response_string_2;
response_2.SerializeToString(&response_string_2);
SetUpResponseWithData(
mock_ntp_background_service().GetNextImageURLForTesting(),
response_string_2);
// Should force a background refresh.
SetUpResponseWithNetworkError(kImageUrl1WithOptions);
EXPECT_CALL(mock_ntp_background_service(),
VerifyImageURL(kImageUrl1WithOptions, testing::_))
.Times(1)
.WillOnce(testing::WithArg<1>([](auto callback) {
std::move(callback).Run(
/*headers_response_code=*/net::HTTP_NOT_FOUND);
}));
histogram_tester_.ExpectTotalCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 0);
custom_background_service_->VerifyCustomBackgroundImageURL();
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kImageUrl2WithOptions, custom_background->custom_background_url);
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_TRUE(custom_background->daily_refresh_enabled);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
histogram_tester_.ExpectTotalCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 1);
ASSERT_EQ(1, histogram_tester_.GetBucketCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected",
NtpImageType::kBackgroundImage));
// Background should be the same.
SetUpResponseWithNetworkSuccess(kImageUrl2WithOptions);
EXPECT_CALL(mock_ntp_background_service(),
VerifyImageURL(kImageUrl2WithOptions, testing::_))
.Times(1)
.WillOnce(testing::WithArg<1>([](auto callback) {
std::move(callback).Run(
/*headers_response_code=*/net::HTTP_OK);
}));
custom_background_service_->VerifyCustomBackgroundImageURL();
task_environment_.RunUntilIdle();
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_EQ(kImageUrl2WithOptions, custom_background->custom_background_url);
EXPECT_EQ(kValidId, custom_background->collection_id);
EXPECT_TRUE(custom_background->daily_refresh_enabled);
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
histogram_tester_.ExpectTotalCount(
"NewTabPage.BackgroundService.Images.Headers.ErrorDetected", 1);
}
TEST_F(NtpCustomBackgroundServiceTest, LocalImageURLsDoNotGetVerified) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated).Times(2);
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile().GetTestingPrefService();
base::FilePath profile_path = profile().GetPath();
base::FilePath path(profile_path.AppendASCII(
chrome::kChromeUIUntrustedNewTabPageBackgroundFilename));
base::WriteFile(path, "background_image");
base::ThreadPoolInstance::Get()->FlushForTesting();
custom_background_service_->SelectLocalBackgroundImage(path);
task_environment_.RunUntilIdle();
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_TRUE(
base::StartsWith(custom_background->custom_background_url.spec(),
chrome::kChromeUIUntrustedNewTabPageBackgroundUrl,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
EXPECT_EQ(true, custom_background->is_uploaded_image);
custom_background_service_->VerifyCustomBackgroundImageURL();
task_environment_.RunUntilIdle();
// Background should be the same.
custom_background = custom_background_service_->GetCustomBackground();
EXPECT_TRUE(
base::StartsWith(custom_background->custom_background_url.spec(),
chrome::kChromeUIUntrustedNewTabPageBackgroundUrl,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
EXPECT_EQ(true, custom_background->is_uploaded_image);
}
TEST_F(NtpCustomBackgroundServiceTest, SetBackgroundToLocalResourceWithId) {
EXPECT_CALL(observer_, OnCustomBackgroundImageUpdated);
sync_preferences::TestingPrefServiceSyncable* pref_service =
profile().GetTestingPrefService();
base::Token token = base::Token::CreateRandom();
custom_background_service_->SetBackgroundToLocalResourceWithId(token, true);
task_environment_.RunUntilIdle();
// Check that local background image was set.
auto custom_background = custom_background_service_->GetCustomBackground();
EXPECT_TRUE(base::StartsWith(
custom_background->custom_background_url.spec(),
chrome::kChromeUIUntrustedNewTabPageUrl + token.ToString() +
chrome::kChromeUIUntrustedNewTabPageBackgroundFilename,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(
pref_service->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice));
EXPECT_EQ(pref_service->GetString(prefs::kNtpCustomBackgroundLocalToDeviceId),
token.ToString());
EXPECT_TRUE(pref_service->GetBoolean(prefs::kNtpCustomBackgroundInspiration));
EXPECT_TRUE(custom_background_service_->IsCustomBackgroundSet());
EXPECT_EQ(true, custom_background->is_uploaded_image);
EXPECT_TRUE(custom_background->is_inspiration_image);
}