|  | // Copyright 2015 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ios/chrome/browser/browser_state/browser_state_info_cache.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/i18n/case_conversion.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/ptr_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/browser_state/browser_state_info_cache_observer.h" | 
|  | #include "ios/chrome/browser/pref_names.h" | 
|  |  | 
|  | namespace { | 
|  | const char kGAIAIdKey[] = "gaia_id"; | 
|  | const char kIsAuthErrorKey[] = "is_auth_error"; | 
|  | const char kUserNameKey[] = "user_name"; | 
|  | } | 
|  |  | 
|  | BrowserStateInfoCache::BrowserStateInfoCache( | 
|  | PrefService* prefs, | 
|  | const base::FilePath& user_data_dir) | 
|  | : prefs_(prefs), user_data_dir_(user_data_dir) { | 
|  | // Populate the cache | 
|  | DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache); | 
|  | base::DictionaryValue* cache = update.Get(); | 
|  | for (base::DictionaryValue::Iterator it(*cache); !it.IsAtEnd(); | 
|  | it.Advance()) { | 
|  | base::DictionaryValue* info = nullptr; | 
|  | cache->GetDictionaryWithoutPathExpansion(it.key(), &info); | 
|  | AddBrowserStateCacheKey(it.key()); | 
|  | } | 
|  | } | 
|  |  | 
|  | BrowserStateInfoCache::~BrowserStateInfoCache() {} | 
|  |  | 
|  | void BrowserStateInfoCache::AddBrowserState( | 
|  | const base::FilePath& browser_state_path, | 
|  | const std::string& gaia_id, | 
|  | const base::string16& user_name) { | 
|  | std::string key = CacheKeyFromBrowserStatePath(browser_state_path); | 
|  | DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache); | 
|  | base::DictionaryValue* cache = update.Get(); | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> info(new base::DictionaryValue); | 
|  | info->SetString(kGAIAIdKey, gaia_id); | 
|  | info->SetString(kUserNameKey, user_name); | 
|  | cache->SetWithoutPathExpansion(key, std::move(info)); | 
|  | AddBrowserStateCacheKey(key); | 
|  |  | 
|  | for (auto& observer : observer_list_) | 
|  | observer.OnBrowserStateAdded(browser_state_path); | 
|  | } | 
|  |  | 
|  | void BrowserStateInfoCache::AddObserver( | 
|  | BrowserStateInfoCacheObserver* observer) { | 
|  | observer_list_.AddObserver(observer); | 
|  | } | 
|  |  | 
|  | void BrowserStateInfoCache::RemoveObserver( | 
|  | BrowserStateInfoCacheObserver* observer) { | 
|  | observer_list_.RemoveObserver(observer); | 
|  | } | 
|  |  | 
|  | void BrowserStateInfoCache::RemoveBrowserState( | 
|  | const base::FilePath& browser_state_path) { | 
|  | size_t browser_state_index = | 
|  | GetIndexOfBrowserStateWithPath(browser_state_path); | 
|  | if (browser_state_index == std::string::npos) { | 
|  | NOTREACHED(); | 
|  | return; | 
|  | } | 
|  | DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache); | 
|  | base::DictionaryValue* cache = update.Get(); | 
|  | std::string key = CacheKeyFromBrowserStatePath(browser_state_path); | 
|  | cache->Remove(key, nullptr); | 
|  | sorted_keys_.erase(std::find(sorted_keys_.begin(), sorted_keys_.end(), key)); | 
|  |  | 
|  | for (auto& observer : observer_list_) | 
|  | observer.OnBrowserStateWasRemoved(browser_state_path); | 
|  | } | 
|  |  | 
|  | size_t BrowserStateInfoCache::GetNumberOfBrowserStates() const { | 
|  | return sorted_keys_.size(); | 
|  | } | 
|  |  | 
|  | size_t BrowserStateInfoCache::GetIndexOfBrowserStateWithPath( | 
|  | const base::FilePath& browser_state_path) const { | 
|  | if (browser_state_path.DirName() != user_data_dir_) | 
|  | return std::string::npos; | 
|  | std::string search_key = CacheKeyFromBrowserStatePath(browser_state_path); | 
|  | for (size_t i = 0; i < sorted_keys_.size(); ++i) { | 
|  | if (sorted_keys_[i] == search_key) | 
|  | return i; | 
|  | } | 
|  | return std::string::npos; | 
|  | } | 
|  |  | 
|  | base::string16 BrowserStateInfoCache::GetUserNameOfBrowserStateAtIndex( | 
|  | size_t index) const { | 
|  | base::string16 user_name; | 
|  | GetInfoForBrowserStateAtIndex(index)->GetString(kUserNameKey, &user_name); | 
|  | return user_name; | 
|  | } | 
|  |  | 
|  | base::FilePath BrowserStateInfoCache::GetPathOfBrowserStateAtIndex( | 
|  | size_t index) const { | 
|  | return user_data_dir_.AppendASCII(sorted_keys_[index]); | 
|  | } | 
|  |  | 
|  | std::string BrowserStateInfoCache::GetGAIAIdOfBrowserStateAtIndex( | 
|  | size_t index) const { | 
|  | std::string gaia_id; | 
|  | GetInfoForBrowserStateAtIndex(index)->GetString(kGAIAIdKey, &gaia_id); | 
|  | return gaia_id; | 
|  | } | 
|  |  | 
|  | bool BrowserStateInfoCache::BrowserStateIsAuthenticatedAtIndex( | 
|  | size_t index) const { | 
|  | // The browser state is authenticated if the gaia_id of the info is not empty. | 
|  | // If it is empty, also check if the user name is not empty.  This latter | 
|  | // check is needed in case the browser state has not been loaded yet and the | 
|  | // gaia_id property has not yet been written. | 
|  | return !GetGAIAIdOfBrowserStateAtIndex(index).empty() || | 
|  | !GetUserNameOfBrowserStateAtIndex(index).empty(); | 
|  | } | 
|  |  | 
|  | bool BrowserStateInfoCache::BrowserStateIsAuthErrorAtIndex(size_t index) const { | 
|  | bool value = false; | 
|  | GetInfoForBrowserStateAtIndex(index)->GetBoolean(kIsAuthErrorKey, &value); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | void BrowserStateInfoCache::SetAuthInfoOfBrowserStateAtIndex( | 
|  | size_t index, | 
|  | const std::string& gaia_id, | 
|  | const base::string16& user_name) { | 
|  | // If both gaia_id and username are unchanged, abort early. | 
|  | if (gaia_id == GetGAIAIdOfBrowserStateAtIndex(index) && | 
|  | user_name == GetUserNameOfBrowserStateAtIndex(index)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | base::Value info = GetInfoForBrowserStateAtIndex(index)->Clone(); | 
|  | info.SetKey(kGAIAIdKey, base::Value(gaia_id)); | 
|  | info.SetKey(kUserNameKey, base::Value(user_name)); | 
|  | SetInfoForBrowserStateAtIndex(index, std::move(info)); | 
|  | } | 
|  |  | 
|  | void BrowserStateInfoCache::SetBrowserStateIsAuthErrorAtIndex(size_t index, | 
|  | bool value) { | 
|  | if (value == BrowserStateIsAuthErrorAtIndex(index)) | 
|  | return; | 
|  |  | 
|  | base::Value info = GetInfoForBrowserStateAtIndex(index)->Clone(); | 
|  | info.SetKey(kIsAuthErrorKey, base::Value(value)); | 
|  | SetInfoForBrowserStateAtIndex(index, std::move(info)); | 
|  | } | 
|  |  | 
|  | const base::FilePath& BrowserStateInfoCache::GetUserDataDir() const { | 
|  | return user_data_dir_; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void BrowserStateInfoCache::RegisterPrefs(PrefRegistrySimple* registry) { | 
|  | registry->RegisterDictionaryPref(prefs::kBrowserStateInfoCache); | 
|  | } | 
|  |  | 
|  | const base::DictionaryValue* | 
|  | BrowserStateInfoCache::GetInfoForBrowserStateAtIndex(size_t index) const { | 
|  | DCHECK_LT(index, GetNumberOfBrowserStates()); | 
|  | const base::DictionaryValue* cache = | 
|  | prefs_->GetDictionary(prefs::kBrowserStateInfoCache); | 
|  | const base::DictionaryValue* info = nullptr; | 
|  | cache->GetDictionaryWithoutPathExpansion(sorted_keys_[index], &info); | 
|  | return info; | 
|  | } | 
|  |  | 
|  | void BrowserStateInfoCache::SetInfoForBrowserStateAtIndex(size_t index, | 
|  | base::Value info) { | 
|  | DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache); | 
|  | base::DictionaryValue* cache = update.Get(); | 
|  | cache->SetKey(sorted_keys_[index], std::move(info)); | 
|  | } | 
|  |  | 
|  | std::string BrowserStateInfoCache::CacheKeyFromBrowserStatePath( | 
|  | const base::FilePath& browser_state_path) const { | 
|  | DCHECK(user_data_dir_ == browser_state_path.DirName()); | 
|  | base::FilePath base_name = browser_state_path.BaseName(); | 
|  | return base_name.MaybeAsASCII(); | 
|  | } | 
|  |  | 
|  | void BrowserStateInfoCache::AddBrowserStateCacheKey(const std::string& key) { | 
|  | sorted_keys_.insert( | 
|  | std::upper_bound(sorted_keys_.begin(), sorted_keys_.end(), key), key); | 
|  | } |