pref_service.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/prefs/pref_service.h"
6
7#include <algorithm>
8#include <string>
9
10#include "base/command_line.h"
11#include "base/file_path.h"
12#include "base/file_util.h"
13#include "base/logging.h"
14#include "base/message_loop.h"
15#include "base/metrics/histogram.h"
16#include "base/stl_util-inl.h"
17#include "base/string_number_conversions.h"
18#include "base/string_util.h"
19#include "base/sys_string_conversions.h"
20#include "base/utf_string_conversions.h"
21#include "build/build_config.h"
22#include "chrome/browser/extensions/extension_pref_store.h"
23#include "chrome/browser/policy/configuration_policy_pref_store.h"
24#include "chrome/browser/prefs/command_line_pref_store.h"
25#include "chrome/browser/prefs/default_pref_store.h"
26#include "chrome/browser/prefs/overlay_persistent_pref_store.h"
27#include "chrome/browser/prefs/pref_notifier_impl.h"
28#include "chrome/browser/prefs/pref_value_store.h"
29#include "chrome/common/json_pref_store.h"
30#include "chrome/common/notification_service.h"
31#include "content/browser/browser_thread.h"
32#include "grit/chromium_strings.h"
33#include "grit/generated_resources.h"
34#include "ui/base/l10n/l10n_util.h"
35
36namespace {
37
38// A helper function for RegisterLocalized*Pref that creates a Value* based on
39// the string value in the locale dll.  Because we control the values in a
40// locale dll, this should always return a Value of the appropriate type.
41Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) {
42  std::string resource_string = l10n_util::GetStringUTF8(message_id);
43  DCHECK(!resource_string.empty());
44  switch (type) {
45    case Value::TYPE_BOOLEAN: {
46      if ("true" == resource_string)
47        return Value::CreateBooleanValue(true);
48      if ("false" == resource_string)
49        return Value::CreateBooleanValue(false);
50      break;
51    }
52
53    case Value::TYPE_INTEGER: {
54      int val;
55      base::StringToInt(resource_string, &val);
56      return Value::CreateIntegerValue(val);
57    }
58
59    case Value::TYPE_DOUBLE: {
60      double val;
61      base::StringToDouble(resource_string, &val);
62      return Value::CreateDoubleValue(val);
63    }
64
65    case Value::TYPE_STRING: {
66      return Value::CreateStringValue(resource_string);
67    }
68
69    default: {
70      NOTREACHED() <<
71          "list and dictionary types cannot have default locale values";
72    }
73  }
74  NOTREACHED();
75  return Value::CreateNullValue();
76}
77
78// Forwards a notification after a PostMessage so that we can wait for the
79// MessageLoop to run.
80void NotifyReadError(PrefService* pref, int message_id) {
81  Source<PrefService> source(pref);
82  NotificationService::current()->Notify(NotificationType::PROFILE_ERROR,
83                                         source, Details<int>(&message_id));
84}
85
86}  // namespace
87
88// static
89PrefService* PrefService::CreatePrefService(const FilePath& pref_filename,
90                                            PrefStore* extension_prefs,
91                                            Profile* profile) {
92  using policy::ConfigurationPolicyPrefStore;
93
94#if defined(OS_LINUX)
95  // We'd like to see what fraction of our users have the preferences
96  // stored on a network file system, as we've had no end of troubles
97  // with NFS/AFS.
98  // TODO(evanm): remove this once we've collected state.
99  file_util::FileSystemType fstype;
100  if (file_util::GetFileSystemType(pref_filename.DirName(), &fstype)) {
101    UMA_HISTOGRAM_ENUMERATION("PrefService.FileSystemType",
102                              static_cast<int>(fstype),
103                              file_util::FILE_SYSTEM_TYPE_COUNT);
104  }
105#endif
106
107  ConfigurationPolicyPrefStore* managed_platform =
108      ConfigurationPolicyPrefStore::CreateManagedPlatformPolicyPrefStore();
109  ConfigurationPolicyPrefStore* managed_cloud =
110      ConfigurationPolicyPrefStore::CreateManagedCloudPolicyPrefStore(profile);
111  CommandLinePrefStore* command_line =
112      new CommandLinePrefStore(CommandLine::ForCurrentProcess());
113  JsonPrefStore* user = new JsonPrefStore(
114      pref_filename,
115      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
116  ConfigurationPolicyPrefStore* recommended_platform =
117      ConfigurationPolicyPrefStore::CreateRecommendedPlatformPolicyPrefStore();
118  ConfigurationPolicyPrefStore* recommended_cloud =
119      ConfigurationPolicyPrefStore::CreateRecommendedCloudPolicyPrefStore(
120          profile);
121  DefaultPrefStore* default_pref_store = new DefaultPrefStore();
122
123  return new PrefService(managed_platform, managed_cloud, extension_prefs,
124                         command_line, user, recommended_platform,
125                         recommended_cloud, default_pref_store);
126}
127
128PrefService* PrefService::CreateIncognitoPrefService(
129    PrefStore* incognito_extension_prefs) {
130  return new PrefService(*this, incognito_extension_prefs);
131}
132
133PrefService::PrefService(PrefStore* managed_platform_prefs,
134                         PrefStore* managed_cloud_prefs,
135                         PrefStore* extension_prefs,
136                         PrefStore* command_line_prefs,
137                         PersistentPrefStore* user_prefs,
138                         PrefStore* recommended_platform_prefs,
139                         PrefStore* recommended_cloud_prefs,
140                         DefaultPrefStore* default_store)
141    : user_pref_store_(user_prefs),
142      default_store_(default_store) {
143  pref_notifier_.reset(new PrefNotifierImpl(this));
144  pref_value_store_.reset(
145      new PrefValueStore(managed_platform_prefs,
146                         managed_cloud_prefs,
147                         extension_prefs,
148                         command_line_prefs,
149                         user_pref_store_,
150                         recommended_platform_prefs,
151                         recommended_cloud_prefs,
152                         default_store,
153                         pref_notifier_.get()));
154  InitFromStorage();
155}
156
157PrefService::PrefService(const PrefService& original,
158                         PrefStore* incognito_extension_prefs)
159      : user_pref_store_(
160            new OverlayPersistentPrefStore(original.user_pref_store_.get())),
161        default_store_(original.default_store_.get()){
162  pref_notifier_.reset(new PrefNotifierImpl(this));
163  pref_value_store_.reset(original.pref_value_store_->CloneAndSpecialize(
164      NULL, // managed_platform_prefs
165      NULL, // managed_cloud_prefs
166      incognito_extension_prefs,
167      NULL, // command_line_prefs
168      user_pref_store_.get(),
169      NULL, // recommended_platform_prefs
170      NULL, // recommended_cloud_prefs
171      default_store_.get(),
172      pref_notifier_.get()));
173  InitFromStorage();
174}
175
176PrefService::~PrefService() {
177  DCHECK(CalledOnValidThread());
178  STLDeleteContainerPointers(prefs_.begin(), prefs_.end());
179  prefs_.clear();
180
181  // Reset pointers so accesses after destruction reliably crash.
182  pref_value_store_.reset();
183  user_pref_store_ = NULL;
184  default_store_ = NULL;
185}
186
187void PrefService::InitFromStorage() {
188  const PersistentPrefStore::PrefReadError error =
189      user_pref_store_->ReadPrefs();
190  if (error == PersistentPrefStore::PREF_READ_ERROR_NONE)
191    return;
192
193  // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for
194  // an example problem that this can cause.
195  // Do some diagnosis and try to avoid losing data.
196  int message_id = 0;
197  if (error <= PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE) {
198    message_id = IDS_PREFERENCES_CORRUPT_ERROR;
199  } else if (error != PersistentPrefStore::PREF_READ_ERROR_NO_FILE) {
200    message_id = IDS_PREFERENCES_UNREADABLE_ERROR;
201  }
202
203  if (message_id) {
204    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
205        NewRunnableFunction(&NotifyReadError, this, message_id));
206  }
207  UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error, 20);
208}
209
210bool PrefService::ReloadPersistentPrefs() {
211  return user_pref_store_->ReadPrefs() ==
212             PersistentPrefStore::PREF_READ_ERROR_NONE;
213}
214
215bool PrefService::SavePersistentPrefs() {
216  DCHECK(CalledOnValidThread());
217
218  return user_pref_store_->WritePrefs();
219}
220
221void PrefService::ScheduleSavePersistentPrefs() {
222  DCHECK(CalledOnValidThread());
223
224  user_pref_store_->ScheduleWritePrefs();
225}
226
227void PrefService::RegisterBooleanPref(const char* path,
228                                      bool default_value) {
229  RegisterPreference(path, Value::CreateBooleanValue(default_value));
230}
231
232void PrefService::RegisterIntegerPref(const char* path, int default_value) {
233  RegisterPreference(path, Value::CreateIntegerValue(default_value));
234}
235
236void PrefService::RegisterDoublePref(const char* path, double default_value) {
237  RegisterPreference(path, Value::CreateDoubleValue(default_value));
238}
239
240void PrefService::RegisterStringPref(const char* path,
241                                     const std::string& default_value) {
242  RegisterPreference(path, Value::CreateStringValue(default_value));
243}
244
245void PrefService::RegisterFilePathPref(const char* path,
246                                       const FilePath& default_value) {
247  RegisterPreference(path, Value::CreateStringValue(default_value.value()));
248}
249
250void PrefService::RegisterListPref(const char* path) {
251  RegisterPreference(path, new ListValue());
252}
253
254void PrefService::RegisterListPref(const char* path, ListValue* default_value) {
255  RegisterPreference(path, default_value);
256}
257
258void PrefService::RegisterDictionaryPref(const char* path) {
259  RegisterPreference(path, new DictionaryValue());
260}
261
262void PrefService::RegisterDictionaryPref(const char* path,
263                                         DictionaryValue* default_value) {
264  RegisterPreference(path, default_value);
265}
266
267void PrefService::RegisterLocalizedBooleanPref(const char* path,
268                                               int locale_default_message_id) {
269  RegisterPreference(
270      path,
271      CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id));
272}
273
274void PrefService::RegisterLocalizedIntegerPref(const char* path,
275                                               int locale_default_message_id) {
276  RegisterPreference(
277      path,
278      CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id));
279}
280
281void PrefService::RegisterLocalizedDoublePref(const char* path,
282                                              int locale_default_message_id) {
283  RegisterPreference(
284      path,
285      CreateLocaleDefaultValue(Value::TYPE_DOUBLE, locale_default_message_id));
286}
287
288void PrefService::RegisterLocalizedStringPref(const char* path,
289                                              int locale_default_message_id) {
290  RegisterPreference(
291      path,
292      CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id));
293}
294
295bool PrefService::GetBoolean(const char* path) const {
296  DCHECK(CalledOnValidThread());
297
298  bool result = false;
299
300  const Preference* pref = FindPreference(path);
301  if (!pref) {
302    NOTREACHED() << "Trying to read an unregistered pref: " << path;
303    return result;
304  }
305  bool rv = pref->GetValue()->GetAsBoolean(&result);
306  DCHECK(rv);
307  return result;
308}
309
310int PrefService::GetInteger(const char* path) const {
311  DCHECK(CalledOnValidThread());
312
313  int result = 0;
314
315  const Preference* pref = FindPreference(path);
316  if (!pref) {
317    NOTREACHED() << "Trying to read an unregistered pref: " << path;
318    return result;
319  }
320  bool rv = pref->GetValue()->GetAsInteger(&result);
321  DCHECK(rv);
322  return result;
323}
324
325double PrefService::GetDouble(const char* path) const {
326  DCHECK(CalledOnValidThread());
327
328  double result = 0.0;
329
330  const Preference* pref = FindPreference(path);
331  if (!pref) {
332    NOTREACHED() << "Trying to read an unregistered pref: " << path;
333    return result;
334  }
335  bool rv = pref->GetValue()->GetAsDouble(&result);
336  DCHECK(rv);
337  return result;
338}
339
340std::string PrefService::GetString(const char* path) const {
341  DCHECK(CalledOnValidThread());
342
343  std::string result;
344
345  const Preference* pref = FindPreference(path);
346  if (!pref) {
347    NOTREACHED() << "Trying to read an unregistered pref: " << path;
348    return result;
349  }
350  bool rv = pref->GetValue()->GetAsString(&result);
351  DCHECK(rv);
352  return result;
353}
354
355FilePath PrefService::GetFilePath(const char* path) const {
356  DCHECK(CalledOnValidThread());
357
358  FilePath result;
359
360  const Preference* pref = FindPreference(path);
361  if (!pref) {
362    NOTREACHED() << "Trying to read an unregistered pref: " << path;
363    return FilePath(result);
364  }
365  bool rv = pref->GetValue()->GetAsFilePath(&result);
366  DCHECK(rv);
367  return result;
368}
369
370bool PrefService::HasPrefPath(const char* path) const {
371  const Preference* pref = FindPreference(path);
372  return pref && !pref->IsDefaultValue();
373}
374
375DictionaryValue* PrefService::GetPreferenceValues() const {
376  DCHECK(CalledOnValidThread());
377  DictionaryValue* out = new DictionaryValue;
378  DefaultPrefStore::const_iterator i = default_store_->begin();
379  for (; i != default_store_->end(); ++i) {
380    const Value* value = FindPreference(i->first.c_str())->GetValue();
381    out->Set(i->first, value->DeepCopy());
382  }
383  return out;
384}
385
386const PrefService::Preference* PrefService::FindPreference(
387    const char* pref_name) const {
388  DCHECK(CalledOnValidThread());
389  Preference p(this, pref_name, Value::TYPE_NULL);
390  PreferenceSet::const_iterator it = prefs_.find(&p);
391  if (it != prefs_.end())
392    return *it;
393  const Value::ValueType type = default_store_->GetType(pref_name);
394  if (type == Value::TYPE_NULL)
395    return NULL;
396  Preference* new_pref = new Preference(this, pref_name, type);
397  prefs_.insert(new_pref);
398  return new_pref;
399}
400
401bool PrefService::ReadOnly() const {
402  return user_pref_store_->ReadOnly();
403}
404
405PrefNotifier* PrefService::pref_notifier() const {
406  return pref_notifier_.get();
407}
408
409bool PrefService::IsManagedPreference(const char* pref_name) const {
410  const Preference* pref = FindPreference(pref_name);
411  return pref && pref->IsManaged();
412}
413
414const DictionaryValue* PrefService::GetDictionary(const char* path) const {
415  DCHECK(CalledOnValidThread());
416
417  const Preference* pref = FindPreference(path);
418  if (!pref) {
419    NOTREACHED() << "Trying to read an unregistered pref: " << path;
420    return NULL;
421  }
422  const Value* value = pref->GetValue();
423  if (value->GetType() != Value::TYPE_DICTIONARY) {
424    NOTREACHED();
425    return NULL;
426  }
427  return static_cast<const DictionaryValue*>(value);
428}
429
430const ListValue* PrefService::GetList(const char* path) const {
431  DCHECK(CalledOnValidThread());
432
433  const Preference* pref = FindPreference(path);
434  if (!pref) {
435    NOTREACHED() << "Trying to read an unregistered pref: " << path;
436    return NULL;
437  }
438  const Value* value = pref->GetValue();
439  if (value->GetType() != Value::TYPE_LIST) {
440    NOTREACHED();
441    return NULL;
442  }
443  return static_cast<const ListValue*>(value);
444}
445
446void PrefService::AddPrefObserver(const char* path,
447                                  NotificationObserver* obs) {
448  pref_notifier_->AddPrefObserver(path, obs);
449}
450
451void PrefService::RemovePrefObserver(const char* path,
452                                     NotificationObserver* obs) {
453  pref_notifier_->RemovePrefObserver(path, obs);
454}
455
456void PrefService::RegisterPreference(const char* path, Value* default_value) {
457  DCHECK(CalledOnValidThread());
458
459  // The main code path takes ownership, but most don't. We'll be safe.
460  scoped_ptr<Value> scoped_value(default_value);
461
462  if (FindPreference(path)) {
463    NOTREACHED() << "Tried to register duplicate pref " << path;
464    return;
465  }
466
467  Value::ValueType orig_type = default_value->GetType();
468  DCHECK(orig_type != Value::TYPE_NULL && orig_type != Value::TYPE_BINARY) <<
469         "invalid preference type: " << orig_type;
470
471  // Hand off ownership.
472  default_store_->SetDefaultValue(path, scoped_value.release());
473}
474
475void PrefService::ClearPref(const char* path) {
476  DCHECK(CalledOnValidThread());
477
478  const Preference* pref = FindPreference(path);
479  if (!pref) {
480    NOTREACHED() << "Trying to clear an unregistered pref: " << path;
481    return;
482  }
483  user_pref_store_->RemoveValue(path);
484}
485
486void PrefService::Set(const char* path, const Value& value) {
487  DCHECK(CalledOnValidThread());
488
489  const Preference* pref = FindPreference(path);
490  if (!pref) {
491    NOTREACHED() << "Trying to write an unregistered pref: " << path;
492    return;
493  }
494
495  if (pref->GetType() != value.GetType()) {
496    NOTREACHED() << "Trying to set pref " << path
497                 << " of type " << pref->GetType()
498                 << " to value of type " << value.GetType();
499  } else {
500    user_pref_store_->SetValue(path, value.DeepCopy());
501  }
502}
503
504void PrefService::SetBoolean(const char* path, bool value) {
505  SetUserPrefValue(path, Value::CreateBooleanValue(value));
506}
507
508void PrefService::SetInteger(const char* path, int value) {
509  SetUserPrefValue(path, Value::CreateIntegerValue(value));
510}
511
512void PrefService::SetDouble(const char* path, double value) {
513  SetUserPrefValue(path, Value::CreateDoubleValue(value));
514}
515
516void PrefService::SetString(const char* path, const std::string& value) {
517  SetUserPrefValue(path, Value::CreateStringValue(value));
518}
519
520void PrefService::SetFilePath(const char* path, const FilePath& value) {
521  SetUserPrefValue(path, Value::CreateFilePathValue(value));
522}
523
524void PrefService::SetInt64(const char* path, int64 value) {
525  SetUserPrefValue(path, Value::CreateStringValue(base::Int64ToString(value)));
526}
527
528int64 PrefService::GetInt64(const char* path) const {
529  DCHECK(CalledOnValidThread());
530
531  const Preference* pref = FindPreference(path);
532  if (!pref) {
533    NOTREACHED() << "Trying to read an unregistered pref: " << path;
534    return 0;
535  }
536  std::string result("0");
537  bool rv = pref->GetValue()->GetAsString(&result);
538  DCHECK(rv);
539
540  int64 val;
541  base::StringToInt64(result, &val);
542  return val;
543}
544
545void PrefService::RegisterInt64Pref(const char* path, int64 default_value) {
546  RegisterPreference(
547      path, Value::CreateStringValue(base::Int64ToString(default_value)));
548}
549
550DictionaryValue* PrefService::GetMutableDictionary(const char* path) {
551  DCHECK(CalledOnValidThread());
552  DLOG_IF(WARNING, IsManagedPreference(path)) <<
553      "Attempt to change managed preference " << path;
554
555  const Preference* pref = FindPreference(path);
556  if (!pref) {
557    NOTREACHED() << "Trying to get an unregistered pref: " << path;
558    return NULL;
559  }
560  if (pref->GetType() != Value::TYPE_DICTIONARY) {
561    NOTREACHED() << "Wrong type for GetMutableDictionary: " << path;
562    return NULL;
563  }
564
565  DictionaryValue* dict = NULL;
566  Value* tmp_value = NULL;
567  // Look for an existing preference in the user store. If it doesn't
568  // exist or isn't the correct type, create a new user preference.
569  if (user_pref_store_->GetValue(path, &tmp_value)
570          != PersistentPrefStore::READ_OK ||
571      !tmp_value->IsType(Value::TYPE_DICTIONARY)) {
572    dict = new DictionaryValue;
573    user_pref_store_->SetValueSilently(path, dict);
574  } else {
575    dict = static_cast<DictionaryValue*>(tmp_value);
576  }
577  return dict;
578}
579
580ListValue* PrefService::GetMutableList(const char* path) {
581  DCHECK(CalledOnValidThread());
582  DLOG_IF(WARNING, IsManagedPreference(path)) <<
583      "Attempt to change managed preference " << path;
584
585  const Preference* pref = FindPreference(path);
586  if (!pref) {
587    NOTREACHED() << "Trying to get an unregistered pref: " << path;
588    return NULL;
589  }
590  if (pref->GetType() != Value::TYPE_LIST) {
591    NOTREACHED() << "Wrong type for GetMutableList: " << path;
592    return NULL;
593  }
594
595  ListValue* list = NULL;
596  Value* tmp_value = NULL;
597  // Look for an existing preference in the user store. If it doesn't
598  // exist or isn't the correct type, create a new user preference.
599  if (user_pref_store_->GetValue(path, &tmp_value)
600          != PersistentPrefStore::READ_OK ||
601      !tmp_value->IsType(Value::TYPE_LIST)) {
602    list = new ListValue;
603    user_pref_store_->SetValueSilently(path, list);
604  } else {
605    list = static_cast<ListValue*>(tmp_value);
606  }
607  return list;
608}
609
610void PrefService::ReportValueChanged(const std::string& key) {
611  user_pref_store_->ReportValueChanged(key);
612}
613
614void PrefService::SetUserPrefValue(const char* path, Value* new_value) {
615  DCHECK(CalledOnValidThread());
616  DLOG_IF(WARNING, IsManagedPreference(path)) <<
617      "Attempt to change managed preference " << path;
618
619  const Preference* pref = FindPreference(path);
620  if (!pref) {
621    NOTREACHED() << "Trying to write an unregistered pref: " << path;
622    return;
623  }
624  if (pref->GetType() != new_value->GetType()) {
625    NOTREACHED() << "Trying to set pref " << path
626                 << " of type " << pref->GetType()
627                 << " to value of type " << new_value->GetType();
628    return;
629  }
630
631  user_pref_store_->SetValue(path, new_value);
632}
633
634///////////////////////////////////////////////////////////////////////////////
635// PrefService::Preference
636
637PrefService::Preference::Preference(const PrefService* service,
638                                    const char* name,
639                                    Value::ValueType type)
640      : name_(name),
641        type_(type),
642        pref_service_(service) {
643  DCHECK(name);
644  DCHECK(service);
645}
646
647Value::ValueType PrefService::Preference::GetType() const {
648  return type_;
649}
650
651const Value* PrefService::Preference::GetValue() const {
652  DCHECK(pref_service_->FindPreference(name_.c_str())) <<
653      "Must register pref before getting its value";
654
655  Value* found_value = NULL;
656  if (pref_value_store()->GetValue(name_, type_, &found_value)) {
657    DCHECK(found_value->IsType(type_));
658    return found_value;
659  }
660
661  // Every registered preference has at least a default value.
662  NOTREACHED() << "no valid value found for registered pref " << name_;
663  return NULL;
664}
665
666bool PrefService::Preference::IsManaged() const {
667  return pref_value_store()->PrefValueInManagedStore(name_.c_str());
668}
669
670bool PrefService::Preference::HasExtensionSetting() const {
671  return pref_value_store()->PrefValueInExtensionStore(name_.c_str());
672}
673
674bool PrefService::Preference::HasUserSetting() const {
675  return pref_value_store()->PrefValueInUserStore(name_.c_str());
676}
677
678bool PrefService::Preference::IsExtensionControlled() const {
679  return pref_value_store()->PrefValueFromExtensionStore(name_.c_str());
680}
681
682bool PrefService::Preference::IsUserControlled() const {
683  return pref_value_store()->PrefValueFromUserStore(name_.c_str());
684}
685
686bool PrefService::Preference::IsDefaultValue() const {
687  return pref_value_store()->PrefValueFromDefaultStore(name_.c_str());
688}
689
690bool PrefService::Preference::IsUserModifiable() const {
691  return pref_value_store()->PrefValueUserModifiable(name_.c_str());
692}
693
694bool PrefService::Preference::IsExtensionModifiable() const {
695  return pref_value_store()->PrefValueExtensionModifiable(name_.c_str());
696}
697