• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/enhanced_bookmark_model.h"
6 
7 #include <iomanip>
8 #include <sstream>
9 
10 #include "base/base64.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/rand_util.h"
14 #include "components/bookmarks/browser/bookmark_model.h"
15 #include "components/bookmarks/browser/bookmark_node.h"
16 #include "components/enhanced_bookmarks/enhanced_bookmark_model_observer.h"
17 #include "components/enhanced_bookmarks/proto/metadata.pb.h"
18 #include "ui/base/models/tree_node_iterator.h"
19 #include "url/gurl.h"
20 
21 namespace {
22 const char* kBookmarkBarId = "f_bookmarks_bar";
23 
24 const char* kIdKey = "stars.id";
25 const char* kImageDataKey = "stars.imageData";
26 const char* kNoteKey = "stars.note";
27 const char* kOldIdKey = "stars.oldId";
28 const char* kPageDataKey = "stars.pageData";
29 const char* kVersionKey = "stars.version";
30 
31 const char* kBookmarkPrefix = "ebc_";
32 
33 // Helper method for working with bookmark metainfo.
DataForMetaInfoField(const BookmarkNode * node,const std::string & field)34 std::string DataForMetaInfoField(const BookmarkNode* node,
35                                  const std::string& field) {
36   std::string value;
37   if (!node->GetMetaInfo(field, &value))
38     return std::string();
39 
40   std::string decoded;
41   if (!base::Base64Decode(value, &decoded))
42     return std::string();
43 
44   return decoded;
45 }
46 
47 // Helper method for working with ImageData_ImageInfo.
PopulateImageData(const image::collections::ImageData_ImageInfo & info,GURL * out_url,int * width,int * height)48 bool PopulateImageData(const image::collections::ImageData_ImageInfo& info,
49                        GURL* out_url,
50                        int* width,
51                        int* height) {
52   if (!info.has_url() || !info.has_width() || !info.has_height())
53     return false;
54 
55   GURL url(info.url());
56   if (!url.is_valid())
57     return false;
58 
59   *out_url = url;
60   *width = info.width();
61   *height = info.height();
62   return true;
63 }
64 
65 // Generate a random remote id, with a prefix that depends on whether the node
66 // is a folder or a bookmark.
GenerateRemoteId()67 std::string GenerateRemoteId() {
68   std::stringstream random_id;
69   random_id << kBookmarkPrefix;
70 
71   // Generate 32 digit hex string random suffix.
72   random_id << std::hex << std::setfill('0') << std::setw(16);
73   random_id << base::RandUint64() << base::RandUint64();
74   return random_id.str();
75 }
76 }  // namespace
77 
78 namespace enhanced_bookmarks {
79 
EnhancedBookmarkModel(BookmarkModel * bookmark_model,const std::string & version)80 EnhancedBookmarkModel::EnhancedBookmarkModel(BookmarkModel* bookmark_model,
81                                              const std::string& version)
82     : bookmark_model_(bookmark_model),
83       loaded_(false),
84       weak_ptr_factory_(this),
85       version_(version) {
86   bookmark_model_->AddObserver(this);
87   if (bookmark_model_->loaded()) {
88     InitializeIdMap();
89     loaded_ = true;
90   }
91 }
92 
~EnhancedBookmarkModel()93 EnhancedBookmarkModel::~EnhancedBookmarkModel() {
94 }
95 
Shutdown()96 void EnhancedBookmarkModel::Shutdown() {
97   FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver,
98                     observers_,
99                     EnhancedBookmarkModelShuttingDown());
100   weak_ptr_factory_.InvalidateWeakPtrs();
101   bookmark_model_->RemoveObserver(this);
102   bookmark_model_ = NULL;
103 }
104 
AddObserver(EnhancedBookmarkModelObserver * observer)105 void EnhancedBookmarkModel::AddObserver(
106     EnhancedBookmarkModelObserver* observer) {
107   observers_.AddObserver(observer);
108 }
109 
RemoveObserver(EnhancedBookmarkModelObserver * observer)110 void EnhancedBookmarkModel::RemoveObserver(
111     EnhancedBookmarkModelObserver* observer) {
112   observers_.RemoveObserver(observer);
113 }
114 
115 // Moves |node| to |new_parent| and inserts it at the given |index|.
Move(const BookmarkNode * node,const BookmarkNode * new_parent,int index)116 void EnhancedBookmarkModel::Move(const BookmarkNode* node,
117                                  const BookmarkNode* new_parent,
118                                  int index) {
119   bookmark_model_->Move(node, new_parent, index);
120 }
121 
122 // Adds a new folder node at the specified position.
AddFolder(const BookmarkNode * parent,int index,const base::string16 & title)123 const BookmarkNode* EnhancedBookmarkModel::AddFolder(
124     const BookmarkNode* parent,
125     int index,
126     const base::string16& title) {
127   return bookmark_model_->AddFolder(parent, index, title);
128 }
129 
130 // Adds a url at the specified position.
AddURL(const BookmarkNode * parent,int index,const base::string16 & title,const GURL & url,const base::Time & creation_time)131 const BookmarkNode* EnhancedBookmarkModel::AddURL(
132     const BookmarkNode* parent,
133     int index,
134     const base::string16& title,
135     const GURL& url,
136     const base::Time& creation_time) {
137   BookmarkNode::MetaInfoMap meta_info;
138   meta_info[kIdKey] = GenerateRemoteId();
139   return bookmark_model_->AddURLWithCreationTimeAndMetaInfo(
140       parent, index, title, url, creation_time, &meta_info);
141 }
142 
GetRemoteId(const BookmarkNode * node)143 std::string EnhancedBookmarkModel::GetRemoteId(const BookmarkNode* node) {
144   if (node == bookmark_model_->bookmark_bar_node())
145     return kBookmarkBarId;
146 
147   std::string id;
148   if (!node->GetMetaInfo(kIdKey, &id))
149     return std::string();
150   return id;
151 }
152 
BookmarkForRemoteId(const std::string & remote_id)153 const BookmarkNode* EnhancedBookmarkModel::BookmarkForRemoteId(
154     const std::string& remote_id) {
155   IdToNodeMap::iterator it = id_map_.find(remote_id);
156   if (it != id_map_.end())
157     return it->second;
158   return NULL;
159 }
160 
SetDescription(const BookmarkNode * node,const std::string & description)161 void EnhancedBookmarkModel::SetDescription(const BookmarkNode* node,
162                                            const std::string& description) {
163   SetMetaInfo(node, kNoteKey, description);
164 }
165 
GetDescription(const BookmarkNode * node)166 std::string EnhancedBookmarkModel::GetDescription(const BookmarkNode* node) {
167   // First, look for a custom note set by the user.
168   std::string description;
169   if (node->GetMetaInfo(kNoteKey, &description) && !description.empty())
170     return description;
171 
172   // If none are present, return the snippet.
173   return GetSnippet(node);
174 }
175 
SetOriginalImage(const BookmarkNode * node,const GURL & url,int width,int height)176 bool EnhancedBookmarkModel::SetOriginalImage(const BookmarkNode* node,
177                                              const GURL& url,
178                                              int width,
179                                              int height) {
180   DCHECK(node->is_url());
181   DCHECK(url.is_valid());
182 
183   std::string decoded(DataForMetaInfoField(node, kImageDataKey));
184   image::collections::ImageData data;
185 
186   // Try to populate the imageData with the existing data.
187   if (decoded != "") {
188     // If the parsing fails, something is wrong. Immediately fail.
189     bool result = data.ParseFromString(decoded);
190     if (!result)
191       return false;
192   }
193 
194   scoped_ptr<image::collections::ImageData_ImageInfo> info(
195       new image::collections::ImageData_ImageInfo);
196   info->set_url(url.spec());
197   info->set_width(width);
198   info->set_height(height);
199   data.set_allocated_original_info(info.release());
200 
201   std::string output;
202   bool result = data.SerializePartialToString(&output);
203   if (!result)
204     return false;
205 
206   std::string encoded;
207   base::Base64Encode(output, &encoded);
208   SetMetaInfo(node, kImageDataKey, encoded);
209   return true;
210 }
211 
GetOriginalImage(const BookmarkNode * node,GURL * url,int * width,int * height)212 bool EnhancedBookmarkModel::GetOriginalImage(const BookmarkNode* node,
213                                              GURL* url,
214                                              int* width,
215                                              int* height) {
216   std::string decoded(DataForMetaInfoField(node, kImageDataKey));
217   if (decoded == "")
218     return false;
219 
220   image::collections::ImageData data;
221   bool result = data.ParseFromString(decoded);
222   if (!result)
223     return false;
224 
225   if (!data.has_original_info())
226     return false;
227 
228   return PopulateImageData(data.original_info(), url, width, height);
229 }
230 
GetThumbnailImage(const BookmarkNode * node,GURL * url,int * width,int * height)231 bool EnhancedBookmarkModel::GetThumbnailImage(const BookmarkNode* node,
232                                               GURL* url,
233                                               int* width,
234                                               int* height) {
235   std::string decoded(DataForMetaInfoField(node, kImageDataKey));
236   if (decoded == "")
237     return false;
238 
239   image::collections::ImageData data;
240   bool result = data.ParseFromString(decoded);
241   if (!result)
242     return false;
243 
244   if (!data.has_thumbnail_info())
245     return false;
246 
247   return PopulateImageData(data.thumbnail_info(), url, width, height);
248 }
249 
GetSnippet(const BookmarkNode * node)250 std::string EnhancedBookmarkModel::GetSnippet(const BookmarkNode* node) {
251   std::string decoded(DataForMetaInfoField(node, kPageDataKey));
252   if (decoded.empty())
253     return decoded;
254 
255   image::collections::PageData data;
256   bool result = data.ParseFromString(decoded);
257   if (!result)
258     return std::string();
259 
260   return data.snippet();
261 }
262 
SetVersionSuffix(const std::string & version_suffix)263 void EnhancedBookmarkModel::SetVersionSuffix(
264     const std::string& version_suffix) {
265   version_suffix_ = version_suffix;
266 }
267 
BookmarkModelChanged()268 void EnhancedBookmarkModel::BookmarkModelChanged() {
269 }
270 
BookmarkModelLoaded(BookmarkModel * model,bool ids_reassigned)271 void EnhancedBookmarkModel::BookmarkModelLoaded(BookmarkModel* model,
272                                                 bool ids_reassigned) {
273   InitializeIdMap();
274   FOR_EACH_OBSERVER(
275       EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkModelLoaded());
276 }
277 
BookmarkNodeAdded(BookmarkModel * model,const BookmarkNode * parent,int index)278 void EnhancedBookmarkModel::BookmarkNodeAdded(BookmarkModel* model,
279                                               const BookmarkNode* parent,
280                                               int index) {
281   const BookmarkNode* node = parent->GetChild(index);
282   AddToIdMap(node);
283   ScheduleResetDuplicateRemoteIds();
284   FOR_EACH_OBSERVER(
285       EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkAdded(node));
286 }
287 
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int old_index,const BookmarkNode * node,const std::set<GURL> & removed_urls)288 void EnhancedBookmarkModel::BookmarkNodeRemoved(
289     BookmarkModel* model,
290     const BookmarkNode* parent,
291     int old_index,
292     const BookmarkNode* node,
293     const std::set<GURL>& removed_urls) {
294   std::string remote_id = GetRemoteId(node);
295   id_map_.erase(remote_id);
296   FOR_EACH_OBSERVER(
297       EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkRemoved(node));
298 }
299 
OnWillChangeBookmarkMetaInfo(BookmarkModel * model,const BookmarkNode * node)300 void EnhancedBookmarkModel::OnWillChangeBookmarkMetaInfo(
301     BookmarkModel* model,
302     const BookmarkNode* node) {
303   prev_remote_id_ = GetRemoteId(node);
304 }
305 
BookmarkMetaInfoChanged(BookmarkModel * model,const BookmarkNode * node)306 void EnhancedBookmarkModel::BookmarkMetaInfoChanged(BookmarkModel* model,
307                                                     const BookmarkNode* node) {
308   std::string remote_id = GetRemoteId(node);
309   if (remote_id != prev_remote_id_) {
310     id_map_.erase(prev_remote_id_);
311     if (!remote_id.empty()) {
312       AddToIdMap(node);
313       ScheduleResetDuplicateRemoteIds();
314     }
315     FOR_EACH_OBSERVER(
316         EnhancedBookmarkModelObserver,
317         observers_,
318         EnhancedBookmarkRemoteIdChanged(node, prev_remote_id_, remote_id));
319   }
320 }
321 
BookmarkAllUserNodesRemoved(BookmarkModel * model,const std::set<GURL> & removed_urls)322 void EnhancedBookmarkModel::BookmarkAllUserNodesRemoved(
323     BookmarkModel* model,
324     const std::set<GURL>& removed_urls) {
325   id_map_.clear();
326   // Re-initialize so non-user nodes with remote ids are present in the map.
327   InitializeIdMap();
328   FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver,
329                     observers_,
330                     EnhancedBookmarkAllUserNodesRemoved());
331 }
332 
InitializeIdMap()333 void EnhancedBookmarkModel::InitializeIdMap() {
334   ui::TreeNodeIterator<const BookmarkNode> iterator(
335       bookmark_model_->root_node());
336   while (iterator.has_next()) {
337     AddToIdMap(iterator.Next());
338   }
339   ScheduleResetDuplicateRemoteIds();
340 }
341 
AddToIdMap(const BookmarkNode * node)342 void EnhancedBookmarkModel::AddToIdMap(const BookmarkNode* node) {
343   std::string remote_id = GetRemoteId(node);
344   if (remote_id.empty())
345     return;
346 
347   // Try to insert the node.
348   std::pair<IdToNodeMap::iterator, bool> result =
349       id_map_.insert(make_pair(remote_id, node));
350   if (!result.second) {
351     // Some node already had the same remote id, so add both nodes to the
352     // to-be-reset set.
353     nodes_to_reset_[result.first->second] = remote_id;
354     nodes_to_reset_[node] = remote_id;
355   }
356 }
357 
ScheduleResetDuplicateRemoteIds()358 void EnhancedBookmarkModel::ScheduleResetDuplicateRemoteIds() {
359   if (!nodes_to_reset_.empty()) {
360     base::MessageLoopProxy::current()->PostTask(
361         FROM_HERE,
362         base::Bind(&EnhancedBookmarkModel::ResetDuplicateRemoteIds,
363                    weak_ptr_factory_.GetWeakPtr()));
364   }
365 }
366 
ResetDuplicateRemoteIds()367 void EnhancedBookmarkModel::ResetDuplicateRemoteIds() {
368   for (NodeToIdMap::iterator it = nodes_to_reset_.begin();
369        it != nodes_to_reset_.end();
370        ++it) {
371     BookmarkNode::MetaInfoMap meta_info;
372     meta_info[kIdKey] = "";
373     meta_info[kOldIdKey] = it->second;
374     SetMultipleMetaInfo(it->first, meta_info);
375   }
376   nodes_to_reset_.clear();
377 }
378 
SetMetaInfo(const BookmarkNode * node,const std::string & field,const std::string & value)379 void EnhancedBookmarkModel::SetMetaInfo(const BookmarkNode* node,
380                                         const std::string& field,
381                                         const std::string& value) {
382   DCHECK(!bookmark_model_->is_permanent_node(node));
383 
384   BookmarkNode::MetaInfoMap meta_info;
385   const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
386   if (old_meta_info)
387     meta_info.insert(old_meta_info->begin(), old_meta_info->end());
388 
389   // Don't update anything if the value to set is already there.
390   BookmarkNode::MetaInfoMap::iterator it = meta_info.find(field);
391   if (it != meta_info.end() && it->second == value)
392     return;
393 
394   meta_info[field] = value;
395   meta_info[kVersionKey] = GetVersionString();
396   bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
397 }
398 
GetVersionString()399 std::string EnhancedBookmarkModel::GetVersionString() {
400   if (version_suffix_.empty())
401     return version_;
402   return version_ + '/' + version_suffix_;
403 }
404 
SetMultipleMetaInfo(const BookmarkNode * node,BookmarkNode::MetaInfoMap meta_info)405 void EnhancedBookmarkModel::SetMultipleMetaInfo(
406     const BookmarkNode* node,
407     BookmarkNode::MetaInfoMap meta_info) {
408   DCHECK(!bookmark_model_->is_permanent_node(node));
409 
410   // Don't update anything if every value is already set correctly.
411   if (node->GetMetaInfoMap()) {
412     bool changed = false;
413     const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
414     for (BookmarkNode::MetaInfoMap::iterator it = meta_info.begin();
415          it != meta_info.end();
416          ++it) {
417       BookmarkNode::MetaInfoMap::const_iterator old_field =
418           old_meta_info->find(it->first);
419       if (old_field == old_meta_info->end() ||
420           old_field->second != it->second) {
421         changed = true;
422         break;
423       }
424     }
425     if (!changed)
426       return;
427 
428     // Fill in the values that aren't changing
429     meta_info.insert(old_meta_info->begin(), old_meta_info->end());
430   }
431 
432   meta_info[kVersionKey] = GetVersionString();
433   bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
434 }
435 
SetAllImages(const BookmarkNode * node,const GURL & image_url,int image_width,int image_height,const GURL & thumbnail_url,int thumbnail_width,int thumbnail_height)436 bool EnhancedBookmarkModel::SetAllImages(const BookmarkNode* node,
437                                          const GURL& image_url,
438                                          int image_width,
439                                          int image_height,
440                                          const GURL& thumbnail_url,
441                                          int thumbnail_width,
442                                          int thumbnail_height) {
443   DCHECK(node->is_url());
444   DCHECK(image_url.is_valid() || image_url.is_empty());
445   DCHECK(thumbnail_url.is_valid() || thumbnail_url.is_empty());
446   std::string decoded(DataForMetaInfoField(node, kImageDataKey));
447   image::collections::ImageData data;
448 
449   // Try to populate the imageData with the existing data.
450   if (decoded != "") {
451     // If the parsing fails, something is wrong. Immediately fail.
452     bool result = data.ParseFromString(decoded);
453     if (!result)
454       return false;
455   }
456 
457   if (image_url.is_empty()) {
458     data.release_original_info();
459   } else {
460     // Regardless of whether an image info exists, we make a new one.
461     // Intentially make a raw pointer.
462     image::collections::ImageData_ImageInfo* info =
463         new image::collections::ImageData_ImageInfo;
464     info->set_url(image_url.spec());
465     info->set_width(image_width);
466     info->set_height(image_height);
467     // This method consumes the raw pointer.
468     data.set_allocated_original_info(info);
469   }
470 
471   if (thumbnail_url.is_empty()) {
472     data.release_thumbnail_info();
473   } else {
474     // Regardless of whether an image info exists, we make a new one.
475     // Intentially make a raw pointer.
476     image::collections::ImageData_ImageInfo* info =
477         new image::collections::ImageData_ImageInfo;
478     info->set_url(thumbnail_url.spec());
479     info->set_width(thumbnail_width);
480     info->set_height(thumbnail_height);
481     // This method consumes the raw pointer.
482     data.set_allocated_thumbnail_info(info);
483   }
484   std::string output;
485   bool result = data.SerializePartialToString(&output);
486   if (!result)
487     return false;
488 
489   std::string encoded;
490   base::Base64Encode(output, &encoded);
491   bookmark_model_->SetNodeMetaInfo(node, kImageDataKey, encoded);
492   return true;
493 }
494 
495 }  // namespace enhanced_bookmarks
496