|  | // Copyright 2013 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "chrome/browser/extensions/install_tracker.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/observer_list.h" | 
|  | #include "chrome/browser/extensions/install_tracker_factory.h" | 
|  | #include "components/prefs/pref_change_registrar.h" | 
|  | #include "extensions/browser/extension_prefs.h" | 
|  | #include "extensions/browser/extension_system.h" | 
|  | #include "extensions/browser/pref_names.h" | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | InstallTracker::InstallTracker(content::BrowserContext* browser_context, | 
|  | ExtensionPrefs* prefs) | 
|  | : browser_context_(browser_context) { | 
|  | extension_registry_observation_.Observe( | 
|  | ExtensionRegistry::Get(browser_context)); | 
|  |  | 
|  | // Prefs may be null in tests. | 
|  | if (prefs) { | 
|  | pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); | 
|  | pref_change_registrar_->Init(prefs->pref_service()); | 
|  | pref_change_registrar_->Add( | 
|  | pref_names::kExtensions, | 
|  | base::BindRepeating(&InstallTracker::OnExtensionPrefChanged, | 
|  | base::Unretained(this))); | 
|  | } | 
|  | } | 
|  |  | 
|  | InstallTracker::~InstallTracker() = default; | 
|  |  | 
|  | // static | 
|  | InstallTracker* InstallTracker::Get(content::BrowserContext* context) { | 
|  | return InstallTrackerFactory::GetForBrowserContext(context); | 
|  | } | 
|  |  | 
|  | void InstallTracker::AddObserver(InstallObserver* observer) { | 
|  | observers_.AddObserver(observer); | 
|  | } | 
|  |  | 
|  | void InstallTracker::RemoveObserver(InstallObserver* observer) { | 
|  | observers_.RemoveObserver(observer); | 
|  | } | 
|  |  | 
|  | const ActiveInstallData* InstallTracker::GetActiveInstall( | 
|  | const std::string& extension_id) const { | 
|  | auto install_data = active_installs_.find(extension_id); | 
|  | if (install_data == active_installs_.end()) | 
|  | return nullptr; | 
|  | else | 
|  | return &(install_data->second); | 
|  | } | 
|  |  | 
|  | void InstallTracker::AddActiveInstall(const ActiveInstallData& install_data) { | 
|  | DCHECK(!install_data.extension_id.empty()); | 
|  | DCHECK(active_installs_.find(install_data.extension_id) == | 
|  | active_installs_.end()); | 
|  | active_installs_.insert( | 
|  | std::make_pair(install_data.extension_id, install_data)); | 
|  | } | 
|  |  | 
|  | void InstallTracker::RemoveActiveInstall(const std::string& extension_id) { | 
|  | active_installs_.erase(extension_id); | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnBeginExtensionInstall( | 
|  | const InstallObserver::ExtensionInstallParams& params) { | 
|  | auto install_data = active_installs_.find(params.extension_id); | 
|  | if (install_data == active_installs_.end()) { | 
|  | ActiveInstallData active_install_data(params.extension_id); | 
|  | active_installs_.insert( | 
|  | std::make_pair(params.extension_id, active_install_data)); | 
|  | } | 
|  |  | 
|  | for (auto& observer : observers_) | 
|  | observer.OnBeginExtensionInstall(browser_context_, params); | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnBeginExtensionDownload(const std::string& extension_id) { | 
|  | for (auto& observer : observers_) | 
|  | observer.OnBeginExtensionDownload(browser_context_, extension_id); | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnDownloadProgress(const std::string& extension_id, | 
|  | int percent_downloaded) { | 
|  | auto install_data = active_installs_.find(extension_id); | 
|  | if (install_data != active_installs_.end()) { | 
|  | install_data->second.percent_downloaded = percent_downloaded; | 
|  | } else { | 
|  | DUMP_WILL_BE_NOTREACHED(); | 
|  | } | 
|  |  | 
|  | for (auto& observer : observers_) { | 
|  | observer.OnDownloadProgress(browser_context_, extension_id, | 
|  | percent_downloaded); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnBeginCrxInstall(const std::string& extension_id) { | 
|  | for (auto& observer : observers_) { | 
|  | observer.OnBeginCrxInstall(browser_context_, extension_id); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnFinishCrxInstall(const base::FilePath& source_file, | 
|  | const std::string& extension_id, | 
|  | const Extension* extension, | 
|  | bool success) { | 
|  | for (auto& observer : observers_) { | 
|  | observer.OnFinishCrxInstall(browser_context_, source_file, extension_id, | 
|  | extension, success); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnInstallFailure( | 
|  | const std::string& extension_id) { | 
|  | RemoveActiveInstall(extension_id); | 
|  | } | 
|  |  | 
|  | void InstallTracker::Shutdown() { | 
|  | // Note: tests may call this method prematurely to avoid shutdown ordering | 
|  | // issues. Make sure observers don't need to handle this awkward complexity by | 
|  | // clearing them here and making this method idempotent. | 
|  | for (auto& observer : observers_) | 
|  | observer.OnShutdown(); | 
|  | observers_.Clear(); | 
|  | pref_change_registrar_.reset(); | 
|  | browser_context_ = nullptr; | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnExtensionInstalled( | 
|  | content::BrowserContext* browser_context, | 
|  | const Extension* extension, | 
|  | bool is_update) { | 
|  | RemoveActiveInstall(extension->id()); | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnAppsReordered( | 
|  | const std::optional<std::string>& extension_id) { | 
|  | for (auto& observer : observers_) | 
|  | observer.OnAppsReordered(browser_context_, extension_id); | 
|  | } | 
|  |  | 
|  | void InstallTracker::OnExtensionPrefChanged() { | 
|  | OnAppsReordered(std::nullopt); | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |