Roll src/third_party/WebKit 9f7fb92:f103b33 (svn 202621:202622)
[chromium-blink-merge.git] / components / enhanced_bookmarks / enhanced_bookmark_model.cc
blobd9088a3dda3f9b97e07b459525297a752fc27c60
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"
7 #include <iomanip>
8 #include <sstream>
10 #include "base/base64.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/rand_util.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "components/bookmarks/browser/bookmark_model.h"
18 #include "components/bookmarks/browser/bookmark_node.h"
19 #include "components/enhanced_bookmarks/enhanced_bookmark_model_observer.h"
20 #include "components/enhanced_bookmarks/proto/metadata.pb.h"
21 #include "ui/base/models/tree_node_iterator.h"
22 #include "url/gurl.h"
24 using bookmarks::BookmarkModel;
25 using bookmarks::BookmarkNode;
27 namespace {
28 const char* kBookmarkBarId = "f_bookmarks_bar";
30 const char* kIdKey = "stars.id";
31 const char* kImageDataKey = "stars.imageData";
32 const char* kNoteKey = "stars.note";
33 const char* kOldIdKey = "stars.oldId";
34 const char* kPageDataKey = "stars.pageData";
35 const char* kVersionKey = "stars.version";
37 const char* kBookmarkPrefix = "ebc_";
39 // Helper method for working with bookmark metainfo.
40 std::string DataForMetaInfoField(const BookmarkNode* node,
41 const std::string& field) {
42 std::string value;
43 if (!node->GetMetaInfo(field, &value))
44 return std::string();
46 std::string decoded;
47 if (!base::Base64Decode(value, &decoded))
48 return std::string();
50 return decoded;
53 // Helper method for working with ImageData_ImageInfo.
54 bool PopulateImageData(const image::collections::ImageData_ImageInfo& info,
55 GURL* out_url,
56 int* width,
57 int* height) {
58 if (!info.has_url() || !info.has_width() || !info.has_height())
59 return false;
61 GURL url(info.url());
62 if (!url.is_valid())
63 return false;
65 *out_url = url;
66 *width = info.width();
67 *height = info.height();
68 return true;
71 // Generate a random remote id, with a prefix that depends on whether the node
72 // is a folder or a bookmark.
73 std::string GenerateRemoteId() {
74 std::stringstream random_id;
75 random_id << kBookmarkPrefix;
77 // Generate 32 digit hex string random suffix.
78 random_id << std::hex << std::setfill('0') << std::setw(16);
79 random_id << base::RandUint64() << base::RandUint64();
80 return random_id.str();
82 } // namespace
84 namespace enhanced_bookmarks {
86 EnhancedBookmarkModel::EnhancedBookmarkModel(BookmarkModel* bookmark_model,
87 const std::string& version)
88 : bookmark_model_(bookmark_model),
89 loaded_(false),
90 version_(version),
91 weak_ptr_factory_(this) {
92 bookmark_model_->AddObserver(this);
93 bookmark_model_->AddNonClonedKey(kIdKey);
94 if (bookmark_model_->loaded()) {
95 InitializeIdMap();
96 loaded_ = true;
100 EnhancedBookmarkModel::~EnhancedBookmarkModel() {
101 Shutdown();
104 void EnhancedBookmarkModel::Shutdown() {
105 if (bookmark_model_) {
106 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver,
107 observers_,
108 EnhancedBookmarkModelShuttingDown());
109 weak_ptr_factory_.InvalidateWeakPtrs();
110 bookmark_model_->RemoveObserver(this);
111 bookmark_model_ = NULL;
115 void EnhancedBookmarkModel::AddObserver(
116 EnhancedBookmarkModelObserver* observer) {
117 observers_.AddObserver(observer);
120 void EnhancedBookmarkModel::RemoveObserver(
121 EnhancedBookmarkModelObserver* observer) {
122 observers_.RemoveObserver(observer);
125 // Moves |node| to |new_parent| and inserts it at the given |index|.
126 void EnhancedBookmarkModel::Move(const BookmarkNode* node,
127 const BookmarkNode* new_parent,
128 int index) {
129 bookmark_model_->Move(node, new_parent, index);
132 // Adds a new folder node at the specified position.
133 const BookmarkNode* EnhancedBookmarkModel::AddFolder(
134 const BookmarkNode* parent,
135 int index,
136 const base::string16& title) {
137 BookmarkNode::MetaInfoMap meta_info;
138 meta_info[kVersionKey] = GetVersionString();
139 return bookmark_model_->AddFolderWithMetaInfo(parent, index, title,
140 &meta_info);
143 // Adds a url at the specified position.
144 const BookmarkNode* EnhancedBookmarkModel::AddURL(
145 const BookmarkNode* parent,
146 int index,
147 const base::string16& title,
148 const GURL& url,
149 const base::Time& creation_time) {
150 BookmarkNode::MetaInfoMap meta_info;
151 meta_info[kIdKey] = GenerateRemoteId();
152 meta_info[kVersionKey] = GetVersionString();
153 return bookmark_model_->AddURLWithCreationTimeAndMetaInfo(
154 parent, index, title, url, creation_time, &meta_info);
157 std::string EnhancedBookmarkModel::GetRemoteId(const BookmarkNode* node) {
158 if (node == bookmark_model_->bookmark_bar_node())
159 return kBookmarkBarId;
161 std::string id;
162 if (!node->GetMetaInfo(kIdKey, &id))
163 return std::string();
164 return id;
167 const BookmarkNode* EnhancedBookmarkModel::BookmarkForRemoteId(
168 const std::string& remote_id) {
169 IdToNodeMap::iterator it = id_map_.find(remote_id);
170 if (it != id_map_.end())
171 return it->second;
172 return NULL;
175 void EnhancedBookmarkModel::SetDescription(const BookmarkNode* node,
176 const std::string& description) {
177 SetMetaInfo(node, kNoteKey, description);
180 std::string EnhancedBookmarkModel::GetDescription(const BookmarkNode* node) {
181 // First, look for a custom note set by the user.
182 std::string description;
183 if (node->GetMetaInfo(kNoteKey, &description) && !description.empty())
184 return description;
186 // If none are present, return the snippet.
187 return GetSnippet(node);
190 bool EnhancedBookmarkModel::SetOriginalImage(const BookmarkNode* node,
191 const GURL& url,
192 int width,
193 int height) {
194 DCHECK(node->is_url());
195 DCHECK(url.is_valid());
197 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
198 image::collections::ImageData data;
200 // Try to populate the imageData with the existing data.
201 if (!decoded.empty()) {
202 // If the parsing fails, something is wrong. Immediately fail.
203 bool result = data.ParseFromString(decoded);
204 if (!result)
205 return false;
208 scoped_ptr<image::collections::ImageData_ImageInfo> info(
209 new image::collections::ImageData_ImageInfo);
210 info->set_url(url.spec());
211 info->set_width(width);
212 info->set_height(height);
213 data.set_allocated_original_info(info.release());
215 std::string output;
216 bool result = data.SerializePartialToString(&output);
217 if (!result)
218 return false;
220 std::string encoded;
221 base::Base64Encode(output, &encoded);
222 SetMetaInfo(node, kImageDataKey, encoded);
223 return true;
226 void EnhancedBookmarkModel::RemoveImageData(const BookmarkNode* node) {
227 DCHECK(node->is_url());
228 image::collections::ImageData data;
229 data.set_user_removed_image(true);
231 std::string encoded_data;
232 base::Base64Encode(data.SerializeAsString(), &encoded_data);
233 SetMetaInfo(node, kImageDataKey, encoded_data);
236 bool EnhancedBookmarkModel::GetOriginalImage(const BookmarkNode* node,
237 GURL* url,
238 int* width,
239 int* height) {
240 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
241 if (decoded.empty())
242 return false;
244 image::collections::ImageData data;
245 bool result = data.ParseFromString(decoded);
246 if (!result)
247 return false;
249 if (!data.has_original_info())
250 return false;
252 return PopulateImageData(data.original_info(), url, width, height);
255 bool EnhancedBookmarkModel::GetThumbnailImage(const BookmarkNode* node,
256 GURL* url,
257 int* width,
258 int* height) {
259 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
260 if (decoded.empty())
261 return false;
263 image::collections::ImageData data;
264 bool result = data.ParseFromString(decoded);
265 if (!result)
266 return false;
268 if (!data.has_thumbnail_info())
269 return false;
271 return PopulateImageData(data.thumbnail_info(), url, width, height);
274 std::string EnhancedBookmarkModel::GetSnippet(const BookmarkNode* node) {
275 std::string decoded(DataForMetaInfoField(node, kPageDataKey));
276 if (decoded.empty())
277 return decoded;
279 image::collections::PageData data;
280 bool result = data.ParseFromString(decoded);
281 if (!result)
282 return std::string();
284 return data.snippet();
287 void EnhancedBookmarkModel::SetVersionSuffix(
288 const std::string& version_suffix) {
289 version_suffix_ = version_suffix;
292 void EnhancedBookmarkModel::BookmarkModelChanged() {
295 void EnhancedBookmarkModel::BookmarkModelLoaded(BookmarkModel* model,
296 bool ids_reassigned) {
297 InitializeIdMap();
298 loaded_ = true;
299 FOR_EACH_OBSERVER(
300 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkModelLoaded());
303 void EnhancedBookmarkModel::BookmarkNodeAdded(BookmarkModel* model,
304 const BookmarkNode* parent,
305 int index) {
306 const BookmarkNode* node = parent->GetChild(index);
307 std::string remote_id;
308 if (node->GetMetaInfo(kIdKey, &remote_id)) {
309 AddToIdMap(node);
310 ScheduleResetDuplicateRemoteIds();
312 FOR_EACH_OBSERVER(
313 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkAdded(node));
316 void EnhancedBookmarkModel::BookmarkNodeRemoved(
317 BookmarkModel* model,
318 const BookmarkNode* parent,
319 int old_index,
320 const BookmarkNode* node,
321 const std::set<GURL>& removed_urls) {
322 RemoveNodeFromMaps(node);
323 FOR_EACH_OBSERVER(
324 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkRemoved(node));
327 void EnhancedBookmarkModel::BookmarkNodeChanged(BookmarkModel* model,
328 const BookmarkNode* node) {
329 FOR_EACH_OBSERVER(
330 EnhancedBookmarkModelObserver, observers_,
331 EnhancedBookmarkNodeChanged(node));
334 void EnhancedBookmarkModel::OnWillChangeBookmarkMetaInfo(
335 BookmarkModel* model,
336 const BookmarkNode* node) {
337 prev_remote_id_ = GetRemoteId(node);
340 void EnhancedBookmarkModel::BookmarkMetaInfoChanged(BookmarkModel* model,
341 const BookmarkNode* node) {
342 std::string remote_id = GetRemoteId(node);
343 if (remote_id != prev_remote_id_) {
344 id_map_.erase(prev_remote_id_);
345 if (!remote_id.empty()) {
346 AddToIdMap(node);
347 ScheduleResetDuplicateRemoteIds();
349 FOR_EACH_OBSERVER(
350 EnhancedBookmarkModelObserver,
351 observers_,
352 EnhancedBookmarkRemoteIdChanged(node, prev_remote_id_, remote_id));
356 void EnhancedBookmarkModel::BookmarkAllUserNodesRemoved(
357 BookmarkModel* model,
358 const std::set<GURL>& removed_urls) {
359 id_map_.clear();
360 // Re-initialize so non-user nodes with remote ids are present in the map.
361 InitializeIdMap();
362 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver,
363 observers_,
364 EnhancedBookmarkAllUserNodesRemoved());
367 void EnhancedBookmarkModel::InitializeIdMap() {
368 ui::TreeNodeIterator<const BookmarkNode> iterator(
369 bookmark_model_->root_node());
370 while (iterator.has_next()) {
371 AddToIdMap(iterator.Next());
373 ScheduleResetDuplicateRemoteIds();
376 void EnhancedBookmarkModel::AddToIdMap(const BookmarkNode* node) {
377 std::string remote_id = GetRemoteId(node);
378 if (remote_id.empty())
379 return;
381 // Try to insert the node.
382 std::pair<IdToNodeMap::iterator, bool> result =
383 id_map_.insert(make_pair(remote_id, node));
384 if (!result.second) {
385 // Some node already had the same remote id, so add both nodes to the
386 // to-be-reset set.
387 nodes_to_reset_[result.first->second] = remote_id;
388 nodes_to_reset_[node] = remote_id;
392 void EnhancedBookmarkModel::RemoveNodeFromMaps(const BookmarkNode* node) {
393 for (int i = 0; i < node->child_count(); i++) {
394 RemoveNodeFromMaps(node->GetChild(i));
396 std::string remote_id = GetRemoteId(node);
397 id_map_.erase(remote_id);
398 nodes_to_reset_.erase(node);
401 void EnhancedBookmarkModel::ScheduleResetDuplicateRemoteIds() {
402 if (!nodes_to_reset_.empty()) {
403 base::ThreadTaskRunnerHandle::Get()->PostTask(
404 FROM_HERE, base::Bind(&EnhancedBookmarkModel::ResetDuplicateRemoteIds,
405 weak_ptr_factory_.GetWeakPtr()));
409 void EnhancedBookmarkModel::ResetDuplicateRemoteIds() {
410 for (NodeToIdMap::iterator it = nodes_to_reset_.begin();
411 it != nodes_to_reset_.end();
412 ++it) {
413 BookmarkNode::MetaInfoMap meta_info;
414 meta_info[kIdKey] = "";
415 meta_info[kOldIdKey] = it->second;
416 SetMultipleMetaInfo(it->first, meta_info);
418 nodes_to_reset_.clear();
421 void EnhancedBookmarkModel::SetMetaInfo(const BookmarkNode* node,
422 const std::string& field,
423 const std::string& value) {
424 DCHECK(!bookmark_model_->is_permanent_node(node));
426 BookmarkNode::MetaInfoMap meta_info;
427 const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
428 if (old_meta_info)
429 meta_info.insert(old_meta_info->begin(), old_meta_info->end());
431 // Don't update anything if the value to set is already there.
432 BookmarkNode::MetaInfoMap::iterator it = meta_info.find(field);
433 if (it != meta_info.end() && it->second == value)
434 return;
436 meta_info[field] = value;
437 meta_info[kVersionKey] = GetVersionString();
438 bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
441 std::string EnhancedBookmarkModel::GetVersionString() {
442 if (version_suffix_.empty())
443 return version_;
444 return version_ + '/' + version_suffix_;
447 void EnhancedBookmarkModel::SetMultipleMetaInfo(
448 const BookmarkNode* node,
449 BookmarkNode::MetaInfoMap meta_info) {
450 DCHECK(!bookmark_model_->is_permanent_node(node));
452 // Don't update anything if every value is already set correctly.
453 if (node->GetMetaInfoMap()) {
454 bool changed = false;
455 const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
456 for (BookmarkNode::MetaInfoMap::iterator it = meta_info.begin();
457 it != meta_info.end();
458 ++it) {
459 BookmarkNode::MetaInfoMap::const_iterator old_field =
460 old_meta_info->find(it->first);
461 if (old_field == old_meta_info->end() ||
462 old_field->second != it->second) {
463 changed = true;
464 break;
467 if (!changed)
468 return;
470 // Fill in the values that aren't changing
471 meta_info.insert(old_meta_info->begin(), old_meta_info->end());
474 meta_info[kVersionKey] = GetVersionString();
475 bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
478 bool EnhancedBookmarkModel::SetAllImages(const BookmarkNode* node,
479 const GURL& image_url,
480 int image_width,
481 int image_height,
482 const GURL& thumbnail_url,
483 int thumbnail_width,
484 int thumbnail_height) {
485 DCHECK(node->is_url());
486 DCHECK(image_url.is_valid() || image_url.is_empty());
487 DCHECK(thumbnail_url.is_valid() || thumbnail_url.is_empty());
488 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
489 image::collections::ImageData data;
491 // Try to populate the imageData with the existing data.
492 if (!decoded.empty()) {
493 // If the parsing fails, something is wrong. Immediately fail.
494 bool result = data.ParseFromString(decoded);
495 if (!result)
496 return false;
499 if (image_url.is_empty()) {
500 data.release_original_info();
501 } else {
502 // Regardless of whether an image info exists, we make a new one.
503 // Intentially make a raw pointer.
504 image::collections::ImageData_ImageInfo* info =
505 new image::collections::ImageData_ImageInfo;
506 info->set_url(image_url.spec());
507 info->set_width(image_width);
508 info->set_height(image_height);
509 // This method consumes the raw pointer.
510 data.set_allocated_original_info(info);
513 if (thumbnail_url.is_empty()) {
514 data.release_thumbnail_info();
515 } else {
516 // Regardless of whether an image info exists, we make a new one.
517 // Intentially make a raw pointer.
518 image::collections::ImageData_ImageInfo* info =
519 new image::collections::ImageData_ImageInfo;
520 info->set_url(thumbnail_url.spec());
521 info->set_width(thumbnail_width);
522 info->set_height(thumbnail_height);
523 // This method consumes the raw pointer.
524 data.set_allocated_thumbnail_info(info);
526 std::string output;
527 bool result = data.SerializePartialToString(&output);
528 if (!result)
529 return false;
531 std::string encoded;
532 base::Base64Encode(output, &encoded);
533 bookmark_model_->SetNodeMetaInfo(node, kImageDataKey, encoded);
534 return true;
537 } // namespace enhanced_bookmarks