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.
5 #include "chrome/browser/bookmarks/bookmark_model.h"
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"
41 // Helper to get a mutable bookmark node.
42 BookmarkNode* AsMutable(const BookmarkNode* node) {
43 return const_cast<BookmarkNode*>(node);
46 // Whitespace characters to strip from bookmark titles.
47 const base::char16 kInvalidChars[] = {
49 0x2028, // Line separator
50 0x2029, // Paragraph separator
56 // BookmarkNode ---------------------------------------------------------------
58 const int64 BookmarkNode::kInvalidSyncTransactionVersion = -1;
60 BookmarkNode::BookmarkNode(const GURL& url)
65 BookmarkNode::BookmarkNode(int64 id, const GURL& url)
70 BookmarkNode::~BookmarkNode() {
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, base::ASCIIToUTF16(" "),
79 ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
82 bool BookmarkNode::IsVisible() const {
86 bool BookmarkNode::GetMetaInfo(const std::string& key,
87 std::string* value) const {
91 MetaInfoMap::const_iterator it = meta_info_map_->find(key);
92 if (it == meta_info_map_->end())
99 bool BookmarkNode::SetMetaInfo(const std::string& key,
100 const std::string& value) {
102 meta_info_map_.reset(new MetaInfoMap);
104 MetaInfoMap::iterator it = meta_info_map_->find(key);
105 if (it == meta_info_map_->end()) {
106 (*meta_info_map_)[key] = value;
109 // Key already in map, check if the value has changed.
110 if (it->second == value)
116 bool BookmarkNode::DeleteMetaInfo(const std::string& key) {
119 bool erased = meta_info_map_->erase(key) != 0;
120 if (meta_info_map_->empty())
121 meta_info_map_.reset();
125 void BookmarkNode::SetMetaInfoMap(const MetaInfoMap& meta_info_map) {
126 if (meta_info_map.empty())
127 meta_info_map_.reset();
129 meta_info_map_.reset(new MetaInfoMap(meta_info_map));
132 const BookmarkNode::MetaInfoMap* BookmarkNode::GetMetaInfoMap() const {
133 return meta_info_map_.get();
136 void BookmarkNode::Initialize(int64 id) {
138 type_ = url_.is_empty() ? FOLDER : URL;
139 date_added_ = Time::Now();
140 favicon_state_ = INVALID_FAVICON;
141 favicon_load_task_id_ = base::CancelableTaskTracker::kBadTaskId;
142 meta_info_map_.reset();
143 sync_transaction_version_ = kInvalidSyncTransactionVersion;
146 void BookmarkNode::InvalidateFavicon() {
148 favicon_ = gfx::Image();
149 favicon_state_ = INVALID_FAVICON;
154 // Comparator used when sorting bookmarks. Folders are sorted first, then
156 class SortComparator : public std::binary_function<const BookmarkNode*,
160 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
162 // Returns true if |n1| preceeds |n2|.
163 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
164 if (n1->type() == n2->type()) {
165 // Types are the same, compare the names.
167 return n1->GetTitle() < n2->GetTitle();
168 return base::i18n::CompareString16WithCollator(
169 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
171 // Types differ, sort such that folders come first.
172 return n1->is_folder();
176 icu::Collator* collator_;
181 // BookmarkPermanentNode -------------------------------------------------------
183 BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
184 : BookmarkNode(id, GURL()),
188 BookmarkPermanentNode::~BookmarkPermanentNode() {
191 bool BookmarkPermanentNode::IsVisible() const {
192 return visible_ || !empty();
195 // BookmarkModel --------------------------------------------------------------
197 BookmarkModel::BookmarkModel(Profile* profile)
201 bookmark_bar_node_(NULL),
205 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
206 loaded_signal_(true, false),
207 extensive_changes_(0) {
209 // Profile is null during testing.
210 DoneLoading(CreateLoadDetails());
214 BookmarkModel::~BookmarkModel() {
215 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
216 BookmarkModelBeingDeleted(this));
219 // The store maintains a reference back to us. We need to tell it we're gone
220 // so that it doesn't try and invoke a method back on us again.
221 store_->BookmarkModelDeleted();
225 void BookmarkModel::Shutdown() {
229 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
230 // details. It is also called when the BookmarkModel is deleted.
231 loaded_signal_.Signal();
234 void BookmarkModel::Load(
235 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
237 // If the store is non-null, it means Load was already invoked. Load should
238 // only be invoked once.
243 expanded_state_tracker_.reset(
244 new BookmarkExpandedStateTracker(this, profile_->GetPrefs()));
246 // Listen for changes to favicons so that we can update the favicon of the
247 // node appropriately.
248 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
249 content::Source<Profile>(profile_));
251 // Load the bookmarks. BookmarkStorage notifies us when done.
252 store_ = new BookmarkStorage(profile_, this, task_runner.get());
253 store_->LoadBookmarks(CreateLoadDetails());
256 const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
257 std::vector<const BookmarkNode*> nodes =
258 bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
259 DCHECK(!nodes.empty()); // This list is always padded with default folders.
263 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
264 observers_.AddObserver(observer);
267 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
268 observers_.RemoveObserver(observer);
271 void BookmarkModel::BeginExtensiveChanges() {
272 if (++extensive_changes_ == 1) {
273 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
274 ExtensiveBookmarkChangesBeginning(this));
278 void BookmarkModel::EndExtensiveChanges() {
279 --extensive_changes_;
280 DCHECK_GE(extensive_changes_, 0);
281 if (extensive_changes_ == 0) {
282 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
283 ExtensiveBookmarkChangesEnded(this));
287 void BookmarkModel::BeginGroupedChanges() {
288 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
289 GroupedBookmarkChangesBeginning(this));
292 void BookmarkModel::EndGroupedChanges() {
293 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
294 GroupedBookmarkChangesEnded(this));
297 void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
298 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
302 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
305 void BookmarkModel::RemoveAll() {
306 std::set<GURL> removed_urls;
307 ScopedVector<BookmarkNode> removed_nodes;
309 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
310 OnWillRemoveAllBookmarks(this));
312 BeginExtensiveChanges();
313 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
314 // its immediate children. For removing all non permanent nodes just remove
315 // all children of non-root permanent nodes.
317 base::AutoLock url_lock(url_lock_);
318 for (int i = 0; i < root_.child_count(); ++i) {
319 BookmarkNode* permanent_node = root_.GetChild(i);
320 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
321 BookmarkNode* child_node = permanent_node->GetChild(j);
322 removed_nodes.push_back(child_node);
323 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
327 EndExtensiveChanges();
329 store_->ScheduleSave();
331 NotifyHistoryAboutRemovedBookmarks(removed_urls);
333 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
334 BookmarkAllNodesRemoved(this));
337 void BookmarkModel::Move(const BookmarkNode* node,
338 const BookmarkNode* new_parent,
340 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
341 is_root_node(new_parent) || is_permanent_node(node)) {
346 if (new_parent->HasAncestor(node)) {
347 // Can't make an ancestor of the node be a child of the node.
352 const BookmarkNode* old_parent = node->parent();
353 int old_index = old_parent->GetIndexOf(node);
355 if (old_parent == new_parent &&
356 (index == old_index || index == old_index + 1)) {
357 // Node is already in this position, nothing to do.
361 SetDateFolderModified(new_parent, Time::Now());
363 if (old_parent == new_parent && index > old_index)
365 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
366 mutable_new_parent->Add(AsMutable(node), index);
369 store_->ScheduleSave();
371 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
372 BookmarkNodeMoved(this, old_parent, old_index,
376 void BookmarkModel::Copy(const BookmarkNode* node,
377 const BookmarkNode* new_parent,
379 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
380 is_root_node(new_parent) || is_permanent_node(node)) {
385 if (new_parent->HasAncestor(node)) {
386 // Can't make an ancestor of the node be a child of the node.
391 SetDateFolderModified(new_parent, Time::Now());
392 BookmarkNodeData drag_data(node);
393 std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
394 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
395 // don't need to send notifications here.
396 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index, true);
399 store_->ScheduleSave();
402 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
404 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
405 BookmarkNode* mutable_node = AsMutable(node);
406 mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
407 LoadFavicon(mutable_node);
409 return node->favicon();
412 void BookmarkModel::SetTitle(const BookmarkNode* node, const base::string16& title) {
417 if (node->GetTitle() == title)
420 if (is_permanent_node(node)) {
425 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
426 OnWillChangeBookmarkNode(this, node));
428 // The title index doesn't support changing the title, instead we remove then
430 index_->Remove(node);
431 AsMutable(node)->SetTitle(title);
435 store_->ScheduleSave();
437 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
438 BookmarkNodeChanged(this, node));
441 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
447 // We cannot change the URL of a folder.
448 if (node->is_folder()) {
453 if (node->url() == url)
456 BookmarkNode* mutable_node = AsMutable(node);
457 mutable_node->InvalidateFavicon();
458 CancelPendingFaviconLoadRequests(mutable_node);
460 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
461 OnWillChangeBookmarkNode(this, node));
464 base::AutoLock url_lock(url_lock_);
465 RemoveNodeFromURLSet(mutable_node);
466 mutable_node->set_url(url);
467 nodes_ordered_by_url_set_.insert(mutable_node);
471 store_->ScheduleSave();
473 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
474 BookmarkNodeChanged(this, node));
477 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
478 const std::string& key,
479 const std::string& value) {
480 std::string old_value;
481 if (node->GetMetaInfo(key, &old_value) && old_value == value)
484 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
485 OnWillChangeBookmarkMetaInfo(this, node));
487 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
488 store_->ScheduleSave();
490 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
491 BookmarkMetaInfoChanged(this, node));
494 void BookmarkModel::SetNodeMetaInfoMap(
495 const BookmarkNode* node,
496 const BookmarkNode::MetaInfoMap& meta_info_map) {
497 const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
498 if ((!old_meta_info_map && meta_info_map.empty()) ||
499 (old_meta_info_map && meta_info_map == *old_meta_info_map))
502 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
503 OnWillChangeBookmarkMetaInfo(this, node));
505 AsMutable(node)->SetMetaInfoMap(meta_info_map);
507 store_->ScheduleSave();
509 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
510 BookmarkMetaInfoChanged(this, node));
513 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
514 const std::string& key) {
515 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
516 if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
519 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
520 OnWillChangeBookmarkMetaInfo(this, node));
522 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
523 store_->ScheduleSave();
525 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
526 BookmarkMetaInfoChanged(this, node));
529 void BookmarkModel::SetNodeSyncTransactionVersion(
530 const BookmarkNode* node,
531 int64 sync_transaction_version) {
532 if (sync_transaction_version == node->sync_transaction_version())
535 AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
537 store_->ScheduleSave();
540 void BookmarkModel::SetDateAdded(const BookmarkNode* node,
541 base::Time date_added) {
547 if (node->date_added() == date_added)
550 if (is_permanent_node(node)) {
555 AsMutable(node)->set_date_added(date_added);
557 // Syncing might result in dates newer than the folder's last modified date.
558 if (date_added > node->parent()->date_folder_modified()) {
559 // Will trigger store_->ScheduleSave().
560 SetDateFolderModified(node->parent(), date_added);
561 } else if (store_.get()) {
562 store_->ScheduleSave();
566 void BookmarkModel::GetNodesByURL(const GURL& url,
567 std::vector<const BookmarkNode*>* nodes) {
568 base::AutoLock url_lock(url_lock_);
569 BookmarkNode tmp_node(url);
570 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
571 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
572 nodes->push_back(*i);
577 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
579 std::vector<const BookmarkNode*> nodes;
580 GetNodesByURL(url, &nodes);
584 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
585 return nodes.front();
588 bool BookmarkModel::HasBookmarks() {
589 base::AutoLock url_lock(url_lock_);
590 return !nodes_ordered_by_url_set_.empty();
593 bool BookmarkModel::IsBookmarked(const GURL& url) {
594 base::AutoLock url_lock(url_lock_);
595 return IsBookmarkedNoLock(url);
598 void BookmarkModel::GetBookmarks(
599 std::vector<BookmarkService::URLAndTitle>* bookmarks) {
600 base::AutoLock url_lock(url_lock_);
601 const GURL* last_url = NULL;
602 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
603 i != nodes_ordered_by_url_set_.end(); ++i) {
604 const GURL* url = &((*i)->url());
605 // Only add unique URLs.
606 if (!last_url || *url != *last_url) {
607 BookmarkService::URLAndTitle bookmark;
609 bookmark.title = (*i)->GetTitle();
610 bookmarks->push_back(bookmark);
616 void BookmarkModel::BlockTillLoaded() {
617 loaded_signal_.Wait();
620 const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
621 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
622 return GetNodeByID(&root_, id);
625 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
627 const base::string16& title) {
628 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
629 // Can't add to the root.
634 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
635 new_node->set_date_folder_modified(Time::Now());
636 // Folders shouldn't have line breaks in their titles.
637 new_node->SetTitle(title);
638 new_node->set_type(BookmarkNode::FOLDER);
640 return AddNode(AsMutable(parent), index, new_node);
643 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
645 const base::string16& title,
647 return AddURLWithCreationTime(parent, index,
648 base::CollapseWhitespace(title, false),
652 const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
653 const BookmarkNode* parent,
655 const base::string16& title,
657 const Time& creation_time) {
658 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
659 !IsValidIndex(parent, index, true)) {
664 // Syncing may result in dates newer than the last modified date.
665 if (creation_time > parent->date_folder_modified())
666 SetDateFolderModified(parent, creation_time);
668 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
669 new_node->SetTitle(title);
670 new_node->set_date_added(creation_time);
671 new_node->set_type(BookmarkNode::URL);
674 // Only hold the lock for the duration of the insert.
675 base::AutoLock url_lock(url_lock_);
676 nodes_ordered_by_url_set_.insert(new_node);
679 return AddNode(AsMutable(parent), index, new_node);
682 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
683 if (!parent || !parent->is_folder() || is_root_node(parent) ||
684 parent->child_count() <= 1) {
688 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
689 OnWillReorderBookmarkNode(this, parent));
691 UErrorCode error = U_ZERO_ERROR;
692 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
693 if (U_FAILURE(error))
694 collator.reset(NULL);
695 BookmarkNode* mutable_parent = AsMutable(parent);
696 std::sort(mutable_parent->children().begin(),
697 mutable_parent->children().end(),
698 SortComparator(collator.get()));
701 store_->ScheduleSave();
703 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
704 BookmarkNodeChildrenReordered(this, parent));
707 void BookmarkModel::ReorderChildren(
708 const BookmarkNode* parent,
709 const std::vector<const BookmarkNode*>& ordered_nodes) {
710 // Ensure that all children in |parent| are in |ordered_nodes|.
711 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
712 for (size_t i = 0; i < ordered_nodes.size(); ++i)
713 DCHECK_EQ(parent, ordered_nodes[i]->parent());
715 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
716 OnWillReorderBookmarkNode(this, parent));
718 AsMutable(parent)->SetChildren(
719 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
722 store_->ScheduleSave();
724 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
725 BookmarkNodeChildrenReordered(this, parent));
728 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
731 AsMutable(parent)->set_date_folder_modified(time);
734 store_->ScheduleSave();
737 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
738 SetDateFolderModified(node, Time());
741 void BookmarkModel::GetBookmarksWithTitlesMatching(
742 const base::string16& text,
744 std::vector<BookmarkTitleMatch>* matches) {
748 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
751 void BookmarkModel::ClearStore() {
752 registrar_.RemoveAll();
756 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
760 case BookmarkNode::BOOKMARK_BAR:
761 bookmark_bar_node_->set_visible(value);
763 case BookmarkNode::OTHER_NODE:
764 other_node_->set_visible(value);
766 case BookmarkNode::MOBILE:
767 mobile_node_->set_visible(value);
774 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
775 BookmarkNode tmp_node(url);
776 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
777 nodes_ordered_by_url_set_.end());
780 void BookmarkModel::RemoveNode(BookmarkNode* node,
781 std::set<GURL>* removed_urls) {
782 if (!loaded_ || !node || is_permanent_node(node)) {
787 url_lock_.AssertAcquired();
788 if (node->is_url()) {
789 RemoveNodeFromURLSet(node);
790 removed_urls->insert(node->url());
791 index_->Remove(node);
794 CancelPendingFaviconLoadRequests(node);
796 // Recurse through children.
797 for (int i = node->child_count() - 1; i >= 0; --i)
798 RemoveNode(node->GetChild(i), removed_urls);
801 void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
802 DCHECK(details_delete_me);
803 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
805 // We should only ever be loaded once.
810 next_node_id_ = details->max_id();
811 if (details->computed_checksum() != details->stored_checksum() ||
812 details->ids_reassigned()) {
813 // If bookmarks file changed externally, the IDs may have changed
814 // externally. In that case, the decoder may have reassigned IDs to make
815 // them unique. So when the file has changed externally, we should save the
816 // bookmarks file to persist new IDs.
818 store_->ScheduleSave();
820 bookmark_bar_node_ = details->release_bb_node();
821 other_node_ = details->release_other_folder_node();
822 mobile_node_ = details->release_mobile_folder_node();
823 index_.reset(details->release_index());
825 // WARNING: order is important here, various places assume the order is
827 root_.Add(bookmark_bar_node_, 0);
828 root_.Add(other_node_, 1);
829 root_.Add(mobile_node_, 2);
831 root_.SetMetaInfoMap(details->model_meta_info_map());
832 root_.set_sync_transaction_version(details->model_sync_transaction_version());
835 base::AutoLock url_lock(url_lock_);
836 // Update nodes_ordered_by_url_set_ from the nodes.
837 PopulateNodesByURL(&root_);
842 loaded_signal_.Signal();
844 // Notify our direct observers.
845 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
846 BookmarkModelLoaded(this, details->ids_reassigned()));
849 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
850 scoped_ptr<BookmarkNode> node(delete_me);
852 const BookmarkNode* parent = node->parent();
854 int index = parent->GetIndexOf(node.get());
856 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
857 OnWillRemoveBookmarks(this, parent, index, node.get()));
859 std::set<GURL> removed_urls;
861 base::AutoLock url_lock(url_lock_);
862 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
866 store_->ScheduleSave();
868 NotifyHistoryAboutRemovedBookmarks(removed_urls);
870 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
871 BookmarkNodeRemoved(this, parent, index, node.get()));
874 void BookmarkModel::RemoveNodeFromURLSet(BookmarkNode* node) {
875 // NOTE: this is called in such a way that url_lock_ is already held. As
876 // such, this doesn't explicitly grab the lock.
877 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
878 DCHECK(i != nodes_ordered_by_url_set_.end());
879 // i points to the first node with the URL, advance until we find the
880 // node we're removing.
883 nodes_ordered_by_url_set_.erase(i);
886 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
887 std::set<GURL>* removed_urls) {
888 // NOTE: this method should be always called with |url_lock_| held.
889 // This method does not explicitly acquires a lock.
890 url_lock_.AssertAcquired();
891 DCHECK(removed_urls);
892 BookmarkNode* parent = AsMutable(node->parent());
894 parent->Remove(node);
895 RemoveNode(node, removed_urls);
896 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
897 // allow duplicates we need to remove any entries that are still bookmarked.
898 for (std::set<GURL>::iterator i = removed_urls->begin();
899 i != removed_urls->end();) {
900 if (IsBookmarkedNoLock(*i)) {
901 // When we erase the iterator pointing at the erasee is
902 // invalidated, so using i++ here within the "erase" call is
903 // important as it advances the iterator before passing the
904 // old value through to erase.
905 removed_urls->erase(i++);
912 void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
913 const std::set<GURL>& removed_bookmark_urls) const {
914 if (removed_bookmark_urls.empty()) {
915 // No point in sending out notification if the starred state didn't change.
920 HistoryService* history =
921 HistoryServiceFactory::GetForProfile(profile_,
922 Profile::EXPLICIT_ACCESS);
924 history->URLsNoLongerBookmarked(removed_bookmark_urls);
928 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
930 BookmarkNode* node) {
931 parent->Add(node, index);
934 store_->ScheduleSave();
936 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
937 BookmarkNodeAdded(this, parent, index));
944 const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
946 if (node->id() == id)
949 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
950 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
957 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
960 return (parent && parent->is_folder() &&
961 (index >= 0 && (index < parent->child_count() ||
962 (allow_end && index == parent->child_count()))));
965 BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
966 BookmarkNode::Type type) {
967 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
968 type == BookmarkNode::OTHER_NODE ||
969 type == BookmarkNode::MOBILE);
970 BookmarkPermanentNode* node =
971 new BookmarkPermanentNode(generate_next_node_id());
972 if (type == BookmarkNode::MOBILE)
973 node->set_visible(false); // Mobile node is initially hidden.
977 case BookmarkNode::BOOKMARK_BAR:
978 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
980 case BookmarkNode::OTHER_NODE:
981 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
983 case BookmarkNode::MOBILE:
984 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
988 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
991 node->SetTitle(l10n_util::GetStringUTF16(title_id));
992 node->set_type(type);
996 void BookmarkModel::OnFaviconDataAvailable(
998 const chrome::FaviconImageResult& image_result) {
1000 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
1001 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
1002 if (!image_result.image.IsEmpty()) {
1003 node->set_favicon(image_result.image);
1004 node->set_icon_url(image_result.icon_url);
1005 FaviconLoaded(node);
1009 void BookmarkModel::LoadFavicon(BookmarkNode* node) {
1010 if (node->is_folder())
1013 DCHECK(node->url().is_valid());
1014 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
1015 profile_, Profile::EXPLICIT_ACCESS);
1016 if (!favicon_service)
1018 FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
1019 FaviconService::FaviconForURLParams(node->url(),
1022 base::Bind(&BookmarkModel::OnFaviconDataAvailable,
1023 base::Unretained(this), node),
1024 &cancelable_task_tracker_);
1025 node->set_favicon_load_task_id(handle);
1028 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
1029 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
1030 BookmarkNodeFaviconChanged(this, node));
1033 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
1034 if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) {
1035 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
1036 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
1040 void BookmarkModel::Observe(int type,
1041 const content::NotificationSource& source,
1042 const content::NotificationDetails& details) {
1044 case chrome::NOTIFICATION_FAVICON_CHANGED: {
1045 // Prevent the observers from getting confused for multiple favicon loads.
1046 content::Details<FaviconChangedDetails> favicon_details(details);
1047 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
1048 i != favicon_details->urls.end(); ++i) {
1049 std::vector<const BookmarkNode*> nodes;
1050 GetNodesByURL(*i, &nodes);
1051 for (size_t i = 0; i < nodes.size(); ++i) {
1052 // Got an updated favicon, for a URL, do a new request.
1053 BookmarkNode* node = AsMutable(nodes[i]);
1054 node->InvalidateFavicon();
1055 CancelPendingFaviconLoadRequests(node);
1056 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
1057 BookmarkNodeFaviconChanged(this, node));
1069 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
1070 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1071 // explicitly grab the lock.
1073 nodes_ordered_by_url_set_.insert(node);
1074 for (int i = 0; i < node->child_count(); ++i)
1075 PopulateNodesByURL(node->GetChild(i));
1078 int64 BookmarkModel::generate_next_node_id() {
1079 return next_node_id_++;
1082 BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
1083 BookmarkPermanentNode* bb_node =
1084 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1085 BookmarkPermanentNode* other_node =
1086 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1087 BookmarkPermanentNode* mobile_node =
1088 CreatePermanentNode(BookmarkNode::MOBILE);
1089 return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
1090 new BookmarkIndex(profile_),