blob: 89f10578805f6089e7bb39da30f49641611065ba [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 "chrome/browser/ui/webui/settings/downloads_handler.h"
#include "base/json/json_reader.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_core_service_impl.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/enterprise/connectors/connectors_prefs.h"
#include "chrome/browser/enterprise/connectors/file_system/account_info_utils.h"
#include "chrome/browser/enterprise/connectors/file_system/service_settings.h"
#include "chrome/browser/enterprise/connectors/file_system/test_helper.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/os_crypt/os_crypt_mocker.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_ui.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_download_manager.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
namespace ec = enterprise_connectors;
using testing::NiceMock;
using WebUIDataReceivedPtr = std::unique_ptr<content::TestWebUI::CallData>;
struct DownloadsSettings {
std::string auto_open_downloads = "";
bool connection_init_enabled = false;
bool connection_init_linked_account = false;
};
const char kAccountName[] = "Jane Doe";
const char kAccountLogin[] = "janedoe@downloads_handler_unittest.com";
const char kFolderId[] = "12345";
const char kFolderLinkFormat[] = "https://app.box.com/folder/%s";
const char kFolderName[] = "ChromeDownloads";
void VerifyLinkedAccountInfo(const base::Value* dict,
bool expect_linked,
std::string expect_account_name,
std::string expect_account_login,
std::string expect_folder_name,
std::string expect_folder_id) {
ASSERT_TRUE(dict->is_dict());
absl::optional<bool> has_account_linked_opt = dict->FindBoolKey("linked");
ASSERT_TRUE(has_account_linked_opt.has_value());
const bool has_account_linked = has_account_linked_opt.value();
ASSERT_EQ(has_account_linked, expect_linked) << *dict;
const std::string* account_name = dict->FindStringPath("account.name");
const std::string* account_login = dict->FindStringPath("account.login");
const std::string* folder_link = dict->FindStringPath("folder.link");
const std::string* folder_name = dict->FindStringPath("folder.name");
ASSERT_EQ(has_account_linked, account_name != nullptr) << *dict;
ASSERT_EQ(has_account_linked, account_login != nullptr) << *dict;
ASSERT_EQ(has_account_linked, folder_link != nullptr) << *dict;
ASSERT_EQ(has_account_linked, folder_name != nullptr) << *dict;
if (has_account_linked) {
ASSERT_EQ(*account_name, expect_account_name);
ASSERT_EQ(*account_login, expect_account_login);
ASSERT_EQ(*folder_name, expect_folder_name);
std::string expect_folder_link =
base::StringPrintf(kFolderLinkFormat, expect_folder_id.c_str());
ASSERT_EQ(*folder_link, expect_folder_link);
}
}
void VerifyDownloadsConnectionPolicyChangedCallback(
const std::vector<WebUIDataReceivedPtr>& web_ui_call_data,
bool expect_connection_policy_enabled,
bool expect_policy_change_received,
size_t wait_till_nth_account_info,
bool expect_account_linked,
std::string expect_account_name,
std::string expect_account_login,
std::string expect_folder_name,
std::string expect_folder_id) {
ASSERT_FALSE(web_ui_call_data.empty());
bool policy_change_received = false;
size_t account_info_received = 0;
for (auto& data : web_ui_call_data) {
EXPECT_EQ("cr.webUIListenerCallback", data->function_name());
ASSERT_TRUE(data->arg1()->is_string());
const std::string& event = data->arg1()->GetString();
if (event == "downloads-connection-policy-changed") {
policy_change_received = true;
ASSERT_TRUE(data->arg2()->is_bool());
ASSERT_TRUE(expect_policy_change_received);
EXPECT_EQ(data->arg2()->GetBool(), expect_connection_policy_enabled);
} else if (event == "downloads-connection-link-changed") {
++account_info_received;
if (account_info_received >= wait_till_nth_account_info) {
VerifyLinkedAccountInfo(data->arg2(), expect_account_linked,
expect_account_name, expect_account_login,
expect_folder_name, expect_folder_id);
}
}
}
ASSERT_EQ(expect_policy_change_received, policy_change_received);
ASSERT_GE(account_info_received, wait_till_nth_account_info);
ASSERT_EQ(expect_connection_policy_enabled, account_info_received > 0);
}
void VerifyAutoOpenDownloadsChangedCallback(const WebUIDataReceivedPtr& data) {
EXPECT_EQ("cr.webUIListenerCallback", data->function_name());
ASSERT_TRUE(data->arg1()->is_string());
EXPECT_EQ("auto-open-downloads-changed", data->arg1()->GetString());
ASSERT_TRUE(data->arg2()->is_bool());
EXPECT_FALSE(data->arg2()->GetBool());
}
} // namespace
namespace settings {
class DownloadsHandlerTest : public testing::TestWithParam<DownloadsSettings> {
public:
DownloadsHandlerTest()
: download_manager_(new NiceMock<content::MockDownloadManager>()),
handler_(&profile_) {
profile_.SetDownloadManagerForTesting(
base::WrapUnique(download_manager_.get()));
std::unique_ptr<ChromeDownloadManagerDelegate> delegate =
std::make_unique<ChromeDownloadManagerDelegate>(&profile_);
chrome_download_manager_delegate_ = delegate.get();
service_ = DownloadCoreServiceFactory::GetForBrowserContext(&profile_);
service_->SetDownloadManagerDelegateForTesting(std::move(delegate));
EXPECT_CALL(*download_manager_, GetBrowserContext())
.WillRepeatedly(testing::Return(&profile_));
EXPECT_CALL(*download_manager_, Shutdown());
handler_.set_web_ui(&test_web_ui_);
}
void SetUp() override {
EXPECT_TRUE(web_ui_call_data().empty());
const auto& param = GetParam();
profile()->GetPrefs()->SetString(prefs::kDownloadExtensionsToOpen,
param.auto_open_downloads);
// Setup the downloads connection feature.
OSCryptMocker::SetUp();
feature_list_.InitWithFeatures({ec::kFileSystemConnectorEnabled}, {});
ASSERT_TRUE(ec::SetFileSystemOAuth2Tokens(
profile()->GetPrefs(), ec::kFileSystemServiceProviderPrefNameBox,
"AToken", "RToken"));
if (param.connection_init_linked_account)
ASSERT_TRUE(param.connection_init_enabled);
SetDownloadsConnectionPolicy(param.connection_init_enabled);
if (param.connection_init_linked_account)
SetLinkedAccount(kAccountName, kAccountLogin, kFolderName, kFolderId);
size_t expected_init_callback_count = 2u;
// SendDownloadsConnectionToJavascript().
expected_init_callback_count += param.connection_init_enabled;
base::ListValue args;
handler()->HandleInitialize(args.GetList());
EXPECT_TRUE(handler()->IsJavascriptAllowed());
ASSERT_EQ(web_ui_call_data().size(), expected_init_callback_count);
VerifyAutoOpenDownloadsChangedCallback();
VerifyDownloadsConnectionPolicyChangedCallback(
param.connection_init_linked_account);
ClearWebUiTrackedCalls();
}
void TearDown() override {
service_->SetDownloadManagerDelegateForTesting(nullptr);
OSCryptMocker::TearDown();
testing::Test::TearDown();
}
void SetDownloadsConnectionPolicy(bool enable) {
profile()->GetPrefs()->Set(
ec::kSendDownloadToCloudPref,
enable ? *base::JSONReader::Read(ec::kWildcardSendDownloadToCloudPref)
: base::ListValue());
ASSERT_EQ(handler_.IsDownloadsConnectionPolicyEnabled(), enable);
connection_policy_enabled_ = enable;
}
void SetLinkedAccount(std::string account_name,
std::string account_login,
std::string folder_name,
std::string folder_id) {
base::DictionaryValue account_info;
account_info.SetStringKey("name", account_name);
account_info.SetStringKey("login", account_login);
ec::SetFileSystemAccountInfo(profile()->GetPrefs(),
ec::kFileSystemServiceProviderPrefNameBox,
std::move(account_info));
ec::SetDefaultFolder(profile()->GetPrefs(),
ec::kFileSystemServiceProviderPrefNameBox, folder_id,
folder_name);
account_name_ = account_name;
account_login_ = account_login;
folder_name_ = folder_name;
folder_id_ = folder_id;
}
void ToggleDownloadsConnectionPolicy() {
SetDownloadsConnectionPolicy(!connection_policy_enabled());
}
void VerifyDownloadsConnectionAccountLinkedUpdateCallback(
bool has_linked_account,
size_t wait_till_nth_account_info,
bool expect_policy_change_received = false) {
ASSERT_NE(web_ui_call_data().size(), 0u);
::VerifyDownloadsConnectionPolicyChangedCallback(
web_ui_call_data(), connection_policy_enabled(),
expect_policy_change_received, wait_till_nth_account_info,
has_linked_account, account_name_, account_login_, folder_name_,
folder_id_);
}
void VerifyDownloadsConnectionPolicyChangedCallback(
bool has_linked_account = GetParam().connection_init_linked_account) {
ASSERT_NE(web_ui_call_data().size(), 0u);
VerifyDownloadsConnectionAccountLinkedUpdateCallback(
has_linked_account, /* wait_till_nth_account_info = */ 0,
/* expect_policy_change_received = */ true);
}
void VerifyAutoOpenDownloadsChangedCallback() const {
ASSERT_FALSE(web_ui_call_data().empty());
::VerifyAutoOpenDownloadsChangedCallback(web_ui_call_data().back());
}
Profile* profile() { return &profile_; }
DownloadsHandler* handler() { return &handler_; }
const std::vector<WebUIDataReceivedPtr>& web_ui_call_data() const {
return test_web_ui_.call_data();
}
void ClearWebUiTrackedCalls() {
test_web_ui_.ClearTrackedCalls();
ASSERT_EQ(web_ui_call_data().size(), 0u);
}
bool connection_policy_enabled() const { return connection_policy_enabled_; }
private:
content::BrowserTaskEnvironment task_environment_;
content::TestWebUI test_web_ui_;
TestingProfile profile_;
raw_ptr<DownloadCoreService> service_;
raw_ptr<content::MockDownloadManager>
download_manager_; // Owned by |profile_|.
raw_ptr<ChromeDownloadManagerDelegate> chrome_download_manager_delegate_;
bool connection_policy_enabled_;
std::string account_name_, account_login_, folder_name_, folder_id_;
// Experimental flag for downloads connection.
base::test::ScopedFeatureList feature_list_;
DownloadsHandler handler_;
};
TEST_P(DownloadsHandlerTest, AutoOpenDownloads) {
// Touch the pref.
profile()->GetPrefs()->SetString(prefs::kDownloadExtensionsToOpen, "def");
EXPECT_EQ(web_ui_call_data().size(), 1u);
VerifyAutoOpenDownloadsChangedCallback();
}
TEST_P(DownloadsHandlerTest, DownloadsConnectionToggle) {
// Toggle the feature policy.
ToggleDownloadsConnectionPolicy();
VerifyDownloadsConnectionPolicyChangedCallback();
ASSERT_EQ(connection_policy_enabled(), !GetParam().connection_init_enabled);
}
TEST_P(DownloadsHandlerTest, DownloadsConnectionSendAccountInfo) {
// Ensure we can enable the feature policy.
if (!connection_policy_enabled()) {
SetDownloadsConnectionPolicy(true);
VerifyDownloadsConnectionPolicyChangedCallback(
/* has_linked_account = */ GetParam().connection_init_linked_account);
}
ASSERT_TRUE(connection_policy_enabled());
ClearWebUiTrackedCalls();
// Once feature is enabled via policy, account info updates are sent.
SetLinkedAccount("John Smith", "johnsmith@example.com", "DifferentFolderName",
"24680");
// Expect number of account updates due to number of prefs being observed. The
// account infos received before this are mid-update.
const size_t expect_account_updates_count =
ec::GetFileSystemConnectorAccountInfoPrefs(
ec::kFileSystemServiceProviderPrefNameBox)
.size();
ASSERT_GE(web_ui_call_data().size(), expect_account_updates_count);
VerifyDownloadsConnectionAccountLinkedUpdateCallback(
/* has_linked_account = */ true,
/* wait_till_nth_account_info = */ expect_account_updates_count,
/* expect_policy_change_received = */ false);
}
const DownloadsSettings test_settings[] =
// .-- .auto_open_downloads
// | .-- .connection_init_enabled
// | | .---- .connection_init_linked_account
// | | | (must be false if `connection_init_enabled` is false)
{{"", true, true}, // Connection enabled + has linked account
{"", true, false}, // Connection enabled + no linked account
{"abc", false, false}}; // Connection disabled + no linked account
INSTANTIATE_TEST_SUITE_P(SettingsPage,
DownloadsHandlerTest,
testing::ValuesIn(test_settings));
} // namespace settings