OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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. | |
4 | |
5 #include "chrome/browser/bookmarks/bookmark_tag_model.h" | |
6 | |
7 #include "base/auto_reset.h" | |
8 #include "base/json/json_string_value_serializer.h" | |
9 #include "base/observer_list.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "base/strings/utf_string_conversions.h" | |
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h" | |
13 #include "chrome/browser/bookmarks/bookmark_tag_model_observer.h" | |
14 #include "ui/base/models/tree_node_iterator.h" | |
15 | |
16 namespace { | |
17 // The key used to store the tag list in the metainfo of a bookmark. | |
18 const char* TAG_KEY = "TAG_KEY"; | |
19 | |
20 // Comparator to sort tags by usage. | |
21 struct TagComparator { | |
22 TagComparator(std::map<BookmarkTag, unsigned int>& tags) : tags_(tags) { | |
23 } | |
24 ~TagComparator() {} | |
25 | |
26 bool operator()(const BookmarkTag& a, const BookmarkTag& b) { | |
27 return (tags_[a] < tags_[b]); | |
28 } | |
29 | |
30 std::map<BookmarkTag, unsigned int>& tags_; | |
31 }; | |
32 | |
33 // The tags are currently stored in the BookmarkNode's metaInfo in JSON | |
34 // format. This function extracts the info from there and returns it in | |
35 // digestible format. | |
36 // If the Bookmark was never tagged before it is implicitely tagged with the | |
37 // title of all its ancestors in the BookmarkModel. | |
38 std::set<BookmarkTag> ExtractTagsFromBookmark( | |
39 const BookmarkNode* bookmark) { | |
sky
2013/10/16 13:46:19
nit: seems like you can fit all on one line.
noyau (Ping after 24h)
2013/10/16 16:51:32
Done.
| |
40 // This is awful BTW. Metainfo is itself an encoded JSON, and here we decode | |
41 // another layer. | |
42 | |
43 // Retrieve the encodedData from the bookmark. If there is no encoded data | |
44 // at all returns the name of all the ancestors as separate tags. | |
45 std::string encoded; | |
46 if (!bookmark->GetMetaInfo(TAG_KEY, &encoded)) { | |
47 std::set<BookmarkTag> tags; | |
48 const BookmarkNode* folder = bookmark->parent(); | |
49 while (folder && folder->type() == BookmarkNode::FOLDER) { | |
50 BookmarkTag trimmed_tag = CollapseWhitespace(folder->GetTitle(), true); | |
51 if (!trimmed_tag.empty()) | |
52 tags.insert(trimmed_tag); | |
53 folder = folder->parent(); | |
54 } | |
55 return tags; | |
56 } | |
57 | |
58 // Decode into a base::Value. If the data is not encoded properly as a list | |
59 // return an empty result. | |
60 JSONStringValueSerializer serializer(&encoded); | |
61 int error_code = 0; | |
62 std::string error_message; | |
63 scoped_ptr<base::Value> result(serializer.Deserialize(&error_code, | |
64 &error_message)); | |
65 | |
66 if (error_code || !result->IsType(base::Value::TYPE_LIST)) | |
67 return std::set<BookmarkTag>(); | |
68 | |
69 base::ListValue* list = NULL; | |
70 if (!result->GetAsList(&list) || list->empty()) | |
71 return std::set<BookmarkTag>(); | |
72 | |
73 // Build the set. | |
74 std::set<BookmarkTag> return_value; | |
75 | |
76 for (base::ListValue::iterator it = list->begin(); | |
77 it != list->end(); ++it) { | |
78 base::Value* item = *it; | |
79 BookmarkTag tag; | |
80 if (!item->GetAsString(&tag)) | |
81 continue; | |
82 return_value.insert(tag); | |
83 } | |
84 return return_value; | |
85 } | |
86 } // namespace | |
87 | |
88 BookmarkTagModel::BookmarkTagModel(BookmarkModel* bookmark_model) | |
89 : bookmark_model_(bookmark_model), | |
90 loaded_(false), | |
91 observers_(ObserverList<BookmarkTagModelObserver>::NOTIFY_EXISTING_ONLY), | |
92 inhibit_change_notifications_(false) { | |
93 bookmark_model_->AddObserver(this); | |
94 if (bookmark_model_->loaded()) | |
95 Load(); | |
96 } | |
97 | |
98 BookmarkTagModel::~BookmarkTagModel() { | |
99 if (bookmark_model_) | |
100 bookmark_model_->RemoveObserver(this); | |
101 } | |
102 | |
103 // BookmarkModel forwarding. | |
104 | |
105 void BookmarkTagModel::AddObserver(BookmarkTagModelObserver* observer) { | |
106 observers_.AddObserver(observer); | |
107 } | |
108 | |
109 void BookmarkTagModel::RemoveObserver(BookmarkTagModelObserver* observer) { | |
110 observers_.RemoveObserver(observer); | |
111 } | |
112 | |
113 void BookmarkTagModel::BeginExtensiveChanges() { | |
114 DCHECK(bookmark_model_); | |
115 bookmark_model_->BeginExtensiveChanges(); | |
116 } | |
117 | |
118 void BookmarkTagModel::EndExtensiveChanges() { | |
119 DCHECK(bookmark_model_); | |
120 bookmark_model_->EndExtensiveChanges(); | |
121 } | |
122 | |
123 bool BookmarkTagModel::IsDoingExtensiveChanges() const { | |
124 DCHECK(bookmark_model_); | |
125 return bookmark_model_->IsDoingExtensiveChanges(); | |
126 } | |
127 | |
128 void BookmarkTagModel::Remove(const BookmarkNode* bookmark) { | |
129 DCHECK(bookmark_model_); | |
130 DCHECK(loaded_); | |
131 const BookmarkNode* parent = bookmark->parent(); | |
132 bookmark_model_->Remove(parent, parent->GetIndexOf(bookmark)); | |
133 } | |
134 | |
135 void BookmarkTagModel::RemoveAll() { | |
136 DCHECK(bookmark_model_); | |
137 DCHECK(loaded_); | |
138 bookmark_model_->RemoveAll(); | |
139 } | |
140 | |
141 const gfx::Image& BookmarkTagModel::GetFavicon(const BookmarkNode* bookmark) { | |
142 DCHECK(bookmark_model_); | |
143 DCHECK(loaded_); | |
144 return bookmark_model_->GetFavicon(bookmark); | |
145 } | |
146 | |
147 void BookmarkTagModel::SetTitle(const BookmarkNode* bookmark, | |
148 const string16& title) { | |
149 DCHECK(bookmark_model_); | |
150 DCHECK(loaded_); | |
151 bookmark_model_->SetTitle(bookmark, title); | |
152 } | |
153 | |
154 void BookmarkTagModel::SetURL(const BookmarkNode* bookmark, const GURL& url) { | |
155 DCHECK(bookmark_model_); | |
156 DCHECK(loaded_); | |
157 bookmark_model_->SetURL(bookmark, url); | |
158 } | |
159 | |
160 void BookmarkTagModel::SetDateAdded(const BookmarkNode* bookmark, | |
161 base::Time date_added) { | |
162 DCHECK(bookmark_model_); | |
163 DCHECK(loaded_); | |
164 bookmark_model_->SetDateAdded(bookmark, date_added); | |
165 } | |
166 | |
167 const BookmarkNode* | |
168 BookmarkTagModel::GetMostRecentlyAddedBookmarkForURL(const GURL& url) { | |
169 DCHECK(bookmark_model_); | |
170 DCHECK(loaded_); | |
171 return bookmark_model_->GetMostRecentlyAddedNodeForURL(url); | |
172 } | |
173 | |
174 // Tags specific code. | |
175 | |
176 const BookmarkNode* BookmarkTagModel::AddURL( | |
177 const string16& title, | |
178 const GURL& url, | |
179 const std::set<BookmarkTag>& tags) { | |
180 DCHECK(bookmark_model_); | |
181 DCHECK(loaded_); | |
182 | |
183 const BookmarkNode* bookmark; | |
184 { | |
185 base::AutoReset<bool> inhibitor(&inhibit_change_notifications_, true); | |
186 const BookmarkNode* parent = bookmark_model_->GetParentForNewNodes(); | |
187 bookmark = bookmark_model_->AddURL(parent, 0, title, url); | |
188 AddTagsToBookmark(tags, bookmark); | |
189 } | |
190 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
191 BookmarkNodeAdded(this, bookmark)); | |
192 | |
193 return bookmark; | |
194 } | |
195 | |
196 std::set<BookmarkTag> BookmarkTagModel::GetTagsForBookmark( | |
197 const BookmarkNode* bookmark) { | |
198 DCHECK(loaded_); | |
199 return bookmark_to_tags_[bookmark]; | |
200 } | |
201 | |
202 void BookmarkTagModel::AddTagsToBookmark( | |
203 const std::set<BookmarkTag>& tags, | |
204 const BookmarkNode* bookmark) { | |
205 DCHECK(bookmark_model_); | |
206 std::set<BookmarkTag> all_tags(GetTagsForBookmark(bookmark)); | |
207 for (std::set<BookmarkTag>::const_iterator it = tags.begin(); | |
208 it != tags.end(); ++it) { | |
209 BookmarkTag trimmed_tag = CollapseWhitespace(*it, true); | |
210 if (trimmed_tag.empty()) | |
211 continue; | |
212 all_tags.insert(trimmed_tag); | |
213 } | |
214 SetTagsOnBookmark(all_tags, bookmark); | |
215 } | |
216 | |
217 void BookmarkTagModel::AddTagsToBookmarks( | |
218 const std::set<BookmarkTag>& tags, | |
219 const std::set<const BookmarkNode*>& bookmarks) { | |
220 for (std::set<const BookmarkNode*>::const_iterator it = bookmarks.begin(); | |
221 it != bookmarks.end(); ++it) { | |
222 AddTagsToBookmark(tags, *it); | |
223 } | |
224 } | |
225 | |
226 void BookmarkTagModel::RemoveTagsFromBookmark( | |
227 const std::set<BookmarkTag>& tags, | |
228 const BookmarkNode* bookmark) { | |
229 std::set<BookmarkTag> all_tags(GetTagsForBookmark(bookmark)); | |
230 for (std::set<BookmarkTag>::const_iterator it = tags.begin(); | |
231 it != tags.end(); ++it) { | |
232 all_tags.erase(*it); | |
233 } | |
234 SetTagsOnBookmark(all_tags, bookmark); | |
235 } | |
236 | |
237 void BookmarkTagModel::RemoveTagsFromBookmarks( | |
238 const std::set<BookmarkTag>& tags, | |
239 const std::set<const BookmarkNode*>& bookmarks){ | |
240 for (std::set<const BookmarkNode*>::const_iterator it = bookmarks.begin(); | |
241 it != bookmarks.end(); ++it) { | |
242 RemoveTagsFromBookmark(tags, *it); | |
243 } | |
244 } | |
245 | |
246 std::set<const BookmarkNode*> BookmarkTagModel::BookmarksForTags( | |
247 const std::set<BookmarkTag>& tags) { | |
248 DCHECK(loaded_); | |
249 // Count for each tags how many times a bookmark appeared. | |
250 std::map<const BookmarkNode*, size_t> bookmark_counts; | |
251 for (std::set<BookmarkTag>::const_iterator it = tags.begin(); | |
252 it != tags.end(); ++it) { | |
253 const std::set<const BookmarkNode*>& subset(tag_to_bookmarks_[*it]); | |
254 for (std::set<const BookmarkNode*>::const_iterator tag_it = subset.begin(); | |
255 tag_it != subset.end(); ++tag_it) { | |
256 bookmark_counts[*tag_it] += 1; | |
257 } | |
258 } | |
259 // Keep only the bookmarks that appeared in all the tags. | |
260 std::set<const BookmarkNode*> common_bookmarks; | |
261 for (std::map<const BookmarkNode*, size_t>::iterator it = | |
262 bookmark_counts.begin(); it != bookmark_counts.end(); ++it) { | |
263 if (it->second == tags.size()) | |
264 common_bookmarks.insert(it->first); | |
265 } | |
266 return common_bookmarks; | |
267 } | |
268 | |
269 std::set<const BookmarkNode*> BookmarkTagModel::BookmarksForTag( | |
270 const BookmarkTag& tag) { | |
271 DCHECK(!tag.empty()); | |
272 return tag_to_bookmarks_[tag]; | |
273 } | |
274 | |
275 std::vector<BookmarkTag> BookmarkTagModel::TagsRelatedToTag( | |
276 const BookmarkTag& tag) { | |
277 DCHECK(loaded_); | |
278 std::map<BookmarkTag, unsigned int> tags; | |
279 | |
280 if (tag.empty()) { | |
281 // Returns all the tags. | |
282 for (std::map<const BookmarkTag, std::set<const BookmarkNode*> >::iterator | |
283 it = tag_to_bookmarks_.begin(); it != tag_to_bookmarks_.end(); ++it) { | |
284 tags[it->first] = it->second.size(); | |
285 } | |
286 } else { | |
287 std::set<const BookmarkNode*> bookmarks(BookmarksForTag(tag)); | |
288 | |
289 for (std::set<const BookmarkNode*>::iterator it = bookmarks.begin(); | |
290 it != bookmarks.end(); ++it) { | |
291 const std::set<BookmarkTag>& subset(bookmark_to_tags_[*it]); | |
292 for (std::set<BookmarkTag>::const_iterator tag_it = subset.begin(); | |
293 tag_it != subset.end(); ++tag_it) { | |
294 tags[*tag_it] += 1; | |
295 } | |
296 } | |
297 tags.erase(tag); // A tag is not related to itself. | |
298 } | |
299 | |
300 std::vector<BookmarkTag> sorted_tags; | |
301 for (std::map<BookmarkTag, unsigned int>::iterator it = tags.begin(); | |
302 it != tags.end(); ++it) { | |
303 sorted_tags.push_back(it->first); | |
304 } | |
305 std::sort(sorted_tags.begin(), sorted_tags.end(), TagComparator(tags)); | |
306 return sorted_tags; | |
307 } | |
308 | |
309 // BookmarkModelObserver methods. | |
310 | |
311 // Invoked when the model has finished loading. | |
312 void BookmarkTagModel::Loaded(BookmarkModel* model, bool ids_reassigned) { | |
313 Load(); | |
314 }; | |
sky
2013/10/16 13:46:19
nit: no ;
noyau (Ping after 24h)
2013/10/16 16:51:32
Done.
| |
315 | |
316 // Invoked from the destructor of the BookmarkModel. | |
sky
2013/10/16 13:46:19
Are these comments really helpful? We generally do
noyau (Ping after 24h)
2013/10/16 16:51:32
You are right, those were duplicated from the Book
| |
317 void BookmarkTagModel::BookmarkModelBeingDeleted(BookmarkModel* model) { | |
318 DCHECK(bookmark_model_); | |
319 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
320 BookmarkTagModelBeingDeleted(this)); | |
321 bookmark_model_ = NULL; | |
322 } | |
323 | |
324 // Invoked when a node has moved. | |
325 void BookmarkTagModel::BookmarkNodeMoved(BookmarkModel* model, | |
326 const BookmarkNode* old_parent, | |
327 int old_index, | |
328 const BookmarkNode* new_parent, | |
329 int new_index) { | |
330 DCHECK(loaded_); | |
331 const BookmarkNode* bookmark = new_parent->GetChild(new_index); | |
332 std::string encoded; | |
333 if (!bookmark->GetMetaInfo(TAG_KEY, &encoded)) { | |
334 // The bookmark moved and the system currently use its ancestors name as a | |
335 // poor approximation for tags. | |
336 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
337 OnWillChangeBookmarkTags(this, bookmark)); | |
338 RemoveBookmark(bookmark); | |
339 LoadBookmark(bookmark); | |
340 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
341 BookmarkTagsChanged(this, bookmark)); | |
342 } | |
343 } | |
344 | |
345 // Invoked when a node has been added. | |
346 void BookmarkTagModel::BookmarkNodeAdded(BookmarkModel* model, | |
347 const BookmarkNode* parent, | |
348 int index) { | |
349 DCHECK(loaded_); | |
350 const BookmarkNode* bookmark = parent->GetChild(index); | |
351 if (!bookmark->is_url()) | |
352 return; | |
353 LoadBookmark(bookmark); | |
354 | |
355 if (!inhibit_change_notifications_) | |
356 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
357 BookmarkNodeAdded(this, bookmark)); | |
358 } | |
359 | |
360 // Invoked before a node is removed. | |
361 // |parent| the parent of the node that will be removed. | |
362 // |old_index| the index of the node about to be removed in |parent|. | |
363 // |node| is the node to be removed. | |
364 void BookmarkTagModel::OnWillRemoveBookmarks(BookmarkModel* model, | |
365 const BookmarkNode* parent, | |
366 int old_index, | |
367 const BookmarkNode* node) { | |
368 DCHECK(loaded_); | |
369 RemoveBookmark(node); | |
370 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
371 OnWillRemoveBookmarks(this, node)); | |
372 } | |
373 | |
374 // Invoked when a node has been removed, the item may still be starred though. | |
375 // |parent| the parent of the node that was removed. | |
376 // |old_index| the index of the removed node in |parent| before it was | |
377 // removed. | |
378 // |node| is the node that was removed. | |
379 void BookmarkTagModel::BookmarkNodeRemoved(BookmarkModel* model, | |
380 const BookmarkNode* parent, | |
sky
2013/10/16 13:46:19
nit: indentation off.
noyau (Ping after 24h)
2013/10/16 16:51:32
Fixed all of them.
| |
381 int old_index, | |
382 const BookmarkNode* node) { | |
383 DCHECK(loaded_); | |
384 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
385 BookmarkNodeRemoved(this, node)); | |
386 } | |
387 | |
388 // Invoked before the title or url of a node is changed. | |
389 void BookmarkTagModel::OnWillChangeBookmarkNode(BookmarkModel* model, | |
390 const BookmarkNode* node) { | |
391 DCHECK(loaded_); | |
392 if (!inhibit_change_notifications_) | |
393 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
394 OnWillChangeBookmarkNode(this, node)); | |
395 } | |
396 | |
397 // Invoked when the title or url of a node changes. | |
398 void BookmarkTagModel::BookmarkNodeChanged(BookmarkModel* model, | |
399 const BookmarkNode* node) { | |
400 DCHECK(loaded_); | |
401 if (node->is_folder()) { | |
402 // A folder title changed. This may change the tags on all the descendants | |
sky
2013/10/16 13:46:19
Do you need to remove node too as iteration doesn'
noyau (Ping after 24h)
2013/10/16 16:51:32
There is no mapping kept for folder nodes, RemoveB
| |
403 // still using the default tag list of all ancestors. | |
404 ExtensiveChanges scoped(ExtensiveChanges(this)); | |
405 ui::TreeNodeIterator<const BookmarkNode> iterator(node); | |
406 while (iterator.has_next()) { | |
407 const BookmarkNode* bookmark = iterator.Next(); | |
408 RemoveBookmark(bookmark); | |
409 LoadBookmark(bookmark); | |
410 } | |
411 } else { | |
412 if (!inhibit_change_notifications_) | |
413 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
414 BookmarkNodeChanged(this, node)); | |
415 } | |
416 } | |
417 | |
418 // Invoked before the metainfo of a node is changed. | |
419 void BookmarkTagModel::OnWillChangeBookmarkMetaInfo(BookmarkModel* model, | |
420 const BookmarkNode* node) { | |
421 DCHECK(loaded_); | |
422 if (!inhibit_change_notifications_) | |
423 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
424 OnWillChangeBookmarkTags(this, node)); | |
425 } | |
426 | |
427 // Invoked when the metainfo on a node changes. | |
428 void BookmarkTagModel::BookmarkMetaInfoChanged(BookmarkModel* model, | |
429 const BookmarkNode* node) { | |
430 DCHECK(loaded_); | |
431 RemoveBookmark(node); | |
432 LoadBookmark(node); | |
433 if (!inhibit_change_notifications_) | |
434 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
435 BookmarkTagsChanged(this, node)); | |
436 } | |
437 | |
438 // Invoked when a favicon has been loaded or changed. | |
439 void BookmarkTagModel::BookmarkNodeFaviconChanged(BookmarkModel* model, | |
440 const BookmarkNode* node) { | |
441 DCHECK(loaded_); | |
442 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
443 BookmarkNodeFaviconChanged(this, node)); | |
444 } | |
445 | |
446 // Invoked before the direct children of |node| have been reordered in some | |
447 // way, such as sorted. | |
448 void BookmarkTagModel::OnWillReorderBookmarkNode(BookmarkModel* model, | |
449 const BookmarkNode* node) { | |
450 // This model doesn't care. | |
451 } | |
452 | |
453 // Invoked when the children (just direct children, not descendants) of | |
454 // |node| have been reordered in some way, such as sorted. | |
455 void BookmarkTagModel::BookmarkNodeChildrenReordered(BookmarkModel* model, | |
456 const BookmarkNode* node) { | |
457 // This model doesn't care. | |
458 } | |
459 | |
460 // Invoked before an extensive set of model changes is about to begin. | |
461 // This tells UI intensive observers to wait until the updates finish to | |
462 // update themselves. | |
463 // These methods should only be used for imports and sync. | |
464 // Observers should still respond to BookmarkNodeRemoved immediately, | |
465 // to avoid holding onto stale node pointers. | |
466 void BookmarkTagModel::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) { | |
467 DCHECK(loaded_); | |
468 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
469 ExtensiveBookmarkChangesBeginning(this)); | |
470 } | |
471 | |
472 // Invoked after an extensive set of model changes has ended. | |
473 // This tells observers to update themselves if they were waiting for the | |
474 // update to finish. | |
475 void BookmarkTagModel::ExtensiveBookmarkChangesEnded(BookmarkModel* model) { | |
476 DCHECK(loaded_); | |
477 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
478 ExtensiveBookmarkChangesEnded(this)); | |
479 } | |
480 | |
481 // Invoked before all non-permanent bookmark nodes are removed. | |
482 void BookmarkTagModel::OnWillRemoveAllBookmarks(BookmarkModel* model) { | |
483 DCHECK(loaded_); | |
484 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
485 OnWillRemoveAllBookmarks(this)); | |
486 } | |
487 | |
488 // Invoked when all non-permanent bookmark nodes have been removed. | |
489 void BookmarkTagModel::BookmarkAllNodesRemoved(BookmarkModel* model){ | |
490 DCHECK(loaded_); | |
491 tag_to_bookmarks_.clear(); | |
492 bookmark_to_tags_.clear(); | |
493 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
494 BookmarkAllNodesRemoved(this)); | |
495 } | |
496 | |
497 // Private methods. | |
498 | |
499 void BookmarkTagModel::SetTagsOnBookmark( | |
500 const std::set<BookmarkTag>& tags, const BookmarkNode* bookmark) { | |
501 DCHECK(bookmark_model_); | |
502 DCHECK(loaded_); | |
503 | |
504 // Build a ListValue. | |
505 std::vector<BookmarkTag> tag_vector(tags.begin(), tags.end()); | |
506 base::ListValue list; | |
507 list.AppendStrings(tag_vector); | |
508 | |
509 // Encodes it. | |
510 std::string encoded; | |
511 JSONStringValueSerializer serializer(&encoded); | |
512 | |
513 // Pushes it in the bookmark's metainfo. Even if the tag list is empty the | |
514 // empty list must be put on the node to avoid reverting to the tag list | |
515 // derived from the hierarchy. | |
516 // The internal caches of the BookmarkTagModel are updated when the | |
517 // notification from the BookmarkModel is received. | |
518 serializer.Serialize(list); | |
519 bookmark_model_->SetNodeMetaInfo(bookmark, TAG_KEY, encoded); | |
520 } | |
521 | |
522 void BookmarkTagModel::Load() { | |
523 DCHECK(bookmark_model_); | |
524 DCHECK(!loaded_); | |
525 ui::TreeNodeIterator<const BookmarkNode> iterator( | |
526 bookmark_model_->root_node()); | |
527 while (iterator.has_next()) | |
528 LoadBookmark(iterator.Next()); | |
529 loaded_ = true; | |
530 FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_, | |
531 Loaded(this)); | |
532 } | |
533 | |
534 void BookmarkTagModel::LoadBookmark(const BookmarkNode* bookmark) { | |
535 DCHECK(bookmark_model_); | |
536 if (bookmark->is_url()) { | |
537 std::set<BookmarkTag> tags(ExtractTagsFromBookmark(bookmark)); | |
538 | |
539 bookmark_to_tags_[bookmark] = tags; | |
540 for (std::set<BookmarkTag>::iterator it = tags.begin(); | |
541 it != tags.end(); ++it) { | |
542 tag_to_bookmarks_[*it].insert(bookmark); | |
543 } | |
544 } | |
545 } | |
546 | |
547 void BookmarkTagModel::RemoveBookmark(const BookmarkNode* bookmark) { | |
548 DCHECK(bookmark_model_); | |
549 if (bookmark->is_url()) { | |
550 std::set<BookmarkTag> tags(bookmark_to_tags_[bookmark]); | |
551 bookmark_to_tags_.erase(bookmark); | |
552 | |
553 for (std::set<BookmarkTag>::iterator it = tags.begin(); | |
554 it != tags.end(); ++it) { | |
555 tag_to_bookmarks_[*it].erase(bookmark); | |
556 // Remove the tags no longer used. | |
557 if (!tag_to_bookmarks_[*it].size()) | |
558 tag_to_bookmarks_.erase(*it); | |
559 } | |
560 } | |
561 } | |
OLD | NEW |