Commit efb67d3e authored by Anastasiia Nikolaienko's avatar Anastasiia Nikolaienko Committed by Commit Bot

Add AD account to Account Manager

Show Active Directory account in the "Accounts" list in Settings.

Bug: 996206
Change-Id: I23fa5c51977c81466b3bff5f38e0772240acbd05
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1763691
Commit-Queue: Anastasiia Nikolaienko <anastasiian@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarKush Sinha <sinhak@chromium.org>
Cr-Commit-Position: refs/heads/master@{#716947}
parent dccf70a9
......@@ -93,6 +93,83 @@ void ShowToast(const std::string& id, const base::string16& message) {
id, message, kToastDurationMs, /*dismiss_text=*/base::nullopt));
}
class AccountBuilder {
public:
AccountBuilder() = default;
~AccountBuilder() = default;
void PopulateFrom(base::DictionaryValue account) {
account_ = std::move(account);
}
bool IsEmpty() const { return account_.empty(); }
AccountBuilder& SetId(const std::string& value) {
account_.SetStringKey("id", value);
return *this;
}
AccountBuilder& SetEmail(const std::string& value) {
account_.SetStringKey("email", value);
return *this;
}
AccountBuilder& SetFullName(const std::string& value) {
account_.SetStringKey("fullName", value);
return *this;
}
AccountBuilder& SetAccountType(const int& value) {
account_.SetIntKey("accountType", value);
return *this;
}
AccountBuilder& SetIsDeviceAccount(const bool& value) {
account_.SetBoolKey("isDeviceAccount", value);
return *this;
}
AccountBuilder& SetIsSignedIn(const bool& value) {
account_.SetBoolKey("isSignedIn", value);
return *this;
}
AccountBuilder& SetUnmigrated(const bool& value) {
account_.SetBoolKey("unmigrated", value);
return *this;
}
AccountBuilder& SetPic(const std::string& value) {
account_.SetStringKey("pic", value);
return *this;
}
AccountBuilder& SetOrganization(const std::string& value) {
account_.SetStringKey("organization", value);
return *this;
}
// Should be called only once.
base::DictionaryValue Build() {
// Check that values were set.
DCHECK(account_.FindStringKey("id"));
DCHECK(account_.FindStringKey("email"));
DCHECK(account_.FindStringKey("fullName"));
DCHECK(account_.FindIntKey("accountType"));
DCHECK(account_.FindBoolKey("isDeviceAccount"));
DCHECK(account_.FindBoolKey("isSignedIn"));
DCHECK(account_.FindBoolKey("unmigrated"));
DCHECK(account_.FindStringKey("pic"));
// "organization" is an optional field.
return std::move(account_);
}
private:
base::DictionaryValue account_;
DISALLOW_COPY_AND_ASSIGN(AccountBuilder);
};
} // namespace
AccountManagerUIHandler::AccountManagerUIHandler(
......@@ -109,6 +186,9 @@ AccountManagerUIHandler::AccountManagerUIHandler(
AccountManagerUIHandler::~AccountManagerUIHandler() = default;
void AccountManagerUIHandler::RegisterMessages() {
if (!profile_)
profile_ = Profile::FromWebUI(web_ui());
web_ui()->RegisterMessageCallback(
"getAccounts",
base::BindRepeating(&AccountManagerUIHandler::HandleGetAccounts,
......@@ -136,6 +216,10 @@ void AccountManagerUIHandler::RegisterMessages() {
weak_factory_.GetWeakPtr()));
}
void AccountManagerUIHandler::SetProfileForTesting(Profile* profile) {
profile_ = profile;
}
void AccountManagerUIHandler::HandleGetAccounts(const base::ListValue* args) {
AllowJavascript();
......@@ -153,14 +237,58 @@ void AccountManagerUIHandler::HandleGetAccounts(const base::ListValue* args) {
void AccountManagerUIHandler::OnGetAccounts(
base::Value callback_id,
const std::vector<AccountManager::Account>& stored_accounts) {
base::Value::ListStorage accounts;
user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile_);
DCHECK(user);
base::DictionaryValue gaia_device_account;
base::ListValue accounts = GetSecondaryGaiaAccounts(
stored_accounts, user->GetAccountId(), &gaia_device_account);
AccountBuilder device_account;
if (user->IsActiveDirectoryUser()) {
device_account.SetId(user->GetAccountId().GetObjGuid())
.SetAccountType(
account_manager::AccountType::ACCOUNT_TYPE_ACTIVE_DIRECTORY)
.SetEmail(user->GetDisplayEmail())
.SetFullName(base::UTF16ToUTF8(user->GetDisplayName()))
.SetIsSignedIn(true)
.SetUnmigrated(false);
gfx::ImageSkia default_icon =
*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_LOGIN_DEFAULT_USER);
device_account.SetPic(webui::GetBitmapDataUrl(
default_icon.GetRepresentation(1.0f).GetBitmap()));
} else {
device_account.PopulateFrom(std::move(gaia_device_account));
}
const AccountId device_account_id =
ProfileHelper::Get()
->GetUserByProfile(Profile::FromWebUI(web_ui()))
->GetAccountId();
if (!device_account.IsEmpty()) {
device_account.SetIsDeviceAccount(true);
// Check if user is managed.
if (profile_->IsChild()) {
device_account.SetOrganization(kFamilyLink);
} else if (user->IsActiveDirectoryUser()) {
device_account.SetOrganization(
GetEnterpriseDomainFromUsername(user->GetDisplayEmail()));
} else if (profile_->GetProfilePolicyConnector()->IsManaged()) {
device_account.SetOrganization(GetEnterpriseDomainFromUsername(
identity_manager_->GetPrimaryAccountInfo().email));
}
base::DictionaryValue device_account;
// Device account must show up at the top.
accounts.GetList().insert(accounts.GetList().begin(),
device_account.Build());
}
ResolveJavascriptCallback(callback_id, accounts);
}
base::ListValue AccountManagerUIHandler::GetSecondaryGaiaAccounts(
const std::vector<AccountManager::Account>& stored_accounts,
const AccountId device_account_id,
base::DictionaryValue* device_account) {
base::ListValue accounts;
for (const auto& stored_account : stored_accounts) {
const AccountManager::AccountKey& account_key = stored_account.key;
// We are only interested in listing GAIA accounts.
......@@ -169,10 +297,6 @@ void AccountManagerUIHandler::OnGetAccounts(
continue;
}
base::DictionaryValue account;
account.SetString("id", account_key.id);
account.SetInteger("accountType", account_key.account_type);
account.SetBoolean("isDeviceAccount", false);
base::Optional<AccountInfo> maybe_account_info =
identity_manager_
......@@ -180,53 +304,34 @@ void AccountManagerUIHandler::OnGetAccounts(
account_key.id);
DCHECK(maybe_account_info.has_value());
account.SetBoolean(
"isSignedIn",
!identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
maybe_account_info->account_id));
account.SetString("fullName", maybe_account_info->full_name);
account.SetString("email", stored_account.raw_email);
AccountBuilder account;
account.SetId(account_key.id)
.SetAccountType(account_key.account_type)
.SetIsDeviceAccount(false)
.SetFullName(maybe_account_info->full_name)
.SetEmail(stored_account.raw_email)
.SetUnmigrated(account_manager_->HasDummyGaiaToken(account_key))
.SetIsSignedIn(!identity_manager_
->HasAccountWithRefreshTokenInPersistentErrorState(
maybe_account_info->account_id));
if (!maybe_account_info->account_image.IsEmpty()) {
account.SetString("pic",
webui::GetBitmapDataUrl(
maybe_account_info->account_image.AsBitmap()));
account.SetPic(webui::GetBitmapDataUrl(
maybe_account_info->account_image.AsBitmap()));
} else {
gfx::ImageSkia default_icon =
*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_LOGIN_DEFAULT_USER);
account.SetString("pic",
webui::GetBitmapDataUrl(
default_icon.GetRepresentation(1.0f).GetBitmap()));
account.SetPic(webui::GetBitmapDataUrl(
default_icon.GetRepresentation(1.0f).GetBitmap()));
}
account.SetBoolean("unmigrated",
account_manager_->HasDummyGaiaToken(account_key));
if (IsSameAccount(account_key, device_account_id)) {
device_account = std::move(account);
*device_account = account.Build();
} else {
accounts.push_back(std::move(account));
accounts.Append(account.Build());
}
}
// Device account must show up at the top.
if (!device_account.empty()) {
device_account.SetBoolean("isDeviceAccount", true);
// Check if user is managed.
const Profile* const profile = Profile::FromWebUI(web_ui());
if (profile->IsChild()) {
device_account.SetString("organization", kFamilyLink);
} else if (profile->GetProfilePolicyConnector()->IsManaged()) {
device_account.SetString(
"organization",
GetEnterpriseDomainFromUsername(
identity_manager_->GetPrimaryAccountInfo().email));
}
accounts.insert(accounts.begin(), std::move(device_account));
}
ResolveJavascriptCallback(callback_id, base::Value(std::move(accounts)));
return accounts;
}
void AccountManagerUIHandler::HandleAddAccount(const base::ListValue* args) {
......@@ -263,9 +368,7 @@ void AccountManagerUIHandler::HandleRemoveAccount(const base::ListValue* args) {
CHECK(dictionary);
const AccountId device_account_id =
ProfileHelper::Get()
->GetUserByProfile(Profile::FromWebUI(web_ui()))
->GetAccountId();
ProfileHelper::Get()->GetUserByProfile(profile_)->GetAccountId();
const AccountManager::AccountKey account_key =
GetAccountKeyFromJsCallback(dictionary);
if (IsSameAccount(account_key, device_account_id)) {
......
......@@ -13,8 +13,11 @@
#include "base/scoped_observer.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
#include "chromeos/components/account_manager/account_manager.h"
#include "components/account_id/account_id.h"
#include "components/signin/public/identity_manager/identity_manager.h"
class Profile;
namespace chromeos {
namespace settings {
......@@ -43,6 +46,10 @@ class AccountManagerUIHandler : public ::settings::SettingsPageUIHandler,
void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
private:
friend class AccountManagerUIHandlerTest;
void SetProfileForTesting(Profile* profile);
// WebUI "getAccounts" message callback.
void HandleGetAccounts(const base::ListValue* args);
......@@ -66,9 +73,19 @@ class AccountManagerUIHandler : public ::settings::SettingsPageUIHandler,
base::Value callback_id,
const std::vector<AccountManager::Account>& stored_accounts);
// Returns secondary Gaia accounts from |stored_accounts| list. If the Device
// Account is a Gaia account, populates |device_account| with information
// about that account, otherwise does not modify |device_account|.
base::ListValue GetSecondaryGaiaAccounts(
const std::vector<AccountManager::Account>& stored_accounts,
const AccountId device_account_id,
base::DictionaryValue* device_account);
// Refreshes the UI.
void RefreshUI();
Profile* profile_ = nullptr;
// A non-owning pointer to |AccountManager|.
AccountManager* const account_manager_;
......@@ -86,6 +103,7 @@ class AccountManagerUIHandler : public ::settings::SettingsPageUIHandler,
identity_manager_observer_;
base::WeakPtrFactory<AccountManagerUIHandler> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(AccountManagerUIHandler);
};
......
// Copyright 2019 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/chromeos/account_manager_handler.h"
#include <memory>
#include <ostream>
#include "base/test/bind_test_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/components/account_manager/account_manager.h"
#include "chromeos/components/account_manager/account_manager_factory.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_type.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr char kGetAccountsMessage[] = "getAccounts";
constexpr char kHandleFunctionName[] = "handleFunctionName";
struct DeviceAccountInfo {
std::string id;
std::string email;
std::string fullName;
std::string organization;
user_manager::UserType user_type;
chromeos::account_manager::AccountType account_type;
std::string token;
friend std::ostream& operator<<(std::ostream& stream,
const DeviceAccountInfo& device_account_info);
};
std::ostream& operator<<(std::ostream& stream,
const DeviceAccountInfo& device_account_info) {
return stream << "{email: " << device_account_info.email
<< ", user_type: " << device_account_info.user_type << "}";
}
DeviceAccountInfo GetActiveDirectoryDeviceAccountInfo() {
return {"fake-ad-id" /*id*/,
"primary@example.com" /*email*/,
"primary" /*fullName*/,
"example.com" /*organization*/,
user_manager::USER_TYPE_ACTIVE_DIRECTORY /*user_type*/,
chromeos::account_manager::AccountType::
ACCOUNT_TYPE_ACTIVE_DIRECTORY /*account_type*/,
chromeos::AccountManager::kActiveDirectoryDummyToken /*token*/};
}
DeviceAccountInfo GetGaiaDeviceAccountInfo() {
return {signin::GetTestGaiaIdForEmail("primary@example.com") /*id*/,
"primary@example.com" /*email*/,
"primary" /*fullName*/,
"" /*organization*/,
user_manager::USER_TYPE_REGULAR /*user_type*/,
chromeos::account_manager::AccountType::
ACCOUNT_TYPE_GAIA /*account_type*/,
"device-account-token" /*token*/};
}
chromeos::AccountManager::Account GetAccountByKey(
std::vector<chromeos::AccountManager::Account> accounts,
chromeos::AccountManager::AccountKey key) {
for (const chromeos::AccountManager::Account& account : accounts) {
if (account.key == key) {
return account;
}
}
return chromeos::AccountManager::Account();
}
std::string ValueOrEmpty(const std::string* str) {
return str ? *str : std::string();
}
} // namespace
namespace chromeos {
namespace settings {
class TestingAccountManagerUIHandler : public AccountManagerUIHandler {
public:
TestingAccountManagerUIHandler(AccountManager* account_manager,
signin::IdentityManager* identity_manager,
content::WebUI* web_ui)
: AccountManagerUIHandler(account_manager, identity_manager) {
set_web_ui(web_ui);
}
private:
DISALLOW_COPY_AND_ASSIGN(TestingAccountManagerUIHandler);
};
class AccountManagerUIHandlerTest
: public InProcessBrowserTest,
public testing::WithParamInterface<DeviceAccountInfo> {
public:
AccountManagerUIHandlerTest() = default;
AccountManagerUIHandlerTest(const AccountManagerUIHandlerTest&) = delete;
AccountManagerUIHandlerTest& operator=(const AccountManagerUIHandlerTest&) =
delete;
void SetUpOnMainThread() override {
user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
std::make_unique<chromeos::FakeChromeUserManager>());
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
TestingProfile::Builder profile_builder;
profile_builder.SetPath(temp_dir_.GetPath().AppendASCII("TestProfile"));
profile_builder.SetProfileName(GetDeviceAccountInfo().email);
profile_ = profile_builder.Build();
const user_manager::User* user;
if (GetDeviceAccountInfo().user_type ==
user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY) {
user = GetFakeUserManager()->AddUserWithAffiliationAndTypeAndProfile(
AccountId::AdFromUserEmailObjGuid(GetDeviceAccountInfo().email,
GetDeviceAccountInfo().id),
true, user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
profile_.get());
} else {
user = GetFakeUserManager()->AddUserWithAffiliationAndTypeAndProfile(
AccountId::FromUserEmailGaiaId(GetDeviceAccountInfo().email,
GetDeviceAccountInfo().id),
true, GetDeviceAccountInfo().user_type, profile_.get());
}
identity_manager_ = IdentityManagerFactory::GetForProfile(profile_.get());
chromeos::AccountManagerFactory* factory =
g_browser_process->platform_part()->GetAccountManagerFactory();
account_manager_ = factory->GetAccountManager(profile_->GetPath().value());
account_manager_->UpsertAccount(
AccountManager::AccountKey{GetDeviceAccountInfo().id,
GetDeviceAccountInfo().account_type},
GetDeviceAccountInfo().email, GetDeviceAccountInfo().token);
handler_ = std::make_unique<TestingAccountManagerUIHandler>(
account_manager_, identity_manager_, &web_ui_);
handler_->SetProfileForTesting(profile_.get());
handler_->RegisterMessages();
handler_->AllowJavascriptForTesting();
base::RunLoop().RunUntilIdle();
}
void TearDownOnMainThread() override {
handler_.reset();
profile_.reset();
user_manager_enabler_.reset();
}
void UpsertAccount(std::string email) {
account_manager_->UpsertAccount(
AccountManager::AccountKey{
signin::GetTestGaiaIdForEmail(email),
chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA},
email, AccountManager::kInvalidToken);
}
std::vector<AccountManager::Account> GetAccountsFromAccountManager() const {
std::vector<AccountManager::Account> accounts;
base::RunLoop run_loop;
account_manager_->GetAccounts(base::BindLambdaForTesting(
[&accounts, &run_loop](
const std::vector<AccountManager::Account>& stored_accounts) {
accounts = stored_accounts;
run_loop.Quit();
}));
run_loop.Run();
return accounts;
}
DeviceAccountInfo GetDeviceAccountInfo() const { return GetParam(); }
content::TestWebUI* web_ui() { return &web_ui_; }
signin::IdentityManager* identity_manager() { return identity_manager_; }
chromeos::AccountManager* account_manager() { return account_manager_; }
private:
chromeos::FakeChromeUserManager* GetFakeUserManager() const {
return static_cast<chromeos::FakeChromeUserManager*>(
user_manager::UserManager::Get());
}
std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<TestingProfile> profile_;
chromeos::AccountManager* account_manager_ = nullptr;
signin::IdentityManager* identity_manager_ = nullptr;
content::TestWebUI web_ui_;
std::unique_ptr<TestingAccountManagerUIHandler> handler_;
};
IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,
OnGetAccountsNoSecondaryAccounts) {
const std::vector<AccountManager::Account> account_manager_accounts =
GetAccountsFromAccountManager();
// Only Primary account.
ASSERT_EQ(1UL, account_manager_accounts.size());
// Call "getAccounts".
base::ListValue args;
args.AppendString(kHandleFunctionName);
web_ui()->HandleReceivedMessage(kGetAccountsMessage, &args);
const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data.function_name());
EXPECT_EQ(kHandleFunctionName, call_data.arg1()->GetString());
ASSERT_TRUE(call_data.arg2()->GetBool());
// Get results from JS callback.
const base::span<const base::Value> result = call_data.arg3()->GetList();
ASSERT_EQ(account_manager_accounts.size(), result.size());
// Check first (device) account.
const base::Value& device_account = result[0];
EXPECT_TRUE(device_account.FindBoolKey("isDeviceAccount").value());
EXPECT_TRUE(device_account.FindBoolKey("isSignedIn").value());
EXPECT_FALSE(device_account.FindBoolKey("unmigrated").value());
EXPECT_EQ(GetDeviceAccountInfo().account_type,
device_account.FindIntKey("accountType"));
EXPECT_EQ(GetDeviceAccountInfo().email,
ValueOrEmpty(device_account.FindStringKey("email")));
EXPECT_EQ(GetDeviceAccountInfo().id,
ValueOrEmpty(device_account.FindStringKey("id")));
EXPECT_EQ(GetDeviceAccountInfo().organization,
ValueOrEmpty(device_account.FindStringKey("organization")));
}
IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTest,
OnGetAccountsWithSecondaryAccounts) {
UpsertAccount("secondary1@example.com");
UpsertAccount("secondary2@example.com");
const std::vector<AccountManager::Account> account_manager_accounts =
GetAccountsFromAccountManager();
ASSERT_EQ(3UL, account_manager_accounts.size());
// Call "getAccounts".
base::ListValue args;
args.AppendString(kHandleFunctionName);
web_ui()->HandleReceivedMessage(kGetAccountsMessage, &args);
const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
EXPECT_EQ("cr.webUIResponse", call_data.function_name());
EXPECT_EQ(kHandleFunctionName, call_data.arg1()->GetString());
ASSERT_TRUE(call_data.arg2()->GetBool());
// Get results from JS callback.
const base::span<const base::Value> result = call_data.arg3()->GetList();
ASSERT_EQ(account_manager_accounts.size(), result.size());
// Check first (device) account.
const base::Value& device_account = result[0];
EXPECT_TRUE(device_account.FindBoolKey("isDeviceAccount").value());
EXPECT_TRUE(device_account.FindBoolKey("isSignedIn").value());
EXPECT_FALSE(device_account.FindBoolKey("unmigrated").value());
EXPECT_EQ(GetDeviceAccountInfo().account_type,
device_account.FindIntKey("accountType"));
EXPECT_EQ(GetDeviceAccountInfo().email,
ValueOrEmpty(device_account.FindStringKey("email")));
EXPECT_EQ(GetDeviceAccountInfo().id,
ValueOrEmpty(device_account.FindStringKey("id")));
EXPECT_EQ(GetDeviceAccountInfo().organization,
ValueOrEmpty(device_account.FindStringKey("organization")));
// Check secondary accounts.
for (const base::Value& account : result) {
if (ValueOrEmpty(account.FindStringKey("id")) == GetDeviceAccountInfo().id)
continue;
EXPECT_FALSE(account.FindBoolKey("isDeviceAccount").value());
AccountManager::Account expected_account =
GetAccountByKey(account_manager_accounts,
{ValueOrEmpty(account.FindStringKey("id")),
account_manager::AccountType::ACCOUNT_TYPE_GAIA});
EXPECT_EQ(account_manager()->HasDummyGaiaToken(expected_account.key),
account.FindBoolKey("unmigrated").value());
EXPECT_EQ(expected_account.key.account_type,
account.FindIntKey("accountType"));
EXPECT_EQ(expected_account.raw_email,
ValueOrEmpty(account.FindStringKey("email")));
base::Optional<AccountInfo> expected_account_info =
identity_manager()
->FindExtendedAccountInfoForAccountWithRefreshTokenByGaiaId(
expected_account.key.id);
EXPECT_TRUE(expected_account_info.has_value());
EXPECT_EQ(expected_account_info->full_name,
ValueOrEmpty(account.FindStringKey("fullName")));
EXPECT_EQ(
!identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState(
expected_account_info->account_id),
account.FindBoolKey("isSignedIn").value());
}
}
INSTANTIATE_TEST_SUITE_P(
AccountManagerUIHandlerTestSuite,
AccountManagerUIHandlerTest,
::testing::Values(GetActiveDirectoryDeviceAccountInfo(),
GetGaiaDeviceAccountInfo()));
} // namespace settings
} // namespace chromeos
......@@ -2380,6 +2380,7 @@ if (!is_android) {
"../browser/ui/webui/chromeos/login/js_calls_container_test_api.h",
"../browser/ui/webui/chromeos/login/oobe_display_chooser_browsertest.cc",
"../browser/ui/webui/chromeos/system_web_dialog_browsertest.cc",
"../browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc",
"../browser/ui/webui/settings/chromeos/device_power_handler_browsertest.cc",
"../browser/ui/window_sizer/window_sizer_ash_uitest.cc",
"base/interactive_test_utils.cc",
......
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