Add a separate way to notify about font changes.

This allows code that needs to observe changes to font family preferences an
efficient and convenient way to register for all font change preferences
without registering for each one individually. This lowers the total number of
preference change observers from ~1200 to 189 for a new profile.

Previously this was very inefficient. For all ~1100 font pref names, there were
three services that registered for each: the font extensions API, the font
family cache, and the prefs tab helper.

Each of these had a PrefRegistrar with a map of 1100 entries mapping strings to
callabacks. The global notifier has a hash map mapping 1100 strings to
unique_ptrs to ObserverList<PrefObserver> containing a vector of 3 pointers.
The net allocation savings with this patch is about 500KB, with all of it
occurring during startup.

There were other designs considered. The most general would be to have a
general prefix matching ability for observing changes. But this is difficult to
make but efficient and support reentrant changes like the normal observers.
Since there are less than 200 registered observers other than fonts, prefix
matching seems not to be a common problem so this approach was avoided.

Instead, this patch adds a single font changed observer (so that the prefix
matching need only be done once globally for each pref change) that interested
components registers with. This registers a new type of global low-level
notification with the pref service. This could have been made more safe by
adding a new registrar type in the prefs service for this purpose, but that
would require an additional indirection and extra complexity for each
preference change.

Bug: 760255
Change-Id: I4629a7e5c95261ad8b15addf663f20c68332d644
Reviewed-on: https://chromium-review.googlesource.com/648617
Commit-Queue: Brett Wilson <[email protected]>
Reviewed-by: Erik Chen <[email protected]>
Cr-Commit-Position: refs/heads/master@{#500155}
diff --git a/chrome/browser/font_pref_change_notifier.cc b/chrome/browser/font_pref_change_notifier.cc
new file mode 100644
index 0000000..919d451c
--- /dev/null
+++ b/chrome/browser/font_pref_change_notifier.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 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/font_pref_change_notifier.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "chrome/common/pref_names_util.h"
+#include "components/prefs/pref_service.h"
+
+FontPrefChangeNotifier::Registrar::Registrar() {}
+FontPrefChangeNotifier::Registrar::~Registrar() {
+  if (is_registered())
+    Unregister();
+}
+
+void FontPrefChangeNotifier::Registrar::Register(
+    FontPrefChangeNotifier* notifier,
+    FontPrefChangeNotifier::Callback cb) {
+  DCHECK(!is_registered());
+  notifier_ = notifier;
+  callback_ = std::move(cb);
+
+  notifier_->AddRegistrar(this);
+}
+
+void FontPrefChangeNotifier::Registrar::Unregister() {
+  DCHECK(is_registered());
+  notifier_->RemoveRegistrar(this);
+
+  notifier_ = nullptr;
+  callback_ = FontPrefChangeNotifier::Callback();
+}
+
+FontPrefChangeNotifier::FontPrefChangeNotifier(PrefService* pref_service)
+    : pref_service_(pref_service) {
+  pref_service_->AddPrefObserverAllPrefs(this);
+}
+
+FontPrefChangeNotifier::~FontPrefChangeNotifier() {
+  pref_service_->RemovePrefObserverAllPrefs(this);
+
+  // There could be a shutdown race between this class and the objects
+  // registered with it. We don't want the registrars to call back into us
+  // when we're deleted, so tell them to unregister now.
+  for (auto& reg : registrars_)
+    reg.Unregister();
+}
+
+void FontPrefChangeNotifier::AddRegistrar(Registrar* registrar) {
+  registrars_.AddObserver(registrar);
+}
+
+void FontPrefChangeNotifier::RemoveRegistrar(Registrar* registrar) {
+  registrars_.RemoveObserver(registrar);
+}
+
+void FontPrefChangeNotifier::OnPreferenceChanged(PrefService* pref_service,
+                                                 const std::string& pref_name) {
+  if (base::StartsWith(pref_name, pref_names_util::kWebKitFontPrefPrefix,
+                       base::CompareCase::SENSITIVE)) {
+    for (auto& reg : registrars_)
+      reg.callback_.Run(pref_name);
+  }
+}