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 RemoveNodeFromURLSet(mutable_node
);
375 mutable_node
->set_url(url
);
376 nodes_ordered_by_url_set_
.insert(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
);
621 // Only hold the lock for the duration of the insert.
622 base::AutoLock
url_lock(url_lock_
);
623 nodes_ordered_by_url_set_
.insert(new_node
);
626 return AddNode(AsMutable(parent
), index
, new_node
);
629 void BookmarkModel::SortChildren(const BookmarkNode
* parent
) {
630 DCHECK(client_
->CanBeEditedByUser(parent
));
632 if (!parent
|| !parent
->is_folder() || is_root_node(parent
) ||
633 parent
->child_count() <= 1) {
637 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
638 OnWillReorderBookmarkNode(this, parent
));
640 UErrorCode error
= U_ZERO_ERROR
;
641 scoped_ptr
<icu::Collator
> collator(icu::Collator::createInstance(error
));
642 if (U_FAILURE(error
))
643 collator
.reset(NULL
);
644 BookmarkNode
* mutable_parent
= AsMutable(parent
);
645 std::sort(mutable_parent
->children().begin(),
646 mutable_parent
->children().end(),
647 SortComparator(collator
.get()));
650 store_
->ScheduleSave();
652 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
653 BookmarkNodeChildrenReordered(this, parent
));
656 void BookmarkModel::ReorderChildren(
657 const BookmarkNode
* parent
,
658 const std::vector
<const BookmarkNode
*>& ordered_nodes
) {
659 DCHECK(client_
->CanBeEditedByUser(parent
));
661 // Ensure that all children in |parent| are in |ordered_nodes|.
662 DCHECK_EQ(static_cast<size_t>(parent
->child_count()), ordered_nodes
.size());
663 for (size_t i
= 0; i
< ordered_nodes
.size(); ++i
)
664 DCHECK_EQ(parent
, ordered_nodes
[i
]->parent());
666 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
667 OnWillReorderBookmarkNode(this, parent
));
669 AsMutable(parent
)->SetChildren(
670 *(reinterpret_cast<const std::vector
<BookmarkNode
*>*>(&ordered_nodes
)));
673 store_
->ScheduleSave();
675 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
676 BookmarkNodeChildrenReordered(this, parent
));
679 void BookmarkModel::SetDateFolderModified(const BookmarkNode
* parent
,
682 AsMutable(parent
)->set_date_folder_modified(time
);
685 store_
->ScheduleSave();
688 void BookmarkModel::ResetDateFolderModified(const BookmarkNode
* node
) {
689 SetDateFolderModified(node
, Time());
692 void BookmarkModel::GetBookmarksMatching(
693 const base::string16
& text
,
695 std::vector
<BookmarkMatch
>* matches
) {
699 index_
->GetBookmarksMatching(text
, max_count
, matches
);
702 void BookmarkModel::ClearStore() {
706 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type
,
708 BookmarkPermanentNode
* node
= AsMutable(PermanentNode(type
));
709 node
->set_visible(value
|| client_
->IsPermanentNodeVisible(node
));
712 const BookmarkPermanentNode
* BookmarkModel::PermanentNode(
713 BookmarkNode::Type type
) {
716 case BookmarkNode::BOOKMARK_BAR
:
717 return bookmark_bar_node_
;
718 case BookmarkNode::OTHER_NODE
:
720 case BookmarkNode::MOBILE
:
728 bool BookmarkModel::IsBookmarkedNoLock(const GURL
& url
) {
729 BookmarkNode
tmp_node(url
);
730 return (nodes_ordered_by_url_set_
.find(&tmp_node
) !=
731 nodes_ordered_by_url_set_
.end());
734 void BookmarkModel::RemoveNode(BookmarkNode
* node
,
735 std::set
<GURL
>* removed_urls
) {
736 if (!loaded_
|| !node
|| is_permanent_node(node
)) {
741 url_lock_
.AssertAcquired();
742 if (node
->is_url()) {
743 RemoveNodeFromURLSet(node
);
744 removed_urls
->insert(node
->url());
745 index_
->Remove(node
);
748 CancelPendingFaviconLoadRequests(node
);
750 // Recurse through children.
751 for (int i
= node
->child_count() - 1; i
>= 0; --i
)
752 RemoveNode(node
->GetChild(i
), removed_urls
);
755 void BookmarkModel::DoneLoading(scoped_ptr
<BookmarkLoadDetails
> details
) {
758 // We should only ever be loaded once.
763 next_node_id_
= details
->max_id();
764 if (details
->computed_checksum() != details
->stored_checksum() ||
765 details
->ids_reassigned()) {
766 // If bookmarks file changed externally, the IDs may have changed
767 // externally. In that case, the decoder may have reassigned IDs to make
768 // them unique. So when the file has changed externally, we should save the
769 // bookmarks file to persist new IDs.
771 store_
->ScheduleSave();
773 bookmark_bar_node_
= details
->release_bb_node();
774 other_node_
= details
->release_other_folder_node();
775 mobile_node_
= details
->release_mobile_folder_node();
776 index_
.reset(details
->release_index());
778 // Get any extra nodes and take ownership of them at the |root_|.
779 std::vector
<BookmarkPermanentNode
*> extra_nodes
;
780 details
->release_extra_nodes(&extra_nodes
);
782 // WARNING: order is important here, various places assume the order is
783 // constant (but can vary between embedders with the initial visibility
784 // of permanent nodes).
785 std::vector
<BookmarkPermanentNode
*> root_children
;
786 root_children
.push_back(bookmark_bar_node_
);
787 root_children
.push_back(other_node_
);
788 root_children
.push_back(mobile_node_
);
789 for (size_t i
= 0; i
< extra_nodes
.size(); ++i
)
790 root_children
.push_back(extra_nodes
[i
]);
791 std::stable_sort(root_children
.begin(),
793 VisibilityComparator(client_
));
794 for (size_t i
= 0; i
< root_children
.size(); ++i
)
795 root_
.Add(root_children
[i
], static_cast<int>(i
));
797 root_
.SetMetaInfoMap(details
->model_meta_info_map());
798 root_
.set_sync_transaction_version(details
->model_sync_transaction_version());
801 base::AutoLock
url_lock(url_lock_
);
802 // Update nodes_ordered_by_url_set_ from the nodes.
803 PopulateNodesByURL(&root_
);
808 loaded_signal_
.Signal();
810 // Notify our direct observers.
811 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
812 BookmarkModelLoaded(this, details
->ids_reassigned()));
815 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode
* delete_me
) {
816 scoped_ptr
<BookmarkNode
> node(delete_me
);
818 const BookmarkNode
* parent
= node
->parent();
820 int index
= parent
->GetIndexOf(node
.get());
822 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
823 OnWillRemoveBookmarks(this, parent
, index
, node
.get()));
825 std::set
<GURL
> removed_urls
;
827 base::AutoLock
url_lock(url_lock_
);
828 RemoveNodeAndGetRemovedUrls(node
.get(), &removed_urls
);
832 store_
->ScheduleSave();
835 BookmarkModelObserver
,
837 BookmarkNodeRemoved(this, parent
, index
, node
.get(), removed_urls
));
840 void BookmarkModel::RemoveNodeFromURLSet(BookmarkNode
* node
) {
841 // NOTE: this is called in such a way that url_lock_ is already held. As
842 // such, this doesn't explicitly grab the lock.
843 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(node
);
844 DCHECK(i
!= nodes_ordered_by_url_set_
.end());
845 // i points to the first node with the URL, advance until we find the
846 // node we're removing.
849 nodes_ordered_by_url_set_
.erase(i
);
852 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode
* node
,
853 std::set
<GURL
>* removed_urls
) {
854 // NOTE: this method should be always called with |url_lock_| held.
855 // This method does not explicitly acquires a lock.
856 url_lock_
.AssertAcquired();
857 DCHECK(removed_urls
);
858 BookmarkNode
* parent
= AsMutable(node
->parent());
860 parent
->Remove(node
);
861 RemoveNode(node
, removed_urls
);
862 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
863 // allow duplicates we need to remove any entries that are still bookmarked.
864 for (std::set
<GURL
>::iterator i
= removed_urls
->begin();
865 i
!= removed_urls
->end();) {
866 if (IsBookmarkedNoLock(*i
)) {
867 // When we erase the iterator pointing at the erasee is
868 // invalidated, so using i++ here within the "erase" call is
869 // important as it advances the iterator before passing the
870 // old value through to erase.
871 removed_urls
->erase(i
++);
878 BookmarkNode
* BookmarkModel::AddNode(BookmarkNode
* parent
,
880 BookmarkNode
* node
) {
881 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
882 OnWillAddBookmarkNode(this, node
));
884 parent
->Add(node
, index
);
887 store_
->ScheduleSave();
889 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
890 BookmarkNodeAdded(this, parent
, index
));
897 bool BookmarkModel::IsValidIndex(const BookmarkNode
* parent
,
900 return (parent
&& parent
->is_folder() &&
901 (index
>= 0 && (index
< parent
->child_count() ||
902 (allow_end
&& index
== parent
->child_count()))));
905 BookmarkPermanentNode
* BookmarkModel::CreatePermanentNode(
906 BookmarkNode::Type type
) {
907 DCHECK(type
== BookmarkNode::BOOKMARK_BAR
||
908 type
== BookmarkNode::OTHER_NODE
||
909 type
== BookmarkNode::MOBILE
);
910 BookmarkPermanentNode
* node
=
911 new BookmarkPermanentNode(generate_next_node_id());
912 node
->set_type(type
);
913 node
->set_visible(client_
->IsPermanentNodeVisible(node
));
917 case BookmarkNode::BOOKMARK_BAR
:
918 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
920 case BookmarkNode::OTHER_NODE
:
921 title_id
= IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME
;
923 case BookmarkNode::MOBILE
:
924 title_id
= IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME
;
928 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
931 node
->SetTitle(l10n_util::GetStringUTF16(title_id
));
935 void BookmarkModel::OnFaviconDataAvailable(
937 favicon_base::IconType icon_type
,
938 const favicon_base::FaviconImageResult
& image_result
) {
940 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
941 node
->set_favicon_state(BookmarkNode::LOADED_FAVICON
);
942 if (!image_result
.image
.IsEmpty()) {
943 node
->set_favicon_type(icon_type
);
944 node
->set_favicon(image_result
.image
);
945 node
->set_icon_url(image_result
.icon_url
);
947 } else if (icon_type
== favicon_base::TOUCH_ICON
) {
948 // Couldn't load the touch icon, fallback to the regular favicon.
949 DCHECK(client_
->PreferTouchIcon());
950 LoadFavicon(node
, favicon_base::FAVICON
);
954 void BookmarkModel::LoadFavicon(
956 favicon_base::IconType icon_type
) {
957 if (node
->is_folder())
960 DCHECK(node
->url().is_valid());
961 node
->set_favicon_state(BookmarkNode::LOADING_FAVICON
);
962 base::CancelableTaskTracker::TaskId taskId
=
963 client_
->GetFaviconImageForPageURL(
967 &BookmarkModel::OnFaviconDataAvailable
,
968 base::Unretained(this),
971 &cancelable_task_tracker_
);
972 if (taskId
!= base::CancelableTaskTracker::kBadTaskId
)
973 node
->set_favicon_load_task_id(taskId
);
976 void BookmarkModel::FaviconLoaded(const BookmarkNode
* node
) {
977 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
978 BookmarkNodeFaviconChanged(this, node
));
981 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode
* node
) {
982 if (node
->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId
) {
983 cancelable_task_tracker_
.TryCancel(node
->favicon_load_task_id());
984 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
988 void BookmarkModel::PopulateNodesByURL(BookmarkNode
* node
) {
989 // NOTE: this is called with url_lock_ already held. As such, this doesn't
990 // explicitly grab the lock.
992 nodes_ordered_by_url_set_
.insert(node
);
993 for (int i
= 0; i
< node
->child_count(); ++i
)
994 PopulateNodesByURL(node
->GetChild(i
));
997 int64
BookmarkModel::generate_next_node_id() {
998 return next_node_id_
++;
1001 scoped_ptr
<BookmarkLoadDetails
> BookmarkModel::CreateLoadDetails(
1002 const std::string
& accept_languages
) {
1003 BookmarkPermanentNode
* bb_node
=
1004 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR
);
1005 BookmarkPermanentNode
* other_node
=
1006 CreatePermanentNode(BookmarkNode::OTHER_NODE
);
1007 BookmarkPermanentNode
* mobile_node
=
1008 CreatePermanentNode(BookmarkNode::MOBILE
);
1009 return scoped_ptr
<BookmarkLoadDetails
>(new BookmarkLoadDetails(
1013 client_
->GetLoadExtraNodesCallback(),
1014 new BookmarkIndex(client_
, accept_languages
),