| // Copyright (c) 2012 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 "components/prefs/pref_notifier_impl.h" | |
| #include "base/debug/dump_without_crashing.h" | |
| #include "base/logging.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "components/prefs/pref_service.h" | |
| PrefNotifierImpl::PrefNotifierImpl() : pref_service_(nullptr) {} | |
| PrefNotifierImpl::PrefNotifierImpl(PrefService* service) | |
| : pref_service_(service) {} | |
| PrefNotifierImpl::~PrefNotifierImpl() { | |
| DCHECK(thread_checker_.CalledOnValidThread()); | |
| // Verify that there are no pref observers when we shut down. | |
| for (const auto& observer_list : pref_observers_) { | |
| if (observer_list.second->begin() != observer_list.second->end()) { | |
| // Generally, there should not be any subscribers left when the profile | |
| // is destroyed because a) those may indicate that the subscriber class | |
| // maintains an active pointer to the profile that might be used for | |
| // accessing a destroyed profile and b) those subscribers will try to | |
| // unsubscribe from a PrefService that has been destroyed with the | |
| // profile. | |
| // There is one exception that is safe: Static objects that are leaked | |
| // on process termination, if these objects just subscribe to preferences | |
| // and never access the profile after destruction. As these objects are | |
| // leaked on termination, it is guaranteed that they don't attempt to | |
| // unsubscribe. | |
| const auto& pref_name = observer_list.first; | |
| LOG(WARNING) << "Pref observer for " << pref_name | |
| << " found at shutdown."; | |
| // TODO(crbug.com/942491, 946668, 945772) The following code collects | |
| // stacktraces that show how the profile is destroyed that owns | |
| // preferences which are known to have subscriptions outliving the | |
| // profile. | |
| if ( | |
| // For GlobalMenuBarX11, crbug.com/946668 | |
| pref_name == "bookmark_bar.show_on_all_tabs" || | |
| // For BrowserWindowPropertyManager, crbug.com/942491 | |
| pref_name == "profile.icon_version" || | |
| // For BrowserWindowDefaultTouchBar, crbug.com/945772 | |
| pref_name == "default_search_provider_data.template_url_data") { | |
| base::debug::DumpWithoutCrashing(); | |
| } | |
| } | |
| } | |
| // Same for initialization observers. | |
| if (!init_observers_.empty()) | |
| LOG(WARNING) << "Init observer found at shutdown."; | |
| pref_observers_.clear(); | |
| init_observers_.clear(); | |
| } | |
| void PrefNotifierImpl::AddPrefObserver(const std::string& path, | |
| PrefObserver* obs) { | |
| // Get the pref observer list associated with the path. | |
| PrefObserverList* observer_list = nullptr; | |
| auto observer_iterator = pref_observers_.find(path); | |
| if (observer_iterator == pref_observers_.end()) { | |
| observer_list = new PrefObserverList; | |
| pref_observers_[path] = base::WrapUnique(observer_list); | |
| } else { | |
| observer_list = observer_iterator->second.get(); | |
| } | |
| // Add the pref observer. ObserverList will DCHECK if it already is | |
| // in the list. | |
| observer_list->AddObserver(obs); | |
| } | |
| void PrefNotifierImpl::RemovePrefObserver(const std::string& path, | |
| PrefObserver* obs) { | |
| DCHECK(thread_checker_.CalledOnValidThread()); | |
| auto observer_iterator = pref_observers_.find(path); | |
| if (observer_iterator == pref_observers_.end()) { | |
| return; | |
| } | |
| PrefObserverList* observer_list = observer_iterator->second.get(); | |
| observer_list->RemoveObserver(obs); | |
| } | |
| void PrefNotifierImpl::AddPrefObserverAllPrefs(PrefObserver* observer) { | |
| DCHECK(thread_checker_.CalledOnValidThread()); | |
| all_prefs_pref_observers_.AddObserver(observer); | |
| } | |
| void PrefNotifierImpl::RemovePrefObserverAllPrefs(PrefObserver* observer) { | |
| DCHECK(thread_checker_.CalledOnValidThread()); | |
| all_prefs_pref_observers_.RemoveObserver(observer); | |
| } | |
| void PrefNotifierImpl::AddInitObserver(base::OnceCallback<void(bool)> obs) { | |
| init_observers_.push_back(std::move(obs)); | |
| } | |
| void PrefNotifierImpl::OnPreferenceChanged(const std::string& path) { | |
| FireObservers(path); | |
| } | |
| void PrefNotifierImpl::OnInitializationCompleted(bool succeeded) { | |
| DCHECK(thread_checker_.CalledOnValidThread()); | |
| // We must move init_observers_ to a local variable before we run | |
| // observers, or we can end up in this method re-entrantly before | |
| // clearing the observers list. | |
| PrefInitObserverList observers; | |
| std::swap(observers, init_observers_); | |
| for (auto& observer : observers) | |
| std::move(observer).Run(succeeded); | |
| } | |
| void PrefNotifierImpl::FireObservers(const std::string& path) { | |
| DCHECK(thread_checker_.CalledOnValidThread()); | |
| // Only send notifications for registered preferences. | |
| if (!pref_service_->FindPreference(path)) | |
| return; | |
| // Fire observers for any preference change. | |
| for (auto& observer : all_prefs_pref_observers_) | |
| observer.OnPreferenceChanged(pref_service_, path); | |
| auto observer_iterator = pref_observers_.find(path); | |
| if (observer_iterator == pref_observers_.end()) | |
| return; | |
| for (PrefObserver& observer : *(observer_iterator->second)) | |
| observer.OnPreferenceChanged(pref_service_, path); | |
| } | |
| void PrefNotifierImpl::SetPrefService(PrefService* pref_service) { | |
| DCHECK(pref_service_ == nullptr); | |
| pref_service_ = pref_service; | |
| } |