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. 4 5 #include "components/bookmarks/browser/bookmark_model.h" 6 7 #include <algorithm> 8 #include <functional> 9 10 #include "base/bind.h" 11 #include "base/bind_helpers.h" 12 #include "base/i18n/string_compare.h" 13 #include "base/logging.h" 14 #include "base/macros.h" 15 #include "base/strings/string_util.h" 16 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h" 17 #include "components/bookmarks/browser/bookmark_index.h" 18 #include "components/bookmarks/browser/bookmark_match.h" 19 #include "components/bookmarks/browser/bookmark_model_observer.h" 20 #include "components/bookmarks/browser/bookmark_node_data.h" 21 #include "components/bookmarks/browser/bookmark_storage.h" 22 #include "components/bookmarks/browser/bookmark_utils.h" 23 #include "components/favicon_base/favicon_types.h" 24 #include "grit/components_strings.h" 25 #include "ui/base/l10n/l10n_util.h" 26 #include "ui/gfx/favicon_size.h" 27 28 using base::Time; 29 using bookmarks::BookmarkExpandedStateTracker; 30 using bookmarks::BookmarkIndex; 31 using bookmarks::BookmarkLoadDetails; 32 using bookmarks::BookmarkMatch; 33 using bookmarks::BookmarkStorage; 34 35 namespace { 36 37 // Helper to get a mutable bookmark node. 38 BookmarkNode* AsMutable(const BookmarkNode* node) { 39 return const_cast<BookmarkNode*>(node); 40 } 41 42 // Helper to get a mutable permanent bookmark node. 43 BookmarkPermanentNode* AsMutable(const BookmarkPermanentNode* node) { 44 return const_cast<BookmarkPermanentNode*>(node); 45 } 46 47 // Comparator used when sorting permanent nodes. Nodes that are initially 48 // visible are sorted before nodes that are initially hidden. 49 class VisibilityComparator 50 : public std::binary_function<const BookmarkPermanentNode*, 51 const BookmarkPermanentNode*, 52 bool> { 53 public: 54 explicit VisibilityComparator(BookmarkClient* client) : client_(client) {} 55 56 // Returns true if |n1| preceeds |n2|. 57 bool operator()(const BookmarkPermanentNode* n1, 58 const BookmarkPermanentNode* n2) { 59 bool n1_visible = client_->IsPermanentNodeVisible(n1); 60 bool n2_visible = client_->IsPermanentNodeVisible(n2); 61 return n1_visible != n2_visible && n1_visible; 62 } 63 64 private: 65 BookmarkClient* client_; 66 }; 67 68 // Comparator used when sorting bookmarks. Folders are sorted first, then 69 // bookmarks. 70 class SortComparator : public std::binary_function<const BookmarkNode*, 71 const BookmarkNode*, 72 bool> { 73 public: 74 explicit SortComparator(icu::Collator* collator) : collator_(collator) {} 75 76 // Returns true if |n1| preceeds |n2|. 77 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) { 78 if (n1->type() == n2->type()) { 79 // Types are the same, compare the names. 80 if (!collator_) 81 return n1->GetTitle() < n2->GetTitle(); 82 return base::i18n::CompareString16WithCollator( 83 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS; 84 } 85 // Types differ, sort such that folders come first. 86 return n1->is_folder(); 87 } 88 89 private: 90 icu::Collator* collator_; 91 }; 92 93 } // namespace 94 95 // BookmarkModel -------------------------------------------------------------- 96 97 BookmarkModel::BookmarkModel(BookmarkClient* client, bool index_urls) 98 : client_(client), 99 loaded_(false), 100 root_(GURL()), 101 bookmark_bar_node_(NULL), 102 other_node_(NULL), 103 mobile_node_(NULL), 104 next_node_id_(1), 105 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY), 106 index_urls_(index_urls), 107 loaded_signal_(true, false), 108 extensive_changes_(0) { 109 DCHECK(client_); 110 } 111 112 BookmarkModel::~BookmarkModel() { 113 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 114 BookmarkModelBeingDeleted(this)); 115 116 if (store_.get()) { 117 // The store maintains a reference back to us. We need to tell it we're gone 118 // so that it doesn't try and invoke a method back on us again. 119 store_->BookmarkModelDeleted(); 120 } 121 } 122 123 void BookmarkModel::Shutdown() { 124 if (loaded_) 125 return; 126 127 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for 128 // details. It is also called when the BookmarkModel is deleted. 129 loaded_signal_.Signal(); 130 } 131 132 void BookmarkModel::Load( 133 PrefService* pref_service, 134 const std::string& accept_languages, 135 const base::FilePath& profile_path, 136 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner, 137 const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner) { 138 if (store_.get()) { 139 // If the store is non-null, it means Load was already invoked. Load should 140 // only be invoked once. 141 NOTREACHED(); 142 return; 143 } 144 145 expanded_state_tracker_.reset( 146 new BookmarkExpandedStateTracker(this, pref_service)); 147 148 // Load the bookmarks. BookmarkStorage notifies us when done. 149 store_ = new BookmarkStorage(this, profile_path, io_task_runner.get()); 150 store_->LoadBookmarks(CreateLoadDetails(accept_languages), ui_task_runner); 151 } 152 153 const BookmarkNode* BookmarkModel::GetParentForNewNodes() { 154 std::vector<const BookmarkNode*> nodes = 155 bookmark_utils::GetMostRecentlyModifiedUserFolders(this, 1); 156 DCHECK(!nodes.empty()); // This list is always padded with default folders. 157 return nodes[0]; 158 } 159 160 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) { 161 observers_.AddObserver(observer); 162 } 163 164 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) { 165 observers_.RemoveObserver(observer); 166 } 167 168 void BookmarkModel::BeginExtensiveChanges() { 169 if (++extensive_changes_ == 1) { 170 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 171 ExtensiveBookmarkChangesBeginning(this)); 172 } 173 } 174 175 void BookmarkModel::EndExtensiveChanges() { 176 --extensive_changes_; 177 DCHECK_GE(extensive_changes_, 0); 178 if (extensive_changes_ == 0) { 179 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 180 ExtensiveBookmarkChangesEnded(this)); 181 } 182 } 183 184 void BookmarkModel::BeginGroupedChanges() { 185 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 186 GroupedBookmarkChangesBeginning(this)); 187 } 188 189 void BookmarkModel::EndGroupedChanges() { 190 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 191 GroupedBookmarkChangesEnded(this)); 192 } 193 194 void BookmarkModel::Remove(const BookmarkNode* parent, int index) { 195 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) { 196 NOTREACHED(); 197 return; 198 } 199 RemoveAndDeleteNode(AsMutable(parent->GetChild(index))); 200 } 201 202 void BookmarkModel::RemoveAllUserBookmarks() { 203 std::set<GURL> removed_urls; 204 ScopedVector<BookmarkNode> removed_nodes; 205 206 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 207 OnWillRemoveAllUserBookmarks(this)); 208 209 BeginExtensiveChanges(); 210 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and 211 // its immediate children. For removing all non permanent nodes just remove 212 // all children of non-root permanent nodes. 213 { 214 base::AutoLock url_lock(url_lock_); 215 for (int i = 0; i < root_.child_count(); ++i) { 216 BookmarkNode* permanent_node = root_.GetChild(i); 217 218 if (!client_->CanBeEditedByUser(permanent_node)) 219 continue; 220 221 for (int j = permanent_node->child_count() - 1; j >= 0; --j) { 222 BookmarkNode* child_node = permanent_node->GetChild(j); 223 removed_nodes.push_back(child_node); 224 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls); 225 } 226 } 227 } 228 EndExtensiveChanges(); 229 if (store_.get()) 230 store_->ScheduleSave(); 231 232 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 233 BookmarkAllUserNodesRemoved(this, removed_urls)); 234 } 235 236 void BookmarkModel::Move(const BookmarkNode* node, 237 const BookmarkNode* new_parent, 238 int index) { 239 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) || 240 is_root_node(new_parent) || is_permanent_node(node)) { 241 NOTREACHED(); 242 return; 243 } 244 245 if (new_parent->HasAncestor(node)) { 246 // Can't make an ancestor of the node be a child of the node. 247 NOTREACHED(); 248 return; 249 } 250 251 const BookmarkNode* old_parent = node->parent(); 252 int old_index = old_parent->GetIndexOf(node); 253 254 if (old_parent == new_parent && 255 (index == old_index || index == old_index + 1)) { 256 // Node is already in this position, nothing to do. 257 return; 258 } 259 260 SetDateFolderModified(new_parent, Time::Now()); 261 262 if (old_parent == new_parent && index > old_index) 263 index--; 264 BookmarkNode* mutable_new_parent = AsMutable(new_parent); 265 mutable_new_parent->Add(AsMutable(node), index); 266 267 if (store_.get()) 268 store_->ScheduleSave(); 269 270 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 271 BookmarkNodeMoved(this, old_parent, old_index, 272 new_parent, index)); 273 } 274 275 void BookmarkModel::Copy(const BookmarkNode* node, 276 const BookmarkNode* new_parent, 277 int index) { 278 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) || 279 is_root_node(new_parent) || is_permanent_node(node)) { 280 NOTREACHED(); 281 return; 282 } 283 284 if (new_parent->HasAncestor(node)) { 285 // Can't make an ancestor of the node be a child of the node. 286 NOTREACHED(); 287 return; 288 } 289 290 SetDateFolderModified(new_parent, Time::Now()); 291 BookmarkNodeData drag_data(node); 292 std::vector<BookmarkNodeData::Element> elements(drag_data.elements); 293 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we 294 // don't need to send notifications here. 295 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index, true); 296 297 if (store_.get()) 298 store_->ScheduleSave(); 299 } 300 301 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) { 302 DCHECK(node); 303 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) { 304 BookmarkNode* mutable_node = AsMutable(node); 305 LoadFavicon( 306 mutable_node, 307 client_->PreferTouchIcon() ? 308 favicon_base::TOUCH_ICON : 309 favicon_base::FAVICON); 310 } 311 return node->favicon(); 312 } 313 314 favicon_base::IconType BookmarkModel::GetFaviconType(const BookmarkNode* node) { 315 DCHECK(node); 316 return node->favicon_type(); 317 } 318 319 void BookmarkModel::SetTitle(const BookmarkNode* node, 320 const base::string16& title) { 321 if (!node) { 322 NOTREACHED(); 323 return; 324 } 325 if (node->GetTitle() == title) 326 return; 327 328 if (is_permanent_node(node) && !client_->CanSetPermanentNodeTitle(node)) { 329 NOTREACHED(); 330 return; 331 } 332 333 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 334 OnWillChangeBookmarkNode(this, node)); 335 336 // The title index doesn't support changing the title, instead we remove then 337 // add it back. 338 index_->Remove(node); 339 AsMutable(node)->SetTitle(title); 340 index_->Add(node); 341 342 if (store_.get()) 343 store_->ScheduleSave(); 344 345 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 346 BookmarkNodeChanged(this, node)); 347 } 348 349 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) { 350 if (!node) { 351 NOTREACHED(); 352 return; 353 } 354 355 // We cannot change the URL of a folder. 356 if (node->is_folder()) { 357 NOTREACHED(); 358 return; 359 } 360 361 if (node->url() == url) 362 return; 363 364 BookmarkNode* mutable_node = AsMutable(node); 365 mutable_node->InvalidateFavicon(); 366 CancelPendingFaviconLoadRequests(mutable_node); 367 368 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 369 OnWillChangeBookmarkNode(this, node)); 370 371 { 372 base::AutoLock url_lock(url_lock_); 373 RemoveNodeFromURLSet(mutable_node); 374 mutable_node->set_url(url); 375 nodes_ordered_by_url_set_.insert(mutable_node); 376 } 377 378 if (store_.get()) 379 store_->ScheduleSave(); 380 381 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 382 BookmarkNodeChanged(this, node)); 383 } 384 385 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node, 386 const std::string& key, 387 const std::string& value) { 388 std::string old_value; 389 if (node->GetMetaInfo(key, &old_value) && old_value == value) 390 return; 391 392 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 393 OnWillChangeBookmarkMetaInfo(this, node)); 394 395 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get()) 396 store_->ScheduleSave(); 397 398 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 399 BookmarkMetaInfoChanged(this, node)); 400 } 401 402 void BookmarkModel::SetNodeMetaInfoMap( 403 const BookmarkNode* node, 404 const BookmarkNode::MetaInfoMap& meta_info_map) { 405 const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap(); 406 if ((!old_meta_info_map && meta_info_map.empty()) || 407 (old_meta_info_map && meta_info_map == *old_meta_info_map)) 408 return; 409 410 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 411 OnWillChangeBookmarkMetaInfo(this, node)); 412 413 AsMutable(node)->SetMetaInfoMap(meta_info_map); 414 if (store_.get()) 415 store_->ScheduleSave(); 416 417 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 418 BookmarkMetaInfoChanged(this, node)); 419 } 420 421 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node, 422 const std::string& key) { 423 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap(); 424 if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end()) 425 return; 426 427 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 428 OnWillChangeBookmarkMetaInfo(this, node)); 429 430 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get()) 431 store_->ScheduleSave(); 432 433 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 434 BookmarkMetaInfoChanged(this, node)); 435 } 436 437 void BookmarkModel::SetNodeSyncTransactionVersion( 438 const BookmarkNode* node, 439 int64 sync_transaction_version) { 440 DCHECK(client_->CanSyncNode(node)); 441 442 if (sync_transaction_version == node->sync_transaction_version()) 443 return; 444 445 AsMutable(node)->set_sync_transaction_version(sync_transaction_version); 446 if (store_.get()) 447 store_->ScheduleSave(); 448 } 449 450 void BookmarkModel::OnFaviconChanged(const std::set<GURL>& urls) { 451 // Ignore events if |Load| has not been called yet. 452 if (!store_) 453 return; 454 455 // Prevent the observers from getting confused for multiple favicon loads. 456 for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) { 457 std::vector<const BookmarkNode*> nodes; 458 GetNodesByURL(*i, &nodes); 459 for (size_t i = 0; i < nodes.size(); ++i) { 460 // Got an updated favicon, for a URL, do a new request. 461 BookmarkNode* node = AsMutable(nodes[i]); 462 node->InvalidateFavicon(); 463 CancelPendingFaviconLoadRequests(node); 464 FOR_EACH_OBSERVER(BookmarkModelObserver, 465 observers_, 466 BookmarkNodeFaviconChanged(this, node)); 467 } 468 } 469 } 470 471 void BookmarkModel::SetDateAdded(const BookmarkNode* node, 472 Time date_added) { 473 if (!node) { 474 NOTREACHED(); 475 return; 476 } 477 478 if (node->date_added() == date_added) 479 return; 480 481 if (is_permanent_node(node)) { 482 NOTREACHED(); 483 return; 484 } 485 486 AsMutable(node)->set_date_added(date_added); 487 488 // Syncing might result in dates newer than the folder's last modified date. 489 if (date_added > node->parent()->date_folder_modified()) { 490 // Will trigger store_->ScheduleSave(). 491 SetDateFolderModified(node->parent(), date_added); 492 } else if (store_.get()) { 493 store_->ScheduleSave(); 494 } 495 } 496 497 void BookmarkModel::GetNodesByURL(const GURL& url, 498 std::vector<const BookmarkNode*>* nodes) { 499 base::AutoLock url_lock(url_lock_); 500 BookmarkNode tmp_node(url); 501 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node); 502 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) { 503 nodes->push_back(*i); 504 ++i; 505 } 506 } 507 508 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedUserNodeForURL( 509 const GURL& url) { 510 std::vector<const BookmarkNode*> nodes; 511 GetNodesByURL(url, &nodes); 512 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded); 513 514 // Look for the first node that the user can edit. 515 for (size_t i = 0; i < nodes.size(); ++i) { 516 if (client_->CanBeEditedByUser(nodes[i])) 517 return nodes[i]; 518 } 519 520 return NULL; 521 } 522 523 bool BookmarkModel::HasBookmarks() { 524 base::AutoLock url_lock(url_lock_); 525 return !nodes_ordered_by_url_set_.empty(); 526 } 527 528 bool BookmarkModel::IsBookmarked(const GURL& url) { 529 base::AutoLock url_lock(url_lock_); 530 return IsBookmarkedNoLock(url); 531 } 532 533 void BookmarkModel::GetBookmarks( 534 std::vector<BookmarkModel::URLAndTitle>* bookmarks) { 535 base::AutoLock url_lock(url_lock_); 536 const GURL* last_url = NULL; 537 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin(); 538 i != nodes_ordered_by_url_set_.end(); ++i) { 539 const GURL* url = &((*i)->url()); 540 // Only add unique URLs. 541 if (!last_url || *url != *last_url) { 542 BookmarkModel::URLAndTitle bookmark; 543 bookmark.url = *url; 544 bookmark.title = (*i)->GetTitle(); 545 bookmarks->push_back(bookmark); 546 } 547 last_url = url; 548 } 549 } 550 551 void BookmarkModel::BlockTillLoaded() { 552 loaded_signal_.Wait(); 553 } 554 555 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent, 556 int index, 557 const base::string16& title) { 558 return AddFolderWithMetaInfo(parent, index, title, NULL); 559 } 560 const BookmarkNode* BookmarkModel::AddFolderWithMetaInfo( 561 const BookmarkNode* parent, 562 int index, 563 const base::string16& title, 564 const BookmarkNode::MetaInfoMap* meta_info) { 565 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) { 566 // Can't add to the root. 567 NOTREACHED(); 568 return NULL; 569 } 570 571 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL()); 572 new_node->set_date_folder_modified(Time::Now()); 573 // Folders shouldn't have line breaks in their titles. 574 new_node->SetTitle(title); 575 new_node->set_type(BookmarkNode::FOLDER); 576 if (meta_info) 577 new_node->SetMetaInfoMap(*meta_info); 578 579 return AddNode(AsMutable(parent), index, new_node); 580 } 581 582 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent, 583 int index, 584 const base::string16& title, 585 const GURL& url) { 586 return AddURLWithCreationTimeAndMetaInfo( 587 parent, 588 index, 589 base::CollapseWhitespace(title, false), 590 url, 591 Time::Now(), 592 NULL); 593 } 594 595 const BookmarkNode* BookmarkModel::AddURLWithCreationTimeAndMetaInfo( 596 const BookmarkNode* parent, 597 int index, 598 const base::string16& title, 599 const GURL& url, 600 const Time& creation_time, 601 const BookmarkNode::MetaInfoMap* meta_info) { 602 if (!loaded_ || !url.is_valid() || is_root_node(parent) || 603 !IsValidIndex(parent, index, true)) { 604 NOTREACHED(); 605 return NULL; 606 } 607 608 // Syncing may result in dates newer than the last modified date. 609 if (creation_time > parent->date_folder_modified()) 610 SetDateFolderModified(parent, creation_time); 611 612 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url); 613 new_node->SetTitle(title); 614 new_node->set_date_added(creation_time); 615 new_node->set_type(BookmarkNode::URL); 616 if (meta_info) 617 new_node->SetMetaInfoMap(*meta_info); 618 619 { 620 // Only hold the lock for the duration of the insert. 621 base::AutoLock url_lock(url_lock_); 622 nodes_ordered_by_url_set_.insert(new_node); 623 } 624 625 return AddNode(AsMutable(parent), index, new_node); 626 } 627 628 void BookmarkModel::SortChildren(const BookmarkNode* parent) { 629 DCHECK(client_->CanBeEditedByUser(parent)); 630 631 if (!parent || !parent->is_folder() || is_root_node(parent) || 632 parent->child_count() <= 1) { 633 return; 634 } 635 636 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 637 OnWillReorderBookmarkNode(this, parent)); 638 639 UErrorCode error = U_ZERO_ERROR; 640 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error)); 641 if (U_FAILURE(error)) 642 collator.reset(NULL); 643 BookmarkNode* mutable_parent = AsMutable(parent); 644 std::sort(mutable_parent->children().begin(), 645 mutable_parent->children().end(), 646 SortComparator(collator.get())); 647 648 if (store_.get()) 649 store_->ScheduleSave(); 650 651 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 652 BookmarkNodeChildrenReordered(this, parent)); 653 } 654 655 void BookmarkModel::ReorderChildren( 656 const BookmarkNode* parent, 657 const std::vector<const BookmarkNode*>& ordered_nodes) { 658 DCHECK(client_->CanBeEditedByUser(parent)); 659 660 // Ensure that all children in |parent| are in |ordered_nodes|. 661 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size()); 662 for (size_t i = 0; i < ordered_nodes.size(); ++i) 663 DCHECK_EQ(parent, ordered_nodes[i]->parent()); 664 665 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 666 OnWillReorderBookmarkNode(this, parent)); 667 668 AsMutable(parent)->SetChildren( 669 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes))); 670 671 if (store_.get()) 672 store_->ScheduleSave(); 673 674 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 675 BookmarkNodeChildrenReordered(this, parent)); 676 } 677 678 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent, 679 const Time time) { 680 DCHECK(parent); 681 AsMutable(parent)->set_date_folder_modified(time); 682 683 if (store_.get()) 684 store_->ScheduleSave(); 685 } 686 687 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) { 688 SetDateFolderModified(node, Time()); 689 } 690 691 void BookmarkModel::GetBookmarksMatching( 692 const base::string16& text, 693 size_t max_count, 694 std::vector<BookmarkMatch>* matches) { 695 if (!loaded_) 696 return; 697 698 index_->GetBookmarksMatching(text, max_count, matches); 699 } 700 701 void BookmarkModel::ClearStore() { 702 store_ = NULL; 703 } 704 705 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type, 706 bool value) { 707 BookmarkPermanentNode* node = AsMutable(PermanentNode(type)); 708 node->set_visible(value || client_->IsPermanentNodeVisible(node)); 709 } 710 711 const BookmarkPermanentNode* BookmarkModel::PermanentNode( 712 BookmarkNode::Type type) { 713 DCHECK(loaded_); 714 switch (type) { 715 case BookmarkNode::BOOKMARK_BAR: 716 return bookmark_bar_node_; 717 case BookmarkNode::OTHER_NODE: 718 return other_node_; 719 case BookmarkNode::MOBILE: 720 return mobile_node_; 721 default: 722 NOTREACHED(); 723 return NULL; 724 } 725 } 726 727 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) { 728 BookmarkNode tmp_node(url); 729 return (nodes_ordered_by_url_set_.find(&tmp_node) != 730 nodes_ordered_by_url_set_.end()); 731 } 732 733 void BookmarkModel::RemoveNode(BookmarkNode* node, 734 std::set<GURL>* removed_urls) { 735 if (!loaded_ || !node || is_permanent_node(node)) { 736 NOTREACHED(); 737 return; 738 } 739 740 url_lock_.AssertAcquired(); 741 if (node->is_url()) { 742 RemoveNodeFromURLSet(node); 743 removed_urls->insert(node->url()); 744 index_->Remove(node); 745 } 746 747 CancelPendingFaviconLoadRequests(node); 748 749 // Recurse through children. 750 for (int i = node->child_count() - 1; i >= 0; --i) 751 RemoveNode(node->GetChild(i), removed_urls); 752 } 753 754 void BookmarkModel::DoneLoading(scoped_ptr<BookmarkLoadDetails> details) { 755 DCHECK(details); 756 if (loaded_) { 757 // We should only ever be loaded once. 758 NOTREACHED(); 759 return; 760 } 761 762 next_node_id_ = details->max_id(); 763 if (details->computed_checksum() != details->stored_checksum() || 764 details->ids_reassigned()) { 765 // If bookmarks file changed externally, the IDs may have changed 766 // externally. In that case, the decoder may have reassigned IDs to make 767 // them unique. So when the file has changed externally, we should save the 768 // bookmarks file to persist new IDs. 769 if (store_.get()) 770 store_->ScheduleSave(); 771 } 772 bookmark_bar_node_ = details->release_bb_node(); 773 other_node_ = details->release_other_folder_node(); 774 mobile_node_ = details->release_mobile_folder_node(); 775 index_.reset(details->release_index()); 776 777 // Get any extra nodes and take ownership of them at the |root_|. 778 std::vector<BookmarkPermanentNode*> extra_nodes; 779 details->release_extra_nodes(&extra_nodes); 780 781 // WARNING: order is important here, various places assume the order is 782 // constant (but can vary between embedders with the initial visibility 783 // of permanent nodes). 784 std::vector<BookmarkPermanentNode*> root_children; 785 root_children.push_back(bookmark_bar_node_); 786 root_children.push_back(other_node_); 787 root_children.push_back(mobile_node_); 788 for (size_t i = 0; i < extra_nodes.size(); ++i) 789 root_children.push_back(extra_nodes[i]); 790 std::stable_sort(root_children.begin(), 791 root_children.end(), 792 VisibilityComparator(client_)); 793 for (size_t i = 0; i < root_children.size(); ++i) 794 root_.Add(root_children[i], static_cast<int>(i)); 795 796 root_.SetMetaInfoMap(details->model_meta_info_map()); 797 root_.set_sync_transaction_version(details->model_sync_transaction_version()); 798 799 { 800 base::AutoLock url_lock(url_lock_); 801 // Update nodes_ordered_by_url_set_ from the nodes. 802 PopulateNodesByURL(&root_); 803 } 804 805 loaded_ = true; 806 807 loaded_signal_.Signal(); 808 809 // Notify our direct observers. 810 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 811 BookmarkModelLoaded(this, details->ids_reassigned())); 812 } 813 814 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) { 815 scoped_ptr<BookmarkNode> node(delete_me); 816 817 const BookmarkNode* parent = node->parent(); 818 DCHECK(parent); 819 int index = parent->GetIndexOf(node.get()); 820 821 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 822 OnWillRemoveBookmarks(this, parent, index, node.get())); 823 824 std::set<GURL> removed_urls; 825 { 826 base::AutoLock url_lock(url_lock_); 827 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls); 828 } 829 830 if (store_.get()) 831 store_->ScheduleSave(); 832 833 FOR_EACH_OBSERVER( 834 BookmarkModelObserver, 835 observers_, 836 BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls)); 837 } 838 839 void BookmarkModel::RemoveNodeFromURLSet(BookmarkNode* node) { 840 // NOTE: this is called in such a way that url_lock_ is already held. As 841 // such, this doesn't explicitly grab the lock. 842 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node); 843 DCHECK(i != nodes_ordered_by_url_set_.end()); 844 // i points to the first node with the URL, advance until we find the 845 // node we're removing. 846 while (*i != node) 847 ++i; 848 nodes_ordered_by_url_set_.erase(i); 849 } 850 851 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node, 852 std::set<GURL>* removed_urls) { 853 // NOTE: this method should be always called with |url_lock_| held. 854 // This method does not explicitly acquires a lock. 855 url_lock_.AssertAcquired(); 856 DCHECK(removed_urls); 857 BookmarkNode* parent = AsMutable(node->parent()); 858 DCHECK(parent); 859 parent->Remove(node); 860 RemoveNode(node, removed_urls); 861 // RemoveNode adds an entry to removed_urls for each node of type URL. As we 862 // allow duplicates we need to remove any entries that are still bookmarked. 863 for (std::set<GURL>::iterator i = removed_urls->begin(); 864 i != removed_urls->end();) { 865 if (IsBookmarkedNoLock(*i)) { 866 // When we erase the iterator pointing at the erasee is 867 // invalidated, so using i++ here within the "erase" call is 868 // important as it advances the iterator before passing the 869 // old value through to erase. 870 removed_urls->erase(i++); 871 } else { 872 ++i; 873 } 874 } 875 } 876 877 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent, 878 int index, 879 BookmarkNode* node) { 880 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 881 OnWillAddBookmarkNode(this, node)); 882 883 parent->Add(node, index); 884 885 if (store_.get()) 886 store_->ScheduleSave(); 887 888 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 889 BookmarkNodeAdded(this, parent, index)); 890 891 index_->Add(node); 892 893 return node; 894 } 895 896 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent, 897 int index, 898 bool allow_end) { 899 return (parent && parent->is_folder() && 900 (index >= 0 && (index < parent->child_count() || 901 (allow_end && index == parent->child_count())))); 902 } 903 904 BookmarkPermanentNode* BookmarkModel::CreatePermanentNode( 905 BookmarkNode::Type type) { 906 DCHECK(type == BookmarkNode::BOOKMARK_BAR || 907 type == BookmarkNode::OTHER_NODE || 908 type == BookmarkNode::MOBILE); 909 BookmarkPermanentNode* node = 910 new BookmarkPermanentNode(generate_next_node_id()); 911 node->set_type(type); 912 node->set_visible(client_->IsPermanentNodeVisible(node)); 913 914 int title_id; 915 switch (type) { 916 case BookmarkNode::BOOKMARK_BAR: 917 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME; 918 break; 919 case BookmarkNode::OTHER_NODE: 920 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME; 921 break; 922 case BookmarkNode::MOBILE: 923 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME; 924 break; 925 default: 926 NOTREACHED(); 927 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME; 928 break; 929 } 930 node->SetTitle(l10n_util::GetStringUTF16(title_id)); 931 return node; 932 } 933 934 void BookmarkModel::OnFaviconDataAvailable( 935 BookmarkNode* node, 936 favicon_base::IconType icon_type, 937 const favicon_base::FaviconImageResult& image_result) { 938 DCHECK(node); 939 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId); 940 node->set_favicon_state(BookmarkNode::LOADED_FAVICON); 941 if (!image_result.image.IsEmpty()) { 942 node->set_favicon_type(icon_type); 943 node->set_favicon(image_result.image); 944 node->set_icon_url(image_result.icon_url); 945 FaviconLoaded(node); 946 } else if (icon_type == favicon_base::TOUCH_ICON) { 947 // Couldn't load the touch icon, fallback to the regular favicon. 948 DCHECK(client_->PreferTouchIcon()); 949 LoadFavicon(node, favicon_base::FAVICON); 950 } 951 } 952 953 void BookmarkModel::LoadFavicon( 954 BookmarkNode* node, 955 favicon_base::IconType icon_type) { 956 if (node->is_folder()) 957 return; 958 959 DCHECK(node->url().is_valid()); 960 node->set_favicon_state(BookmarkNode::LOADING_FAVICON); 961 base::CancelableTaskTracker::TaskId taskId = client_->GetFaviconImageForURL( 962 node->url(), 963 icon_type, 964 icon_type == favicon_base::FAVICON ? gfx::kFaviconSize : 0, 965 base::Bind( 966 &BookmarkModel::OnFaviconDataAvailable, 967 base::Unretained(this), 968 node, 969 icon_type), 970 &cancelable_task_tracker_); 971 if (taskId != base::CancelableTaskTracker::kBadTaskId) 972 node->set_favicon_load_task_id(taskId); 973 } 974 975 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) { 976 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 977 BookmarkNodeFaviconChanged(this, node)); 978 } 979 980 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) { 981 if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) { 982 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id()); 983 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId); 984 } 985 } 986 987 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) { 988 // NOTE: this is called with url_lock_ already held. As such, this doesn't 989 // explicitly grab the lock. 990 if (node->is_url()) 991 nodes_ordered_by_url_set_.insert(node); 992 for (int i = 0; i < node->child_count(); ++i) 993 PopulateNodesByURL(node->GetChild(i)); 994 } 995 996 int64 BookmarkModel::generate_next_node_id() { 997 return next_node_id_++; 998 } 999 1000 scoped_ptr<BookmarkLoadDetails> BookmarkModel::CreateLoadDetails( 1001 const std::string& accept_languages) { 1002 BookmarkPermanentNode* bb_node = 1003 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR); 1004 BookmarkPermanentNode* other_node = 1005 CreatePermanentNode(BookmarkNode::OTHER_NODE); 1006 BookmarkPermanentNode* mobile_node = 1007 CreatePermanentNode(BookmarkNode::MOBILE); 1008 return scoped_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails( 1009 bb_node, 1010 other_node, 1011 mobile_node, 1012 client_->GetLoadExtraNodesCallback(), 1013 new BookmarkIndex(client_, index_urls_, accept_languages), 1014 next_node_id_)); 1015 } 1016