| // Copyright (c) 2011 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 "chrome/browser/prefs/pref_member.h" |
| |
| #include "base/logging.h" |
| #include "base/value_conversions.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "content/common/notification_type.h" |
| |
| namespace subtle { |
| |
| PrefMemberBase::PrefMemberBase() |
| : observer_(NULL), |
| prefs_(NULL), |
| setting_value_(false) { |
| } |
| |
| PrefMemberBase::~PrefMemberBase() { |
| Destroy(); |
| } |
| |
| |
| void PrefMemberBase::Init(const char* pref_name, PrefService* prefs, |
| NotificationObserver* observer) { |
| DCHECK(pref_name); |
| DCHECK(prefs); |
| DCHECK(pref_name_.empty()); // Check that Init is only called once. |
| observer_ = observer; |
| prefs_ = prefs; |
| pref_name_ = pref_name; |
| DCHECK(!pref_name_.empty()); |
| |
| // Add ourselves as a pref observer so we can keep our local value in sync. |
| prefs_->AddPrefObserver(pref_name, this); |
| } |
| |
| void PrefMemberBase::Destroy() { |
| if (prefs_ && !pref_name_.empty()) { |
| prefs_->RemovePrefObserver(pref_name_.c_str(), this); |
| prefs_ = NULL; |
| } |
| } |
| |
| void PrefMemberBase::MoveToThread(BrowserThread::ID thread_id) { |
| VerifyValuePrefName(); |
| // Load the value from preferences if it hasn't been loaded so far. |
| if (!internal()) |
| UpdateValueFromPref(); |
| internal()->MoveToThread(thread_id); |
| } |
| |
| void PrefMemberBase::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| VerifyValuePrefName(); |
| DCHECK(NotificationType::PREF_CHANGED == type); |
| UpdateValueFromPref(); |
| if (!setting_value_ && observer_) |
| observer_->Observe(type, source, details); |
| } |
| |
| void PrefMemberBase::UpdateValueFromPref() const { |
| VerifyValuePrefName(); |
| const PrefService::Preference* pref = |
| prefs_->FindPreference(pref_name_.c_str()); |
| DCHECK(pref); |
| if (!internal()) |
| CreateInternal(); |
| internal()->UpdateValue(pref->GetValue()->DeepCopy(), pref->IsManaged()); |
| } |
| |
| void PrefMemberBase::VerifyPref() const { |
| VerifyValuePrefName(); |
| if (!internal()) |
| UpdateValueFromPref(); |
| } |
| |
| PrefMemberBase::Internal::Internal() : thread_id_(BrowserThread::UI) { } |
| PrefMemberBase::Internal::~Internal() { } |
| |
| bool PrefMemberBase::Internal::IsOnCorrectThread() const { |
| // In unit tests, there may not be a UI thread. |
| return (BrowserThread::CurrentlyOn(thread_id_) || |
| (thread_id_ == BrowserThread::UI && |
| !BrowserThread::IsMessageLoopValid(BrowserThread::UI))); |
| } |
| |
| void PrefMemberBase::Internal::UpdateValue(Value* v, bool is_managed) const { |
| scoped_ptr<Value> value(v); |
| if (IsOnCorrectThread()) { |
| bool rv = UpdateValueInternal(*value); |
| DCHECK(rv); |
| is_managed_ = is_managed; |
| } else { |
| bool rv = BrowserThread::PostTask( |
| thread_id_, FROM_HERE, |
| NewRunnableMethod(this, |
| &PrefMemberBase::Internal::UpdateValue, |
| value.release(), is_managed)); |
| DCHECK(rv); |
| } |
| } |
| |
| void PrefMemberBase::Internal::MoveToThread(BrowserThread::ID thread_id) { |
| CheckOnCorrectThread(); |
| thread_id_ = thread_id; |
| } |
| |
| } // namespace subtle |
| |
| template <> |
| void PrefMember<bool>::UpdatePref(const bool& value) { |
| prefs()->SetBoolean(pref_name().c_str(), value); |
| } |
| |
| template <> |
| bool PrefMember<bool>::Internal::UpdateValueInternal(const Value& value) const { |
| return value.GetAsBoolean(&value_); |
| } |
| |
| template <> |
| void PrefMember<int>::UpdatePref(const int& value) { |
| prefs()->SetInteger(pref_name().c_str(), value); |
| } |
| |
| template <> |
| bool PrefMember<int>::Internal::UpdateValueInternal(const Value& value) const { |
| return value.GetAsInteger(&value_); |
| } |
| |
| template <> |
| void PrefMember<double>::UpdatePref(const double& value) { |
| prefs()->SetDouble(pref_name().c_str(), value); |
| } |
| |
| template <> |
| bool PrefMember<double>::Internal::UpdateValueInternal(const Value& value) |
| const { |
| return value.GetAsDouble(&value_); |
| } |
| |
| template <> |
| void PrefMember<std::string>::UpdatePref(const std::string& value) { |
| prefs()->SetString(pref_name().c_str(), value); |
| } |
| |
| template <> |
| bool PrefMember<std::string>::Internal::UpdateValueInternal(const Value& value) |
| const { |
| return value.GetAsString(&value_); |
| } |
| |
| template <> |
| void PrefMember<FilePath>::UpdatePref(const FilePath& value) { |
| prefs()->SetFilePath(pref_name().c_str(), value); |
| } |
| |
| template <> |
| bool PrefMember<FilePath>::Internal::UpdateValueInternal(const Value& value) |
| const { |
| return base::GetValueAsFilePath(value, &value_); |
| } |
| |
| template <> |
| void PrefMember<ListValue*>::UpdatePref(ListValue*const& value) { |
| // prefs takes ownership of the value passed, so make a copy. |
| prefs()->SetList(pref_name().c_str(), value->DeepCopy()); |
| } |
| |
| template <> |
| bool PrefMember<ListValue*>::Internal::UpdateValueInternal(const Value& value) |
| const { |
| // Verify the type before doing the DeepCopy to avoid leaking the copy. |
| if (value.GetType() != Value::TYPE_LIST) |
| return false; |
| |
| // ListPrefMember keeps a copy of the ListValue and of its contents. |
| // GetAsList() assigns its |this| (the DeepCopy) to |value_|. |
| delete value_; |
| return value.DeepCopy()->GetAsList(&value_); |
| } |
| |
| template <> |
| PrefMember<ListValue*>::Internal::~Internal() { |
| delete value_; |
| } |
| |