blob: 2906dade394e825073de264b804dbb5d480657f2 [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 <string>
#include "base/check.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/scoped_observation.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/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
// 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 {
scoped_identity_manager_observer_.Reset();
std::move(callback_).Run(profile_);
}
void OnIdentityManagerShutdown(signin::IdentityManager* manager) override {
scoped_identity_manager_observer_.Reset();
std::move(callback_).Run(nullptr);
}
Profile* profile_;
signin::IdentityManager* identity_manager_;
base::ScopedObservation<signin::IdentityManager,
signin::IdentityManager::Observer>
scoped_identity_manager_observer_{this};
base::OnceCallback<void(Profile*)> callback_;
};
// 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());
scoped_identity_manager_observer_.Observe(identity_manager_);
}
DiceSignedInProfileCreator::DiceSignedInProfileCreator(
Profile* source_profile,
CoreAccountId account_id,
const std::u16string& local_profile_name,
absl::optional<size_t> icon_index,
bool use_guest_profile,
base::OnceCallback<void(Profile*)> callback)
: source_profile_(source_profile),
account_id_(account_id),
callback_(std::move(callback)) {
// Passing the sign-in token to an ephemeral Guest profile is part of the
// experiment to surface a Guest mode link in the DiceWebSigninIntercept
// and is only used to sign in to the web through account consistency and
// does NOT enable sync or any other browser level functionality.
// TODO(https://crbug.com/1225171): Revise the comment after Guest mode plans
// are finalized.
if (use_guest_profile) {
// TODO(https://crbug.com/1225171): Re-enabled if ephemeral based Guest mode
// is added. Remove the code otherwise.
NOTREACHED();
// Make sure the callback is not called synchronously.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&ProfileManager::CreateProfileAsync,
base::Unretained(g_browser_process->profile_manager()),
ProfileManager::GetGuestProfilePath(),
base::BindRepeating(
&DiceSignedInProfileCreator::OnNewProfileCreated,
weak_pointer_factory_.GetWeakPtr())));
} else {
ProfileAttributesStorage& storage =
g_browser_process->profile_manager()->GetProfileAttributesStorage();
if (!icon_index.has_value())
icon_index = storage.ChooseAvatarIconIndexForNewProfile();
std::u16string name = local_profile_name.empty()
? storage.ChooseNameForNewProfile(*icon_index)
: local_profile_name;
ProfileManager::CreateMultiProfileAsync(
name, *icon_index, /*is_hidden=*/false,
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);
}