chromium/chrome/browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager.cc

// Copyright 2022 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/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager.h"

#include <memory>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/no_destructor.h"
#include "chrome/browser/enterprise/util/affiliation.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile_keyed_service_factory.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/browser_resources.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "extensions/browser/extension_system.h"

using ::extensions::ComponentLoader;

namespace chromeos {
namespace {

class ContactCenterInsightsExtensionManagerFactory
    : public ProfileKeyedServiceFactory {
 public:
  ContactCenterInsightsExtensionManagerFactory();
  ContactCenterInsightsExtensionManagerFactory(
      const ContactCenterInsightsExtensionManagerFactory&) = delete;
  ContactCenterInsightsExtensionManagerFactory& operator=(
      const ContactCenterInsightsExtensionManagerFactory&) = delete;
  ~ContactCenterInsightsExtensionManagerFactory() override;

  // Returns an instance of `ContactCenterInsightsExtensionManager` for the
  // given profile.
  ContactCenterInsightsExtensionManager* GetForProfile(Profile* profile);

 private:
  KeyedService* BuildServiceInstanceFor(
      content::BrowserContext* context) const override;
};

ContactCenterInsightsExtensionManagerFactory::
    ContactCenterInsightsExtensionManagerFactory()
    : ProfileKeyedServiceFactory(
          "ContactCenterInsightsExtensionManager",
          ProfileSelections::Builder()
              .WithRegular(ProfileSelection::kOriginalOnly)
              // TODO(crbug.com/40257657): Check if this service is needed in
              // Guest mode.
              .WithGuest(ProfileSelection::kOriginalOnly)
              // TODO(crbug.com/41488885): Check if this service is needed for
              // Ash Internals.
              .WithAshInternals(ProfileSelection::kOriginalOnly)
              .Build()) {}

ContactCenterInsightsExtensionManagerFactory::
    ~ContactCenterInsightsExtensionManagerFactory() = default;

ContactCenterInsightsExtensionManager*
ContactCenterInsightsExtensionManagerFactory::GetForProfile(Profile* profile) {
  DCHECK(profile);
  return static_cast<ContactCenterInsightsExtensionManager*>(
      GetServiceForBrowserContext(profile, true));
}

KeyedService*
ContactCenterInsightsExtensionManagerFactory::BuildServiceInstanceFor(
    content::BrowserContext* context) const {
  auto* const profile = Profile::FromBrowserContext(context);
  auto* const component_loader = ::extensions::ExtensionSystem::Get(profile)
                                     ->extension_service()
                                     ->component_loader();
  return new ContactCenterInsightsExtensionManager(
      component_loader, profile,
      std::make_unique<ContactCenterInsightsExtensionManager::Delegate>());
}

}  // namespace

void ContactCenterInsightsExtensionManager::Delegate::InstallExtension(
    ComponentLoader* component_loader) {
  component_loader->Add(
      IDR_CONTACT_CENTER_INSIGHTS_MANIFEST,
      base::FilePath(FILE_PATH_LITERAL("chromeos/contact_center_insights")));
}

void ContactCenterInsightsExtensionManager::Delegate::UninstallExtension(
    ComponentLoader* component_loader) {
  component_loader->Remove(extension_misc::kContactCenterInsightsExtensionId);
}

bool ContactCenterInsightsExtensionManager::Delegate::IsProfileAffiliated(
    Profile* profile) const {
  return ::enterprise_util::IsProfileAffiliated(profile);
}

bool ContactCenterInsightsExtensionManager::Delegate::IsExtensionInstalled(
    ComponentLoader* component_loader) const {
  return component_loader->Exists(
      extension_misc::kContactCenterInsightsExtensionId);
}

// static
ContactCenterInsightsExtensionManager*
ContactCenterInsightsExtensionManager::GetForProfile(Profile* profile) {
  return static_cast<ContactCenterInsightsExtensionManagerFactory*>(
             GetFactory())
      ->GetForProfile(profile);
}

ContactCenterInsightsExtensionManager::ContactCenterInsightsExtensionManager(
    ComponentLoader* component_loader,
    Profile* profile,
    std::unique_ptr<ContactCenterInsightsExtensionManager::Delegate> delegate)
    : component_loader_(component_loader),
      profile_(profile),
      delegate_(std::move(delegate)) {
  Init();
}

ContactCenterInsightsExtensionManager::
    ~ContactCenterInsightsExtensionManager() = default;

// static
BrowserContextKeyedServiceFactory*
ContactCenterInsightsExtensionManager::GetFactory() {
  static base::NoDestructor<ContactCenterInsightsExtensionManagerFactory>
      g_factory;
  return g_factory.get();
}

void ContactCenterInsightsExtensionManager::Init() {
  if (CanInstallExtension()) {
    delegate_->InstallExtension(component_loader_);
  } else {
    // Remove extension if installed because it does not meet necessary
    // pre-conditions.
    RemoveExtensionIfInstalled();
  }

  registrar_.Init(profile_->GetPrefs());
  if (delegate_->IsProfileAffiliated(profile_)) {
    // Setup registrar so it starts listening to relevant pref changes.
    registrar_.Add(::prefs::kInsightsExtensionEnabled,
                   base::BindRepeating(
                       &ContactCenterInsightsExtensionManager::OnPrefChanged,
                       weak_ptr_factory_.GetWeakPtr()));
  }
}

bool ContactCenterInsightsExtensionManager::CanInstallExtension() const {
  if (!delegate_->IsProfileAffiliated(profile_)) {
    return false;
  }

  const auto& pref_service = *profile_->GetPrefs();
  return pref_service.HasPrefPath(::prefs::kInsightsExtensionEnabled) &&
         pref_service.GetBoolean(::prefs::kInsightsExtensionEnabled);
}

void ContactCenterInsightsExtensionManager::OnPrefChanged() {
  if (CanInstallExtension()) {
    // This will be a no-op if the extension is already installed with the same
    // version, so it is okay to attempt an installation here.
    delegate_->InstallExtension(component_loader_);
    return;
  }

  // Remove/unload component extension if installed because it does
  // not meet necessary pre-conditions.
  RemoveExtensionIfInstalled();
}

void ContactCenterInsightsExtensionManager::RemoveExtensionIfInstalled() {
  if (delegate_->IsExtensionInstalled(component_loader_)) {
    delegate_->UninstallExtension(component_loader_);
  }
}

// static
void ContactCenterInsightsExtensionManager::EnsureFactoryBuilt() {
  ContactCenterInsightsExtensionManager::GetFactory();
}

}  // namespace chromeos