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