Roll leveldb 3f7758:803d69 (v1.17 -> v1.18)
[chromium-blink-merge.git] / components / enhanced_bookmarks / enhanced_bookmark_model.cc
blobffcc02aa0ce2df762870a24d7dbfc7c40db536a9
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/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"
20 #include "url/gurl.h"
22 namespace {
23 const char* kBookmarkBarId = "f_bookmarks_bar";
25 const char* kFlagsKey = "stars.flags";
26 const char* kIdKey = "stars.id";
27 const char* kImageDataKey = "stars.imageData";
28 const char* kNoteKey = "stars.note";
29 const char* kOldIdKey = "stars.oldId";
30 const char* kPageDataKey = "stars.pageData";
31 const char* kVersionKey = "stars.version";
33 const char* kBookmarkPrefix = "ebc_";
35 enum Flags {
36 // When set the server will attempt to fill in image and snippet information.
37 NEEDS_OFFLINE_PROCESSING = 0x1,
40 // Helper method for working with bookmark metainfo.
41 std::string DataForMetaInfoField(const BookmarkNode* node,
42 const std::string& field) {
43 std::string value;
44 if (!node->GetMetaInfo(field, &value))
45 return std::string();
47 std::string decoded;
48 if (!base::Base64Decode(value, &decoded))
49 return std::string();
51 return decoded;
54 // Helper method for working with ImageData_ImageInfo.
55 bool PopulateImageData(const image::collections::ImageData_ImageInfo& info,
56 GURL* out_url,
57 int* width,
58 int* height) {
59 if (!info.has_url() || !info.has_width() || !info.has_height())
60 return false;
62 GURL url(info.url());
63 if (!url.is_valid())
64 return false;
66 *out_url = url;
67 *width = info.width();
68 *height = info.height();
69 return true;
72 // Generate a random remote id, with a prefix that depends on whether the node
73 // is a folder or a bookmark.
74 std::string GenerateRemoteId() {
75 std::stringstream random_id;
76 random_id << kBookmarkPrefix;
78 // Generate 32 digit hex string random suffix.
79 random_id << std::hex << std::setfill('0') << std::setw(16);
80 random_id << base::RandUint64() << base::RandUint64();
81 return random_id.str();
83 } // namespace
85 namespace enhanced_bookmarks {
87 EnhancedBookmarkModel::EnhancedBookmarkModel(BookmarkModel* bookmark_model,
88 const std::string& version)
89 : bookmark_model_(bookmark_model),
90 loaded_(false),
91 version_(version),
92 weak_ptr_factory_(this) {
93 bookmark_model_->AddObserver(this);
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 return bookmark_model_->AddFolder(parent, index, title);
140 // Adds a url at the specified position.
141 const BookmarkNode* EnhancedBookmarkModel::AddURL(
142 const BookmarkNode* parent,
143 int index,
144 const base::string16& title,
145 const GURL& url,
146 const base::Time& creation_time) {
147 BookmarkNode::MetaInfoMap meta_info;
148 meta_info[kIdKey] = GenerateRemoteId();
149 return bookmark_model_->AddURLWithCreationTimeAndMetaInfo(
150 parent, index, title, url, creation_time, &meta_info);
153 std::string EnhancedBookmarkModel::GetRemoteId(const BookmarkNode* node) {
154 if (node == bookmark_model_->bookmark_bar_node())
155 return kBookmarkBarId;
157 std::string id;
158 if (!node->GetMetaInfo(kIdKey, &id))
159 return std::string();
160 return id;
163 const BookmarkNode* EnhancedBookmarkModel::BookmarkForRemoteId(
164 const std::string& remote_id) {
165 IdToNodeMap::iterator it = id_map_.find(remote_id);
166 if (it != id_map_.end())
167 return it->second;
168 return NULL;
171 void EnhancedBookmarkModel::SetDescription(const BookmarkNode* node,
172 const std::string& description) {
173 SetMetaInfo(node, kNoteKey, description);
176 std::string EnhancedBookmarkModel::GetDescription(const BookmarkNode* node) {
177 // First, look for a custom note set by the user.
178 std::string description;
179 if (node->GetMetaInfo(kNoteKey, &description) && !description.empty())
180 return description;
182 // If none are present, return the snippet.
183 return GetSnippet(node);
186 bool EnhancedBookmarkModel::SetOriginalImage(const BookmarkNode* node,
187 const GURL& url,
188 int width,
189 int height) {
190 DCHECK(node->is_url());
191 DCHECK(url.is_valid());
193 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
194 image::collections::ImageData data;
196 // Try to populate the imageData with the existing data.
197 if (decoded != "") {
198 // If the parsing fails, something is wrong. Immediately fail.
199 bool result = data.ParseFromString(decoded);
200 if (!result)
201 return false;
204 scoped_ptr<image::collections::ImageData_ImageInfo> info(
205 new image::collections::ImageData_ImageInfo);
206 info->set_url(url.spec());
207 info->set_width(width);
208 info->set_height(height);
209 data.set_allocated_original_info(info.release());
211 std::string output;
212 bool result = data.SerializePartialToString(&output);
213 if (!result)
214 return false;
216 std::string encoded;
217 base::Base64Encode(output, &encoded);
218 SetMetaInfo(node, kImageDataKey, encoded);
219 return true;
222 bool EnhancedBookmarkModel::GetOriginalImage(const BookmarkNode* node,
223 GURL* url,
224 int* width,
225 int* height) {
226 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
227 if (decoded == "")
228 return false;
230 image::collections::ImageData data;
231 bool result = data.ParseFromString(decoded);
232 if (!result)
233 return false;
235 if (!data.has_original_info())
236 return false;
238 return PopulateImageData(data.original_info(), url, width, height);
241 bool EnhancedBookmarkModel::GetThumbnailImage(const BookmarkNode* node,
242 GURL* url,
243 int* width,
244 int* height) {
245 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
246 if (decoded == "")
247 return false;
249 image::collections::ImageData data;
250 bool result = data.ParseFromString(decoded);
251 if (!result)
252 return false;
254 if (!data.has_thumbnail_info())
255 return false;
257 return PopulateImageData(data.thumbnail_info(), url, width, height);
260 std::string EnhancedBookmarkModel::GetSnippet(const BookmarkNode* node) {
261 std::string decoded(DataForMetaInfoField(node, kPageDataKey));
262 if (decoded.empty())
263 return decoded;
265 image::collections::PageData data;
266 bool result = data.ParseFromString(decoded);
267 if (!result)
268 return std::string();
270 return data.snippet();
273 void EnhancedBookmarkModel::SetVersionSuffix(
274 const std::string& version_suffix) {
275 version_suffix_ = version_suffix;
278 void EnhancedBookmarkModel::BookmarkModelChanged() {
281 void EnhancedBookmarkModel::BookmarkModelLoaded(BookmarkModel* model,
282 bool ids_reassigned) {
283 InitializeIdMap();
284 loaded_ = true;
285 FOR_EACH_OBSERVER(
286 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkModelLoaded());
289 void EnhancedBookmarkModel::BookmarkNodeAdded(BookmarkModel* model,
290 const BookmarkNode* parent,
291 int index) {
292 const BookmarkNode* node = parent->GetChild(index);
293 std::string remote_id;
294 if (node->GetMetaInfo(kIdKey, &remote_id)) {
295 AddToIdMap(node);
296 ScheduleResetDuplicateRemoteIds();
297 } else if (node->is_url()) {
298 set_needs_offline_processing_tasks_[node] =
299 make_linked_ptr(new base::CancelableClosure(
300 base::Bind(&EnhancedBookmarkModel::SetNeedsOfflineProcessing,
301 weak_ptr_factory_.GetWeakPtr(),
302 base::Unretained(node))));
303 base::MessageLoopProxy::current()->PostTask(
304 FROM_HERE, set_needs_offline_processing_tasks_[node]->callback());
306 FOR_EACH_OBSERVER(
307 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkAdded(node));
310 void EnhancedBookmarkModel::BookmarkNodeRemoved(
311 BookmarkModel* model,
312 const BookmarkNode* parent,
313 int old_index,
314 const BookmarkNode* node,
315 const std::set<GURL>& removed_urls) {
316 std::string remote_id = GetRemoteId(node);
317 id_map_.erase(remote_id);
318 nodes_to_reset_.erase(node);
319 set_needs_offline_processing_tasks_.erase(node);
320 FOR_EACH_OBSERVER(
321 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkRemoved(node));
324 void EnhancedBookmarkModel::BookmarkNodeChanged(BookmarkModel* model,
325 const BookmarkNode* node) {
326 FOR_EACH_OBSERVER(
327 EnhancedBookmarkModelObserver, observers_,
328 EnhancedBookmarkNodeChanged(node));
331 void EnhancedBookmarkModel::OnWillChangeBookmarkMetaInfo(
332 BookmarkModel* model,
333 const BookmarkNode* node) {
334 prev_remote_id_ = GetRemoteId(node);
337 void EnhancedBookmarkModel::BookmarkMetaInfoChanged(BookmarkModel* model,
338 const BookmarkNode* node) {
339 std::string remote_id = GetRemoteId(node);
340 if (remote_id != prev_remote_id_) {
341 id_map_.erase(prev_remote_id_);
342 if (!remote_id.empty()) {
343 AddToIdMap(node);
344 ScheduleResetDuplicateRemoteIds();
346 FOR_EACH_OBSERVER(
347 EnhancedBookmarkModelObserver,
348 observers_,
349 EnhancedBookmarkRemoteIdChanged(node, prev_remote_id_, remote_id));
353 void EnhancedBookmarkModel::BookmarkAllUserNodesRemoved(
354 BookmarkModel* model,
355 const std::set<GURL>& removed_urls) {
356 id_map_.clear();
357 // Re-initialize so non-user nodes with remote ids are present in the map.
358 InitializeIdMap();
359 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver,
360 observers_,
361 EnhancedBookmarkAllUserNodesRemoved());
364 void EnhancedBookmarkModel::InitializeIdMap() {
365 ui::TreeNodeIterator<const BookmarkNode> iterator(
366 bookmark_model_->root_node());
367 while (iterator.has_next()) {
368 AddToIdMap(iterator.Next());
370 ScheduleResetDuplicateRemoteIds();
373 void EnhancedBookmarkModel::AddToIdMap(const BookmarkNode* node) {
374 std::string remote_id = GetRemoteId(node);
375 if (remote_id.empty())
376 return;
378 // Try to insert the node.
379 std::pair<IdToNodeMap::iterator, bool> result =
380 id_map_.insert(make_pair(remote_id, node));
381 if (!result.second) {
382 // Some node already had the same remote id, so add both nodes to the
383 // to-be-reset set.
384 nodes_to_reset_[result.first->second] = remote_id;
385 nodes_to_reset_[node] = remote_id;
389 void EnhancedBookmarkModel::ScheduleResetDuplicateRemoteIds() {
390 if (!nodes_to_reset_.empty()) {
391 base::MessageLoopProxy::current()->PostTask(
392 FROM_HERE,
393 base::Bind(&EnhancedBookmarkModel::ResetDuplicateRemoteIds,
394 weak_ptr_factory_.GetWeakPtr()));
398 void EnhancedBookmarkModel::ResetDuplicateRemoteIds() {
399 for (NodeToIdMap::iterator it = nodes_to_reset_.begin();
400 it != nodes_to_reset_.end();
401 ++it) {
402 BookmarkNode::MetaInfoMap meta_info;
403 meta_info[kIdKey] = "";
404 meta_info[kOldIdKey] = it->second;
405 SetMultipleMetaInfo(it->first, meta_info);
407 nodes_to_reset_.clear();
410 void EnhancedBookmarkModel::SetNeedsOfflineProcessing(
411 const BookmarkNode* node) {
412 set_needs_offline_processing_tasks_.erase(node);
413 int flags = 0;
414 std::string flags_str;
415 if (node->GetMetaInfo(kFlagsKey, &flags_str)) {
416 if (!base::StringToInt(flags_str, &flags))
417 flags = 0;
419 flags |= NEEDS_OFFLINE_PROCESSING;
420 SetMetaInfo(node, kFlagsKey, base::IntToString(flags));
423 void EnhancedBookmarkModel::SetMetaInfo(const BookmarkNode* node,
424 const std::string& field,
425 const std::string& value) {
426 DCHECK(!bookmark_model_->is_permanent_node(node));
428 BookmarkNode::MetaInfoMap meta_info;
429 const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
430 if (old_meta_info)
431 meta_info.insert(old_meta_info->begin(), old_meta_info->end());
433 // Don't update anything if the value to set is already there.
434 BookmarkNode::MetaInfoMap::iterator it = meta_info.find(field);
435 if (it != meta_info.end() && it->second == value)
436 return;
438 meta_info[field] = value;
439 meta_info[kVersionKey] = GetVersionString();
440 bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
443 std::string EnhancedBookmarkModel::GetVersionString() {
444 if (version_suffix_.empty())
445 return version_;
446 return version_ + '/' + version_suffix_;
449 void EnhancedBookmarkModel::SetMultipleMetaInfo(
450 const BookmarkNode* node,
451 BookmarkNode::MetaInfoMap meta_info) {
452 DCHECK(!bookmark_model_->is_permanent_node(node));
454 // Don't update anything if every value is already set correctly.
455 if (node->GetMetaInfoMap()) {
456 bool changed = false;
457 const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
458 for (BookmarkNode::MetaInfoMap::iterator it = meta_info.begin();
459 it != meta_info.end();
460 ++it) {
461 BookmarkNode::MetaInfoMap::const_iterator old_field =
462 old_meta_info->find(it->first);
463 if (old_field == old_meta_info->end() ||
464 old_field->second != it->second) {
465 changed = true;
466 break;
469 if (!changed)
470 return;
472 // Fill in the values that aren't changing
473 meta_info.insert(old_meta_info->begin(), old_meta_info->end());
476 meta_info[kVersionKey] = GetVersionString();
477 bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
480 bool EnhancedBookmarkModel::SetAllImages(const BookmarkNode* node,
481 const GURL& image_url,
482 int image_width,
483 int image_height,
484 const GURL& thumbnail_url,
485 int thumbnail_width,
486 int thumbnail_height) {
487 DCHECK(node->is_url());
488 DCHECK(image_url.is_valid() || image_url.is_empty());
489 DCHECK(thumbnail_url.is_valid() || thumbnail_url.is_empty());
490 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
491 image::collections::ImageData data;
493 // Try to populate the imageData with the existing data.
494 if (decoded != "") {
495 // If the parsing fails, something is wrong. Immediately fail.
496 bool result = data.ParseFromString(decoded);
497 if (!result)
498 return false;
501 if (image_url.is_empty()) {
502 data.release_original_info();
503 } else {
504 // Regardless of whether an image info exists, we make a new one.
505 // Intentially make a raw pointer.
506 image::collections::ImageData_ImageInfo* info =
507 new image::collections::ImageData_ImageInfo;
508 info->set_url(image_url.spec());
509 info->set_width(image_width);
510 info->set_height(image_height);
511 // This method consumes the raw pointer.
512 data.set_allocated_original_info(info);
515 if (thumbnail_url.is_empty()) {
516 data.release_thumbnail_info();
517 } else {
518 // Regardless of whether an image info exists, we make a new one.
519 // Intentially make a raw pointer.
520 image::collections::ImageData_ImageInfo* info =
521 new image::collections::ImageData_ImageInfo;
522 info->set_url(thumbnail_url.spec());
523 info->set_width(thumbnail_width);
524 info->set_height(thumbnail_height);
525 // This method consumes the raw pointer.
526 data.set_allocated_thumbnail_info(info);
528 std::string output;
529 bool result = data.SerializePartialToString(&output);
530 if (!result)
531 return false;
533 std::string encoded;
534 base::Base64Encode(output, &encoded);
535 bookmark_model_->SetNodeMetaInfo(node, kImageDataKey, encoded);
536 return true;
539 } // namespace enhanced_bookmarks