blob: e95c18a0bf4458f1d9749596dfa7838ce278f0ac [file] [log] [blame]
// Copyright 2020 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/signin/dice_signed_in_profile_creator.h"
#include "base/check.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/strings/string16.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
namespace {
// A helper class to watch identity manager lifetime.
class DiceSignedInProfileCreatorShutdownNotifierFactory
: public BrowserContextKeyedServiceShutdownNotifierFactory {
public:
static DiceSignedInProfileCreatorShutdownNotifierFactory* GetInstance() {
static base::NoDestructor<DiceSignedInProfileCreatorShutdownNotifierFactory>
factory;
return factory.get();
}
DiceSignedInProfileCreatorShutdownNotifierFactory(
const DiceSignedInProfileCreatorShutdownNotifierFactory&) = delete;
DiceSignedInProfileCreatorShutdownNotifierFactory& operator=(
const DiceSignedInProfileCreatorShutdownNotifierFactory&) = delete;
private:
friend class base::NoDestructor<
DiceSignedInProfileCreatorShutdownNotifierFactory>;
DiceSignedInProfileCreatorShutdownNotifierFactory()
: BrowserContextKeyedServiceShutdownNotifierFactory(
"DiceSignedInProfileCreatorShutdownNotifier") {
DependsOn(IdentityManagerFactory::GetInstance());
}
~DiceSignedInProfileCreatorShutdownNotifierFactory() override = default;
};
} // namespace
// Waits until the tokens are loaded and calls the callback. The callback is
// called immediately if the tokens are already loaded, and called with nullptr
// if the profile is destroyed before the tokens are loaded.
class TokensLoadedCallbackRunner : public signin::IdentityManager::Observer {
public:
~TokensLoadedCallbackRunner() override = default;
TokensLoadedCallbackRunner(const TokensLoadedCallbackRunner&) = delete;
TokensLoadedCallbackRunner& operator=(const TokensLoadedCallbackRunner&) =
delete;
// Runs the callback when the tokens are loaded. If tokens are already loaded
// the callback is called synchronously and this returns nullptr.
static std::unique_ptr<TokensLoadedCallbackRunner> RunWhenLoaded(
Profile* profile,
base::OnceCallback<void(Profile*)> callback);
private:
TokensLoadedCallbackRunner(Profile* profile,
base::OnceCallback<void(Profile*)> callback);
// signin::IdentityManager::Observer implementation:
void OnRefreshTokensLoaded() override {
shutdown_subscription_.reset();
scoped_identity_manager_observer_.RemoveAll();
std::move(callback_).Run(profile_);
}
void OnShutdown() {
scoped_identity_manager_observer_.RemoveAll();
shutdown_subscription_.reset();
std::move(callback_).Run(nullptr);
}
Profile* profile_;
signin::IdentityManager* identity_manager_;
ScopedObserver<signin::IdentityManager, signin::IdentityManager::Observer>
scoped_identity_manager_observer_{this};
base::OnceCallback<void(Profile*)> callback_;
std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
shutdown_subscription_;
};
// static
std::unique_ptr<TokensLoadedCallbackRunner>
TokensLoadedCallbackRunner::RunWhenLoaded(
Profile* profile,
base::OnceCallback<void(Profile*)> callback) {
if (IdentityManagerFactory::GetForProfile(profile)
->AreRefreshTokensLoaded()) {
std::move(callback).Run(profile);
return nullptr;
}
return base::WrapUnique(
new TokensLoadedCallbackRunner(profile, std::move(callback)));
}
TokensLoadedCallbackRunner::TokensLoadedCallbackRunner(
Profile* profile,
base::OnceCallback<void(Profile*)> callback)
: profile_(profile),
identity_manager_(IdentityManagerFactory::GetForProfile(profile)),
callback_(std::move(callback)) {
DCHECK(profile_);
DCHECK(identity_manager_);
DCHECK(callback_);
DCHECK(!identity_manager_->AreRefreshTokensLoaded());
// To catch the case where the profile is destroyed before the tokens are
// loaded.
shutdown_subscription_ =
DiceSignedInProfileCreatorShutdownNotifierFactory::GetInstance()
->Get(profile)
->Subscribe(base::Bind(&TokensLoadedCallbackRunner::OnShutdown,
base::Unretained(this)));
scoped_identity_manager_observer_.Add(identity_manager_);
}
DiceSignedInProfileCreator::DiceSignedInProfileCreator(
Profile* source_profile,
CoreAccountId account_id,
const base::string16& local_profile_name,
base::Optional<size_t> icon_index,
base::OnceCallback<void(Profile*)> callback)
: source_profile_(source_profile),
account_id_(account_id),
callback_(std::move(callback)) {
ProfileAttributesStorage& storage =
g_browser_process->profile_manager()->GetProfileAttributesStorage();
if (!icon_index.has_value())
icon_index = storage.ChooseAvatarIconIndexForNewProfile();
base::string16 name = local_profile_name.empty()
? storage.ChooseNameForNewProfile(*icon_index)
: local_profile_name;
ProfileManager::CreateMultiProfileAsync(
name, profiles::GetDefaultAvatarIconUrl(*icon_index),
base::BindRepeating(&DiceSignedInProfileCreator::OnNewProfileCreated,
weak_pointer_factory_.GetWeakPtr()));
}
DiceSignedInProfileCreator::DiceSignedInProfileCreator(
Profile* source_profile,
CoreAccountId account_id,
const base::FilePath& target_profile_path,
base::OnceCallback<void(Profile*)> callback)
: source_profile_(source_profile),
account_id_(account_id),
callback_(std::move(callback)) {
// Make sure the callback is not called synchronously.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
base::IgnoreResult(&ProfileManager::LoadProfileByPath),
base::Unretained(g_browser_process->profile_manager()),
target_profile_path, /*incognito=*/false,
base::BindOnce(&DiceSignedInProfileCreator::OnNewProfileInitialized,
weak_pointer_factory_.GetWeakPtr())));
}
DiceSignedInProfileCreator::~DiceSignedInProfileCreator() = default;
void DiceSignedInProfileCreator::OnNewProfileCreated(
Profile* new_profile,
Profile::CreateStatus status) {
switch (status) {
case Profile::CREATE_STATUS_CREATED:
// Ignore this, wait for profile to be initialized.
return;
case Profile::CREATE_STATUS_INITIALIZED:
OnNewProfileInitialized(new_profile);
return;
case Profile::CREATE_STATUS_REMOTE_FAIL:
case Profile::CREATE_STATUS_CANCELED:
case Profile::MAX_CREATE_STATUS:
NOTREACHED() << "Invalid profile creation status";
FALLTHROUGH;
case Profile::CREATE_STATUS_LOCAL_FAIL:
NOTREACHED() << "Error creating new profile";
if (callback_)
std::move(callback_).Run(nullptr);
return;
}
}
void DiceSignedInProfileCreator::OnNewProfileInitialized(Profile* new_profile) {
if (!new_profile) {
if (callback_)
std::move(callback_).Run(nullptr);
return;
}
DCHECK(!tokens_loaded_callback_runner_);
// base::Unretained is fine because the runner is owned by this.
auto tokens_loaded_callback_runner =
TokensLoadedCallbackRunner::RunWhenLoaded(
new_profile,
base::BindOnce(&DiceSignedInProfileCreator::OnNewProfileTokensLoaded,
base::Unretained(this)));
// If the callback was called synchronously, |this| may have been deleted.
if (tokens_loaded_callback_runner) {
tokens_loaded_callback_runner_ = std::move(tokens_loaded_callback_runner);
}
}
void DiceSignedInProfileCreator::OnNewProfileTokensLoaded(
Profile* new_profile) {
tokens_loaded_callback_runner_.reset();
if (!new_profile) {
if (callback_)
std::move(callback_).Run(nullptr);
return;
}
auto* accounts_mutator =
IdentityManagerFactory::GetForProfile(source_profile_)
->GetAccountsMutator();
auto* new_profile_accounts_mutator =
IdentityManagerFactory::GetForProfile(new_profile)->GetAccountsMutator();
accounts_mutator->MoveAccount(new_profile_accounts_mutator, account_id_);
if (callback_)
std::move(callback_).Run(new_profile);
}