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_
= 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::Remove(const BookmarkNode
* parent
, int index
) {
288 if (!loaded_
|| !IsValidIndex(parent
, index
, false) || is_root_node(parent
)) {
292 RemoveAndDeleteNode(AsMutable(parent
->GetChild(index
)));
295 void BookmarkModel::RemoveAll() {
296 std::set
<GURL
> removed_urls
;
297 ScopedVector
<BookmarkNode
> removed_nodes
;
299 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
300 OnWillRemoveAllBookmarks(this));
302 BeginExtensiveChanges();
303 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
304 // its immediate children. For removing all non permanent nodes just remove
305 // all children of non-root permanent nodes.
307 base::AutoLock
url_lock(url_lock_
);
308 for (int i
= 0; i
< root_
.child_count(); ++i
) {
309 BookmarkNode
* permanent_node
= root_
.GetChild(i
);
310 for (int j
= permanent_node
->child_count() - 1; j
>= 0; --j
) {
311 BookmarkNode
* child_node
= permanent_node
->GetChild(j
);
312 removed_nodes
.push_back(child_node
);
313 RemoveNodeAndGetRemovedUrls(child_node
, &removed_urls
);
317 EndExtensiveChanges();
319 store_
->ScheduleSave();
321 NotifyHistoryAboutRemovedBookmarks(removed_urls
);
323 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
324 BookmarkAllNodesRemoved(this));
327 void BookmarkModel::Move(const BookmarkNode
* node
,
328 const BookmarkNode
* new_parent
,
330 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
331 is_root_node(new_parent
) || is_permanent_node(node
)) {
336 if (new_parent
->HasAncestor(node
)) {
337 // Can't make an ancestor of the node be a child of the node.
342 const BookmarkNode
* old_parent
= node
->parent();
343 int old_index
= old_parent
->GetIndexOf(node
);
345 if (old_parent
== new_parent
&&
346 (index
== old_index
|| index
== old_index
+ 1)) {
347 // Node is already in this position, nothing to do.
351 SetDateFolderModified(new_parent
, Time::Now());
353 if (old_parent
== new_parent
&& index
> old_index
)
355 BookmarkNode
* mutable_new_parent
= AsMutable(new_parent
);
356 mutable_new_parent
->Add(AsMutable(node
), index
);
359 store_
->ScheduleSave();
361 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
362 BookmarkNodeMoved(this, old_parent
, old_index
,
366 void BookmarkModel::Copy(const BookmarkNode
* node
,
367 const BookmarkNode
* new_parent
,
369 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
370 is_root_node(new_parent
) || is_permanent_node(node
)) {
375 if (new_parent
->HasAncestor(node
)) {
376 // Can't make an ancestor of the node be a child of the node.
381 SetDateFolderModified(new_parent
, Time::Now());
382 BookmarkNodeData
drag_data(node
);
383 std::vector
<BookmarkNodeData::Element
> elements(drag_data
.elements
);
384 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
385 // don't need to send notifications here.
386 bookmark_utils::CloneBookmarkNode(this, elements
, new_parent
, index
, true);
389 store_
->ScheduleSave();
392 const gfx::Image
& BookmarkModel::GetFavicon(const BookmarkNode
* node
) {
394 if (node
->favicon_state() == BookmarkNode::INVALID_FAVICON
) {
395 BookmarkNode
* mutable_node
= AsMutable(node
);
396 mutable_node
->set_favicon_state(BookmarkNode::LOADING_FAVICON
);
397 LoadFavicon(mutable_node
);
399 return node
->favicon();
402 void BookmarkModel::SetTitle(const BookmarkNode
* node
, const base::string16
& title
) {
407 if (node
->GetTitle() == title
)
410 if (is_permanent_node(node
)) {
415 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
416 OnWillChangeBookmarkNode(this, node
));
418 // The title index doesn't support changing the title, instead we remove then
420 index_
->Remove(node
);
421 AsMutable(node
)->SetTitle(title
);
425 store_
->ScheduleSave();
427 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
428 BookmarkNodeChanged(this, node
));
431 void BookmarkModel::SetURL(const BookmarkNode
* node
, const GURL
& url
) {
437 // We cannot change the URL of a folder.
438 if (node
->is_folder()) {
443 if (node
->url() == url
)
446 BookmarkNode
* mutable_node
= AsMutable(node
);
447 mutable_node
->InvalidateFavicon();
448 CancelPendingFaviconLoadRequests(mutable_node
);
450 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
451 OnWillChangeBookmarkNode(this, node
));
454 base::AutoLock
url_lock(url_lock_
);
455 RemoveNodeFromURLSet(mutable_node
);
456 mutable_node
->set_url(url
);
457 nodes_ordered_by_url_set_
.insert(mutable_node
);
461 store_
->ScheduleSave();
463 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
464 BookmarkNodeChanged(this, node
));
467 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode
* node
,
468 const std::string
& key
,
469 const std::string
& value
) {
470 std::string old_value
;
471 if (node
->GetMetaInfo(key
, &old_value
) && old_value
== value
)
474 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
475 OnWillChangeBookmarkMetaInfo(this, node
));
477 if (AsMutable(node
)->SetMetaInfo(key
, value
) && store_
.get())
478 store_
->ScheduleSave();
480 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
481 BookmarkMetaInfoChanged(this, node
));
484 void BookmarkModel::SetNodeMetaInfoMap(
485 const BookmarkNode
* node
,
486 const BookmarkNode::MetaInfoMap
& meta_info_map
) {
487 const BookmarkNode::MetaInfoMap
* old_meta_info_map
= node
->GetMetaInfoMap();
488 if ((!old_meta_info_map
&& meta_info_map
.empty()) ||
489 (old_meta_info_map
&& meta_info_map
== *old_meta_info_map
))
492 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
493 OnWillChangeBookmarkMetaInfo(this, node
));
495 AsMutable(node
)->SetMetaInfoMap(meta_info_map
);
497 store_
->ScheduleSave();
499 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
500 BookmarkMetaInfoChanged(this, node
));
503 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode
* node
,
504 const std::string
& key
) {
505 const BookmarkNode::MetaInfoMap
* meta_info_map
= node
->GetMetaInfoMap();
506 if (!meta_info_map
|| meta_info_map
->find(key
) == meta_info_map
->end())
509 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
510 OnWillChangeBookmarkMetaInfo(this, node
));
512 if (AsMutable(node
)->DeleteMetaInfo(key
) && store_
.get())
513 store_
->ScheduleSave();
515 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
516 BookmarkMetaInfoChanged(this, node
));
519 void BookmarkModel::SetNodeSyncTransactionVersion(
520 const BookmarkNode
* node
,
521 int64 sync_transaction_version
) {
522 if (sync_transaction_version
== node
->sync_transaction_version())
525 AsMutable(node
)->set_sync_transaction_version(sync_transaction_version
);
527 store_
->ScheduleSave();
530 void BookmarkModel::SetDateAdded(const BookmarkNode
* node
,
531 base::Time date_added
) {
537 if (node
->date_added() == date_added
)
540 if (is_permanent_node(node
)) {
545 AsMutable(node
)->set_date_added(date_added
);
547 // Syncing might result in dates newer than the folder's last modified date.
548 if (date_added
> node
->parent()->date_folder_modified()) {
549 // Will trigger store_->ScheduleSave().
550 SetDateFolderModified(node
->parent(), date_added
);
551 } else if (store_
.get()) {
552 store_
->ScheduleSave();
556 void BookmarkModel::GetNodesByURL(const GURL
& url
,
557 std::vector
<const BookmarkNode
*>* nodes
) {
558 base::AutoLock
url_lock(url_lock_
);
559 BookmarkNode
tmp_node(url
);
560 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(&tmp_node
);
561 while (i
!= nodes_ordered_by_url_set_
.end() && (*i
)->url() == url
) {
562 nodes
->push_back(*i
);
567 const BookmarkNode
* BookmarkModel::GetMostRecentlyAddedNodeForURL(
569 std::vector
<const BookmarkNode
*> nodes
;
570 GetNodesByURL(url
, &nodes
);
574 std::sort(nodes
.begin(), nodes
.end(), &bookmark_utils::MoreRecentlyAdded
);
575 return nodes
.front();
578 bool BookmarkModel::HasBookmarks() {
579 base::AutoLock
url_lock(url_lock_
);
580 return !nodes_ordered_by_url_set_
.empty();
583 bool BookmarkModel::IsBookmarked(const GURL
& url
) {
584 base::AutoLock
url_lock(url_lock_
);
585 return IsBookmarkedNoLock(url
);
588 void BookmarkModel::GetBookmarks(
589 std::vector
<BookmarkService::URLAndTitle
>* bookmarks
) {
590 base::AutoLock
url_lock(url_lock_
);
591 const GURL
* last_url
= NULL
;
592 for (NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.begin();
593 i
!= nodes_ordered_by_url_set_
.end(); ++i
) {
594 const GURL
* url
= &((*i
)->url());
595 // Only add unique URLs.
596 if (!last_url
|| *url
!= *last_url
) {
597 BookmarkService::URLAndTitle bookmark
;
599 bookmark
.title
= (*i
)->GetTitle();
600 bookmarks
->push_back(bookmark
);
606 void BookmarkModel::BlockTillLoaded() {
607 loaded_signal_
.Wait();
610 const BookmarkNode
* BookmarkModel::GetNodeByID(int64 id
) const {
611 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
612 return GetNodeByID(&root_
, id
);
615 const BookmarkNode
* BookmarkModel::AddFolder(const BookmarkNode
* parent
,
617 const base::string16
& title
) {
618 if (!loaded_
|| is_root_node(parent
) || !IsValidIndex(parent
, index
, true)) {
619 // Can't add to the root.
624 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), GURL());
625 new_node
->set_date_folder_modified(Time::Now());
626 // Folders shouldn't have line breaks in their titles.
627 new_node
->SetTitle(title
);
628 new_node
->set_type(BookmarkNode::FOLDER
);
630 return AddNode(AsMutable(parent
), index
, new_node
);
633 const BookmarkNode
* BookmarkModel::AddURL(const BookmarkNode
* parent
,
635 const base::string16
& title
,
637 return AddURLWithCreationTime(parent
, index
,
638 CollapseWhitespace(title
, false),
642 const BookmarkNode
* BookmarkModel::AddURLWithCreationTime(
643 const BookmarkNode
* parent
,
645 const base::string16
& title
,
647 const Time
& creation_time
) {
648 if (!loaded_
|| !url
.is_valid() || is_root_node(parent
) ||
649 !IsValidIndex(parent
, index
, true)) {
654 // Syncing may result in dates newer than the last modified date.
655 if (creation_time
> parent
->date_folder_modified())
656 SetDateFolderModified(parent
, creation_time
);
658 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), url
);
659 new_node
->SetTitle(title
);
660 new_node
->set_date_added(creation_time
);
661 new_node
->set_type(BookmarkNode::URL
);
664 // Only hold the lock for the duration of the insert.
665 base::AutoLock
url_lock(url_lock_
);
666 nodes_ordered_by_url_set_
.insert(new_node
);
669 return AddNode(AsMutable(parent
), index
, new_node
);
672 void BookmarkModel::SortChildren(const BookmarkNode
* parent
) {
673 if (!parent
|| !parent
->is_folder() || is_root_node(parent
) ||
674 parent
->child_count() <= 1) {
678 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
679 OnWillReorderBookmarkNode(this, parent
));
681 UErrorCode error
= U_ZERO_ERROR
;
682 scoped_ptr
<icu::Collator
> collator(icu::Collator::createInstance(error
));
683 if (U_FAILURE(error
))
684 collator
.reset(NULL
);
685 BookmarkNode
* mutable_parent
= AsMutable(parent
);
686 std::sort(mutable_parent
->children().begin(),
687 mutable_parent
->children().end(),
688 SortComparator(collator
.get()));
691 store_
->ScheduleSave();
693 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
694 BookmarkNodeChildrenReordered(this, parent
));
697 void BookmarkModel::ReorderChildren(
698 const BookmarkNode
* parent
,
699 const std::vector
<const BookmarkNode
*>& ordered_nodes
) {
700 // Ensure that all children in |parent| are in |ordered_nodes|.
701 DCHECK_EQ(static_cast<size_t>(parent
->child_count()), ordered_nodes
.size());
702 for (size_t i
= 0; i
< ordered_nodes
.size(); ++i
)
703 DCHECK_EQ(parent
, ordered_nodes
[i
]->parent());
705 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
706 OnWillReorderBookmarkNode(this, parent
));
708 AsMutable(parent
)->SetChildren(
709 *(reinterpret_cast<const std::vector
<BookmarkNode
*>*>(&ordered_nodes
)));
712 store_
->ScheduleSave();
714 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
715 BookmarkNodeChildrenReordered(this, parent
));
718 void BookmarkModel::SetDateFolderModified(const BookmarkNode
* parent
,
721 AsMutable(parent
)->set_date_folder_modified(time
);
724 store_
->ScheduleSave();
727 void BookmarkModel::ResetDateFolderModified(const BookmarkNode
* node
) {
728 SetDateFolderModified(node
, Time());
731 void BookmarkModel::GetBookmarksWithTitlesMatching(
732 const base::string16
& text
,
734 std::vector
<BookmarkTitleMatch
>* matches
) {
738 index_
->GetBookmarksWithTitlesMatching(text
, max_count
, matches
);
741 void BookmarkModel::ClearStore() {
742 registrar_
.RemoveAll();
746 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type
,
750 case BookmarkNode::BOOKMARK_BAR
:
751 bookmark_bar_node_
->set_visible(value
);
753 case BookmarkNode::OTHER_NODE
:
754 other_node_
->set_visible(value
);
756 case BookmarkNode::MOBILE
:
757 mobile_node_
->set_visible(value
);
764 bool BookmarkModel::IsBookmarkedNoLock(const GURL
& url
) {
765 BookmarkNode
tmp_node(url
);
766 return (nodes_ordered_by_url_set_
.find(&tmp_node
) !=
767 nodes_ordered_by_url_set_
.end());
770 void BookmarkModel::RemoveNode(BookmarkNode
* node
,
771 std::set
<GURL
>* removed_urls
) {
772 if (!loaded_
|| !node
|| is_permanent_node(node
)) {
777 url_lock_
.AssertAcquired();
778 if (node
->is_url()) {
779 RemoveNodeFromURLSet(node
);
780 removed_urls
->insert(node
->url());
781 index_
->Remove(node
);
784 CancelPendingFaviconLoadRequests(node
);
786 // Recurse through children.
787 for (int i
= node
->child_count() - 1; i
>= 0; --i
)
788 RemoveNode(node
->GetChild(i
), removed_urls
);
791 void BookmarkModel::DoneLoading(BookmarkLoadDetails
* details_delete_me
) {
792 DCHECK(details_delete_me
);
793 scoped_ptr
<BookmarkLoadDetails
> details(details_delete_me
);
795 // We should only ever be loaded once.
800 next_node_id_
= details
->max_id();
801 if (details
->computed_checksum() != details
->stored_checksum() ||
802 details
->ids_reassigned()) {
803 // If bookmarks file changed externally, the IDs may have changed
804 // externally. In that case, the decoder may have reassigned IDs to make
805 // them unique. So when the file has changed externally, we should save the
806 // bookmarks file to persist new IDs.
808 store_
->ScheduleSave();
810 bookmark_bar_node_
= details
->release_bb_node();
811 other_node_
= details
->release_other_folder_node();
812 mobile_node_
= details
->release_mobile_folder_node();
813 index_
.reset(details
->release_index());
815 // WARNING: order is important here, various places assume the order is
817 root_
.Add(bookmark_bar_node_
, 0);
818 root_
.Add(other_node_
, 1);
819 root_
.Add(mobile_node_
, 2);
821 root_
.SetMetaInfoMap(details
->model_meta_info_map());
822 root_
.set_sync_transaction_version(details
->model_sync_transaction_version());
825 base::AutoLock
url_lock(url_lock_
);
826 // Update nodes_ordered_by_url_set_ from the nodes.
827 PopulateNodesByURL(&root_
);
832 loaded_signal_
.Signal();
834 // Notify our direct observers.
835 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
836 BookmarkModelLoaded(this, details
->ids_reassigned()));
839 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode
* delete_me
) {
840 scoped_ptr
<BookmarkNode
> node(delete_me
);
842 const BookmarkNode
* parent
= node
->parent();
844 int index
= parent
->GetIndexOf(node
.get());
846 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
847 OnWillRemoveBookmarks(this, parent
, index
, node
.get()));
849 std::set
<GURL
> removed_urls
;
851 base::AutoLock
url_lock(url_lock_
);
852 RemoveNodeAndGetRemovedUrls(node
.get(), &removed_urls
);
856 store_
->ScheduleSave();
858 NotifyHistoryAboutRemovedBookmarks(removed_urls
);
860 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
861 BookmarkNodeRemoved(this, parent
, index
, node
.get()));
864 void BookmarkModel::RemoveNodeFromURLSet(BookmarkNode
* node
) {
865 // NOTE: this is called in such a way that url_lock_ is already held. As
866 // such, this doesn't explicitly grab the lock.
867 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(node
);
868 DCHECK(i
!= nodes_ordered_by_url_set_
.end());
869 // i points to the first node with the URL, advance until we find the
870 // node we're removing.
873 nodes_ordered_by_url_set_
.erase(i
);
876 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode
* node
,
877 std::set
<GURL
>* removed_urls
) {
878 // NOTE: this method should be always called with |url_lock_| held.
879 // This method does not explicitly acquires a lock.
880 url_lock_
.AssertAcquired();
881 DCHECK(removed_urls
);
882 BookmarkNode
* parent
= AsMutable(node
->parent());
884 parent
->Remove(node
);
885 RemoveNode(node
, removed_urls
);
886 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
887 // allow duplicates we need to remove any entries that are still bookmarked.
888 for (std::set
<GURL
>::iterator i
= removed_urls
->begin();
889 i
!= removed_urls
->end();) {
890 if (IsBookmarkedNoLock(*i
)) {
891 // When we erase the iterator pointing at the erasee is
892 // invalidated, so using i++ here within the "erase" call is
893 // important as it advances the iterator before passing the
894 // old value through to erase.
895 removed_urls
->erase(i
++);
902 void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
903 const std::set
<GURL
>& removed_bookmark_urls
) const {
904 if (removed_bookmark_urls
.empty()) {
905 // No point in sending out notification if the starred state didn't change.
910 HistoryService
* history
=
911 HistoryServiceFactory::GetForProfile(profile_
,
912 Profile::EXPLICIT_ACCESS
);
914 history
->URLsNoLongerBookmarked(removed_bookmark_urls
);
918 BookmarkNode
* BookmarkModel::AddNode(BookmarkNode
* parent
,
920 BookmarkNode
* node
) {
921 parent
->Add(node
, index
);
924 store_
->ScheduleSave();
926 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
927 BookmarkNodeAdded(this, parent
, index
));
934 const BookmarkNode
* BookmarkModel::GetNodeByID(const BookmarkNode
* node
,
936 if (node
->id() == id
)
939 for (int i
= 0, child_count
= node
->child_count(); i
< child_count
; ++i
) {
940 const BookmarkNode
* result
= GetNodeByID(node
->GetChild(i
), id
);
947 bool BookmarkModel::IsValidIndex(const BookmarkNode
* parent
,
950 return (parent
&& parent
->is_folder() &&
951 (index
>= 0 && (index
< parent
->child_count() ||
952 (allow_end
&& index
== parent
->child_count()))));
955 BookmarkPermanentNode
* BookmarkModel::CreatePermanentNode(
956 BookmarkNode::Type type
) {
957 DCHECK(type
== BookmarkNode::BOOKMARK_BAR
||
958 type
== BookmarkNode::OTHER_NODE
||
959 type
== BookmarkNode::MOBILE
);
960 BookmarkPermanentNode
* node
=
961 new BookmarkPermanentNode(generate_next_node_id());
962 if (type
== BookmarkNode::MOBILE
)
963 node
->set_visible(false); // Mobile node is initially hidden.
967 case BookmarkNode::BOOKMARK_BAR
:
968 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
970 case BookmarkNode::OTHER_NODE
:
971 title_id
= IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME
;
973 case BookmarkNode::MOBILE
:
974 title_id
= IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME
;
978 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
981 node
->SetTitle(l10n_util::GetStringUTF16(title_id
));
982 node
->set_type(type
);
986 void BookmarkModel::OnFaviconDataAvailable(
988 const chrome::FaviconImageResult
& image_result
) {
990 node
->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId
);
991 node
->set_favicon_state(BookmarkNode::LOADED_FAVICON
);
992 if (!image_result
.image
.IsEmpty()) {
993 node
->set_favicon(image_result
.image
);
994 node
->set_icon_url(image_result
.icon_url
);
999 void BookmarkModel::LoadFavicon(BookmarkNode
* node
) {
1000 if (node
->is_folder())
1003 DCHECK(node
->url().is_valid());
1004 FaviconService
* favicon_service
= FaviconServiceFactory::GetForProfile(
1005 profile_
, Profile::EXPLICIT_ACCESS
);
1006 if (!favicon_service
)
1008 FaviconService::Handle handle
= favicon_service
->GetFaviconImageForURL(
1009 FaviconService::FaviconForURLParams(node
->url(),
1012 base::Bind(&BookmarkModel::OnFaviconDataAvailable
,
1013 base::Unretained(this), node
),
1014 &cancelable_task_tracker_
);
1015 node
->set_favicon_load_task_id(handle
);
1018 void BookmarkModel::FaviconLoaded(const BookmarkNode
* node
) {
1019 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
1020 BookmarkNodeFaviconChanged(this, node
));
1023 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode
* node
) {
1024 if (node
->favicon_load_task_id() != CancelableTaskTracker::kBadTaskId
) {
1025 cancelable_task_tracker_
.TryCancel(node
->favicon_load_task_id());
1026 node
->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId
);
1030 void BookmarkModel::Observe(int type
,
1031 const content::NotificationSource
& source
,
1032 const content::NotificationDetails
& details
) {
1034 case chrome::NOTIFICATION_FAVICON_CHANGED
: {
1035 // Prevent the observers from getting confused for multiple favicon loads.
1036 content::Details
<FaviconChangedDetails
> favicon_details(details
);
1037 for (std::set
<GURL
>::const_iterator i
= favicon_details
->urls
.begin();
1038 i
!= favicon_details
->urls
.end(); ++i
) {
1039 std::vector
<const BookmarkNode
*> nodes
;
1040 GetNodesByURL(*i
, &nodes
);
1041 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
1042 // Got an updated favicon, for a URL, do a new request.
1043 BookmarkNode
* node
= AsMutable(nodes
[i
]);
1044 node
->InvalidateFavicon();
1045 CancelPendingFaviconLoadRequests(node
);
1046 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
1047 BookmarkNodeFaviconChanged(this, node
));
1059 void BookmarkModel::PopulateNodesByURL(BookmarkNode
* node
) {
1060 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1061 // explicitly grab the lock.
1063 nodes_ordered_by_url_set_
.insert(node
);
1064 for (int i
= 0; i
< node
->child_count(); ++i
)
1065 PopulateNodesByURL(node
->GetChild(i
));
1068 int64
BookmarkModel::generate_next_node_id() {
1069 return next_node_id_
++;
1072 BookmarkLoadDetails
* BookmarkModel::CreateLoadDetails() {
1073 BookmarkPermanentNode
* bb_node
=
1074 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR
);
1075 BookmarkPermanentNode
* other_node
=
1076 CreatePermanentNode(BookmarkNode::OTHER_NODE
);
1077 BookmarkPermanentNode
* mobile_node
=
1078 CreatePermanentNode(BookmarkNode::MOBILE
);
1079 return new BookmarkLoadDetails(bb_node
, other_node
, mobile_node
,
1080 new BookmarkIndex(profile_
),