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