| // 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_ |