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/enhanced_bookmark_model.h"
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 "base/strings/string_number_conversions.h"
15 #include "components/bookmarks/browser/bookmark_model.h"
16 #include "components/bookmarks/browser/bookmark_node.h"
17 #include "components/enhanced_bookmarks/enhanced_bookmark_model_observer.h"
18 #include "components/enhanced_bookmarks/proto/metadata.pb.h"
19 #include "ui/base/models/tree_node_iterator.h"
22 using bookmarks::BookmarkModel
;
23 using bookmarks::BookmarkNode
;
26 const char* kBookmarkBarId
= "f_bookmarks_bar";
28 const char* kIdKey
= "stars.id";
29 const char* kImageDataKey
= "stars.imageData";
30 const char* kNoteKey
= "stars.note";
31 const char* kOldIdKey
= "stars.oldId";
32 const char* kPageDataKey
= "stars.pageData";
33 const char* kVersionKey
= "stars.version";
35 const char* kBookmarkPrefix
= "ebc_";
37 // Helper method for working with bookmark metainfo.
38 std::string
DataForMetaInfoField(const BookmarkNode
* node
,
39 const std::string
& field
) {
41 if (!node
->GetMetaInfo(field
, &value
))
45 if (!base::Base64Decode(value
, &decoded
))
51 // Helper method for working with ImageData_ImageInfo.
52 bool PopulateImageData(const image::collections::ImageData_ImageInfo
& info
,
56 if (!info
.has_url() || !info
.has_width() || !info
.has_height())
64 *width
= info
.width();
65 *height
= info
.height();
69 // Generate a random remote id, with a prefix that depends on whether the node
70 // is a folder or a bookmark.
71 std::string
GenerateRemoteId() {
72 std::stringstream random_id
;
73 random_id
<< kBookmarkPrefix
;
75 // Generate 32 digit hex string random suffix.
76 random_id
<< std::hex
<< std::setfill('0') << std::setw(16);
77 random_id
<< base::RandUint64() << base::RandUint64();
78 return random_id
.str();
82 namespace enhanced_bookmarks
{
84 EnhancedBookmarkModel::EnhancedBookmarkModel(BookmarkModel
* bookmark_model
,
85 const std::string
& version
)
86 : bookmark_model_(bookmark_model
),
89 weak_ptr_factory_(this) {
90 bookmark_model_
->AddObserver(this);
91 bookmark_model_
->AddNonClonedKey(kIdKey
);
92 if (bookmark_model_
->loaded()) {
98 EnhancedBookmarkModel::~EnhancedBookmarkModel() {
102 void EnhancedBookmarkModel::Shutdown() {
103 if (bookmark_model_
) {
104 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver
,
106 EnhancedBookmarkModelShuttingDown());
107 weak_ptr_factory_
.InvalidateWeakPtrs();
108 bookmark_model_
->RemoveObserver(this);
109 bookmark_model_
= NULL
;
113 void EnhancedBookmarkModel::AddObserver(
114 EnhancedBookmarkModelObserver
* observer
) {
115 observers_
.AddObserver(observer
);
118 void EnhancedBookmarkModel::RemoveObserver(
119 EnhancedBookmarkModelObserver
* observer
) {
120 observers_
.RemoveObserver(observer
);
123 // Moves |node| to |new_parent| and inserts it at the given |index|.
124 void EnhancedBookmarkModel::Move(const BookmarkNode
* node
,
125 const BookmarkNode
* new_parent
,
127 bookmark_model_
->Move(node
, new_parent
, index
);
130 // Adds a new folder node at the specified position.
131 const BookmarkNode
* EnhancedBookmarkModel::AddFolder(
132 const BookmarkNode
* parent
,
134 const base::string16
& title
) {
135 BookmarkNode::MetaInfoMap meta_info
;
136 meta_info
[kVersionKey
] = GetVersionString();
137 return bookmark_model_
->AddFolderWithMetaInfo(parent
, index
, title
,
141 // Adds a url at the specified position.
142 const BookmarkNode
* EnhancedBookmarkModel::AddURL(
143 const BookmarkNode
* parent
,
145 const base::string16
& title
,
147 const base::Time
& creation_time
) {
148 BookmarkNode::MetaInfoMap meta_info
;
149 meta_info
[kIdKey
] = GenerateRemoteId();
150 meta_info
[kVersionKey
] = GetVersionString();
151 return bookmark_model_
->AddURLWithCreationTimeAndMetaInfo(
152 parent
, index
, title
, url
, creation_time
, &meta_info
);
155 std::string
EnhancedBookmarkModel::GetRemoteId(const BookmarkNode
* node
) {
156 if (node
== bookmark_model_
->bookmark_bar_node())
157 return kBookmarkBarId
;
160 if (!node
->GetMetaInfo(kIdKey
, &id
))
161 return std::string();
165 const BookmarkNode
* EnhancedBookmarkModel::BookmarkForRemoteId(
166 const std::string
& remote_id
) {
167 IdToNodeMap::iterator it
= id_map_
.find(remote_id
);
168 if (it
!= id_map_
.end())
173 void EnhancedBookmarkModel::SetDescription(const BookmarkNode
* node
,
174 const std::string
& description
) {
175 SetMetaInfo(node
, kNoteKey
, description
);
178 std::string
EnhancedBookmarkModel::GetDescription(const BookmarkNode
* node
) {
179 // First, look for a custom note set by the user.
180 std::string description
;
181 if (node
->GetMetaInfo(kNoteKey
, &description
) && !description
.empty())
184 // If none are present, return the snippet.
185 return GetSnippet(node
);
188 bool EnhancedBookmarkModel::SetOriginalImage(const BookmarkNode
* node
,
192 DCHECK(node
->is_url());
193 DCHECK(url
.is_valid());
195 std::string
decoded(DataForMetaInfoField(node
, kImageDataKey
));
196 image::collections::ImageData data
;
198 // Try to populate the imageData with the existing data.
199 if (!decoded
.empty()) {
200 // If the parsing fails, something is wrong. Immediately fail.
201 bool result
= data
.ParseFromString(decoded
);
206 scoped_ptr
<image::collections::ImageData_ImageInfo
> info(
207 new image::collections::ImageData_ImageInfo
);
208 info
->set_url(url
.spec());
209 info
->set_width(width
);
210 info
->set_height(height
);
211 data
.set_allocated_original_info(info
.release());
214 bool result
= data
.SerializePartialToString(&output
);
219 base::Base64Encode(output
, &encoded
);
220 SetMetaInfo(node
, kImageDataKey
, encoded
);
224 void EnhancedBookmarkModel::RemoveImageData(const BookmarkNode
* node
) {
225 DCHECK(node
->is_url());
226 image::collections::ImageData data
;
227 data
.set_user_removed_image(true);
229 std::string encoded_data
;
230 base::Base64Encode(data
.SerializeAsString(), &encoded_data
);
231 SetMetaInfo(node
, kImageDataKey
, encoded_data
);
234 bool EnhancedBookmarkModel::GetOriginalImage(const BookmarkNode
* node
,
238 std::string
decoded(DataForMetaInfoField(node
, kImageDataKey
));
242 image::collections::ImageData data
;
243 bool result
= data
.ParseFromString(decoded
);
247 if (!data
.has_original_info())
250 return PopulateImageData(data
.original_info(), url
, width
, height
);
253 bool EnhancedBookmarkModel::GetThumbnailImage(const BookmarkNode
* node
,
257 std::string
decoded(DataForMetaInfoField(node
, kImageDataKey
));
261 image::collections::ImageData data
;
262 bool result
= data
.ParseFromString(decoded
);
266 if (!data
.has_thumbnail_info())
269 return PopulateImageData(data
.thumbnail_info(), url
, width
, height
);
272 std::string
EnhancedBookmarkModel::GetSnippet(const BookmarkNode
* node
) {
273 std::string
decoded(DataForMetaInfoField(node
, kPageDataKey
));
277 image::collections::PageData data
;
278 bool result
= data
.ParseFromString(decoded
);
280 return std::string();
282 return data
.snippet();
285 void EnhancedBookmarkModel::SetVersionSuffix(
286 const std::string
& version_suffix
) {
287 version_suffix_
= version_suffix
;
290 void EnhancedBookmarkModel::BookmarkModelChanged() {
293 void EnhancedBookmarkModel::BookmarkModelLoaded(BookmarkModel
* model
,
294 bool ids_reassigned
) {
298 EnhancedBookmarkModelObserver
, observers_
, EnhancedBookmarkModelLoaded());
301 void EnhancedBookmarkModel::BookmarkNodeAdded(BookmarkModel
* model
,
302 const BookmarkNode
* parent
,
304 const BookmarkNode
* node
= parent
->GetChild(index
);
305 std::string remote_id
;
306 if (node
->GetMetaInfo(kIdKey
, &remote_id
)) {
308 ScheduleResetDuplicateRemoteIds();
311 EnhancedBookmarkModelObserver
, observers_
, EnhancedBookmarkAdded(node
));
314 void EnhancedBookmarkModel::BookmarkNodeRemoved(
315 BookmarkModel
* model
,
316 const BookmarkNode
* parent
,
318 const BookmarkNode
* node
,
319 const std::set
<GURL
>& removed_urls
) {
320 RemoveNodeFromMaps(node
);
322 EnhancedBookmarkModelObserver
, observers_
, EnhancedBookmarkRemoved(node
));
325 void EnhancedBookmarkModel::BookmarkNodeChanged(BookmarkModel
* model
,
326 const BookmarkNode
* node
) {
328 EnhancedBookmarkModelObserver
, observers_
,
329 EnhancedBookmarkNodeChanged(node
));
332 void EnhancedBookmarkModel::OnWillChangeBookmarkMetaInfo(
333 BookmarkModel
* model
,
334 const BookmarkNode
* node
) {
335 prev_remote_id_
= GetRemoteId(node
);
338 void EnhancedBookmarkModel::BookmarkMetaInfoChanged(BookmarkModel
* model
,
339 const BookmarkNode
* node
) {
340 std::string remote_id
= GetRemoteId(node
);
341 if (remote_id
!= prev_remote_id_
) {
342 id_map_
.erase(prev_remote_id_
);
343 if (!remote_id
.empty()) {
345 ScheduleResetDuplicateRemoteIds();
348 EnhancedBookmarkModelObserver
,
350 EnhancedBookmarkRemoteIdChanged(node
, prev_remote_id_
, remote_id
));
354 void EnhancedBookmarkModel::BookmarkAllUserNodesRemoved(
355 BookmarkModel
* model
,
356 const std::set
<GURL
>& removed_urls
) {
358 // Re-initialize so non-user nodes with remote ids are present in the map.
360 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver
,
362 EnhancedBookmarkAllUserNodesRemoved());
365 void EnhancedBookmarkModel::InitializeIdMap() {
366 ui::TreeNodeIterator
<const BookmarkNode
> iterator(
367 bookmark_model_
->root_node());
368 while (iterator
.has_next()) {
369 AddToIdMap(iterator
.Next());
371 ScheduleResetDuplicateRemoteIds();
374 void EnhancedBookmarkModel::AddToIdMap(const BookmarkNode
* node
) {
375 std::string remote_id
= GetRemoteId(node
);
376 if (remote_id
.empty())
379 // Try to insert the node.
380 std::pair
<IdToNodeMap::iterator
, bool> result
=
381 id_map_
.insert(make_pair(remote_id
, node
));
382 if (!result
.second
) {
383 // Some node already had the same remote id, so add both nodes to the
385 nodes_to_reset_
[result
.first
->second
] = remote_id
;
386 nodes_to_reset_
[node
] = remote_id
;
390 void EnhancedBookmarkModel::RemoveNodeFromMaps(const BookmarkNode
* node
) {
391 for (int i
= 0; i
< node
->child_count(); i
++) {
392 RemoveNodeFromMaps(node
->GetChild(i
));
394 std::string remote_id
= GetRemoteId(node
);
395 id_map_
.erase(remote_id
);
396 nodes_to_reset_
.erase(node
);
399 void EnhancedBookmarkModel::ScheduleResetDuplicateRemoteIds() {
400 if (!nodes_to_reset_
.empty()) {
401 base::MessageLoopProxy::current()->PostTask(
403 base::Bind(&EnhancedBookmarkModel::ResetDuplicateRemoteIds
,
404 weak_ptr_factory_
.GetWeakPtr()));
408 void EnhancedBookmarkModel::ResetDuplicateRemoteIds() {
409 for (NodeToIdMap::iterator it
= nodes_to_reset_
.begin();
410 it
!= nodes_to_reset_
.end();
412 BookmarkNode::MetaInfoMap meta_info
;
413 meta_info
[kIdKey
] = "";
414 meta_info
[kOldIdKey
] = it
->second
;
415 SetMultipleMetaInfo(it
->first
, meta_info
);
417 nodes_to_reset_
.clear();
420 void EnhancedBookmarkModel::SetMetaInfo(const BookmarkNode
* node
,
421 const std::string
& field
,
422 const std::string
& value
) {
423 DCHECK(!bookmark_model_
->is_permanent_node(node
));
425 BookmarkNode::MetaInfoMap meta_info
;
426 const BookmarkNode::MetaInfoMap
* old_meta_info
= node
->GetMetaInfoMap();
428 meta_info
.insert(old_meta_info
->begin(), old_meta_info
->end());
430 // Don't update anything if the value to set is already there.
431 BookmarkNode::MetaInfoMap::iterator it
= meta_info
.find(field
);
432 if (it
!= meta_info
.end() && it
->second
== value
)
435 meta_info
[field
] = value
;
436 meta_info
[kVersionKey
] = GetVersionString();
437 bookmark_model_
->SetNodeMetaInfoMap(node
, meta_info
);
440 std::string
EnhancedBookmarkModel::GetVersionString() {
441 if (version_suffix_
.empty())
443 return version_
+ '/' + version_suffix_
;
446 void EnhancedBookmarkModel::SetMultipleMetaInfo(
447 const BookmarkNode
* node
,
448 BookmarkNode::MetaInfoMap meta_info
) {
449 DCHECK(!bookmark_model_
->is_permanent_node(node
));
451 // Don't update anything if every value is already set correctly.
452 if (node
->GetMetaInfoMap()) {
453 bool changed
= false;
454 const BookmarkNode::MetaInfoMap
* old_meta_info
= node
->GetMetaInfoMap();
455 for (BookmarkNode::MetaInfoMap::iterator it
= meta_info
.begin();
456 it
!= meta_info
.end();
458 BookmarkNode::MetaInfoMap::const_iterator old_field
=
459 old_meta_info
->find(it
->first
);
460 if (old_field
== old_meta_info
->end() ||
461 old_field
->second
!= it
->second
) {
469 // Fill in the values that aren't changing
470 meta_info
.insert(old_meta_info
->begin(), old_meta_info
->end());
473 meta_info
[kVersionKey
] = GetVersionString();
474 bookmark_model_
->SetNodeMetaInfoMap(node
, meta_info
);
477 bool EnhancedBookmarkModel::SetAllImages(const BookmarkNode
* node
,
478 const GURL
& image_url
,
481 const GURL
& thumbnail_url
,
483 int thumbnail_height
) {
484 DCHECK(node
->is_url());
485 DCHECK(image_url
.is_valid() || image_url
.is_empty());
486 DCHECK(thumbnail_url
.is_valid() || thumbnail_url
.is_empty());
487 std::string
decoded(DataForMetaInfoField(node
, kImageDataKey
));
488 image::collections::ImageData data
;
490 // Try to populate the imageData with the existing data.
491 if (!decoded
.empty()) {
492 // If the parsing fails, something is wrong. Immediately fail.
493 bool result
= data
.ParseFromString(decoded
);
498 if (image_url
.is_empty()) {
499 data
.release_original_info();
501 // Regardless of whether an image info exists, we make a new one.
502 // Intentially make a raw pointer.
503 image::collections::ImageData_ImageInfo
* info
=
504 new image::collections::ImageData_ImageInfo
;
505 info
->set_url(image_url
.spec());
506 info
->set_width(image_width
);
507 info
->set_height(image_height
);
508 // This method consumes the raw pointer.
509 data
.set_allocated_original_info(info
);
512 if (thumbnail_url
.is_empty()) {
513 data
.release_thumbnail_info();
515 // Regardless of whether an image info exists, we make a new one.
516 // Intentially make a raw pointer.
517 image::collections::ImageData_ImageInfo
* info
=
518 new image::collections::ImageData_ImageInfo
;
519 info
->set_url(thumbnail_url
.spec());
520 info
->set_width(thumbnail_width
);
521 info
->set_height(thumbnail_height
);
522 // This method consumes the raw pointer.
523 data
.set_allocated_thumbnail_info(info
);
526 bool result
= data
.SerializePartialToString(&output
);
531 base::Base64Encode(output
, &encoded
);
532 bookmark_model_
->SetNodeMetaInfo(node
, kImageDataKey
, encoded
);
536 } // namespace enhanced_bookmarks