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/metadata_accessor.h"
6
7#include <iomanip>
8
9#include "base/base64.h"
10#include "base/rand_util.h"
11#include "components/bookmarks/browser/bookmark_model.h"
12#include "components/enhanced_bookmarks/proto/metadata.pb.h"
13#include "ui/base/models/tree_node_iterator.h"
14
15using namespace image::collections;
16
17namespace {
18
19// Helper method for working with bookmark metainfo.
20std::string DataForMetaInfoField(const BookmarkNode* node,
21                                 const std::string& field) {
22  const BookmarkNode::MetaInfoMap* map = node->GetMetaInfoMap();
23  if (!map)
24    return "";
25
26  BookmarkNode::MetaInfoMap::const_iterator it = map->find(field);
27  if (it == map->end())
28    return "";
29
30  std::string decoded;
31  bool result = base::Base64Decode((*it).second, &decoded);
32  if (!result)
33    return "";
34
35  return decoded;
36}
37
38// Sets a new remote id on a bookmark.
39std::string SetRemoteIdOnBookmark(BookmarkModel* bookmark_model,
40                                  const BookmarkNode* node) {
41  // Generate 16 digit hex string random id.
42  std::stringstream random_id;
43  random_id << std::hex << std::setfill('0') << std::setw(16);
44  random_id << base::RandUint64() << base::RandUint64();
45  std::string random_id_str = random_id.str();
46  bookmark_model->SetNodeMetaInfo(
47      node, enhanced_bookmarks::kIdDataKey, random_id_str);
48  return random_id_str;
49}
50
51// Helper method for working with ImageData_ImageInfo.
52bool PopulateImageData(const ImageData_ImageInfo& info,
53                       GURL* out_url,
54                       int* width,
55                       int* height) {
56  if (!info.has_url() || !info.has_width() || !info.has_height())
57    return false;
58
59  GURL url(info.url());
60  if (!url.is_valid())
61    return false;
62
63  *out_url = url;
64  *width = info.width();
65  *height = info.height();
66  return true;
67}
68
69}  // namespace
70
71namespace enhanced_bookmarks {
72
73const char* kPageDataKey = "stars.pageData";
74const char* kImageDataKey = "stars.imageData";
75const char* kIdDataKey = "stars.id";
76const char* kNoteKey = "stars.note";
77
78std::string RemoteIdFromBookmark(BookmarkModel* bookmark_model,
79                                 const BookmarkNode* node) {
80  const BookmarkNode::MetaInfoMap* map = node->GetMetaInfoMap();
81  if (!map)
82    return SetRemoteIdOnBookmark(bookmark_model, node);
83
84  BookmarkNode::MetaInfoMap::const_iterator it = map->find(kIdDataKey);
85  if (it == map->end())
86    return SetRemoteIdOnBookmark(bookmark_model, node);
87
88  DCHECK(it->second.length());
89  return it->second;
90}
91
92void SetDescriptionForBookmark(BookmarkModel* bookmark_model,
93                               const BookmarkNode* node,
94                               const std::string& description) {
95  bookmark_model->SetNodeMetaInfo(node, kNoteKey, description);
96}
97
98std::string DescriptionFromBookmark(const BookmarkNode* node) {
99  const BookmarkNode::MetaInfoMap* map = node->GetMetaInfoMap();
100  if (!map)
101    return "";
102
103  // First, look for a custom note set by the user.
104  BookmarkNode::MetaInfoMap::const_iterator it = map->find(kNoteKey);
105  if (it != map->end() && it->second != "")
106    return it->second;
107
108  // If none are present, return the snippet.
109  return SnippetFromBookmark(node);
110}
111
112bool SetOriginalImageForBookmark(BookmarkModel* bookmark_model,
113                                 const BookmarkNode* node,
114                                 const GURL& url,
115                                 int width,
116                                 int height) {
117  DCHECK(url.is_valid());
118
119  std::string decoded(DataForMetaInfoField(node, kImageDataKey));
120  ImageData data;
121
122  // Try to populate the imageData with the existing data.
123  if (decoded != "") {
124    // If the parsing fails, something is wrong. Immediately fail.
125    bool result = data.ParseFromString(decoded);
126    if (!result)
127      return false;
128  }
129
130  scoped_ptr<ImageData_ImageInfo> info(new ImageData_ImageInfo);
131  info->set_url(url.spec());
132  info->set_width(width);
133  info->set_height(height);
134  data.set_allocated_original_info(info.release());
135
136  std::string output;
137  bool result = data.SerializePartialToString(&output);
138  if (!result)
139    return false;
140
141  std::string encoded;
142  base::Base64Encode(output, &encoded);
143  bookmark_model->SetNodeMetaInfo(node, kImageDataKey, encoded);
144  // Ensure that the bookmark has a stars.id, to trigger the server processing.
145  RemoteIdFromBookmark(bookmark_model, node);
146  return true;
147}
148
149bool OriginalImageFromBookmark(const BookmarkNode* node,
150                               GURL* url,
151                               int* width,
152                               int* height) {
153  std::string decoded(DataForMetaInfoField(node, kImageDataKey));
154  if (decoded == "")
155    return false;
156
157  ImageData data;
158  bool result = data.ParseFromString(decoded);
159  if (!result)
160    return false;
161
162  if (!data.has_original_info())
163    return false;
164
165  return PopulateImageData(data.original_info(), url, width, height);
166}
167
168bool ThumbnailImageFromBookmark(const BookmarkNode* node,
169                                GURL* url,
170                                int* width,
171                                int* height) {
172  std::string decoded(DataForMetaInfoField(node, kImageDataKey));
173  if (decoded == "")
174    return false;
175
176  ImageData data;
177  bool result = data.ParseFromString(decoded);
178  if (!result)
179    return false;
180
181  if (!data.has_thumbnail_info())
182    return false;
183
184  return PopulateImageData(data.thumbnail_info(), url, width, height);
185}
186
187std::string SnippetFromBookmark(const BookmarkNode* node) {
188  std::string decoded(DataForMetaInfoField(node, kPageDataKey));
189  if (decoded == "")
190    return decoded;
191
192  PageData data;
193  bool result = data.ParseFromString(decoded);
194  if (!result)
195    return "";
196
197  return data.snippet();
198}
199
200bool SetAllImagesForBookmark(BookmarkModel* bookmark_model,
201                             const BookmarkNode* node,
202                             const GURL& image_url,
203                             int image_width,
204                             int image_height,
205                             const GURL& thumbnail_url,
206                             int thumbnail_width,
207                             int thumbnail_height) {
208  DCHECK(image_url.is_valid() || image_url.is_empty());
209  DCHECK(thumbnail_url.is_valid() || thumbnail_url.is_empty());
210  std::string decoded(DataForMetaInfoField(node, kImageDataKey));
211  ImageData data;
212
213  // Try to populate the imageData with the existing data.
214  if (decoded != "") {
215    // If the parsing fails, something is wrong. Immediately fail.
216    bool result = data.ParseFromString(decoded);
217    if (!result)
218      return false;
219  }
220
221  if (image_url.is_empty()) {
222    data.release_original_info();
223  } else {
224    // Regardless of whether an image info exists, we make a new one.
225    // Intentially make a raw pointer.
226    ImageData_ImageInfo* info = new ImageData_ImageInfo;
227    info->set_url(image_url.spec());
228    info->set_width(image_width);
229    info->set_height(image_height);
230    // This method consumes the raw pointer.
231    data.set_allocated_original_info(info);
232  }
233
234  if (thumbnail_url.is_empty()) {
235    data.release_thumbnail_info();
236  } else {
237    // Regardless of whether an image info exists, we make a new one.
238    // Intentially make a raw pointer.
239    ImageData_ImageInfo* info = new ImageData_ImageInfo;
240    info->set_url(thumbnail_url.spec());
241    info->set_width(thumbnail_width);
242    info->set_height(thumbnail_height);
243    // This method consumes the raw pointer.
244    data.set_allocated_thumbnail_info(info);
245  }
246  std::string output;
247  bool result = data.SerializePartialToString(&output);
248  if (!result)
249    return false;
250
251  std::string encoded;
252  base::Base64Encode(output, &encoded);
253  bookmark_model->SetNodeMetaInfo(node, kImageDataKey, encoded);
254  return true;
255}
256
257}  // namespace enhanced_bookmarks
258