1 // Copyright 2014 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.
5 #include "components/enhanced_bookmarks/bookmark_server_cluster_service.h"
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/values.h"
12 #include "components/bookmarks/browser/bookmark_model.h"
13 #include "components/enhanced_bookmarks/enhanced_bookmark_model.h"
14 #include "components/enhanced_bookmarks/enhanced_bookmark_utils.h"
15 #include "components/enhanced_bookmarks/pref_names.h"
16 #include "components/enhanced_bookmarks/proto/cluster.pb.h"
17 #include "components/pref_registry/pref_registry_syncable.h"
18 #include "components/signin/core/browser/signin_manager.h"
19 #include "net/base/url_util.h"
20 #include "net/url_request/url_fetcher.h"
21 #include "net/url_request/url_request_context_getter.h"
23 using bookmarks::BookmarkNode
;
26 const char kClusterUrl
[] = "https://www.google.com/stars/cluster";
27 const int kPrefServiceVersion
= 1;
28 const char kPrefServiceVersionKey
[] = "version";
29 const char kPrefServiceDataKey
[] = "data";
30 const char kAuthIdKey
[] = "auth_id";
33 namespace enhanced_bookmarks
{
35 BookmarkServerClusterService::BookmarkServerClusterService(
36 const std::string
& application_language_code
,
37 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter
,
38 ProfileOAuth2TokenService
* token_service
,
39 SigninManagerBase
* signin_manager
,
40 enhanced_bookmarks::EnhancedBookmarkModel
* enhanced_bookmark_model
,
41 PrefService
* pref_service
)
42 : BookmarkServerService(request_context_getter
,
45 enhanced_bookmark_model
),
46 application_language_code_(application_language_code
),
47 pref_service_(pref_service
) {
51 TriggerTokenRequest(false);
53 GetSigninManager()->AddObserver(this);
56 BookmarkServerClusterService::~BookmarkServerClusterService() {
57 GetSigninManager()->RemoveObserver(this);
60 const std::vector
<const BookmarkNode
*>
61 BookmarkServerClusterService::BookmarksForClusterNamed(
62 const std::string
& cluster_name
) const {
63 std::vector
<const BookmarkNode
*> results
;
65 ClusterMap::const_iterator cluster_it
= cluster_data_
.find(cluster_name
);
66 if (cluster_it
== cluster_data_
.end())
69 for (auto& star_id
: cluster_it
->second
) {
70 const BookmarkNode
* bookmark
= BookmarkForRemoteId(star_id
);
72 results
.push_back(bookmark
);
77 const std::vector
<std::string
>
78 BookmarkServerClusterService::ClustersForBookmark(
79 const BookmarkNode
* bookmark
) const {
80 const std::string
& star_id
= RemoteIDForBookmark(bookmark
);
82 // TODO(noyau): if this turns out to be a perf bottleneck this may be improved
83 // by storing a reverse map from id to cluster.
84 std::vector
<std::string
> clusters
;
85 for (auto& pair
: cluster_data_
) {
86 const std::vector
<std::string
>& stars_ids
= pair
.second
;
87 if (std::find(stars_ids
.begin(), stars_ids
.end(), star_id
) !=
89 clusters
.push_back(pair
.first
);
94 const std::vector
<std::string
> BookmarkServerClusterService::GetClusters()
96 std::vector
<std::string
> cluster_names
;
98 for (auto& pair
: cluster_data_
) {
99 for (auto& star_id
: pair
.second
) {
100 const BookmarkNode
* bookmark
= BookmarkForRemoteId(star_id
);
102 // Only add clusters that have children.
103 cluster_names
.push_back(pair
.first
);
109 return cluster_names
;
113 void BookmarkServerClusterService::RegisterPrefs(
114 user_prefs::PrefRegistrySyncable
* registry
) {
115 registry
->RegisterDictionaryPref(
116 prefs::kBookmarkClusters
,
117 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
120 scoped_ptr
<net::URLFetcher
> BookmarkServerClusterService::CreateFetcher() {
121 // Add the necessary arguments to the URI.
122 GURL
url(kClusterUrl
);
123 url
= net::AppendQueryParameter(url
, "output", "proto");
126 if (!application_language_code_
.empty())
127 url
= net::AppendQueryParameter(url
, "hl", application_language_code_
);
129 url
= net::AppendQueryParameter(url
, "v", model_
->GetVersionString());
131 // Build the URLFetcher to perform the request.
132 scoped_ptr
<net::URLFetcher
> url_fetcher(
133 net::URLFetcher::Create(url
, net::URLFetcher::POST
, this));
135 // Binary encode a basic request proto.
136 image_collections::ClusterRequest request_proto
;
137 request_proto
.set_cluster_all(true);
139 std::string proto_output
;
140 bool result
= request_proto
.SerializePartialToString(&proto_output
);
143 url_fetcher
->SetUploadData("application/octet-stream", proto_output
);
147 bool BookmarkServerClusterService::ProcessResponse(const std::string
& response
,
148 bool* should_notify
) {
149 DCHECK(*should_notify
);
150 image_collections::ClusterResponse response_proto
;
151 bool result
= response_proto
.ParseFromString(response
);
153 return false; // Not formatted properly.
155 ClusterMap new_cluster_data
;
156 for (const auto& cluster
: response_proto
.clusters()) {
157 const std::string
& title
= cluster
.title();
160 std::vector
<std::string
> stars_ids
;
161 for (auto& doc
: cluster
.docs()) {
163 stars_ids
.push_back(doc
);
165 if (stars_ids
.size())
166 new_cluster_data
[title
] = stars_ids
;
169 if (new_cluster_data
.size() == cluster_data_
.size() &&
170 std::equal(new_cluster_data
.begin(),
171 new_cluster_data
.end(),
172 cluster_data_
.begin())) {
173 *should_notify
= false;
175 SwapModel(&new_cluster_data
);
180 void BookmarkServerClusterService::CleanAfterFailure() {
181 if (cluster_data_
.empty())
188 void BookmarkServerClusterService::EnhancedBookmarkModelLoaded() {
189 TriggerTokenRequest(false);
192 void BookmarkServerClusterService::EnhancedBookmarkAdded(
193 const BookmarkNode
* node
) {
197 void BookmarkServerClusterService::EnhancedBookmarkRemoved(
198 const BookmarkNode
* node
) {
199 // It is possible to remove the entries from the map here, but as those are
200 // filtered in ClustersForBookmark() this is not strictly necessary.
203 void BookmarkServerClusterService::EnhancedBookmarkNodeChanged(
204 const BookmarkNode
* node
) {
208 void BookmarkServerClusterService::EnhancedBookmarkAllUserNodesRemoved() {
209 if (!cluster_data_
.empty()) {
215 void BookmarkServerClusterService::EnhancedBookmarkRemoteIdChanged(
216 const BookmarkNode
* node
,
217 const std::string
& old_remote_id
,
218 const std::string
& remote_id
) {
219 std::vector
<std::string
> clusters
;
220 for (auto& pair
: cluster_data_
) {
221 std::vector
<std::string
>& stars_ids
= pair
.second
;
222 std::replace(stars_ids
.begin(), stars_ids
.end(), old_remote_id
, remote_id
);
226 void BookmarkServerClusterService::GoogleSignedOut(
227 const std::string
& account_id
,
228 const std::string
& username
) {
229 if (!cluster_data_
.empty()) {
235 void BookmarkServerClusterService::SwapModel(ClusterMap
* cluster_map
) {
236 cluster_data_
.swap(*cluster_map
);
237 const std::string
& auth_id
= GetSigninManager()->GetAuthenticatedAccountId();
238 scoped_ptr
<base::DictionaryValue
> dictionary(
239 Serialize(cluster_data_
, auth_id
));
240 pref_service_
->Set(prefs::kBookmarkClusters
, *dictionary
);
243 void BookmarkServerClusterService::LoadModel() {
244 const base::DictionaryValue
* dictionary
=
245 pref_service_
->GetDictionary(prefs::kBookmarkClusters
);
246 const std::string
& auth_id
= GetSigninManager()->GetAuthenticatedAccountId();
248 ClusterMap loaded_data
;
249 bool result
= BookmarkServerClusterService::Deserialize(
250 *dictionary
, auth_id
, &loaded_data
);
252 cluster_data_
.swap(loaded_data
);
259 scoped_ptr
<base::DictionaryValue
> BookmarkServerClusterService::Serialize(
260 const ClusterMap
& cluster_map
,
261 const std::string
& auth_id
) {
262 // Create a list of all clusters. For each cluster, make another list. The
263 // first element in the list is the key (cluster name). All subsequent
264 // elements are stars ids.
265 scoped_ptr
<base::ListValue
> all_clusters(new base::ListValue
);
266 for (auto& pair
: cluster_map
) {
267 scoped_ptr
<base::ListValue
> cluster(new base::ListValue
);
268 cluster
->AppendString(pair
.first
);
269 cluster
->AppendStrings(pair
.second
);
270 all_clusters
->Append(cluster
.release());
273 // The dictionary that will be serialized has two fields: a version field and
275 scoped_ptr
<base::DictionaryValue
> data(new base::DictionaryValue
);
276 data
->SetInteger(kPrefServiceVersionKey
, kPrefServiceVersion
);
277 data
->Set(kPrefServiceDataKey
, all_clusters
.release());
278 data
->SetString(kAuthIdKey
, auth_id
);
284 bool BookmarkServerClusterService::Deserialize(
285 const base::DictionaryValue
& value
,
286 const std::string
& auth_id
,
287 ClusterMap
* out_map
) {
292 if (!value
.GetInteger(kPrefServiceVersionKey
, &version
))
294 if (version
!= kPrefServiceVersion
)
299 if (!value
.GetString(kAuthIdKey
, &id
))
304 const base::ListValue
* all_clusters
= NULL
;
305 if (!value
.GetList(kPrefServiceDataKey
, &all_clusters
))
308 for (size_t index
= 0; index
< all_clusters
->GetSize(); ++index
) {
309 const base::ListValue
* cluster
= NULL
;
310 if (!all_clusters
->GetList(index
, &cluster
))
312 if (cluster
->GetSize() < 1)
315 if (!cluster
->GetString(0, &key
))
317 std::vector
<std::string
> stars_ids
;
318 for (size_t index
= 1; index
< cluster
->GetSize(); ++index
) {
319 std::string stars_id
;
320 if (!cluster
->GetString(index
, &stars_id
))
322 stars_ids
.push_back(stars_id
);
324 output
.insert(std::make_pair(key
, stars_ids
));
326 out_map
->swap(output
);
330 } // namespace enhanced_bookmarks