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