Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / components / enhanced_bookmarks / bookmark_server_cluster_service.cc
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.
4
5 #include "components/enhanced_bookmarks/bookmark_server_cluster_service.h"
6
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"
22
23 namespace {
24 const char kClusterUrl[] = "https://www.google.com/stars/cluster";
25 const int kPrefServiceVersion = 1;
26 const char kPrefServiceVersionKey[] = "version";
27 const char kPrefServiceDataKey[] = "data";
28 const char kAuthIdKey[] = "auth_id";
29 }  // namespace
30
31 namespace enhanced_bookmarks {
32
33 BookmarkServerClusterService::BookmarkServerClusterService(
34     const std::string& application_language_code,
35     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
36     ProfileOAuth2TokenService* token_service,
37     SigninManagerBase* signin_manager,
38     enhanced_bookmarks::EnhancedBookmarkModel* enhanced_bookmark_model,
39     PrefService* pref_service)
40     : BookmarkServerService(request_context_getter,
41                             token_service,
42                             signin_manager,
43                             enhanced_bookmark_model),
44       application_language_code_(application_language_code),
45       pref_service_(pref_service) {
46   LoadModel();
47
48   if (model_->loaded())
49     TriggerTokenRequest(false);
50
51   GetSigninManager()->AddObserver(this);
52 }
53
54 BookmarkServerClusterService::~BookmarkServerClusterService() {
55   GetSigninManager()->RemoveObserver(this);
56 }
57
58 const std::vector<const BookmarkNode*>
59 BookmarkServerClusterService::BookmarksForClusterNamed(
60     const std::string& cluster_name) const {
61   std::vector<const BookmarkNode*> results;
62
63   ClusterMap::const_iterator cluster_it = cluster_data_.find(cluster_name);
64   if (cluster_it == cluster_data_.end())
65     return results;
66
67   for (auto& star_id : cluster_it->second) {
68     const BookmarkNode* bookmark = BookmarkForRemoteId(star_id);
69     if (bookmark)
70       results.push_back(bookmark);
71   }
72   return results;
73 }
74
75 const std::vector<std::string>
76 BookmarkServerClusterService::ClustersForBookmark(
77     const BookmarkNode* bookmark) const {
78   const std::string& star_id = RemoteIDForBookmark(bookmark);
79
80   // TODO(noyau): if this turns out to be a perf bottleneck this may be improved
81   // by storing a reverse map from id to cluster.
82   std::vector<std::string> clusters;
83   for (auto& pair : cluster_data_) {
84     const std::vector<std::string>& stars_ids = pair.second;
85     if (std::find(stars_ids.begin(), stars_ids.end(), star_id) !=
86         stars_ids.end())
87       clusters.push_back(pair.first);
88   }
89   return clusters;
90 }
91
92 const std::vector<std::string> BookmarkServerClusterService::GetClusters()
93     const {
94   std::vector<std::string> cluster_names;
95
96   for (auto& pair : cluster_data_) {
97     for (auto& star_id : pair.second) {
98       const BookmarkNode* bookmark = BookmarkForRemoteId(star_id);
99       if (bookmark) {
100         // Only add clusters that have children.
101         cluster_names.push_back(pair.first);
102         break;
103       }
104     }
105   }
106
107   return cluster_names;
108 }
109
110 // static
111 void BookmarkServerClusterService::RegisterPrefs(
112     user_prefs::PrefRegistrySyncable* registry) {
113   registry->RegisterDictionaryPref(
114       prefs::kBookmarkClusters,
115       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
116 }
117
118 scoped_ptr<net::URLFetcher> BookmarkServerClusterService::CreateFetcher() {
119   // Add the necessary arguments to the URI.
120   GURL url(kClusterUrl);
121   url = net::AppendQueryParameter(url, "output", "proto");
122
123   // Append language.
124   if (!application_language_code_.empty())
125     url = net::AppendQueryParameter(url, "hl", application_language_code_);
126
127   url = net::AppendQueryParameter(url, "v", model_->GetVersionString());
128
129   // Build the URLFetcher to perform the request.
130   scoped_ptr<net::URLFetcher> url_fetcher(
131       net::URLFetcher::Create(url, net::URLFetcher::POST, this));
132
133   // Binary encode a basic request proto.
134   image_collections::ClusterRequest request_proto;
135   request_proto.set_cluster_all(true);
136
137   std::string proto_output;
138   bool result = request_proto.SerializePartialToString(&proto_output);
139   DCHECK(result);
140
141   url_fetcher->SetUploadData("application/octet-stream", proto_output);
142   return url_fetcher;
143 }
144
145 bool BookmarkServerClusterService::ProcessResponse(const std::string& response,
146                                                    bool* should_notify) {
147   DCHECK(*should_notify);
148   image_collections::ClusterResponse response_proto;
149   bool result = response_proto.ParseFromString(response);
150   if (!result)
151     return false;  // Not formatted properly.
152
153   ClusterMap new_cluster_data;
154   for (const auto& cluster : response_proto.clusters()) {
155     const std::string& title = cluster.title();
156     if (title.empty())
157       continue;
158     std::vector<std::string> stars_ids;
159     for (auto& doc : cluster.docs()) {
160       if (!doc.empty())
161         stars_ids.push_back(doc);
162     }
163     if (stars_ids.size())
164       new_cluster_data[title] = stars_ids;
165   }
166
167   if (new_cluster_data.size() == cluster_data_.size() &&
168       std::equal(new_cluster_data.begin(),
169                  new_cluster_data.end(),
170                  cluster_data_.begin())) {
171     *should_notify = false;
172   } else {
173     SwapModel(&new_cluster_data);
174   }
175   return true;
176 }
177
178 void BookmarkServerClusterService::CleanAfterFailure() {
179   if (cluster_data_.empty())
180     return;
181
182   ClusterMap empty;
183   SwapModel(&empty);
184 }
185
186 void BookmarkServerClusterService::EnhancedBookmarkModelLoaded() {
187   TriggerTokenRequest(false);
188 }
189
190 void BookmarkServerClusterService::EnhancedBookmarkAdded(
191     const BookmarkNode* node) {
192   // Nothing to do.
193 }
194
195 void BookmarkServerClusterService::EnhancedBookmarkRemoved(
196     const BookmarkNode* node) {
197   // It is possible to remove the entries from the map here, but as those are
198   // filtered in ClustersForBookmark() this is not strictly necessary.
199 }
200
201 void BookmarkServerClusterService::EnhancedBookmarkNodeChanged(
202     const BookmarkNode* node) {
203   // Nothing to do.
204 }
205
206 void BookmarkServerClusterService::EnhancedBookmarkAllUserNodesRemoved() {
207   if (!cluster_data_.empty()) {
208     ClusterMap empty;
209     SwapModel(&empty);
210   }
211 }
212
213 void BookmarkServerClusterService::EnhancedBookmarkRemoteIdChanged(
214     const BookmarkNode* node,
215     const std::string& old_remote_id,
216     const std::string& remote_id) {
217   std::vector<std::string> clusters;
218   for (auto& pair : cluster_data_) {
219     std::vector<std::string>& stars_ids = pair.second;
220     std::replace(stars_ids.begin(), stars_ids.end(), old_remote_id, remote_id);
221   }
222 }
223
224 void BookmarkServerClusterService::GoogleSignedOut(
225     const std::string& account_id,
226     const std::string& username) {
227   if (!cluster_data_.empty()) {
228     ClusterMap empty;
229     SwapModel(&empty);
230   }
231 }
232
233 void BookmarkServerClusterService::SwapModel(ClusterMap* cluster_map) {
234   cluster_data_.swap(*cluster_map);
235   const std::string& auth_id = GetSigninManager()->GetAuthenticatedAccountId();
236   scoped_ptr<base::DictionaryValue> dictionary(
237       Serialize(cluster_data_, auth_id));
238   pref_service_->Set(prefs::kBookmarkClusters, *dictionary);
239 }
240
241 void BookmarkServerClusterService::LoadModel() {
242   const base::DictionaryValue* dictionary =
243       pref_service_->GetDictionary(prefs::kBookmarkClusters);
244   const std::string& auth_id = GetSigninManager()->GetAuthenticatedAccountId();
245
246   ClusterMap loaded_data;
247   bool result = BookmarkServerClusterService::Deserialize(
248       *dictionary, auth_id, &loaded_data);
249   if (result)
250     cluster_data_.swap(loaded_data);
251 }
252
253 //
254 // Serialization.
255 //
256 // static
257 scoped_ptr<base::DictionaryValue> BookmarkServerClusterService::Serialize(
258     const ClusterMap& cluster_map,
259     const std::string& auth_id) {
260   // Create a list of all clusters. For each cluster, make another list. The
261   // first element in the list is the key (cluster name). All subsequent
262   // elements are stars ids.
263   scoped_ptr<base::ListValue> all_clusters(new base::ListValue);
264   for (auto& pair : cluster_map) {
265     scoped_ptr<base::ListValue> cluster(new base::ListValue);
266     cluster->AppendString(pair.first);
267     cluster->AppendStrings(pair.second);
268     all_clusters->Append(cluster.release());
269   }
270
271   // The dictionary that will be serialized has two fields: a version field and
272   // a data field.
273   scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue);
274   data->SetInteger(kPrefServiceVersionKey, kPrefServiceVersion);
275   data->Set(kPrefServiceDataKey, all_clusters.release());
276   data->SetString(kAuthIdKey, auth_id);
277
278   return data.Pass();
279 }
280
281 // static
282 bool BookmarkServerClusterService::Deserialize(
283     const base::DictionaryValue& value,
284     const std::string& auth_id,
285     ClusterMap* out_map) {
286   ClusterMap output;
287
288   // Check version.
289   int version;
290   if (!value.GetInteger(kPrefServiceVersionKey, &version))
291     return false;
292   if (version != kPrefServiceVersion)
293     return false;
294
295   // Check auth id.
296   std::string id;
297   if (!value.GetString(kAuthIdKey, &id))
298     return false;
299   if (id != auth_id)
300     return false;
301
302   const base::ListValue* all_clusters = NULL;
303   if (!value.GetList(kPrefServiceDataKey, &all_clusters))
304     return false;
305
306   for (size_t index = 0; index < all_clusters->GetSize(); ++index) {
307     const base::ListValue* cluster = NULL;
308     if (!all_clusters->GetList(index, &cluster))
309       return false;
310     if (cluster->GetSize() < 1)
311       return false;
312     std::string key;
313     if (!cluster->GetString(0, &key))
314       return false;
315     std::vector<std::string> stars_ids;
316     for (size_t index = 1; index < cluster->GetSize(); ++index) {
317       std::string stars_id;
318       if (!cluster->GetString(index, &stars_id))
319         return false;
320       stars_ids.push_back(stars_id);
321     }
322     output.insert(std::make_pair(key, stars_ids));
323   }
324   out_map->swap(output);
325   return true;
326 }
327
328 }  // namespace enhanced_bookmarks