blob: 534914501b89bd85da3b63ca0b0121733e3000c8 [file] [log] [blame]
// Copyright 2021 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/ui/webui/settings/privacy_sandbox_handler.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "components/privacy_sandbox/canonical_topic.h"
#include "components/privacy_sandbox/privacy_sandbox_settings.h"
#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_web_ui.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace {
using Topic = browsing_topics::Topic;
constexpr char kCallbackId1[] = "test-callback-id";
constexpr char kCallbackId2[] = "test-callback-id-2";
class MockPrivacySandboxService : public PrivacySandboxService {
public:
MOCK_METHOD(void,
GetFledgeJoiningEtldPlusOneForDisplay,
(base::OnceCallback<void(std::vector<std::string>)>),
(override));
MOCK_METHOD(std::vector<std::string>,
GetBlockedFledgeJoiningTopFramesForDisplay,
(),
(const override));
MOCK_METHOD(void,
SetFledgeJoiningAllowed,
((const std::string&), bool),
(const override));
MOCK_METHOD(std::vector<privacy_sandbox::CanonicalTopic>,
GetCurrentTopTopics,
(),
(const override));
MOCK_METHOD(std::vector<privacy_sandbox::CanonicalTopic>,
GetBlockedTopics,
(),
(const override));
MOCK_METHOD(void,
SetTopicAllowed,
(privacy_sandbox::CanonicalTopic, bool),
(override));
};
std::unique_ptr<KeyedService> BuildMockPrivacySandboxService(
content::BrowserContext* context) {
return std::make_unique<::testing::StrictMock<MockPrivacySandboxService>>();
}
void ValidateFledgeInfo(content::TestWebUI* web_ui,
std::string expected_callback_id,
std::vector<std::string> expected_joining_sites,
std::vector<std::string> expected_blocked_sites) {
const content::TestWebUI::CallData& data = *web_ui->call_data().back();
EXPECT_EQ(expected_callback_id, data.arg1()->GetString());
EXPECT_EQ("cr.webUIResponse", data.function_name());
ASSERT_TRUE(data.arg2()->GetBool());
ASSERT_TRUE(data.arg3()->is_dict());
auto* blocked_sites = data.arg3()->FindListKey("blockedSites");
ASSERT_TRUE(blocked_sites);
ASSERT_EQ(expected_blocked_sites.size(),
blocked_sites->GetListDeprecated().size());
for (size_t i = 0; i < expected_blocked_sites.size(); i++) {
EXPECT_EQ(expected_blocked_sites[i],
blocked_sites->GetListDeprecated()[i].GetString());
}
auto* joining_sites = data.arg3()->FindListKey("joiningSites");
ASSERT_TRUE(joining_sites);
ASSERT_EQ(expected_joining_sites.size(),
joining_sites->GetListDeprecated().size());
for (size_t i = 0; i < expected_joining_sites.size(); i++) {
EXPECT_EQ(expected_joining_sites[i],
joining_sites->GetListDeprecated()[i].GetString());
}
}
void ValidateTopicsInfo(
std::vector<privacy_sandbox::CanonicalTopic> expected_topics,
base::Value::ConstListView actual_topics) {
ASSERT_EQ(expected_topics.size(), actual_topics.size());
for (size_t i = 0; i < expected_topics.size(); i++) {
const auto& actual_topic = actual_topics[i];
const auto& expected_topic = expected_topics[i];
ASSERT_TRUE(actual_topic.is_dict());
ASSERT_EQ(expected_topic.topic_id().value(),
actual_topic.FindIntKey("topicId"));
ASSERT_EQ(expected_topic.taxonomy_version(),
actual_topic.FindIntKey("taxonomyVersion"));
ASSERT_EQ(expected_topic.GetLocalizedRepresentation(),
base::UTF8ToUTF16(*actual_topic.FindStringKey("displayString")));
}
}
} // namespace
namespace settings {
class PrivacySandboxHandlerTest : public testing::Test {
public:
PrivacySandboxHandlerTest()
: browser_task_environment_(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
web_ui_ = std::make_unique<content::TestWebUI>();
web_ui_->set_web_contents(web_contents_.get());
handler_ = std::make_unique<PrivacySandboxHandler>();
handler_->set_web_ui(web_ui());
handler_->AllowJavascript();
web_ui_->ClearTrackedCalls();
}
void TearDown() override {
handler_->set_web_ui(nullptr);
handler_.reset();
web_ui_.reset();
}
content::TestWebUI* web_ui() { return web_ui_.get(); }
PrivacySandboxHandler* handler() { return handler_.get(); }
TestingProfile* profile() { return &profile_; }
privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings() {
return PrivacySandboxSettingsFactory::GetForProfile(profile());
}
private:
content::BrowserTaskEnvironment browser_task_environment_;
content::RenderViewHostTestEnabler render_view_host_test_enabler_;
TestingProfile profile_;
std::unique_ptr<content::WebContents> web_contents_ =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
std::unique_ptr<content::TestWebUI> web_ui_;
std::unique_ptr<PrivacySandboxHandler> handler_;
};
class PrivacySandboxHandlerTestMockService : public PrivacySandboxHandlerTest {
public:
void SetUp() override {
PrivacySandboxHandlerTest::SetUp();
mock_privacy_sandbox_service_ = static_cast<MockPrivacySandboxService*>(
PrivacySandboxServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), base::BindRepeating(&BuildMockPrivacySandboxService)));
}
MockPrivacySandboxService* mock_privacy_sandbox_service() {
return mock_privacy_sandbox_service_;
}
private:
raw_ptr<MockPrivacySandboxService> mock_privacy_sandbox_service_;
};
TEST_F(PrivacySandboxHandlerTestMockService, SetFledgeJoiningAllowed) {
// Confirm that the handler forward FLEDGE allowed changes to the service.
const std::string kTestSite = "example.com";
EXPECT_CALL(*mock_privacy_sandbox_service(),
SetFledgeJoiningAllowed(kTestSite, true));
base::Value args(base::Value::Type::LIST);
args.Append(kTestSite);
args.Append(true);
handler()->HandleSetFledgeJoiningAllowed(args.GetList());
}
TEST_F(PrivacySandboxHandlerTestMockService, GetFledgeState) {
// Confirm that FLEDGE state is correctly returned. As FLEDGE state is
// retrieved async, the handler must also support multiple requests in flight.
using Callback = base::OnceCallback<void(std::vector<std::string>)>;
Callback callback_one;
Callback callback_two;
EXPECT_CALL(*mock_privacy_sandbox_service(),
GetFledgeJoiningEtldPlusOneForDisplay(testing::_))
.Times(2)
.WillOnce([&](Callback callback) { callback_one = std::move(callback); })
.WillOnce([&](Callback callback) { callback_two = std::move(callback); });
base::Value args(base::Value::Type::LIST);
args.Append(kCallbackId1);
handler()->HandleGetFledgeState(args.GetList());
args.ClearList();
args.Append(kCallbackId2);
handler()->HandleGetFledgeState(args.GetList());
// Provide different sets of information to each request to the FLEDGE
// backend.
const std::vector<std::string> kJoiningSites1 = {"e.com", "f.com"};
const std::vector<std::string> kJoiningSites2 = {"g.com", "h.com"};
const std::vector<std::string> kBlockedSites1 = {"a.com", "b.com"};
const std::vector<std::string> kBlockedSites2 = {"c.com", "d.com"};
EXPECT_CALL(*mock_privacy_sandbox_service(),
GetBlockedFledgeJoiningTopFramesForDisplay())
.Times(2)
.WillOnce(testing::Return(kBlockedSites1))
.WillOnce(testing::Return(kBlockedSites2));
std::move(callback_one).Run(kJoiningSites1);
ValidateFledgeInfo(web_ui(), kCallbackId1, kJoiningSites1, kBlockedSites1);
std::move(callback_two).Run(kJoiningSites2);
ValidateFledgeInfo(web_ui(), kCallbackId2, kJoiningSites2, kBlockedSites2);
}
TEST_F(PrivacySandboxHandlerTestMockService, SetTopicAllowed) {
// Confirm that the handler correctly constructs the CanonicalTopic and
// passes it to the PrivacySandboxService.
const privacy_sandbox::CanonicalTopic kTestTopic(
Topic(1), privacy_sandbox::CanonicalTopic::AVAILABLE_TAXONOMY);
EXPECT_CALL(*mock_privacy_sandbox_service(),
SetTopicAllowed(kTestTopic, false))
.Times(1);
base::Value args(base::Value::Type::LIST);
args.Append(kTestTopic.topic_id().value());
args.Append(kTestTopic.taxonomy_version());
args.Append(false);
handler()->HandleSetTopicAllowed(args.GetList());
}
TEST_F(PrivacySandboxHandlerTestMockService, GetTopicsState) {
const std::vector<privacy_sandbox::CanonicalTopic> kBlockedTopics = {
privacy_sandbox::CanonicalTopic(
Topic(1), privacy_sandbox::CanonicalTopic::AVAILABLE_TAXONOMY),
privacy_sandbox::CanonicalTopic(
Topic(2), privacy_sandbox::CanonicalTopic::AVAILABLE_TAXONOMY)};
const std::vector<privacy_sandbox::CanonicalTopic> kTopTopics = {
privacy_sandbox::CanonicalTopic(
Topic(3), privacy_sandbox::CanonicalTopic::AVAILABLE_TAXONOMY),
privacy_sandbox::CanonicalTopic(
Topic(4), privacy_sandbox::CanonicalTopic::AVAILABLE_TAXONOMY)};
EXPECT_CALL(*mock_privacy_sandbox_service(), GetCurrentTopTopics())
.Times(1)
.WillOnce(testing::Return(kTopTopics));
EXPECT_CALL(*mock_privacy_sandbox_service(), GetBlockedTopics())
.Times(1)
.WillOnce(testing::Return(kBlockedTopics));
base::Value args(base::Value::Type::LIST);
args.Append(kCallbackId1);
handler()->HandleGetTopicsState(args.GetList());
const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
EXPECT_EQ(kCallbackId1, data.arg1()->GetString());
EXPECT_EQ("cr.webUIResponse", data.function_name());
ASSERT_TRUE(data.arg2()->GetBool());
ASSERT_TRUE(data.arg3()->is_dict());
ValidateTopicsInfo(
kTopTopics, data.arg3()->FindListKey("topTopics")->GetListDeprecated());
ValidateTopicsInfo(
kBlockedTopics,
data.arg3()->FindListKey("blockedTopics")->GetListDeprecated());
}
} // namespace settings