Pref service: Add a ScopedDictionaryPrefUpdate to track value changes.

Currently, if a pref is changed and the pref service is enabled, the
entire value is sent to the pref service and other clients. For large
prefs used by extensions or content settings this can result
milliseconds of busy time on the UI thread.

ScopedDictionaryPrefUpdate tracks which components of a pref are changed
so only the changes need to be sent to the pref service and other pref
clients.

Bug: 654988
Change-Id: Ied5dc6d0fadaa04817330fddce08efdc8b1d0fce
Reviewed-on: https://chromium-review.googlesource.com/476370
Reviewed-by: Tom Sepez <[email protected]>
Reviewed-by: Bernhard Bauer <[email protected]>
Commit-Queue: Sam McNally <[email protected]>
Cr-Commit-Position: refs/heads/master@{#468484}
diff --git a/components/prefs/BUILD.gn b/components/prefs/BUILD.gn
index e3462a4a..029c712 100644
--- a/components/prefs/BUILD.gn
+++ b/components/prefs/BUILD.gn
@@ -43,6 +43,7 @@
     "scoped_user_pref_update.h",
     "value_map_pref_store.cc",
     "value_map_pref_store.h",
+    "writeable_pref_store.cc",
     "writeable_pref_store.h",
   ]
 
diff --git a/components/prefs/pref_service.cc b/components/prefs/pref_service.cc
index 4ebea53..dccebf4 100644
--- a/components/prefs/pref_service.cc
+++ b/components/prefs/pref_service.cc
@@ -482,6 +482,14 @@
   user_pref_store_->ReportValueChanged(key, GetWriteFlags(FindPreference(key)));
 }
 
+void PrefService::ReportUserPrefChanged(
+    const std::string& key,
+    std::set<std::vector<std::string>> path_components) {
+  DCHECK(CalledOnValidThread());
+  user_pref_store_->ReportSubValuesChanged(key, std::move(path_components),
+                                           GetWriteFlags(FindPreference(key)));
+}
+
 void PrefService::SetUserPrefValue(const std::string& path,
                                    std::unique_ptr<base::Value> new_value) {
   DCHECK(CalledOnValidThread());
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h
index a08d7f8..a360afa4 100644
--- a/components/prefs/pref_service.h
+++ b/components/prefs/pref_service.h
@@ -39,6 +39,10 @@
 class FilePath;
 }
 
+namespace prefs {
+class ScopedDictionaryPrefUpdate;
+}
+
 namespace subtle {
 class PrefMemberBase;
 class ScopedUserPrefUpdateBase;
@@ -331,6 +335,7 @@
   // Give access to ReportUserPrefChanged() and GetMutableUserPref().
   friend class subtle::ScopedUserPrefUpdateBase;
   friend class PrefServiceTest_WriteablePrefStoreFlags_Test;
+  friend class prefs::ScopedDictionaryPrefUpdate;
 
   // Registration of pref change observers must be done using the
   // PrefChangeRegistrar, which is declared as a friend here to grant it
@@ -354,8 +359,12 @@
   virtual void RemovePrefObserver(const std::string& path, PrefObserver* obs);
 
   // Sends notification of a changed preference. This needs to be called by
-  // a ScopedUserPrefUpdate if a DictionaryValue or ListValue is changed.
+  // a ScopedUserPrefUpdate or ScopedDictionaryPrefUpdate if a DictionaryValue
+  // or ListValue is changed.
   void ReportUserPrefChanged(const std::string& key);
+  void ReportUserPrefChanged(
+      const std::string& key,
+      std::set<std::vector<std::string>> path_components);
 
   // Sets the value for this pref path in the user pref store and informs the
   // PrefNotifier of the change.
diff --git a/components/prefs/writeable_pref_store.cc b/components/prefs/writeable_pref_store.cc
new file mode 100644
index 0000000..d51985e
--- /dev/null
+++ b/components/prefs/writeable_pref_store.cc
@@ -0,0 +1,14 @@
+// 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 "components/prefs/writeable_pref_store.h"
+
+void WriteablePrefStore::ReportSubValuesChanged(
+    const std::string& key,
+    std::set<std::vector<std::string>> path_components,
+    uint32_t flags) {
+  // Default implementation. Subclasses may use |path_components| to improve
+  // performance.
+  ReportValueChanged(key, flags);
+}
diff --git a/components/prefs/writeable_pref_store.h b/components/prefs/writeable_pref_store.h
index 6cfbcee3..9a69c7cb 100644
--- a/components/prefs/writeable_pref_store.h
+++ b/components/prefs/writeable_pref_store.h
@@ -8,7 +8,9 @@
 #include <stdint.h>
 
 #include <memory>
+#include <set>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "components/prefs/pref_store.h"
@@ -46,13 +48,25 @@
   virtual bool GetMutableValue(const std::string& key,
                                base::Value** result) = 0;
 
-  // Triggers a value changed notification. This function needs to be called
-  // if one retrieves a list or dictionary with GetMutableValue and change its
-  // value. SetValue takes care of notifications itself. Note that
-  // ReportValueChanged will trigger notifications even if nothing has changed.
-  // |flags| is a bitmask of PrefWriteFlags.
+  // Triggers a value changed notification. This function or
+  // ReportSubValuesChanged needs to be called if one retrieves a list or
+  // dictionary with GetMutableValue and change its value. SetValue takes care
+  // of notifications itself. Note that ReportValueChanged will trigger
+  // notifications even if nothing has changed.  |flags| is a bitmask of
+  // PrefWriteFlags.
   virtual void ReportValueChanged(const std::string& key, uint32_t flags) = 0;
 
+  // Triggers a value changed notification for |path_components| in the |key|
+  // pref. This function or ReportValueChanged needs to be called if one
+  // retrieves a list or dictionary with GetMutableValue and change its value.
+  // SetValue takes care of notifications itself. Note that
+  // ReportSubValuesChanged will trigger notifications even if nothing has
+  // changed. |flags| is a bitmask of PrefWriteFlags.
+  virtual void ReportSubValuesChanged(
+      const std::string& key,
+      std::set<std::vector<std::string>> path_components,
+      uint32_t flags);
+
   // Same as SetValue, but doesn't generate notifications. This is used by
   // PrefService::GetMutableUserPref() in order to put empty entries
   // into the user pref store. Using SetValue is not an option since existing