15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_pref_value_map.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/prefs/pref_value_map.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::ExtensionPrefsScope;
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct ExtensionPrefValueMap::ExtensionEntry {
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Installation time of the extension.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time install_time;
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Whether extension is enabled in the profile.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool enabled;
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Whether the extension has access to the incognito profile.
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool incognito_enabled;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extension controlled preferences for the regular profile.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefValueMap regular_profile_preferences;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extension controlled preferences that should *only* apply to the regular
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // profile.
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefValueMap regular_only_profile_preferences;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Persistent extension controlled preferences for the incognito profile,
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // empty for regular profile ExtensionPrefStore.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefValueMap incognito_profile_preferences_persistent;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Session only extension controlled preferences for the incognito profile.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These preferences are deleted when the incognito profile is destroyed.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefValueMap incognito_profile_preferences_session_only;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionPrefValueMap::ExtensionPrefValueMap() : destroyed_(false) {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionPrefValueMap::~ExtensionPrefValueMap() {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!destroyed_) {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NotifyOfDestruction();
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    destroyed_ = true;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteValues(&entries_);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entries_.clear();
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::Shutdown() {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NotifyOfDestruction();
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  destroyed_ = true;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::SetExtensionPref(const std::string& ext_id,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             const std::string& key,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             ExtensionPrefsScope scope,
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                             base::Value* value) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefValueMap* prefs = GetExtensionPrefValueMap(ext_id, scope);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (prefs->SetValue(key, value))
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NotifyPrefValueChanged(key);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::RemoveExtensionPref(
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& ext_id,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& key,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionPrefsScope scope) {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefValueMap* prefs = GetExtensionPrefValueMap(ext_id, scope);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (prefs->RemoveValue(key))
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NotifyPrefValueChanged(key);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ExtensionPrefValueMap::CanExtensionControlPref(
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& pref_key,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool incognito) const {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator ext = entries_.find(extension_id);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ext == entries_.end()) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (incognito && !ext->second->incognito_enabled)
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator winner =
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetEffectivePrefValueController(pref_key, incognito, NULL);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (winner == entries_.end())
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return winner->second->install_time <= ext->second->install_time;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::ClearAllIncognitoSessionOnlyPreferences() {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::set<std::string> KeySet;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KeySet deleted_keys;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::iterator i;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = entries_.begin(); i != entries_.end(); ++i) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrefValueMap& inc_prefs =
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i->second->incognito_profile_preferences_session_only;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrefValueMap::iterator j;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (j = inc_prefs.begin(); j != inc_prefs.end(); ++j)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      deleted_keys.insert(j->first);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    inc_prefs.Clear();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KeySet::iterator k;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (k = deleted_keys.begin(); k != deleted_keys.end(); ++k)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NotifyPrefValueChanged(*k);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ExtensionPrefValueMap::DoesExtensionControlPref(
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& pref_key,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool* from_incognito) const {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool incognito = (from_incognito != NULL);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator winner =
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetEffectivePrefValueController(pref_key, incognito, from_incognito);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (winner == entries_.end())
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return winner->first == extension_id;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::RegisterExtension(const std::string& ext_id,
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              const base::Time& install_time,
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                              bool is_enabled,
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                              bool is_incognito_enabled) {
125d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (entries_.find(ext_id) == entries_.end()) {
126d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    entries_[ext_id] = new ExtensionEntry;
127d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
128d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Only update the install time if the extension is newly installed.
129d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    entries_[ext_id]->install_time = install_time;
130d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
131d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entries_[ext_id]->enabled = is_enabled;
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  entries_[ext_id]->incognito_enabled = is_incognito_enabled;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::UnregisterExtension(const std::string& ext_id) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::iterator i = entries_.find(ext_id);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (i == entries_.end())
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<std::string> keys;  // keys set by this extension
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetExtensionControlledKeys(*(i->second), &keys);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delete i->second;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  entries_.erase(i);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NotifyPrefValueChanged(keys);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::SetExtensionState(const std::string& ext_id,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              bool is_enabled) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator i = entries_.find(ext_id);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This may happen when sync sets the extension state for an
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // extension that is not installed.
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (i == entries_.end())
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (i->second->enabled == is_enabled)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<std::string> keys;  // keys set by this extension
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetExtensionControlledKeys(*(i->second), &keys);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  i->second->enabled = is_enabled;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NotifyPrefValueChanged(keys);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void ExtensionPrefValueMap::SetExtensionIncognitoState(
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& ext_id,
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bool is_incognito_enabled) {
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ExtensionEntryMap::const_iterator i = entries_.find(ext_id);
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // This may happen when sync sets the extension state for an
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // extension that is not installed.
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (i == entries_.end())
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (i->second->incognito_enabled == is_incognito_enabled)
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::set<std::string> keys;  // keys set by this extension
175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  GetExtensionControlledKeys(*(i->second), &keys);
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  i->second->incognito_enabled = is_incognito_enabled;
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NotifyPrefValueChanged(keys);
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PrefValueMap* ExtensionPrefValueMap::GetExtensionPrefValueMap(
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& ext_id,
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionPrefsScope scope) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator i = entries_.find(ext_id);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(i != entries_.end());
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (scope) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeRegular:
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->regular_profile_preferences);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeRegularOnly:
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->regular_only_profile_preferences);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeIncognitoPersistent:
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->incognito_profile_preferences_persistent);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeIncognitoSessionOnly:
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->incognito_profile_preferences_session_only);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const PrefValueMap* ExtensionPrefValueMap::GetExtensionPrefValueMap(
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& ext_id,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionPrefsScope scope) const {
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator i = entries_.find(ext_id);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(i != entries_.end());
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (scope) {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeRegular:
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->regular_profile_preferences);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeRegularOnly:
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->regular_only_profile_preferences);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeIncognitoPersistent:
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->incognito_profile_preferences_persistent);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case extensions::kExtensionPrefsScopeIncognitoSessionOnly:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return &(i->second->incognito_profile_preferences_session_only);
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::GetExtensionControlledKeys(
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ExtensionEntry& entry,
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::set<std::string>* out) const {
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefValueMap::const_iterator i;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefValueMap& regular_prefs = entry.regular_profile_preferences;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = regular_prefs.begin(); i != regular_prefs.end(); ++i)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    out->insert(i->first);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefValueMap& regular_only_prefs =
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entry.regular_only_profile_preferences;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = regular_only_prefs.begin(); i != regular_only_prefs.end(); ++i)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    out->insert(i->first);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefValueMap& inc_prefs_pers =
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entry.incognito_profile_preferences_persistent;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = inc_prefs_pers.begin(); i != inc_prefs_pers.end(); ++i)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    out->insert(i->first);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefValueMap& inc_prefs_session =
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      entry.incognito_profile_preferences_session_only;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = inc_prefs_session.begin(); i != inc_prefs_session.end(); ++i)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    out->insert(i->first);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const base::Value* ExtensionPrefValueMap::GetEffectivePrefValue(
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& key,
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool incognito,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool* from_incognito) const {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator winner =
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetEffectivePrefValueController(key, incognito, from_incognito);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (winner == entries_.end())
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::Value* value = NULL;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string& ext_id = winner->first;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First search for incognito session only preferences.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (incognito) {
257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DCHECK(winner->second->incognito_enabled);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PrefValueMap* prefs = GetExtensionPrefValueMap(
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ext_id, extensions::kExtensionPrefsScopeIncognitoSessionOnly);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs->GetValue(key, &value);
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (value)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return value;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If no incognito session only preference exists, fall back to persistent
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // incognito preference.
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs = GetExtensionPrefValueMap(
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ext_id,
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        extensions::kExtensionPrefsScopeIncognitoPersistent);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs->GetValue(key, &value);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (value)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return value;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Regular-only preference.
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PrefValueMap* prefs = GetExtensionPrefValueMap(
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ext_id, extensions::kExtensionPrefsScopeRegularOnly);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs->GetValue(key, &value);
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (value)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return value;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Regular preference.
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const PrefValueMap* prefs = GetExtensionPrefValueMap(
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ext_id, extensions::kExtensionPrefsScopeRegular);
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  prefs->GetValue(key, &value);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionPrefValueMap::ExtensionEntryMap::const_iterator
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionPrefValueMap::GetEffectivePrefValueController(
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& key,
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool incognito,
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool* from_incognito) const {
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator winner = entries_.end();
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time winners_install_time;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator i;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = entries_.begin(); i != entries_.end(); ++i) {
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& ext_id = i->first;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const base::Time& install_time = i->second->install_time;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const bool enabled = i->second->enabled;
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const bool incognito_enabled = i->second->incognito_enabled;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!enabled)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (install_time < winners_install_time)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (incognito && !incognito_enabled)
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      continue;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::Value* value = NULL;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PrefValueMap* prefs = GetExtensionPrefValueMap(
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ext_id, extensions::kExtensionPrefsScopeRegular);
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (prefs->GetValue(key, &value)) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      winner = i;
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      winners_install_time = install_time;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (from_incognito)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *from_incognito = false;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!incognito) {
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const PrefValueMap* prefs = GetExtensionPrefValueMap(
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ext_id, extensions::kExtensionPrefsScopeRegularOnly);
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (prefs->GetValue(key, &value)) {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        winner = i;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        winners_install_time = install_time;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (from_incognito)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *from_incognito = false;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Ignore the following prefs, because they're incognito-only.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs = GetExtensionPrefValueMap(
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ext_id, extensions::kExtensionPrefsScopeIncognitoPersistent);
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (prefs->GetValue(key, &value)) {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      winner = i;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      winners_install_time = install_time;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (from_incognito)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *from_incognito = true;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs = GetExtensionPrefValueMap(
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ext_id, extensions::kExtensionPrefsScopeIncognitoSessionOnly);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (prefs->GetValue(key, &value)) {
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      winner = i;
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      winners_install_time = install_time;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (from_incognito)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *from_incognito = true;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return winner;
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::AddObserver(
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionPrefValueMap::Observer* observer) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.AddObserver(observer);
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Collect all currently used keys and notify the new observer.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<std::string> keys;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionEntryMap::const_iterator i;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = entries_.begin(); i != entries_.end(); ++i)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetExtensionControlledKeys(*(i->second), &keys);
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<std::string>::const_iterator j;
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (j = keys.begin(); j != keys.end(); ++j)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    observer->OnPrefValueChanged(*j);
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::RemoveObserver(
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionPrefValueMap::Observer* observer) {
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.RemoveObserver(observer);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
374f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)std::string ExtensionPrefValueMap::GetExtensionControllingPref(
375f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const std::string& pref_key) const {
376f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ExtensionEntryMap::const_iterator winner =
377f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      GetEffectivePrefValueController(pref_key, false, NULL);
378f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (winner == entries_.end())
379f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return std::string();
380f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return winner->first;
381f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
382f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::NotifyInitializationCompleted() {
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_,
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    OnInitializationCompleted());
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::NotifyPrefValueChanged(
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::set<std::string>& keys) {
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<std::string>::const_iterator i;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i = keys.begin(); i != keys.end(); ++i)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NotifyPrefValueChanged(*i);
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::NotifyPrefValueChanged(const std::string& key) {
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_,
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    OnPrefValueChanged(key));
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionPrefValueMap::NotifyOfDestruction() {
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FOR_EACH_OBSERVER(ExtensionPrefValueMap::Observer, observers_,
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    OnExtensionPrefValueMapDestruction());
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
404