chromium/ios/chrome/browser/shared/model/profile/profile_attributes_storage_ios.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ios/chrome/browser/shared/model/profile/profile_attributes_storage_ios.h"

#include <stddef.h>

#include <utility>

#include "base/check_op.h"
#include "base/functional/callback.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "ios/chrome/browser/shared/model/prefs/pref_names.h"
#include "ios/chrome/browser/shared/model/profile/profile_attributes_ios.h"

ProfileAttributesStorageIOS::ProfileAttributesStorageIOS(PrefService* prefs)
    : prefs_(prefs) {
  // Populate the cache
  for (const auto pair : prefs_->GetDict(prefs::kProfileInfoCache)) {
    sorted_keys_.push_back(pair.first);
  }
  base::ranges::sort(sorted_keys_);
}

ProfileAttributesStorageIOS::~ProfileAttributesStorageIOS() = default;

void ProfileAttributesStorageIOS::AddProfile(std::string_view name) {
  // Inserts the profile name in sorted position.
  auto iterator = base::ranges::upper_bound(sorted_keys_, name);
  CHECK(iterator == sorted_keys_.end() || *iterator != name);
  sorted_keys_.insert(iterator, std::string(name));

  // Inserts an empty dictionary for the profile in the preferences.
  {
    ScopedDictPrefUpdate update(prefs_, prefs::kProfileInfoCache);
    update->Set(name, base::Value::Dict());
  }

  // Update the number of created profile.
  prefs_->SetInteger(prefs::kNumberOfProfiles, sorted_keys_.size());

  // Insert the newly created profile in the list of last active profiles.
  {
    ScopedListPrefUpdate update(prefs_, prefs::kLastActiveProfiles);
    update->Append(base::Value(name));
  }
}

void ProfileAttributesStorageIOS::RemoveProfile(std::string_view name) {
  // Remove the profile name from the sorted dictionary.
  auto iterator = base::ranges::find(sorted_keys_, name);
  CHECK(iterator != sorted_keys_.end() && *iterator == name);
  sorted_keys_.erase(iterator);

  // Detach any scene that may still be referencing this profile.
  {
    ScopedDictPrefUpdate update(prefs_, prefs::kProfileForScene);

    base::Value::Dict dict;
    for (auto [key, value] : update.Get()) {
      if (value.GetString() != name) {
        dict.Set(key, std::move(value));
      }
    }

    *update = std::move(dict);
  }

  // Remove the profile from the list of last active profiles (if present).
  {
    ScopedListPrefUpdate update(prefs_, prefs::kLastActiveProfiles);
    update->EraseValue(base::Value(name));
  }

  // Update the number of created profile.
  prefs_->SetInteger(prefs::kNumberOfProfiles, sorted_keys_.size());

  // Remove the information about the profile from the preferences.
  {
    ScopedDictPrefUpdate update(prefs_, prefs::kProfileInfoCache);
    update->Remove(name);
  }
}

size_t ProfileAttributesStorageIOS::GetNumberOfProfiles() const {
  return sorted_keys_.size();
}

bool ProfileAttributesStorageIOS::HasProfileWithName(
    std::string_view name) const {
  return GetIndexOfProfileWithName(name) != std::string::npos;
}

ProfileAttributesIOS
ProfileAttributesStorageIOS::GetAttributesForProfileAtIndex(
    size_t index) const {
  DCHECK_LT(index, sorted_keys_.size());
  const std::string& profile_name = sorted_keys_[index];
  return ProfileAttributesIOS(
      profile_name,
      prefs_->GetDict(prefs::kProfileInfoCache).FindDict(profile_name));
}

ProfileAttributesIOS
ProfileAttributesStorageIOS::GetAttributesForProfileWithName(
    std::string_view name) const {
  const size_t index = GetIndexOfProfileWithName(name);
  return GetAttributesForProfileAtIndex(index);
}

void ProfileAttributesStorageIOS::UpdateAttributesForProfileAtIndex(
    size_t index,
    ProfileAttributesCallback callback) {
  DCHECK_LT(index, sorted_keys_.size());
  const std::string& name = sorted_keys_[index];
  const base::Value::Dict* values =
      prefs_->GetDict(prefs::kProfileInfoCache).FindDict(name);

  base::Value::Dict updated_values =
      std::move(callback).Run(ProfileAttributesIOS(name, values)).GetStorage();
  if (!values || *values != updated_values) {
    ScopedDictPrefUpdate update(prefs_, prefs::kProfileInfoCache);
    update->Set(name, std::move(updated_values));
  }
}

void ProfileAttributesStorageIOS::UpdateAttributesForProfileWithName(
    std::string_view name,
    ProfileAttributesCallback callback) {
  const size_t index = GetIndexOfProfileWithName(name);
  UpdateAttributesForProfileAtIndex(index, std::move(callback));
}

size_t ProfileAttributesStorageIOS::GetIndexOfProfileWithName(
    std::string_view name) const {
  auto iterator = base::ranges::lower_bound(sorted_keys_, name);
  if (iterator == sorted_keys_.end() || *iterator != name) {
    return std::string::npos;
  }
  return std::distance(sorted_keys_.begin(), iterator);
}

void ProfileAttributesStorageIOS::SetProfileNameForSceneID(
    std::string_view scene_id,
    std::string_view profile_name) {
  DCHECK(!profile_name.empty());
  DCHECK(HasProfileWithName(profile_name));
  ScopedDictPrefUpdate update(prefs_, prefs::kProfileForScene);
  update->Set(scene_id, profile_name);
}

void ProfileAttributesStorageIOS::ClearProfileNameForSceneID(
    std::string_view scene_id) {
  ScopedDictPrefUpdate update(prefs_, prefs::kProfileForScene);
  update->Remove(scene_id);
}

const std::string& ProfileAttributesStorageIOS::GetProfileNameForSceneID(
    std::string_view scene_id) {
  if (const std::string* profile_name =
          prefs_->GetDict(prefs::kProfileForScene).FindString(scene_id)) {
    DCHECK(HasProfileWithName(*profile_name));
    return *profile_name;
  }

  return base::EmptyString();
}

// static
void ProfileAttributesStorageIOS::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterDictionaryPref(prefs::kProfileInfoCache);
  registry->RegisterIntegerPref(prefs::kNumberOfProfiles, 0);
  registry->RegisterListPref(prefs::kLastActiveProfiles);
  registry->RegisterDictionaryPref(prefs::kProfileForScene);
}