ozone: fix HDPMLegacy - do the PF after overlays, also clear old overlays
[chromium-blink-merge.git] / components / enhanced_bookmarks / enhanced_bookmark_model.cc
blobf9d347829557a623e004544d94273a3565723d2e
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 using bookmarks::BookmarkModel;
24 namespace {
25 const char* kBookmarkBarId = "f_bookmarks_bar";
27 const char* kFlagsKey = "stars.flags";
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 enum Flags {
38 // When set the server will attempt to fill in image and snippet information.
39 NEEDS_OFFLINE_PROCESSING = 0x1,
42 // Helper method for working with bookmark metainfo.
43 std::string DataForMetaInfoField(const BookmarkNode* node,
44 const std::string& field) {
45 std::string value;
46 if (!node->GetMetaInfo(field, &value))
47 return std::string();
49 std::string decoded;
50 if (!base::Base64Decode(value, &decoded))
51 return std::string();
53 return decoded;
56 // Helper method for working with ImageData_ImageInfo.
57 bool PopulateImageData(const image::collections::ImageData_ImageInfo& info,
58 GURL* out_url,
59 int* width,
60 int* height) {
61 if (!info.has_url() || !info.has_width() || !info.has_height())
62 return false;
64 GURL url(info.url());
65 if (!url.is_valid())
66 return false;
68 *out_url = url;
69 *width = info.width();
70 *height = info.height();
71 return true;
74 // Generate a random remote id, with a prefix that depends on whether the node
75 // is a folder or a bookmark.
76 std::string GenerateRemoteId() {
77 std::stringstream random_id;
78 random_id << kBookmarkPrefix;
80 // Generate 32 digit hex string random suffix.
81 random_id << std::hex << std::setfill('0') << std::setw(16);
82 random_id << base::RandUint64() << base::RandUint64();
83 return random_id.str();
85 } // namespace
87 namespace enhanced_bookmarks {
89 EnhancedBookmarkModel::EnhancedBookmarkModel(BookmarkModel* bookmark_model,
90 const std::string& version)
91 : bookmark_model_(bookmark_model),
92 loaded_(false),
93 version_(version),
94 weak_ptr_factory_(this) {
95 bookmark_model_->AddObserver(this);
96 bookmark_model_->AddNonClonedKey(kIdKey);
97 if (bookmark_model_->loaded()) {
98 InitializeIdMap();
99 loaded_ = true;
103 EnhancedBookmarkModel::~EnhancedBookmarkModel() {
104 Shutdown();
107 void EnhancedBookmarkModel::Shutdown() {
108 if (bookmark_model_) {
109 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver,
110 observers_,
111 EnhancedBookmarkModelShuttingDown());
112 weak_ptr_factory_.InvalidateWeakPtrs();
113 bookmark_model_->RemoveObserver(this);
114 bookmark_model_ = NULL;
118 void EnhancedBookmarkModel::AddObserver(
119 EnhancedBookmarkModelObserver* observer) {
120 observers_.AddObserver(observer);
123 void EnhancedBookmarkModel::RemoveObserver(
124 EnhancedBookmarkModelObserver* observer) {
125 observers_.RemoveObserver(observer);
128 // Moves |node| to |new_parent| and inserts it at the given |index|.
129 void EnhancedBookmarkModel::Move(const BookmarkNode* node,
130 const BookmarkNode* new_parent,
131 int index) {
132 bookmark_model_->Move(node, new_parent, index);
135 // Adds a new folder node at the specified position.
136 const BookmarkNode* EnhancedBookmarkModel::AddFolder(
137 const BookmarkNode* parent,
138 int index,
139 const base::string16& title) {
140 BookmarkNode::MetaInfoMap meta_info;
141 meta_info[kVersionKey] = GetVersionString();
142 return bookmark_model_->AddFolderWithMetaInfo(parent, index, title,
143 &meta_info);
146 // Adds a url at the specified position.
147 const BookmarkNode* EnhancedBookmarkModel::AddURL(
148 const BookmarkNode* parent,
149 int index,
150 const base::string16& title,
151 const GURL& url,
152 const base::Time& creation_time) {
153 BookmarkNode::MetaInfoMap meta_info;
154 meta_info[kIdKey] = GenerateRemoteId();
155 meta_info[kVersionKey] = GetVersionString();
156 return bookmark_model_->AddURLWithCreationTimeAndMetaInfo(
157 parent, index, title, url, creation_time, &meta_info);
160 std::string EnhancedBookmarkModel::GetRemoteId(const BookmarkNode* node) {
161 if (node == bookmark_model_->bookmark_bar_node())
162 return kBookmarkBarId;
164 std::string id;
165 if (!node->GetMetaInfo(kIdKey, &id))
166 return std::string();
167 return id;
170 const BookmarkNode* EnhancedBookmarkModel::BookmarkForRemoteId(
171 const std::string& remote_id) {
172 IdToNodeMap::iterator it = id_map_.find(remote_id);
173 if (it != id_map_.end())
174 return it->second;
175 return NULL;
178 void EnhancedBookmarkModel::SetDescription(const BookmarkNode* node,
179 const std::string& description) {
180 SetMetaInfo(node, kNoteKey, description);
183 std::string EnhancedBookmarkModel::GetDescription(const BookmarkNode* node) {
184 // First, look for a custom note set by the user.
185 std::string description;
186 if (node->GetMetaInfo(kNoteKey, &description) && !description.empty())
187 return description;
189 // If none are present, return the snippet.
190 return GetSnippet(node);
193 bool EnhancedBookmarkModel::SetOriginalImage(const BookmarkNode* node,
194 const GURL& url,
195 int width,
196 int height) {
197 DCHECK(node->is_url());
198 DCHECK(url.is_valid());
200 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
201 image::collections::ImageData data;
203 // Try to populate the imageData with the existing data.
204 if (!decoded.empty()) {
205 // If the parsing fails, something is wrong. Immediately fail.
206 bool result = data.ParseFromString(decoded);
207 if (!result)
208 return false;
211 scoped_ptr<image::collections::ImageData_ImageInfo> info(
212 new image::collections::ImageData_ImageInfo);
213 info->set_url(url.spec());
214 info->set_width(width);
215 info->set_height(height);
216 data.set_allocated_original_info(info.release());
218 std::string output;
219 bool result = data.SerializePartialToString(&output);
220 if (!result)
221 return false;
223 std::string encoded;
224 base::Base64Encode(output, &encoded);
225 SetMetaInfo(node, kImageDataKey, encoded);
226 return true;
229 bool EnhancedBookmarkModel::GetOriginalImage(const BookmarkNode* node,
230 GURL* url,
231 int* width,
232 int* height) {
233 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
234 if (decoded.empty())
235 return false;
237 image::collections::ImageData data;
238 bool result = data.ParseFromString(decoded);
239 if (!result)
240 return false;
242 if (!data.has_original_info())
243 return false;
245 return PopulateImageData(data.original_info(), url, width, height);
248 bool EnhancedBookmarkModel::GetThumbnailImage(const BookmarkNode* node,
249 GURL* url,
250 int* width,
251 int* height) {
252 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
253 if (decoded.empty())
254 return false;
256 image::collections::ImageData data;
257 bool result = data.ParseFromString(decoded);
258 if (!result)
259 return false;
261 if (!data.has_thumbnail_info())
262 return false;
264 return PopulateImageData(data.thumbnail_info(), url, width, height);
267 std::string EnhancedBookmarkModel::GetSnippet(const BookmarkNode* node) {
268 std::string decoded(DataForMetaInfoField(node, kPageDataKey));
269 if (decoded.empty())
270 return decoded;
272 image::collections::PageData data;
273 bool result = data.ParseFromString(decoded);
274 if (!result)
275 return std::string();
277 return data.snippet();
280 void EnhancedBookmarkModel::SetVersionSuffix(
281 const std::string& version_suffix) {
282 version_suffix_ = version_suffix;
285 void EnhancedBookmarkModel::BookmarkModelChanged() {
288 void EnhancedBookmarkModel::BookmarkModelLoaded(BookmarkModel* model,
289 bool ids_reassigned) {
290 InitializeIdMap();
291 loaded_ = true;
292 FOR_EACH_OBSERVER(
293 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkModelLoaded());
296 void EnhancedBookmarkModel::BookmarkNodeAdded(BookmarkModel* model,
297 const BookmarkNode* parent,
298 int index) {
299 const BookmarkNode* node = parent->GetChild(index);
300 std::string remote_id;
301 if (node->GetMetaInfo(kIdKey, &remote_id)) {
302 AddToIdMap(node);
303 ScheduleResetDuplicateRemoteIds();
304 } else if (node->is_url()) {
305 set_needs_offline_processing_tasks_[node] =
306 make_linked_ptr(new base::CancelableClosure(
307 base::Bind(&EnhancedBookmarkModel::SetNeedsOfflineProcessing,
308 weak_ptr_factory_.GetWeakPtr(),
309 base::Unretained(node))));
310 base::MessageLoopProxy::current()->PostTask(
311 FROM_HERE, set_needs_offline_processing_tasks_[node]->callback());
313 FOR_EACH_OBSERVER(
314 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkAdded(node));
317 void EnhancedBookmarkModel::BookmarkNodeRemoved(
318 BookmarkModel* model,
319 const BookmarkNode* parent,
320 int old_index,
321 const BookmarkNode* node,
322 const std::set<GURL>& removed_urls) {
323 RemoveNodeFromMaps(node);
324 FOR_EACH_OBSERVER(
325 EnhancedBookmarkModelObserver, observers_, EnhancedBookmarkRemoved(node));
328 void EnhancedBookmarkModel::BookmarkNodeChanged(BookmarkModel* model,
329 const BookmarkNode* node) {
330 FOR_EACH_OBSERVER(
331 EnhancedBookmarkModelObserver, observers_,
332 EnhancedBookmarkNodeChanged(node));
335 void EnhancedBookmarkModel::OnWillChangeBookmarkMetaInfo(
336 BookmarkModel* model,
337 const BookmarkNode* node) {
338 prev_remote_id_ = GetRemoteId(node);
341 void EnhancedBookmarkModel::BookmarkMetaInfoChanged(BookmarkModel* model,
342 const BookmarkNode* node) {
343 std::string remote_id = GetRemoteId(node);
344 if (remote_id != prev_remote_id_) {
345 id_map_.erase(prev_remote_id_);
346 if (!remote_id.empty()) {
347 AddToIdMap(node);
348 ScheduleResetDuplicateRemoteIds();
350 FOR_EACH_OBSERVER(
351 EnhancedBookmarkModelObserver,
352 observers_,
353 EnhancedBookmarkRemoteIdChanged(node, prev_remote_id_, remote_id));
357 void EnhancedBookmarkModel::BookmarkAllUserNodesRemoved(
358 BookmarkModel* model,
359 const std::set<GURL>& removed_urls) {
360 id_map_.clear();
361 // Re-initialize so non-user nodes with remote ids are present in the map.
362 InitializeIdMap();
363 FOR_EACH_OBSERVER(EnhancedBookmarkModelObserver,
364 observers_,
365 EnhancedBookmarkAllUserNodesRemoved());
368 void EnhancedBookmarkModel::InitializeIdMap() {
369 ui::TreeNodeIterator<const BookmarkNode> iterator(
370 bookmark_model_->root_node());
371 while (iterator.has_next()) {
372 AddToIdMap(iterator.Next());
374 ScheduleResetDuplicateRemoteIds();
377 void EnhancedBookmarkModel::AddToIdMap(const BookmarkNode* node) {
378 std::string remote_id = GetRemoteId(node);
379 if (remote_id.empty())
380 return;
382 // Try to insert the node.
383 std::pair<IdToNodeMap::iterator, bool> result =
384 id_map_.insert(make_pair(remote_id, node));
385 if (!result.second) {
386 // Some node already had the same remote id, so add both nodes to the
387 // to-be-reset set.
388 nodes_to_reset_[result.first->second] = remote_id;
389 nodes_to_reset_[node] = remote_id;
393 void EnhancedBookmarkModel::RemoveNodeFromMaps(const BookmarkNode* node) {
394 for (int i = 0; i < node->child_count(); i++) {
395 RemoveNodeFromMaps(node->GetChild(i));
397 std::string remote_id = GetRemoteId(node);
398 id_map_.erase(remote_id);
399 nodes_to_reset_.erase(node);
400 set_needs_offline_processing_tasks_.erase(node);
403 void EnhancedBookmarkModel::ScheduleResetDuplicateRemoteIds() {
404 if (!nodes_to_reset_.empty()) {
405 base::MessageLoopProxy::current()->PostTask(
406 FROM_HERE,
407 base::Bind(&EnhancedBookmarkModel::ResetDuplicateRemoteIds,
408 weak_ptr_factory_.GetWeakPtr()));
412 void EnhancedBookmarkModel::ResetDuplicateRemoteIds() {
413 for (NodeToIdMap::iterator it = nodes_to_reset_.begin();
414 it != nodes_to_reset_.end();
415 ++it) {
416 BookmarkNode::MetaInfoMap meta_info;
417 meta_info[kIdKey] = "";
418 meta_info[kOldIdKey] = it->second;
419 SetMultipleMetaInfo(it->first, meta_info);
421 nodes_to_reset_.clear();
424 void EnhancedBookmarkModel::SetNeedsOfflineProcessing(
425 const BookmarkNode* node) {
426 set_needs_offline_processing_tasks_.erase(node);
427 int flags = 0;
428 std::string flags_str;
429 if (node->GetMetaInfo(kFlagsKey, &flags_str)) {
430 if (!base::StringToInt(flags_str, &flags))
431 flags = 0;
433 flags |= NEEDS_OFFLINE_PROCESSING;
434 SetMetaInfo(node, kFlagsKey, base::IntToString(flags));
437 void EnhancedBookmarkModel::SetMetaInfo(const BookmarkNode* node,
438 const std::string& field,
439 const std::string& value) {
440 DCHECK(!bookmark_model_->is_permanent_node(node));
442 BookmarkNode::MetaInfoMap meta_info;
443 const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
444 if (old_meta_info)
445 meta_info.insert(old_meta_info->begin(), old_meta_info->end());
447 // Don't update anything if the value to set is already there.
448 BookmarkNode::MetaInfoMap::iterator it = meta_info.find(field);
449 if (it != meta_info.end() && it->second == value)
450 return;
452 meta_info[field] = value;
453 meta_info[kVersionKey] = GetVersionString();
454 bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
457 std::string EnhancedBookmarkModel::GetVersionString() {
458 if (version_suffix_.empty())
459 return version_;
460 return version_ + '/' + version_suffix_;
463 void EnhancedBookmarkModel::SetMultipleMetaInfo(
464 const BookmarkNode* node,
465 BookmarkNode::MetaInfoMap meta_info) {
466 DCHECK(!bookmark_model_->is_permanent_node(node));
468 // Don't update anything if every value is already set correctly.
469 if (node->GetMetaInfoMap()) {
470 bool changed = false;
471 const BookmarkNode::MetaInfoMap* old_meta_info = node->GetMetaInfoMap();
472 for (BookmarkNode::MetaInfoMap::iterator it = meta_info.begin();
473 it != meta_info.end();
474 ++it) {
475 BookmarkNode::MetaInfoMap::const_iterator old_field =
476 old_meta_info->find(it->first);
477 if (old_field == old_meta_info->end() ||
478 old_field->second != it->second) {
479 changed = true;
480 break;
483 if (!changed)
484 return;
486 // Fill in the values that aren't changing
487 meta_info.insert(old_meta_info->begin(), old_meta_info->end());
490 meta_info[kVersionKey] = GetVersionString();
491 bookmark_model_->SetNodeMetaInfoMap(node, meta_info);
494 bool EnhancedBookmarkModel::SetAllImages(const BookmarkNode* node,
495 const GURL& image_url,
496 int image_width,
497 int image_height,
498 const GURL& thumbnail_url,
499 int thumbnail_width,
500 int thumbnail_height) {
501 DCHECK(node->is_url());
502 DCHECK(image_url.is_valid() || image_url.is_empty());
503 DCHECK(thumbnail_url.is_valid() || thumbnail_url.is_empty());
504 std::string decoded(DataForMetaInfoField(node, kImageDataKey));
505 image::collections::ImageData data;
507 // Try to populate the imageData with the existing data.
508 if (!decoded.empty()) {
509 // If the parsing fails, something is wrong. Immediately fail.
510 bool result = data.ParseFromString(decoded);
511 if (!result)
512 return false;
515 if (image_url.is_empty()) {
516 data.release_original_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(image_url.spec());
523 info->set_width(image_width);
524 info->set_height(image_height);
525 // This method consumes the raw pointer.
526 data.set_allocated_original_info(info);
529 if (thumbnail_url.is_empty()) {
530 data.release_thumbnail_info();
531 } else {
532 // Regardless of whether an image info exists, we make a new one.
533 // Intentially make a raw pointer.
534 image::collections::ImageData_ImageInfo* info =
535 new image::collections::ImageData_ImageInfo;
536 info->set_url(thumbnail_url.spec());
537 info->set_width(thumbnail_width);
538 info->set_height(thumbnail_height);
539 // This method consumes the raw pointer.
540 data.set_allocated_thumbnail_info(info);
542 std::string output;
543 bool result = data.SerializePartialToString(&output);
544 if (!result)
545 return false;
547 std::string encoded;
548 base::Base64Encode(output, &encoded);
549 bookmark_model_->SetNodeMetaInfo(node, kImageDataKey, encoded);
550 return true;
553 } // namespace enhanced_bookmarks