blob: 65fca908467b0e602b7752f1951c239ca3ee3ca0 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_SYNC_ACCOUNT_EXTENSION_TRACKER_H_
#define CHROME_BROWSER_EXTENSIONS_SYNC_ACCOUNT_EXTENSION_TRACKER_H_
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
class BrowserContextKeyedServiceFactory;
class Profile;
namespace content {
class BrowserContext;
}
namespace extensions {
// This service manages each extension's AccountExtensionType, which describes
// whether the extension is associated with a signed in user's account data.
class AccountExtensionTracker : public KeyedService,
public signin::IdentityManager::Observer {
public:
// Maximum delay between initiating a sign in from the extension installed
// bubble and completing the sign in for the associated extension to still be
// promoted to an account extension. Beyond this delay, it is assumed that the
// user did not intend to sign in after installing the extension.
static constexpr base::TimeDelta kMaxSigninFromExtensionBubbleDelay =
base::Minutes(50);
enum AccountExtensionType {
// The extension is only associated with the current device. This is used
// for:
// - all unsyncable extensions
// - all extensions if there is no signed in user with sync enabled
// - all extensions installed before the user signed in, that are not part
// the user's account's data.
kLocal = 0,
// The extension is part of the signed in user's account data but was
// installed on this device before the user has signed in.
kAccountInstalledLocally = 1,
// The extension is part of the signed in user's account data and was
// installed on this device after the user has signed in.
kAccountInstalledSignedIn = 2,
kLast = 2,
};
class Observer : public base::CheckedObserver {
public:
// Called when an extension's eligibility to be uploaded to the user's
// account may have changed.
virtual void OnExtensionUploadabilityChanged(const ExtensionId& id) = 0;
// Called when whether extensions can be uploaded to the user's account may
// be changed. Usually emitted when the initial sync download completes or
// when the user is no longer syncing extensions in transport mode.
virtual void OnExtensionsUploadabilityChanged() = 0;
};
explicit AccountExtensionTracker(Profile* profile);
AccountExtensionTracker(const AccountExtensionTracker&) = delete;
AccountExtensionTracker& operator=(const AccountExtensionTracker&) = delete;
~AccountExtensionTracker() override;
// Convenience method to get the AccountExtensionTracker for a profile.
// Creates the tracker for the profile if none exists.
static AccountExtensionTracker* Get(content::BrowserContext* context);
// Returns the singleton instance of the factory for this KeyedService.
static BrowserContextKeyedServiceFactory* GetFactory();
// Computes and sets the AccountExtensionType for `extension` when it is
// installed. Needs to be called by the sync service before it handles the
// install.
void SetAccountExtensionTypeOnExtensionInstalled(const Extension& extension);
// IdentityManagerObserver implementation.
void OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event_details) override;
// Called when sync data is received for the given `extension_id`.
void OnExtensionSyncDataReceived(const ExtensionId& extension_id);
// Called just after the initial set of extension sync data is received.
// i.e. during browser startup (if extensions sync is already enabled), or
// once the initial download completes after extensions sync gets enabled.
void OnInitialExtensionsSyncDataReceived();
AccountExtensionType GetAccountExtensionType(
const ExtensionId& extension_id) const;
// Returns all account extensions with type `kAccountInstalledSignedIn`.
std::vector<const Extension*> GetSignedInAccountExtensions() const;
// Returns all local extensions that can be uploaded to a signed-in user's
// account.
std::vector<const Extension*> GetUploadableLocalExtensions() const;
#if !BUILDFLAG(IS_CHROMEOS)
// Called when the user initiates a signin from a promo that appears after an
// extension with the given `extension_id` is installed.
void OnSignInInitiatedFromExtensionPromo(const ExtensionId& extension_id);
#endif // !BUILDFLAG(IS_CHROMEOS)
// Whether the given `extension` can be uploaded to/associated with the
// current signed in user.
bool CanUploadAsAccountExtension(const Extension& extension) const;
// Called when the user initiates an upload for the given `extension_id` to
// their account.
void OnAccountUploadInitiatedForExtension(const ExtensionId& extension_id);
void set_uninstall_account_extensions_on_signout(
bool uninstall_account_extensions_on_signout) {
uninstall_account_extensions_on_signout_ =
uninstall_account_extensions_on_signout;
}
void SetAccountExtensionTypeForTesting(const ExtensionId& extension_id,
AccountExtensionType type);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
private:
// Sets the extension's AccountExtensionType. Called when the extension is
// installed (not updated) or when there is incoming sync data for the
// extension, which implies that it's associated with a user's account data.
void SetAccountExtensionType(const ExtensionId& extension_id,
AccountExtensionType type);
// Removes `extension_id` in `extensions_installed_with_signin_promo_`.
void RemoveExpiredExtension(const ExtensionId& extension_id);
// Promotes `extension_id` from a local to an account extension specified by
// `type`. Unlike just calling `SetAccountExtensionType`, this always alerts
// observers that the extension's uploadability may have changed.
void PromoteLocalToAccountExtension(const ExtensionId& extension_id,
AccountExtensionType type);
// Notifies observers that the eligibility of multiple extensions to be
// uploaded to the user's account may have changed.
void NotifyOnExtensionsUploadabilityChanged();
const raw_ptr<Profile> profile_;
// Keeps track of extensions for which a signin promo was shown after
// installation.
std::vector<ExtensionId> extensions_installed_with_signin_promo_;
// Whether account extensions with type `kAccountInstalledSignedIn` should be
// uninstalled when the primary user signs out.
bool uninstall_account_extensions_on_signout_ = false;
base::ObserverList<Observer> observers_;
// IdentityManager observer.
base::ScopedObservation<signin::IdentityManager,
signin::IdentityManager::Observer>
identity_manager_observation_{this};
// Must be the last member variable. See WeakPtrFactory documentation for
// details.
base::WeakPtrFactory<AccountExtensionTracker> weak_factory_{this};
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_SYNC_ACCOUNT_EXTENSION_TRACKER_H_