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::BookmarkModelObserver
;
35 using bookmarks::BookmarkNodeData
;
36 using bookmarks::BookmarkStorage
;
40 // Helper to get a mutable bookmark node.
41 BookmarkNode
* AsMutable(const BookmarkNode
* node
) {
42 return const_cast<BookmarkNode
*>(node
);
45 // Helper to get a mutable permanent bookmark node.
46 BookmarkPermanentNode
* AsMutable(const BookmarkPermanentNode
* node
) {
47 return const_cast<BookmarkPermanentNode
*>(node
);
50 // Comparator used when sorting permanent nodes. Nodes that are initially
51 // visible are sorted before nodes that are initially hidden.
52 class VisibilityComparator
53 : public std::binary_function
<const BookmarkPermanentNode
*,
54 const BookmarkPermanentNode
*,
57 explicit VisibilityComparator(BookmarkClient
* client
) : client_(client
) {}
59 // Returns true if |n1| preceeds |n2|.
60 bool operator()(const BookmarkPermanentNode
* n1
,
61 const BookmarkPermanentNode
* n2
) {
62 bool n1_visible
= client_
->IsPermanentNodeVisible(n1
);
63 bool n2_visible
= client_
->IsPermanentNodeVisible(n2
);
64 return n1_visible
!= n2_visible
&& n1_visible
;
68 BookmarkClient
* client_
;
71 // Comparator used when sorting bookmarks. Folders are sorted first, then
73 class SortComparator
: public std::binary_function
<const BookmarkNode
*,
77 explicit SortComparator(icu::Collator
* collator
) : collator_(collator
) {}
79 // Returns true if |n1| preceeds |n2|.
80 bool operator()(const BookmarkNode
* n1
, const BookmarkNode
* n2
) {
81 if (n1
->type() == n2
->type()) {
82 // Types are the same, compare the names.
84 return n1
->GetTitle() < n2
->GetTitle();
85 return base::i18n::CompareString16WithCollator(
86 collator_
, n1
->GetTitle(), n2
->GetTitle()) == UCOL_LESS
;
88 // Types differ, sort such that folders come first.
89 return n1
->is_folder();
93 icu::Collator
* collator_
;
98 // BookmarkModel --------------------------------------------------------------
100 BookmarkModel::BookmarkModel(BookmarkClient
* client
)
104 bookmark_bar_node_(NULL
),
108 observers_(ObserverList
<BookmarkModelObserver
>::NOTIFY_EXISTING_ONLY
),
109 loaded_signal_(true, false),
110 extensive_changes_(0) {
114 BookmarkModel::~BookmarkModel() {
115 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
116 BookmarkModelBeingDeleted(this));
119 // The store maintains a reference back to us. We need to tell it we're gone
120 // so that it doesn't try and invoke a method back on us again.
121 store_
->BookmarkModelDeleted();
125 void BookmarkModel::Shutdown() {
129 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
130 // details. It is also called when the BookmarkModel is deleted.
131 loaded_signal_
.Signal();
134 void BookmarkModel::Load(
135 PrefService
* pref_service
,
136 const std::string
& accept_languages
,
137 const base::FilePath
& profile_path
,
138 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
,
139 const scoped_refptr
<base::SequencedTaskRunner
>& ui_task_runner
) {
141 // If the store is non-null, it means Load was already invoked. Load should
142 // only be invoked once.
147 expanded_state_tracker_
.reset(
148 new BookmarkExpandedStateTracker(this, pref_service
));
150 // Load the bookmarks. BookmarkStorage notifies us when done.
151 store_
.reset(new BookmarkStorage(this, profile_path
, io_task_runner
.get()));
152 store_
->LoadBookmarks(CreateLoadDetails(accept_languages
), ui_task_runner
);
155 const BookmarkNode
* BookmarkModel::GetParentForNewNodes() {
156 std::vector
<const BookmarkNode
*> nodes
=
157 bookmarks::GetMostRecentlyModifiedUserFolders(this, 1);
158 DCHECK(!nodes
.empty()); // This list is always padded with default folders.
162 void BookmarkModel::AddObserver(BookmarkModelObserver
* observer
) {
163 observers_
.AddObserver(observer
);
166 void BookmarkModel::RemoveObserver(BookmarkModelObserver
* observer
) {
167 observers_
.RemoveObserver(observer
);
170 void BookmarkModel::BeginExtensiveChanges() {
171 if (++extensive_changes_
== 1) {
172 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
173 ExtensiveBookmarkChangesBeginning(this));
177 void BookmarkModel::EndExtensiveChanges() {
178 --extensive_changes_
;
179 DCHECK_GE(extensive_changes_
, 0);
180 if (extensive_changes_
== 0) {
181 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
182 ExtensiveBookmarkChangesEnded(this));
186 void BookmarkModel::BeginGroupedChanges() {
187 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
188 GroupedBookmarkChangesBeginning(this));
191 void BookmarkModel::EndGroupedChanges() {
192 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
193 GroupedBookmarkChangesEnded(this));
196 void BookmarkModel::Remove(const BookmarkNode
* parent
, int index
) {
197 if (!loaded_
|| !IsValidIndex(parent
, index
, false) || is_root_node(parent
)) {
201 RemoveAndDeleteNode(AsMutable(parent
->GetChild(index
)));
204 void BookmarkModel::RemoveAllUserBookmarks() {
205 std::set
<GURL
> removed_urls
;
206 ScopedVector
<BookmarkNode
> removed_nodes
;
208 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
209 OnWillRemoveAllUserBookmarks(this));
211 BeginExtensiveChanges();
212 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
213 // its immediate children. For removing all non permanent nodes just remove
214 // all children of non-root permanent nodes.
216 base::AutoLock
url_lock(url_lock_
);
217 for (int i
= 0; i
< root_
.child_count(); ++i
) {
218 BookmarkNode
* permanent_node
= root_
.GetChild(i
);
220 if (!client_
->CanBeEditedByUser(permanent_node
))
223 for (int j
= permanent_node
->child_count() - 1; j
>= 0; --j
) {
224 BookmarkNode
* child_node
= permanent_node
->GetChild(j
);
225 removed_nodes
.push_back(child_node
);
226 RemoveNodeAndGetRemovedUrls(child_node
, &removed_urls
);
230 EndExtensiveChanges();
232 store_
->ScheduleSave();
234 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
235 BookmarkAllUserNodesRemoved(this, removed_urls
));
238 void BookmarkModel::Move(const BookmarkNode
* node
,
239 const BookmarkNode
* new_parent
,
241 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
242 is_root_node(new_parent
) || is_permanent_node(node
)) {
247 if (new_parent
->HasAncestor(node
)) {
248 // Can't make an ancestor of the node be a child of the node.
253 const BookmarkNode
* old_parent
= node
->parent();
254 int old_index
= old_parent
->GetIndexOf(node
);
256 if (old_parent
== new_parent
&&
257 (index
== old_index
|| index
== old_index
+ 1)) {
258 // Node is already in this position, nothing to do.
262 SetDateFolderModified(new_parent
, Time::Now());
264 if (old_parent
== new_parent
&& index
> old_index
)
266 BookmarkNode
* mutable_new_parent
= AsMutable(new_parent
);
267 mutable_new_parent
->Add(AsMutable(node
), index
);
270 store_
->ScheduleSave();
272 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
273 BookmarkNodeMoved(this, old_parent
, old_index
,
277 void BookmarkModel::Copy(const BookmarkNode
* node
,
278 const BookmarkNode
* new_parent
,
280 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
281 is_root_node(new_parent
) || is_permanent_node(node
)) {
286 if (new_parent
->HasAncestor(node
)) {
287 // Can't make an ancestor of the node be a child of the node.
292 SetDateFolderModified(new_parent
, Time::Now());
293 BookmarkNodeData
drag_data(node
);
294 std::vector
<BookmarkNodeData::Element
> elements(drag_data
.elements
);
295 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
296 // don't need to send notifications here.
297 bookmarks::CloneBookmarkNode(this, elements
, new_parent
, index
, true);
300 store_
->ScheduleSave();
303 const gfx::Image
& BookmarkModel::GetFavicon(const BookmarkNode
* node
) {
305 if (node
->favicon_state() == BookmarkNode::INVALID_FAVICON
) {
306 BookmarkNode
* mutable_node
= AsMutable(node
);
307 LoadFavicon(mutable_node
,
308 client_
->PreferTouchIcon() ? favicon_base::TOUCH_ICON
309 : favicon_base::FAVICON
);
311 return node
->favicon();
314 favicon_base::IconType
BookmarkModel::GetFaviconType(const BookmarkNode
* node
) {
316 return node
->favicon_type();
319 void BookmarkModel::SetTitle(const BookmarkNode
* node
,
320 const base::string16
& title
) {
325 if (node
->GetTitle() == title
)
328 if (is_permanent_node(node
) && !client_
->CanSetPermanentNodeTitle(node
)) {
333 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
334 OnWillChangeBookmarkNode(this, node
));
336 // The title index doesn't support changing the title, instead we remove then
338 index_
->Remove(node
);
339 AsMutable(node
)->SetTitle(title
);
343 store_
->ScheduleSave();
345 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
346 BookmarkNodeChanged(this, node
));
349 void BookmarkModel::SetURL(const BookmarkNode
* node
, const GURL
& url
) {
355 // We cannot change the URL of a folder.
356 if (node
->is_folder()) {
361 if (node
->url() == url
)
364 BookmarkNode
* mutable_node
= AsMutable(node
);
365 mutable_node
->InvalidateFavicon();
366 CancelPendingFaviconLoadRequests(mutable_node
);
368 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
369 OnWillChangeBookmarkNode(this, node
));
372 base::AutoLock
url_lock(url_lock_
);
373 RemoveNodeFromInternalMaps(mutable_node
);
374 mutable_node
->set_url(url
);
375 AddNodeToInternalMaps(mutable_node
);
379 store_
->ScheduleSave();
381 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
382 BookmarkNodeChanged(this, node
));
385 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode
* node
,
386 const std::string
& key
,
387 const std::string
& value
) {
388 std::string old_value
;
389 if (node
->GetMetaInfo(key
, &old_value
) && old_value
== value
)
392 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
393 OnWillChangeBookmarkMetaInfo(this, node
));
395 if (AsMutable(node
)->SetMetaInfo(key
, value
) && store_
.get())
396 store_
->ScheduleSave();
398 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
399 BookmarkMetaInfoChanged(this, node
));
402 void BookmarkModel::SetNodeMetaInfoMap(
403 const BookmarkNode
* node
,
404 const BookmarkNode::MetaInfoMap
& meta_info_map
) {
405 const BookmarkNode::MetaInfoMap
* old_meta_info_map
= node
->GetMetaInfoMap();
406 if ((!old_meta_info_map
&& meta_info_map
.empty()) ||
407 (old_meta_info_map
&& meta_info_map
== *old_meta_info_map
))
410 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
411 OnWillChangeBookmarkMetaInfo(this, node
));
413 AsMutable(node
)->SetMetaInfoMap(meta_info_map
);
415 store_
->ScheduleSave();
417 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
418 BookmarkMetaInfoChanged(this, node
));
421 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode
* node
,
422 const std::string
& key
) {
423 const BookmarkNode::MetaInfoMap
* meta_info_map
= node
->GetMetaInfoMap();
424 if (!meta_info_map
|| meta_info_map
->find(key
) == meta_info_map
->end())
427 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
428 OnWillChangeBookmarkMetaInfo(this, node
));
430 if (AsMutable(node
)->DeleteMetaInfo(key
) && store_
.get())
431 store_
->ScheduleSave();
433 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
434 BookmarkMetaInfoChanged(this, node
));
437 void BookmarkModel::AddNonClonedKey(const std::string
& key
) {
438 non_cloned_keys_
.insert(key
);
441 void BookmarkModel::SetNodeSyncTransactionVersion(
442 const BookmarkNode
* node
,
443 int64 sync_transaction_version
) {
444 DCHECK(client_
->CanSyncNode(node
));
446 if (sync_transaction_version
== node
->sync_transaction_version())
449 AsMutable(node
)->set_sync_transaction_version(sync_transaction_version
);
451 store_
->ScheduleSave();
454 void BookmarkModel::OnFaviconChanged(const std::set
<GURL
>& urls
) {
455 // Ignore events if |Load| has not been called yet.
459 // Prevent the observers from getting confused for multiple favicon loads.
460 for (std::set
<GURL
>::const_iterator i
= urls
.begin(); i
!= urls
.end(); ++i
) {
461 std::vector
<const BookmarkNode
*> nodes
;
462 GetNodesByURL(*i
, &nodes
);
463 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
464 // Got an updated favicon, for a URL, do a new request.
465 BookmarkNode
* node
= AsMutable(nodes
[i
]);
466 node
->InvalidateFavicon();
467 CancelPendingFaviconLoadRequests(node
);
468 FOR_EACH_OBSERVER(BookmarkModelObserver
,
470 BookmarkNodeFaviconChanged(this, node
));
475 void BookmarkModel::SetDateAdded(const BookmarkNode
* node
,
482 if (node
->date_added() == date_added
)
485 if (is_permanent_node(node
)) {
490 AsMutable(node
)->set_date_added(date_added
);
492 // Syncing might result in dates newer than the folder's last modified date.
493 if (date_added
> node
->parent()->date_folder_modified()) {
494 // Will trigger store_->ScheduleSave().
495 SetDateFolderModified(node
->parent(), date_added
);
496 } else if (store_
.get()) {
497 store_
->ScheduleSave();
501 void BookmarkModel::GetNodesByURL(const GURL
& url
,
502 std::vector
<const BookmarkNode
*>* nodes
) {
503 base::AutoLock
url_lock(url_lock_
);
504 BookmarkNode
tmp_node(url
);
505 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(&tmp_node
);
506 while (i
!= nodes_ordered_by_url_set_
.end() && (*i
)->url() == url
) {
507 nodes
->push_back(*i
);
512 const BookmarkNode
* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
514 std::vector
<const BookmarkNode
*> nodes
;
515 GetNodesByURL(url
, &nodes
);
516 std::sort(nodes
.begin(), nodes
.end(), &bookmarks::MoreRecentlyAdded
);
518 // Look for the first node that the user can edit.
519 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
520 if (client_
->CanBeEditedByUser(nodes
[i
]))
527 bool BookmarkModel::HasBookmarks() {
528 base::AutoLock
url_lock(url_lock_
);
529 return !nodes_ordered_by_url_set_
.empty();
532 bool BookmarkModel::IsBookmarked(const GURL
& url
) {
533 base::AutoLock
url_lock(url_lock_
);
534 return IsBookmarkedNoLock(url
);
537 void BookmarkModel::GetBookmarks(
538 std::vector
<BookmarkModel::URLAndTitle
>* bookmarks
) {
539 base::AutoLock
url_lock(url_lock_
);
540 const GURL
* last_url
= NULL
;
541 for (NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.begin();
542 i
!= nodes_ordered_by_url_set_
.end(); ++i
) {
543 const GURL
* url
= &((*i
)->url());
544 // Only add unique URLs.
545 if (!last_url
|| *url
!= *last_url
) {
546 BookmarkModel::URLAndTitle bookmark
;
548 bookmark
.title
= (*i
)->GetTitle();
549 bookmarks
->push_back(bookmark
);
555 void BookmarkModel::BlockTillLoaded() {
556 loaded_signal_
.Wait();
559 const BookmarkNode
* BookmarkModel::AddFolder(const BookmarkNode
* parent
,
561 const base::string16
& title
) {
562 return AddFolderWithMetaInfo(parent
, index
, title
, NULL
);
564 const BookmarkNode
* BookmarkModel::AddFolderWithMetaInfo(
565 const BookmarkNode
* parent
,
567 const base::string16
& title
,
568 const BookmarkNode::MetaInfoMap
* meta_info
) {
569 if (!loaded_
|| is_root_node(parent
) || !IsValidIndex(parent
, index
, true)) {
570 // Can't add to the root.
575 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), GURL());
576 new_node
->set_date_folder_modified(Time::Now());
577 // Folders shouldn't have line breaks in their titles.
578 new_node
->SetTitle(title
);
579 new_node
->set_type(BookmarkNode::FOLDER
);
581 new_node
->SetMetaInfoMap(*meta_info
);
583 return AddNode(AsMutable(parent
), index
, new_node
);
586 const BookmarkNode
* BookmarkModel::AddURL(const BookmarkNode
* parent
,
588 const base::string16
& title
,
590 return AddURLWithCreationTimeAndMetaInfo(
593 base::CollapseWhitespace(title
, false),
599 const BookmarkNode
* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
600 const BookmarkNode
* parent
,
602 const base::string16
& title
,
604 const Time
& creation_time
,
605 const BookmarkNode::MetaInfoMap
* meta_info
) {
606 if (!loaded_
|| !url
.is_valid() || is_root_node(parent
) ||
607 !IsValidIndex(parent
, index
, true)) {
612 // Syncing may result in dates newer than the last modified date.
613 if (creation_time
> parent
->date_folder_modified())
614 SetDateFolderModified(parent
, creation_time
);
616 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), url
);
617 new_node
->SetTitle(title
);
618 new_node
->set_date_added(creation_time
);
619 new_node
->set_type(BookmarkNode::URL
);
621 new_node
->SetMetaInfoMap(*meta_info
);
623 return AddNode(AsMutable(parent
), index
, new_node
);
626 void BookmarkModel::SortChildren(const BookmarkNode
* parent
) {
627 DCHECK(client_
->CanBeEditedByUser(parent
));
629 if (!parent
|| !parent
->is_folder() || is_root_node(parent
) ||
630 parent
->child_count() <= 1) {
634 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
635 OnWillReorderBookmarkNode(this, parent
));
637 UErrorCode error
= U_ZERO_ERROR
;
638 scoped_ptr
<icu::Collator
> collator(icu::Collator::createInstance(error
));
639 if (U_FAILURE(error
))
640 collator
.reset(NULL
);
641 BookmarkNode
* mutable_parent
= AsMutable(parent
);
642 std::sort(mutable_parent
->children().begin(),
643 mutable_parent
->children().end(),
644 SortComparator(collator
.get()));
647 store_
->ScheduleSave();
649 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
650 BookmarkNodeChildrenReordered(this, parent
));
653 void BookmarkModel::ReorderChildren(
654 const BookmarkNode
* parent
,
655 const std::vector
<const BookmarkNode
*>& ordered_nodes
) {
656 DCHECK(client_
->CanBeEditedByUser(parent
));
658 // Ensure that all children in |parent| are in |ordered_nodes|.
659 DCHECK_EQ(static_cast<size_t>(parent
->child_count()), ordered_nodes
.size());
660 for (size_t i
= 0; i
< ordered_nodes
.size(); ++i
)
661 DCHECK_EQ(parent
, ordered_nodes
[i
]->parent());
663 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
664 OnWillReorderBookmarkNode(this, parent
));
666 AsMutable(parent
)->SetChildren(
667 *(reinterpret_cast<const std::vector
<BookmarkNode
*>*>(&ordered_nodes
)));
670 store_
->ScheduleSave();
672 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
673 BookmarkNodeChildrenReordered(this, parent
));
676 void BookmarkModel::SetDateFolderModified(const BookmarkNode
* parent
,
679 AsMutable(parent
)->set_date_folder_modified(time
);
682 store_
->ScheduleSave();
685 void BookmarkModel::ResetDateFolderModified(const BookmarkNode
* node
) {
686 SetDateFolderModified(node
, Time());
689 void BookmarkModel::GetBookmarksMatching(const base::string16
& text
,
691 std::vector
<BookmarkMatch
>* matches
) {
692 GetBookmarksMatching(text
, max_count
,
693 query_parser::MatchingAlgorithm::DEFAULT
, matches
);
696 void BookmarkModel::GetBookmarksMatching(
697 const base::string16
& text
,
699 query_parser::MatchingAlgorithm matching_algorithm
,
700 std::vector
<BookmarkMatch
>* matches
) {
704 index_
->GetBookmarksMatching(text
, max_count
, matching_algorithm
, matches
);
707 void BookmarkModel::ClearStore() {
711 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type
,
713 BookmarkPermanentNode
* node
= AsMutable(PermanentNode(type
));
714 node
->set_visible(value
|| client_
->IsPermanentNodeVisible(node
));
717 const BookmarkPermanentNode
* BookmarkModel::PermanentNode(
718 BookmarkNode::Type type
) {
721 case BookmarkNode::BOOKMARK_BAR
:
722 return bookmark_bar_node_
;
723 case BookmarkNode::OTHER_NODE
:
725 case BookmarkNode::MOBILE
:
733 bool BookmarkModel::IsBookmarkedNoLock(const GURL
& url
) {
734 BookmarkNode
tmp_node(url
);
735 return (nodes_ordered_by_url_set_
.find(&tmp_node
) !=
736 nodes_ordered_by_url_set_
.end());
739 void BookmarkModel::RemoveNode(BookmarkNode
* node
,
740 std::set
<GURL
>* removed_urls
) {
741 if (!loaded_
|| !node
|| is_permanent_node(node
)) {
746 url_lock_
.AssertAcquired();
747 if (node
->is_url()) {
748 RemoveNodeFromInternalMaps(node
);
749 removed_urls
->insert(node
->url());
752 CancelPendingFaviconLoadRequests(node
);
754 // Recurse through children.
755 for (int i
= node
->child_count() - 1; i
>= 0; --i
)
756 RemoveNode(node
->GetChild(i
), removed_urls
);
759 void BookmarkModel::DoneLoading(scoped_ptr
<BookmarkLoadDetails
> details
) {
762 // We should only ever be loaded once.
767 next_node_id_
= details
->max_id();
768 if (details
->computed_checksum() != details
->stored_checksum() ||
769 details
->ids_reassigned()) {
770 // If bookmarks file changed externally, the IDs may have changed
771 // externally. In that case, the decoder may have reassigned IDs to make
772 // them unique. So when the file has changed externally, we should save the
773 // bookmarks file to persist new IDs.
775 store_
->ScheduleSave();
777 bookmark_bar_node_
= details
->release_bb_node();
778 other_node_
= details
->release_other_folder_node();
779 mobile_node_
= details
->release_mobile_folder_node();
780 index_
.reset(details
->release_index());
782 // Get any extra nodes and take ownership of them at the |root_|.
783 std::vector
<BookmarkPermanentNode
*> extra_nodes
;
784 details
->release_extra_nodes(&extra_nodes
);
786 // WARNING: order is important here, various places assume the order is
787 // constant (but can vary between embedders with the initial visibility
788 // of permanent nodes).
789 std::vector
<BookmarkPermanentNode
*> root_children
;
790 root_children
.push_back(bookmark_bar_node_
);
791 root_children
.push_back(other_node_
);
792 root_children
.push_back(mobile_node_
);
793 for (size_t i
= 0; i
< extra_nodes
.size(); ++i
)
794 root_children
.push_back(extra_nodes
[i
]);
795 std::stable_sort(root_children
.begin(),
797 VisibilityComparator(client_
));
798 for (size_t i
= 0; i
< root_children
.size(); ++i
)
799 root_
.Add(root_children
[i
], static_cast<int>(i
));
801 root_
.SetMetaInfoMap(details
->model_meta_info_map());
802 root_
.set_sync_transaction_version(details
->model_sync_transaction_version());
805 base::AutoLock
url_lock(url_lock_
);
806 // Update nodes_ordered_by_url_set_ from the nodes.
807 PopulateNodesByURL(&root_
);
812 loaded_signal_
.Signal();
814 // Notify our direct observers.
815 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
816 BookmarkModelLoaded(this, details
->ids_reassigned()));
819 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode
* delete_me
) {
820 scoped_ptr
<BookmarkNode
> node(delete_me
);
822 const BookmarkNode
* parent
= node
->parent();
824 int index
= parent
->GetIndexOf(node
.get());
826 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
827 OnWillRemoveBookmarks(this, parent
, index
, node
.get()));
829 std::set
<GURL
> removed_urls
;
831 base::AutoLock
url_lock(url_lock_
);
832 RemoveNodeAndGetRemovedUrls(node
.get(), &removed_urls
);
836 store_
->ScheduleSave();
839 BookmarkModelObserver
,
841 BookmarkNodeRemoved(this, parent
, index
, node
.get(), removed_urls
));
844 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode
* node
) {
845 index_
->Remove(node
);
846 // NOTE: this is called in such a way that url_lock_ is already held. As
847 // such, this doesn't explicitly grab the lock.
848 url_lock_
.AssertAcquired();
849 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(node
);
850 DCHECK(i
!= nodes_ordered_by_url_set_
.end());
851 // i points to the first node with the URL, advance until we find the
852 // node we're removing.
855 nodes_ordered_by_url_set_
.erase(i
);
858 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode
* node
,
859 std::set
<GURL
>* removed_urls
) {
860 // NOTE: this method should be always called with |url_lock_| held.
861 // This method does not explicitly acquires a lock.
862 url_lock_
.AssertAcquired();
863 DCHECK(removed_urls
);
864 BookmarkNode
* parent
= AsMutable(node
->parent());
866 parent
->Remove(node
);
867 RemoveNode(node
, removed_urls
);
868 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
869 // allow duplicates we need to remove any entries that are still bookmarked.
870 for (std::set
<GURL
>::iterator i
= removed_urls
->begin();
871 i
!= removed_urls
->end();) {
872 if (IsBookmarkedNoLock(*i
)) {
873 // When we erase the iterator pointing at the erasee is
874 // invalidated, so using i++ here within the "erase" call is
875 // important as it advances the iterator before passing the
876 // old value through to erase.
877 removed_urls
->erase(i
++);
884 BookmarkNode
* BookmarkModel::AddNode(BookmarkNode
* parent
,
886 BookmarkNode
* node
) {
887 parent
->Add(node
, index
);
890 store_
->ScheduleSave();
892 if (node
->type() == BookmarkNode::URL
) {
893 base::AutoLock
url_lock(url_lock_
);
894 AddNodeToInternalMaps(node
);
899 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
900 BookmarkNodeAdded(this, parent
, index
));
905 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode
* node
) {
907 url_lock_
.AssertAcquired();
908 nodes_ordered_by_url_set_
.insert(node
);
911 bool BookmarkModel::IsValidIndex(const BookmarkNode
* parent
,
914 return (parent
&& parent
->is_folder() &&
915 (index
>= 0 && (index
< parent
->child_count() ||
916 (allow_end
&& index
== parent
->child_count()))));
919 BookmarkPermanentNode
* BookmarkModel::CreatePermanentNode(
920 BookmarkNode::Type type
) {
921 DCHECK(type
== BookmarkNode::BOOKMARK_BAR
||
922 type
== BookmarkNode::OTHER_NODE
||
923 type
== BookmarkNode::MOBILE
);
924 BookmarkPermanentNode
* node
=
925 new BookmarkPermanentNode(generate_next_node_id());
926 node
->set_type(type
);
927 node
->set_visible(client_
->IsPermanentNodeVisible(node
));
931 case BookmarkNode::BOOKMARK_BAR
:
932 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
934 case BookmarkNode::OTHER_NODE
:
935 title_id
= IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME
;
937 case BookmarkNode::MOBILE
:
938 title_id
= IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME
;
942 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
945 node
->SetTitle(l10n_util::GetStringUTF16(title_id
));
949 void BookmarkModel::OnFaviconDataAvailable(
951 favicon_base::IconType icon_type
,
952 const favicon_base::FaviconImageResult
& image_result
) {
954 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
955 node
->set_favicon_state(BookmarkNode::LOADED_FAVICON
);
956 if (!image_result
.image
.IsEmpty()) {
957 node
->set_favicon_type(icon_type
);
958 node
->set_favicon(image_result
.image
);
959 node
->set_icon_url(image_result
.icon_url
);
961 } else if (icon_type
== favicon_base::TOUCH_ICON
) {
962 // Couldn't load the touch icon, fallback to the regular favicon.
963 DCHECK(client_
->PreferTouchIcon());
964 LoadFavicon(node
, favicon_base::FAVICON
);
968 void BookmarkModel::LoadFavicon(BookmarkNode
* node
,
969 favicon_base::IconType icon_type
) {
970 if (node
->is_folder())
973 DCHECK(node
->url().is_valid());
974 node
->set_favicon_state(BookmarkNode::LOADING_FAVICON
);
975 base::CancelableTaskTracker::TaskId taskId
=
976 client_
->GetFaviconImageForPageURL(
980 &BookmarkModel::OnFaviconDataAvailable
,
981 base::Unretained(this),
984 &cancelable_task_tracker_
);
985 if (taskId
!= base::CancelableTaskTracker::kBadTaskId
)
986 node
->set_favicon_load_task_id(taskId
);
989 void BookmarkModel::FaviconLoaded(const BookmarkNode
* node
) {
990 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
991 BookmarkNodeFaviconChanged(this, node
));
994 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode
* node
) {
995 if (node
->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId
) {
996 cancelable_task_tracker_
.TryCancel(node
->favicon_load_task_id());
997 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
1001 void BookmarkModel::PopulateNodesByURL(BookmarkNode
* node
) {
1002 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1003 // explicitly grab the lock.
1005 nodes_ordered_by_url_set_
.insert(node
);
1006 for (int i
= 0; i
< node
->child_count(); ++i
)
1007 PopulateNodesByURL(node
->GetChild(i
));
1010 int64
BookmarkModel::generate_next_node_id() {
1011 return next_node_id_
++;
1014 scoped_ptr
<BookmarkLoadDetails
> BookmarkModel::CreateLoadDetails(
1015 const std::string
& accept_languages
) {
1016 BookmarkPermanentNode
* bb_node
=
1017 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR
);
1018 BookmarkPermanentNode
* other_node
=
1019 CreatePermanentNode(BookmarkNode::OTHER_NODE
);
1020 BookmarkPermanentNode
* mobile_node
=
1021 CreatePermanentNode(BookmarkNode::MOBILE
);
1022 return scoped_ptr
<BookmarkLoadDetails
>(new BookmarkLoadDetails(
1026 client_
->GetLoadExtraNodesCallback(),
1027 new BookmarkIndex(client_
, accept_languages
),