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 "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"
22 const char* kBookmarkBarId
= "f_bookmarks_bar";
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";
31 const char* kBookmarkPrefix
= "ebc_";
33 // Helper method for working with bookmark metainfo.
34 std::string
DataForMetaInfoField(const BookmarkNode
* node
,
35 const std::string
& field
) {
37 if (!node
->GetMetaInfo(field
, &value
))
41 if (!base::Base64Decode(value
, &decoded
))
47 // Helper method for working with ImageData_ImageInfo.
48 bool PopulateImageData(const image::collections::ImageData_ImageInfo
& info
,
52 if (!info
.has_url() || !info
.has_width() || !info
.has_height())
60 *width
= info
.width();
61 *height
= info
.height();
65 // Generate a random remote id, with a prefix that depends on whether the node
66 // is a folder or a bookmark.
67 std::string
GenerateRemoteId() {
68 std::stringstream random_id
;
69 random_id
<< kBookmarkPrefix
;
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();
78 namespace enhanced_bookmarks
{
80 EnhancedBookmarkModel::EnhancedBookmarkModel(BookmarkModel
* bookmark_model
,
81 const std::string
& version
)
82 : bookmark_model_(bookmark_model
),
84 weak_ptr_factory_(this),
86 bookmark_model_
->AddObserver(this);
87 if (bookmark_model_
->loaded()) {
93 EnhancedBookmarkModel::~EnhancedBookmarkModel() {
96 void EnhancedBookmarkModel::Shutdown() {
97 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver
,
99 EnhancedBookmarkModelShuttingDown());
100 weak_ptr_factory_
.InvalidateWeakPtrs();
101 bookmark_model_
->RemoveObserver(this);
102 bookmark_model_
= NULL
;
105 void EnhancedBookmarkModel::AddObserver(
106 EnhancedBookmarkModelObserver
* observer
) {
107 observers_
.AddObserver(observer
);
110 void EnhancedBookmarkModel::RemoveObserver(
111 EnhancedBookmarkModelObserver
* observer
) {
112 observers_
.RemoveObserver(observer
);
115 // Moves |node| to |new_parent| and inserts it at the given |index|.
116 void EnhancedBookmarkModel::Move(const BookmarkNode
* node
,
117 const BookmarkNode
* new_parent
,
119 bookmark_model_
->Move(node
, new_parent
, index
);
122 // Adds a new folder node at the specified position.
123 const BookmarkNode
* EnhancedBookmarkModel::AddFolder(
124 const BookmarkNode
* parent
,
126 const base::string16
& title
) {
127 return bookmark_model_
->AddFolder(parent
, index
, title
);
130 // Adds a url at the specified position.
131 const BookmarkNode
* EnhancedBookmarkModel::AddURL(
132 const BookmarkNode
* parent
,
134 const base::string16
& title
,
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
);
143 std::string
EnhancedBookmarkModel::GetRemoteId(const BookmarkNode
* node
) {
144 if (node
== bookmark_model_
->bookmark_bar_node())
145 return kBookmarkBarId
;
148 if (!node
->GetMetaInfo(kIdKey
, &id
))
149 return std::string();
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())
161 void EnhancedBookmarkModel::SetDescription(const BookmarkNode
* node
,
162 const std::string
& description
) {
163 SetMetaInfo(node
, kNoteKey
, description
);
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())
172 // If none are present, return the snippet.
173 return GetSnippet(node
);
176 bool EnhancedBookmarkModel::SetOriginalImage(const BookmarkNode
* node
,
180 DCHECK(node
->is_url());
181 DCHECK(url
.is_valid());
183 std::string
decoded(DataForMetaInfoField(node
, kImageDataKey
));
184 image::collections::ImageData data
;
186 // Try to populate the imageData with the existing data.
188 // If the parsing fails, something is wrong. Immediately fail.
189 bool result
= data
.ParseFromString(decoded
);
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());
202 bool result
= data
.SerializePartialToString(&output
);
207 base::Base64Encode(output
, &encoded
);
208 SetMetaInfo(node
, kImageDataKey
, encoded
);
212 bool EnhancedBookmarkModel::GetOriginalImage(const BookmarkNode
* node
,
216 std::string
decoded(DataForMetaInfoField(node
, kImageDataKey
));
220 image::collections::ImageData data
;
221 bool result
= data
.ParseFromString(decoded
);
225 if (!data
.has_original_info())
228 return PopulateImageData(data
.original_info(), url
, width
, height
);
231 bool EnhancedBookmarkModel::GetThumbnailImage(const BookmarkNode
* node
,
235 std::string
decoded(DataForMetaInfoField(node
, kImageDataKey
));
239 image::collections::ImageData data
;
240 bool result
= data
.ParseFromString(decoded
);
244 if (!data
.has_thumbnail_info())
247 return PopulateImageData(data
.thumbnail_info(), url
, width
, height
);
250 std::string
EnhancedBookmarkModel::GetSnippet(const BookmarkNode
* node
) {
251 std::string
decoded(DataForMetaInfoField(node
, kPageDataKey
));
255 image::collections::PageData data
;
256 bool result
= data
.ParseFromString(decoded
);
258 return std::string();
260 return data
.snippet();
263 void EnhancedBookmarkModel::SetVersionSuffix(
264 const std::string
& version_suffix
) {
265 version_suffix_
= version_suffix
;
268 void EnhancedBookmarkModel::BookmarkModelChanged() {
271 void EnhancedBookmarkModel::BookmarkModelLoaded(BookmarkModel
* model
,
272 bool ids_reassigned
) {
275 EnhancedBookmarkModelObserver
, observers_
, EnhancedBookmarkModelLoaded());
278 void EnhancedBookmarkModel::BookmarkNodeAdded(BookmarkModel
* model
,
279 const BookmarkNode
* parent
,
281 const BookmarkNode
* node
= parent
->GetChild(index
);
283 ScheduleResetDuplicateRemoteIds();
285 EnhancedBookmarkModelObserver
, observers_
, EnhancedBookmarkAdded(node
));
288 void EnhancedBookmarkModel::BookmarkNodeRemoved(
289 BookmarkModel
* model
,
290 const BookmarkNode
* parent
,
292 const BookmarkNode
* node
,
293 const std::set
<GURL
>& removed_urls
) {
294 std::string remote_id
= GetRemoteId(node
);
295 id_map_
.erase(remote_id
);
297 EnhancedBookmarkModelObserver
, observers_
, EnhancedBookmarkRemoved(node
));
300 void EnhancedBookmarkModel::OnWillChangeBookmarkMetaInfo(
301 BookmarkModel
* model
,
302 const BookmarkNode
* node
) {
303 prev_remote_id_
= GetRemoteId(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()) {
313 ScheduleResetDuplicateRemoteIds();
316 EnhancedBookmarkModelObserver
,
318 EnhancedBookmarkRemoteIdChanged(node
, prev_remote_id_
, remote_id
));
322 void EnhancedBookmarkModel::BookmarkAllUserNodesRemoved(
323 BookmarkModel
* model
,
324 const std::set
<GURL
>& removed_urls
) {
326 // Re-initialize so non-user nodes with remote ids are present in the map.
328 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver
,
330 EnhancedBookmarkAllUserNodesRemoved());
333 void EnhancedBookmarkModel::InitializeIdMap() {
334 ui::TreeNodeIterator
<const BookmarkNode
> iterator(
335 bookmark_model_
->root_node());
336 while (iterator
.has_next()) {
337 AddToIdMap(iterator
.Next());
339 ScheduleResetDuplicateRemoteIds();
342 void EnhancedBookmarkModel::AddToIdMap(const BookmarkNode
* node
) {
343 std::string remote_id
= GetRemoteId(node
);
344 if (remote_id
.empty())
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
353 nodes_to_reset_
[result
.first
->second
] = remote_id
;
354 nodes_to_reset_
[node
] = remote_id
;
358 void EnhancedBookmarkModel::ScheduleResetDuplicateRemoteIds() {
359 if (!nodes_to_reset_
.empty()) {
360 base::MessageLoopProxy::current()->PostTask(
362 base::Bind(&EnhancedBookmarkModel::ResetDuplicateRemoteIds
,
363 weak_ptr_factory_
.GetWeakPtr()));
367 void EnhancedBookmarkModel::ResetDuplicateRemoteIds() {
368 for (NodeToIdMap::iterator it
= nodes_to_reset_
.begin();
369 it
!= nodes_to_reset_
.end();
371 BookmarkNode::MetaInfoMap meta_info
;
372 meta_info
[kIdKey
] = "";
373 meta_info
[kOldIdKey
] = it
->second
;
374 SetMultipleMetaInfo(it
->first
, meta_info
);
376 nodes_to_reset_
.clear();
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
));
384 BookmarkNode::MetaInfoMap meta_info
;
385 const BookmarkNode::MetaInfoMap
* old_meta_info
= node
->GetMetaInfoMap();
387 meta_info
.insert(old_meta_info
->begin(), old_meta_info
->end());
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
)
394 meta_info
[field
] = value
;
395 meta_info
[kVersionKey
] = GetVersionString();
396 bookmark_model_
->SetNodeMetaInfoMap(node
, meta_info
);
399 std::string
EnhancedBookmarkModel::GetVersionString() {
400 if (version_suffix_
.empty())
402 return version_
+ '/' + version_suffix_
;
405 void EnhancedBookmarkModel::SetMultipleMetaInfo(
406 const BookmarkNode
* node
,
407 BookmarkNode::MetaInfoMap meta_info
) {
408 DCHECK(!bookmark_model_
->is_permanent_node(node
));
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();
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
) {
428 // Fill in the values that aren't changing
429 meta_info
.insert(old_meta_info
->begin(), old_meta_info
->end());
432 meta_info
[kVersionKey
] = GetVersionString();
433 bookmark_model_
->SetNodeMetaInfoMap(node
, meta_info
);
436 bool EnhancedBookmarkModel::SetAllImages(const BookmarkNode
* node
,
437 const GURL
& image_url
,
440 const GURL
& thumbnail_url
,
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
;
449 // Try to populate the imageData with the existing data.
451 // If the parsing fails, something is wrong. Immediately fail.
452 bool result
= data
.ParseFromString(decoded
);
457 if (image_url
.is_empty()) {
458 data
.release_original_info();
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
);
471 if (thumbnail_url
.is_empty()) {
472 data
.release_thumbnail_info();
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
);
485 bool result
= data
.SerializePartialToString(&output
);
490 base::Base64Encode(output
, &encoded
);
491 bookmark_model_
->SetNodeMetaInfo(node
, kImageDataKey
, encoded
);
495 } // namespace enhanced_bookmarks