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.
5 #include "components/bookmarks/browser/bookmark_model.h"
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"
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;
39 // Helper to get a mutable bookmark node.
40 BookmarkNode* AsMutable(const BookmarkNode* node) {
41 return const_cast<BookmarkNode*>(node);
44 // Helper to get a mutable permanent bookmark node.
45 BookmarkPermanentNode* AsMutable(const BookmarkPermanentNode* node) {
46 return const_cast<BookmarkPermanentNode*>(node);
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*,
56 explicit VisibilityComparator(BookmarkClient* client) : client_(client) {}
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;
67 BookmarkClient* client_;
70 // Comparator used when sorting bookmarks. Folders are sorted first, then
72 class SortComparator : public std::binary_function<const BookmarkNode*,
76 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
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.
83 return n1->GetTitle() < n2->GetTitle();
84 return base::i18n::CompareString16WithCollator(
85 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
87 // Types differ, sort such that folders come first.
88 return n1->is_folder();
92 icu::Collator* collator_;
97 // BookmarkModel --------------------------------------------------------------
99 BookmarkModel::BookmarkModel(BookmarkClient* client)
103 bookmark_bar_node_(NULL),
107 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
108 loaded_signal_(true, false),
109 extensive_changes_(0) {
113 BookmarkModel::~BookmarkModel() {
114 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
115 BookmarkModelBeingDeleted(this));
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();
124 void BookmarkModel::Shutdown() {
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();
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) {
140 // If the store is non-null, it means Load was already invoked. Load should
141 // only be invoked once.
146 expanded_state_tracker_.reset(
147 new BookmarkExpandedStateTracker(this, pref_service));
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);
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.
161 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
162 observers_.AddObserver(observer);
165 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
166 observers_.RemoveObserver(observer);
169 void BookmarkModel::BeginExtensiveChanges() {
170 if (++extensive_changes_ == 1) {
171 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
172 ExtensiveBookmarkChangesBeginning(this));
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));
185 void BookmarkModel::BeginGroupedChanges() {
186 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
187 GroupedBookmarkChangesBeginning(this));
190 void BookmarkModel::EndGroupedChanges() {
191 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
192 GroupedBookmarkChangesEnded(this));
195 void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
196 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
200 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
203 void BookmarkModel::RemoveAllUserBookmarks() {
204 std::set<GURL> removed_urls;
205 ScopedVector<BookmarkNode> removed_nodes;
207 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
208 OnWillRemoveAllUserBookmarks(this));
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.
215 base::AutoLock url_lock(url_lock_);
216 for (int i = 0; i < root_.child_count(); ++i) {
217 BookmarkNode* permanent_node = root_.GetChild(i);
219 if (!client_->CanBeEditedByUser(permanent_node))
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);
229 EndExtensiveChanges();
231 store_->ScheduleSave();
233 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
234 BookmarkAllUserNodesRemoved(this, removed_urls));
237 void BookmarkModel::Move(const BookmarkNode* node,
238 const BookmarkNode* new_parent,
240 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
241 is_root_node(new_parent) || is_permanent_node(node)) {
246 if (new_parent->HasAncestor(node)) {
247 // Can't make an ancestor of the node be a child of the node.
252 const BookmarkNode* old_parent = node->parent();
253 int old_index = old_parent->GetIndexOf(node);
255 if (old_parent == new_parent &&
256 (index == old_index || index == old_index + 1)) {
257 // Node is already in this position, nothing to do.
261 SetDateFolderModified(new_parent, Time::Now());
263 if (old_parent == new_parent && index > old_index)
265 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
266 mutable_new_parent->Add(AsMutable(node), index);
269 store_->ScheduleSave();
271 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
272 BookmarkNodeMoved(this, old_parent, old_index,
276 void BookmarkModel::Copy(const BookmarkNode* node,
277 const BookmarkNode* new_parent,
279 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
280 is_root_node(new_parent) || is_permanent_node(node)) {
285 if (new_parent->HasAncestor(node)) {
286 // Can't make an ancestor of the node be a child of the node.
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);
299 store_->ScheduleSave();
302 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
304 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
305 BookmarkNode* mutable_node = AsMutable(node);
308 client_->PreferTouchIcon() ?
309 favicon_base::TOUCH_ICON :
310 favicon_base::FAVICON);
312 return node->favicon();
315 favicon_base::IconType BookmarkModel::GetFaviconType(const BookmarkNode* node) {
317 return node->favicon_type();
320 void BookmarkModel::SetTitle(const BookmarkNode* node,
321 const base::string16& title) {
326 if (node->GetTitle() == title)
329 if (is_permanent_node(node) && !client_->CanSetPermanentNodeTitle(node)) {
334 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
335 OnWillChangeBookmarkNode(this, node));
337 // The title index doesn't support changing the title, instead we remove then
339 index_->Remove(node);
340 AsMutable(node)->SetTitle(title);
344 store_->ScheduleSave();
346 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
347 BookmarkNodeChanged(this, node));
350 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
356 // We cannot change the URL of a folder.
357 if (node->is_folder()) {
362 if (node->url() == url)
365 BookmarkNode* mutable_node = AsMutable(node);
366 mutable_node->InvalidateFavicon();
367 CancelPendingFaviconLoadRequests(mutable_node);
369 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
370 OnWillChangeBookmarkNode(this, node));
373 base::AutoLock url_lock(url_lock_);
374 RemoveNodeFromInternalMaps(mutable_node);
375 mutable_node->set_url(url);
376 AddNodeToInternalMaps(mutable_node);
380 store_->ScheduleSave();
382 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
383 BookmarkNodeChanged(this, node));
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)
393 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
394 OnWillChangeBookmarkMetaInfo(this, node));
396 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
397 store_->ScheduleSave();
399 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
400 BookmarkMetaInfoChanged(this, node));
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))
411 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
412 OnWillChangeBookmarkMetaInfo(this, node));
414 AsMutable(node)->SetMetaInfoMap(meta_info_map);
416 store_->ScheduleSave();
418 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
419 BookmarkMetaInfoChanged(this, node));
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())
428 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
429 OnWillChangeBookmarkMetaInfo(this, node));
431 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
432 store_->ScheduleSave();
434 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
435 BookmarkMetaInfoChanged(this, node));
438 void BookmarkModel::SetNodeSyncTransactionVersion(
439 const BookmarkNode* node,
440 int64 sync_transaction_version) {
441 DCHECK(client_->CanSyncNode(node));
443 if (sync_transaction_version == node->sync_transaction_version())
446 AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
448 store_->ScheduleSave();
451 void BookmarkModel::OnFaviconChanged(const std::set<GURL>& urls) {
452 // Ignore events if |Load| has not been called yet.
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,
467 BookmarkNodeFaviconChanged(this, node));
472 void BookmarkModel::SetDateAdded(const BookmarkNode* node,
479 if (node->date_added() == date_added)
482 if (is_permanent_node(node)) {
487 AsMutable(node)->set_date_added(date_added);
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();
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);
509 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
511 std::vector<const BookmarkNode*> nodes;
512 GetNodesByURL(url, &nodes);
513 std::sort(nodes.begin(), nodes.end(), &bookmarks::MoreRecentlyAdded);
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]))
524 bool BookmarkModel::HasBookmarks() {
525 base::AutoLock url_lock(url_lock_);
526 return !nodes_ordered_by_url_set_.empty();
529 bool BookmarkModel::IsBookmarked(const GURL& url) {
530 base::AutoLock url_lock(url_lock_);
531 return IsBookmarkedNoLock(url);
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;
545 bookmark.title = (*i)->GetTitle();
546 bookmarks->push_back(bookmark);
552 void BookmarkModel::BlockTillLoaded() {
553 loaded_signal_.Wait();
556 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
558 const base::string16& title) {
559 return AddFolderWithMetaInfo(parent, index, title, NULL);
561 const BookmarkNode* BookmarkModel::AddFolderWithMetaInfo(
562 const BookmarkNode* parent,
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.
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);
578 new_node->SetMetaInfoMap(*meta_info);
580 return AddNode(AsMutable(parent), index, new_node);
583 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
585 const base::string16& title,
587 return AddURLWithCreationTimeAndMetaInfo(
590 base::CollapseWhitespace(title, false),
596 const BookmarkNode* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
597 const BookmarkNode* parent,
599 const base::string16& title,
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)) {
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);
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);
618 new_node->SetMetaInfoMap(*meta_info);
620 return AddNode(AsMutable(parent), index, new_node);
623 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
624 DCHECK(client_->CanBeEditedByUser(parent));
626 if (!parent || !parent->is_folder() || is_root_node(parent) ||
627 parent->child_count() <= 1) {
631 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
632 OnWillReorderBookmarkNode(this, parent));
634 UErrorCode error = U_ZERO_ERROR;
635 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
636 if (U_FAILURE(error))
637 collator.reset(NULL);
638 BookmarkNode* mutable_parent = AsMutable(parent);
639 std::sort(mutable_parent->children().begin(),
640 mutable_parent->children().end(),
641 SortComparator(collator.get()));
644 store_->ScheduleSave();
646 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
647 BookmarkNodeChildrenReordered(this, parent));
650 void BookmarkModel::ReorderChildren(
651 const BookmarkNode* parent,
652 const std::vector<const BookmarkNode*>& ordered_nodes) {
653 DCHECK(client_->CanBeEditedByUser(parent));
655 // Ensure that all children in |parent| are in |ordered_nodes|.
656 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
657 for (size_t i = 0; i < ordered_nodes.size(); ++i)
658 DCHECK_EQ(parent, ordered_nodes[i]->parent());
660 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
661 OnWillReorderBookmarkNode(this, parent));
663 AsMutable(parent)->SetChildren(
664 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
667 store_->ScheduleSave();
669 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
670 BookmarkNodeChildrenReordered(this, parent));
673 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
676 AsMutable(parent)->set_date_folder_modified(time);
679 store_->ScheduleSave();
682 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
683 SetDateFolderModified(node, Time());
686 void BookmarkModel::GetBookmarksMatching(const base::string16& text,
688 std::vector<BookmarkMatch>* matches) {
689 GetBookmarksMatching(text, max_count,
690 query_parser::MatchingAlgorithm::DEFAULT, matches);
693 void BookmarkModel::GetBookmarksMatching(
694 const base::string16& text,
696 query_parser::MatchingAlgorithm matching_algorithm,
697 std::vector<BookmarkMatch>* matches) {
701 index_->GetBookmarksMatching(text, max_count, matching_algorithm, matches);
704 void BookmarkModel::ClearStore() {
708 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
710 BookmarkPermanentNode* node = AsMutable(PermanentNode(type));
711 node->set_visible(value || client_->IsPermanentNodeVisible(node));
714 const BookmarkPermanentNode* BookmarkModel::PermanentNode(
715 BookmarkNode::Type type) {
718 case BookmarkNode::BOOKMARK_BAR:
719 return bookmark_bar_node_;
720 case BookmarkNode::OTHER_NODE:
722 case BookmarkNode::MOBILE:
730 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
731 BookmarkNode tmp_node(url);
732 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
733 nodes_ordered_by_url_set_.end());
736 void BookmarkModel::RemoveNode(BookmarkNode* node,
737 std::set<GURL>* removed_urls) {
738 if (!loaded_ || !node || is_permanent_node(node)) {
743 url_lock_.AssertAcquired();
744 if (node->is_url()) {
745 RemoveNodeFromInternalMaps(node);
746 removed_urls->insert(node->url());
749 CancelPendingFaviconLoadRequests(node);
751 // Recurse through children.
752 for (int i = node->child_count() - 1; i >= 0; --i)
753 RemoveNode(node->GetChild(i), removed_urls);
756 void BookmarkModel::DoneLoading(scoped_ptr<BookmarkLoadDetails> details) {
759 // We should only ever be loaded once.
764 next_node_id_ = details->max_id();
765 if (details->computed_checksum() != details->stored_checksum() ||
766 details->ids_reassigned()) {
767 // If bookmarks file changed externally, the IDs may have changed
768 // externally. In that case, the decoder may have reassigned IDs to make
769 // them unique. So when the file has changed externally, we should save the
770 // bookmarks file to persist new IDs.
772 store_->ScheduleSave();
774 bookmark_bar_node_ = details->release_bb_node();
775 other_node_ = details->release_other_folder_node();
776 mobile_node_ = details->release_mobile_folder_node();
777 index_.reset(details->release_index());
779 // Get any extra nodes and take ownership of them at the |root_|.
780 std::vector<BookmarkPermanentNode*> extra_nodes;
781 details->release_extra_nodes(&extra_nodes);
783 // WARNING: order is important here, various places assume the order is
784 // constant (but can vary between embedders with the initial visibility
785 // of permanent nodes).
786 std::vector<BookmarkPermanentNode*> root_children;
787 root_children.push_back(bookmark_bar_node_);
788 root_children.push_back(other_node_);
789 root_children.push_back(mobile_node_);
790 for (size_t i = 0; i < extra_nodes.size(); ++i)
791 root_children.push_back(extra_nodes[i]);
792 std::stable_sort(root_children.begin(),
794 VisibilityComparator(client_));
795 for (size_t i = 0; i < root_children.size(); ++i)
796 root_.Add(root_children[i], static_cast<int>(i));
798 root_.SetMetaInfoMap(details->model_meta_info_map());
799 root_.set_sync_transaction_version(details->model_sync_transaction_version());
802 base::AutoLock url_lock(url_lock_);
803 // Update nodes_ordered_by_url_set_ from the nodes.
804 PopulateNodesByURL(&root_);
809 loaded_signal_.Signal();
811 // Notify our direct observers.
812 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
813 BookmarkModelLoaded(this, details->ids_reassigned()));
816 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
817 scoped_ptr<BookmarkNode> node(delete_me);
819 const BookmarkNode* parent = node->parent();
821 int index = parent->GetIndexOf(node.get());
823 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
824 OnWillRemoveBookmarks(this, parent, index, node.get()));
826 std::set<GURL> removed_urls;
828 base::AutoLock url_lock(url_lock_);
829 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
833 store_->ScheduleSave();
836 BookmarkModelObserver,
838 BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls));
841 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode* node) {
842 index_->Remove(node);
843 // NOTE: this is called in such a way that url_lock_ is already held. As
844 // such, this doesn't explicitly grab the lock.
845 url_lock_.AssertAcquired();
846 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
847 DCHECK(i != nodes_ordered_by_url_set_.end());
848 // i points to the first node with the URL, advance until we find the
849 // node we're removing.
852 nodes_ordered_by_url_set_.erase(i);
855 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
856 std::set<GURL>* removed_urls) {
857 // NOTE: this method should be always called with |url_lock_| held.
858 // This method does not explicitly acquires a lock.
859 url_lock_.AssertAcquired();
860 DCHECK(removed_urls);
861 BookmarkNode* parent = AsMutable(node->parent());
863 parent->Remove(node);
864 RemoveNode(node, removed_urls);
865 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
866 // allow duplicates we need to remove any entries that are still bookmarked.
867 for (std::set<GURL>::iterator i = removed_urls->begin();
868 i != removed_urls->end();) {
869 if (IsBookmarkedNoLock(*i)) {
870 // When we erase the iterator pointing at the erasee is
871 // invalidated, so using i++ here within the "erase" call is
872 // important as it advances the iterator before passing the
873 // old value through to erase.
874 removed_urls->erase(i++);
881 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
883 BookmarkNode* node) {
884 parent->Add(node, index);
887 store_->ScheduleSave();
889 if (node->type() == BookmarkNode::URL) {
890 base::AutoLock url_lock(url_lock_);
891 AddNodeToInternalMaps(node);
896 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
897 BookmarkNodeAdded(this, parent, index));
902 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode* node) {
904 url_lock_.AssertAcquired();
905 nodes_ordered_by_url_set_.insert(node);
908 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
911 return (parent && parent->is_folder() &&
912 (index >= 0 && (index < parent->child_count() ||
913 (allow_end && index == parent->child_count()))));
916 BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
917 BookmarkNode::Type type) {
918 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
919 type == BookmarkNode::OTHER_NODE ||
920 type == BookmarkNode::MOBILE);
921 BookmarkPermanentNode* node =
922 new BookmarkPermanentNode(generate_next_node_id());
923 node->set_type(type);
924 node->set_visible(client_->IsPermanentNodeVisible(node));
928 case BookmarkNode::BOOKMARK_BAR:
929 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
931 case BookmarkNode::OTHER_NODE:
932 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
934 case BookmarkNode::MOBILE:
935 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
939 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
942 node->SetTitle(l10n_util::GetStringUTF16(title_id));
946 void BookmarkModel::OnFaviconDataAvailable(
948 favicon_base::IconType icon_type,
949 const favicon_base::FaviconImageResult& image_result) {
951 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
952 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
953 if (!image_result.image.IsEmpty()) {
954 node->set_favicon_type(icon_type);
955 node->set_favicon(image_result.image);
956 node->set_icon_url(image_result.icon_url);
958 } else if (icon_type == favicon_base::TOUCH_ICON) {
959 // Couldn't load the touch icon, fallback to the regular favicon.
960 DCHECK(client_->PreferTouchIcon());
961 LoadFavicon(node, favicon_base::FAVICON);
965 void BookmarkModel::LoadFavicon(
967 favicon_base::IconType icon_type) {
968 if (node->is_folder())
971 DCHECK(node->url().is_valid());
972 node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
973 base::CancelableTaskTracker::TaskId taskId =
974 client_->GetFaviconImageForPageURL(
978 &BookmarkModel::OnFaviconDataAvailable,
979 base::Unretained(this),
982 &cancelable_task_tracker_);
983 if (taskId != base::CancelableTaskTracker::kBadTaskId)
984 node->set_favicon_load_task_id(taskId);
987 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
988 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
989 BookmarkNodeFaviconChanged(this, node));
992 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
993 if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) {
994 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
995 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
999 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
1000 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1001 // explicitly grab the lock.
1003 nodes_ordered_by_url_set_.insert(node);
1004 for (int i = 0; i < node->child_count(); ++i)
1005 PopulateNodesByURL(node->GetChild(i));
1008 int64 BookmarkModel::generate_next_node_id() {
1009 return next_node_id_++;
1012 scoped_ptr<BookmarkLoadDetails> BookmarkModel::CreateLoadDetails(
1013 const std::string& accept_languages) {
1014 BookmarkPermanentNode* bb_node =
1015 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1016 BookmarkPermanentNode* other_node =
1017 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1018 BookmarkPermanentNode* mobile_node =
1019 CreatePermanentNode(BookmarkNode::MOBILE);
1020 return scoped_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails(
1024 client_->GetLoadExtraNodesCallback(),
1025 new BookmarkIndex(client_, accept_languages),