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/profiler/scoped_tracker.h"
16 #include "base/strings/string_util.h"
17 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
18 #include "components/bookmarks/browser/bookmark_index.h"
19 #include "components/bookmarks/browser/bookmark_match.h"
20 #include "components/bookmarks/browser/bookmark_model_observer.h"
21 #include "components/bookmarks/browser/bookmark_node_data.h"
22 #include "components/bookmarks/browser/bookmark_storage.h"
23 #include "components/bookmarks/browser/bookmark_utils.h"
24 #include "components/favicon_base/favicon_types.h"
25 #include "grit/components_strings.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/gfx/favicon_size.h"
35 // Helper to get a mutable bookmark node.
36 BookmarkNode
* AsMutable(const BookmarkNode
* node
) {
37 return const_cast<BookmarkNode
*>(node
);
40 // Helper to get a mutable permanent bookmark node.
41 BookmarkPermanentNode
* AsMutable(const BookmarkPermanentNode
* node
) {
42 return const_cast<BookmarkPermanentNode
*>(node
);
45 // Comparator used when sorting permanent nodes. Nodes that are initially
46 // visible are sorted before nodes that are initially hidden.
47 class VisibilityComparator
48 : public std::binary_function
<const BookmarkPermanentNode
*,
49 const BookmarkPermanentNode
*,
52 explicit VisibilityComparator(BookmarkClient
* client
) : client_(client
) {}
54 // Returns true if |n1| preceeds |n2|.
55 bool operator()(const BookmarkPermanentNode
* n1
,
56 const BookmarkPermanentNode
* n2
) {
57 bool n1_visible
= client_
->IsPermanentNodeVisible(n1
);
58 bool n2_visible
= client_
->IsPermanentNodeVisible(n2
);
59 return n1_visible
!= n2_visible
&& n1_visible
;
63 BookmarkClient
* client_
;
66 // Comparator used when sorting bookmarks. Folders are sorted first, then
68 class SortComparator
: public std::binary_function
<const BookmarkNode
*,
72 explicit SortComparator(icu::Collator
* collator
) : collator_(collator
) {}
74 // Returns true if |n1| preceeds |n2|.
75 bool operator()(const BookmarkNode
* n1
, const BookmarkNode
* n2
) {
76 if (n1
->type() == n2
->type()) {
77 // Types are the same, compare the names.
79 return n1
->GetTitle() < n2
->GetTitle();
80 return base::i18n::CompareString16WithCollator(
81 *collator_
, n1
->GetTitle(), n2
->GetTitle()) == UCOL_LESS
;
83 // Types differ, sort such that folders come first.
84 return n1
->is_folder();
88 icu::Collator
* collator_
;
93 // BookmarkModel --------------------------------------------------------------
95 BookmarkModel::BookmarkModel(BookmarkClient
* client
)
99 bookmark_bar_node_(NULL
),
104 base::ObserverList
<BookmarkModelObserver
>::NOTIFY_EXISTING_ONLY
),
105 loaded_signal_(true, false),
106 extensive_changes_(0) {
110 BookmarkModel::~BookmarkModel() {
111 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
112 BookmarkModelBeingDeleted(this));
115 // The store maintains a reference back to us. We need to tell it we're gone
116 // so that it doesn't try and invoke a method back on us again.
117 store_
->BookmarkModelDeleted();
121 void BookmarkModel::Shutdown() {
125 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
126 // details. It is also called when the BookmarkModel is deleted.
127 loaded_signal_
.Signal();
130 void BookmarkModel::Load(
131 PrefService
* pref_service
,
132 const std::string
& accept_languages
,
133 const base::FilePath
& profile_path
,
134 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
,
135 const scoped_refptr
<base::SequencedTaskRunner
>& ui_task_runner
) {
137 // If the store is non-null, it means Load was already invoked. Load should
138 // only be invoked once.
143 expanded_state_tracker_
.reset(
144 new BookmarkExpandedStateTracker(this, pref_service
));
146 // Load the bookmarks. BookmarkStorage notifies us when done.
147 store_
.reset(new BookmarkStorage(this, profile_path
, io_task_runner
.get()));
148 store_
->LoadBookmarks(CreateLoadDetails(accept_languages
), ui_task_runner
);
151 const BookmarkNode
* BookmarkModel::GetParentForNewNodes() {
152 std::vector
<const BookmarkNode
*> nodes
=
153 GetMostRecentlyModifiedUserFolders(this, 1);
154 DCHECK(!nodes
.empty()); // This list is always padded with default folders.
158 void BookmarkModel::AddObserver(BookmarkModelObserver
* observer
) {
159 observers_
.AddObserver(observer
);
162 void BookmarkModel::RemoveObserver(BookmarkModelObserver
* observer
) {
163 observers_
.RemoveObserver(observer
);
166 void BookmarkModel::BeginExtensiveChanges() {
167 if (++extensive_changes_
== 1) {
168 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
169 ExtensiveBookmarkChangesBeginning(this));
173 void BookmarkModel::EndExtensiveChanges() {
174 --extensive_changes_
;
175 DCHECK_GE(extensive_changes_
, 0);
176 if (extensive_changes_
== 0) {
177 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
178 ExtensiveBookmarkChangesEnded(this));
182 void BookmarkModel::BeginGroupedChanges() {
183 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
184 GroupedBookmarkChangesBeginning(this));
187 void BookmarkModel::EndGroupedChanges() {
188 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
189 GroupedBookmarkChangesEnded(this));
192 void BookmarkModel::Remove(const BookmarkNode
* node
) {
195 DCHECK(!is_root_node(node
));
196 RemoveAndDeleteNode(AsMutable(node
));
199 void BookmarkModel::RemoveAllUserBookmarks() {
200 std::set
<GURL
> removed_urls
;
201 ScopedVector
<BookmarkNode
> removed_nodes
;
203 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
204 OnWillRemoveAllUserBookmarks(this));
206 BeginExtensiveChanges();
207 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
208 // its immediate children. For removing all non permanent nodes just remove
209 // all children of non-root permanent nodes.
211 base::AutoLock
url_lock(url_lock_
);
212 for (int i
= 0; i
< root_
.child_count(); ++i
) {
213 BookmarkNode
* permanent_node
= root_
.GetChild(i
);
215 if (!client_
->CanBeEditedByUser(permanent_node
))
218 for (int j
= permanent_node
->child_count() - 1; j
>= 0; --j
) {
219 BookmarkNode
* child_node
= permanent_node
->GetChild(j
);
220 removed_nodes
.push_back(child_node
);
221 RemoveNodeAndGetRemovedUrls(child_node
, &removed_urls
);
225 EndExtensiveChanges();
227 store_
->ScheduleSave();
229 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
230 BookmarkAllUserNodesRemoved(this, removed_urls
));
233 void BookmarkModel::Move(const BookmarkNode
* node
,
234 const BookmarkNode
* new_parent
,
236 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
237 is_root_node(new_parent
) || is_permanent_node(node
)) {
242 if (new_parent
->HasAncestor(node
)) {
243 // Can't make an ancestor of the node be a child of the node.
248 const BookmarkNode
* old_parent
= node
->parent();
249 int old_index
= old_parent
->GetIndexOf(node
);
251 if (old_parent
== new_parent
&&
252 (index
== old_index
|| index
== old_index
+ 1)) {
253 // Node is already in this position, nothing to do.
257 SetDateFolderModified(new_parent
, Time::Now());
259 if (old_parent
== new_parent
&& index
> old_index
)
261 BookmarkNode
* mutable_new_parent
= AsMutable(new_parent
);
262 mutable_new_parent
->Add(AsMutable(node
), index
);
265 store_
->ScheduleSave();
267 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
268 BookmarkNodeMoved(this, old_parent
, old_index
,
272 void BookmarkModel::Copy(const BookmarkNode
* node
,
273 const BookmarkNode
* new_parent
,
275 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
276 is_root_node(new_parent
) || is_permanent_node(node
)) {
281 if (new_parent
->HasAncestor(node
)) {
282 // Can't make an ancestor of the node be a child of the node.
287 SetDateFolderModified(new_parent
, Time::Now());
288 BookmarkNodeData
drag_data(node
);
289 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
290 // don't need to send notifications here.
291 CloneBookmarkNode(this, drag_data
.elements
, new_parent
, index
, true);
294 store_
->ScheduleSave();
297 const gfx::Image
& BookmarkModel::GetFavicon(const BookmarkNode
* node
) {
299 if (node
->favicon_state() == BookmarkNode::INVALID_FAVICON
) {
300 BookmarkNode
* mutable_node
= AsMutable(node
);
301 LoadFavicon(mutable_node
,
302 client_
->PreferTouchIcon() ? favicon_base::TOUCH_ICON
303 : favicon_base::FAVICON
);
305 return node
->favicon();
308 favicon_base::IconType
BookmarkModel::GetFaviconType(const BookmarkNode
* node
) {
310 return node
->favicon_type();
313 void BookmarkModel::SetTitle(const BookmarkNode
* node
,
314 const base::string16
& title
) {
317 if (node
->GetTitle() == title
)
320 if (is_permanent_node(node
) && !client_
->CanSetPermanentNodeTitle(node
)) {
325 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
326 OnWillChangeBookmarkNode(this, node
));
328 // The title index doesn't support changing the title, instead we remove then
330 index_
->Remove(node
);
331 AsMutable(node
)->SetTitle(title
);
335 store_
->ScheduleSave();
337 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
338 BookmarkNodeChanged(this, node
));
341 void BookmarkModel::SetURL(const BookmarkNode
* node
, const GURL
& url
) {
342 DCHECK(node
&& !node
->is_folder());
344 if (node
->url() == url
)
347 BookmarkNode
* mutable_node
= AsMutable(node
);
348 mutable_node
->InvalidateFavicon();
349 CancelPendingFaviconLoadRequests(mutable_node
);
351 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
352 OnWillChangeBookmarkNode(this, node
));
355 base::AutoLock
url_lock(url_lock_
);
356 RemoveNodeFromInternalMaps(mutable_node
);
357 mutable_node
->set_url(url
);
358 AddNodeToInternalMaps(mutable_node
);
362 store_
->ScheduleSave();
364 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
365 BookmarkNodeChanged(this, node
));
368 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode
* node
,
369 const std::string
& key
,
370 const std::string
& value
) {
371 std::string old_value
;
372 if (node
->GetMetaInfo(key
, &old_value
) && old_value
== value
)
375 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
376 OnWillChangeBookmarkMetaInfo(this, node
));
378 if (AsMutable(node
)->SetMetaInfo(key
, value
) && store_
.get())
379 store_
->ScheduleSave();
381 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
382 BookmarkMetaInfoChanged(this, node
));
385 void BookmarkModel::SetNodeMetaInfoMap(
386 const BookmarkNode
* node
,
387 const BookmarkNode::MetaInfoMap
& meta_info_map
) {
388 const BookmarkNode::MetaInfoMap
* old_meta_info_map
= node
->GetMetaInfoMap();
389 if ((!old_meta_info_map
&& meta_info_map
.empty()) ||
390 (old_meta_info_map
&& meta_info_map
== *old_meta_info_map
))
393 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
394 OnWillChangeBookmarkMetaInfo(this, node
));
396 AsMutable(node
)->SetMetaInfoMap(meta_info_map
);
398 store_
->ScheduleSave();
400 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
401 BookmarkMetaInfoChanged(this, node
));
404 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode
* node
,
405 const std::string
& key
) {
406 const BookmarkNode::MetaInfoMap
* meta_info_map
= node
->GetMetaInfoMap();
407 if (!meta_info_map
|| meta_info_map
->find(key
) == meta_info_map
->end())
410 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
411 OnWillChangeBookmarkMetaInfo(this, node
));
413 if (AsMutable(node
)->DeleteMetaInfo(key
) && store_
.get())
414 store_
->ScheduleSave();
416 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
417 BookmarkMetaInfoChanged(this, node
));
420 void BookmarkModel::AddNonClonedKey(const std::string
& key
) {
421 non_cloned_keys_
.insert(key
);
424 void BookmarkModel::SetNodeSyncTransactionVersion(
425 const BookmarkNode
* node
,
426 int64 sync_transaction_version
) {
427 DCHECK(client_
->CanSyncNode(node
));
429 if (sync_transaction_version
== node
->sync_transaction_version())
432 AsMutable(node
)->set_sync_transaction_version(sync_transaction_version
);
434 store_
->ScheduleSave();
437 void BookmarkModel::OnFaviconChanged(const std::set
<GURL
>& urls
) {
438 // Ignore events if |Load| has not been called yet.
442 // Prevent the observers from getting confused for multiple favicon loads.
443 for (std::set
<GURL
>::const_iterator i
= urls
.begin(); i
!= urls
.end(); ++i
) {
444 std::vector
<const BookmarkNode
*> nodes
;
445 GetNodesByURL(*i
, &nodes
);
446 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
447 // Got an updated favicon, for a URL, do a new request.
448 BookmarkNode
* node
= AsMutable(nodes
[i
]);
449 node
->InvalidateFavicon();
450 CancelPendingFaviconLoadRequests(node
);
451 FOR_EACH_OBSERVER(BookmarkModelObserver
,
453 BookmarkNodeFaviconChanged(this, node
));
458 void BookmarkModel::SetDateAdded(const BookmarkNode
* node
, Time date_added
) {
459 DCHECK(node
&& !is_permanent_node(node
));
461 if (node
->date_added() == date_added
)
464 AsMutable(node
)->set_date_added(date_added
);
466 // Syncing might result in dates newer than the folder's last modified date.
467 if (date_added
> node
->parent()->date_folder_modified()) {
468 // Will trigger store_->ScheduleSave().
469 SetDateFolderModified(node
->parent(), date_added
);
470 } else if (store_
.get()) {
471 store_
->ScheduleSave();
475 void BookmarkModel::GetNodesByURL(const GURL
& url
,
476 std::vector
<const BookmarkNode
*>* nodes
) {
477 base::AutoLock
url_lock(url_lock_
);
478 BookmarkNode
tmp_node(url
);
479 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(&tmp_node
);
480 while (i
!= nodes_ordered_by_url_set_
.end() && (*i
)->url() == url
) {
481 nodes
->push_back(*i
);
486 const BookmarkNode
* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
488 std::vector
<const BookmarkNode
*> nodes
;
489 GetNodesByURL(url
, &nodes
);
490 std::sort(nodes
.begin(), nodes
.end(), &MoreRecentlyAdded
);
492 // Look for the first node that the user can edit.
493 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
494 if (client_
->CanBeEditedByUser(nodes
[i
]))
501 bool BookmarkModel::HasBookmarks() {
502 base::AutoLock
url_lock(url_lock_
);
503 return !nodes_ordered_by_url_set_
.empty();
506 bool BookmarkModel::IsBookmarked(const GURL
& url
) {
507 base::AutoLock
url_lock(url_lock_
);
508 return IsBookmarkedNoLock(url
);
511 void BookmarkModel::GetBookmarks(
512 std::vector
<BookmarkModel::URLAndTitle
>* bookmarks
) {
513 base::AutoLock
url_lock(url_lock_
);
514 const GURL
* last_url
= NULL
;
515 for (NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.begin();
516 i
!= nodes_ordered_by_url_set_
.end(); ++i
) {
517 const GURL
* url
= &((*i
)->url());
518 // Only add unique URLs.
519 if (!last_url
|| *url
!= *last_url
) {
520 BookmarkModel::URLAndTitle bookmark
;
522 bookmark
.title
= (*i
)->GetTitle();
523 bookmarks
->push_back(bookmark
);
529 void BookmarkModel::BlockTillLoaded() {
530 loaded_signal_
.Wait();
533 const BookmarkNode
* BookmarkModel::AddFolder(const BookmarkNode
* parent
,
535 const base::string16
& title
) {
536 return AddFolderWithMetaInfo(parent
, index
, title
, NULL
);
538 const BookmarkNode
* BookmarkModel::AddFolderWithMetaInfo(
539 const BookmarkNode
* parent
,
541 const base::string16
& title
,
542 const BookmarkNode::MetaInfoMap
* meta_info
) {
543 if (!loaded_
|| is_root_node(parent
) || !IsValidIndex(parent
, index
, true)) {
544 // Can't add to the root.
549 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), GURL());
550 new_node
->set_date_folder_modified(Time::Now());
551 // Folders shouldn't have line breaks in their titles.
552 new_node
->SetTitle(title
);
553 new_node
->set_type(BookmarkNode::FOLDER
);
555 new_node
->SetMetaInfoMap(*meta_info
);
557 return AddNode(AsMutable(parent
), index
, new_node
);
560 const BookmarkNode
* BookmarkModel::AddURL(const BookmarkNode
* parent
,
562 const base::string16
& title
,
564 return AddURLWithCreationTimeAndMetaInfo(
567 base::CollapseWhitespace(title
, false),
573 const BookmarkNode
* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
574 const BookmarkNode
* parent
,
576 const base::string16
& title
,
578 const Time
& creation_time
,
579 const BookmarkNode::MetaInfoMap
* meta_info
) {
580 if (!loaded_
|| !url
.is_valid() || is_root_node(parent
) ||
581 !IsValidIndex(parent
, index
, true)) {
586 // Syncing may result in dates newer than the last modified date.
587 if (creation_time
> parent
->date_folder_modified())
588 SetDateFolderModified(parent
, creation_time
);
590 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), url
);
591 new_node
->SetTitle(title
);
592 new_node
->set_date_added(creation_time
);
593 new_node
->set_type(BookmarkNode::URL
);
595 new_node
->SetMetaInfoMap(*meta_info
);
597 return AddNode(AsMutable(parent
), index
, new_node
);
600 void BookmarkModel::SortChildren(const BookmarkNode
* parent
) {
601 DCHECK(client_
->CanBeEditedByUser(parent
));
603 if (!parent
|| !parent
->is_folder() || is_root_node(parent
) ||
604 parent
->child_count() <= 1) {
608 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
609 OnWillReorderBookmarkNode(this, parent
));
611 UErrorCode error
= U_ZERO_ERROR
;
612 scoped_ptr
<icu::Collator
> collator(icu::Collator::createInstance(error
));
613 if (U_FAILURE(error
))
614 collator
.reset(NULL
);
615 BookmarkNode
* mutable_parent
= AsMutable(parent
);
616 std::sort(mutable_parent
->children().begin(),
617 mutable_parent
->children().end(),
618 SortComparator(collator
.get()));
621 store_
->ScheduleSave();
623 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
624 BookmarkNodeChildrenReordered(this, parent
));
627 void BookmarkModel::ReorderChildren(
628 const BookmarkNode
* parent
,
629 const std::vector
<const BookmarkNode
*>& ordered_nodes
) {
630 DCHECK(client_
->CanBeEditedByUser(parent
));
632 // Ensure that all children in |parent| are in |ordered_nodes|.
633 DCHECK_EQ(static_cast<size_t>(parent
->child_count()), ordered_nodes
.size());
634 for (size_t i
= 0; i
< ordered_nodes
.size(); ++i
)
635 DCHECK_EQ(parent
, ordered_nodes
[i
]->parent());
637 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
638 OnWillReorderBookmarkNode(this, parent
));
640 AsMutable(parent
)->SetChildren(
641 *(reinterpret_cast<const std::vector
<BookmarkNode
*>*>(&ordered_nodes
)));
644 store_
->ScheduleSave();
646 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
647 BookmarkNodeChildrenReordered(this, parent
));
650 void BookmarkModel::SetDateFolderModified(const BookmarkNode
* parent
,
653 AsMutable(parent
)->set_date_folder_modified(time
);
656 store_
->ScheduleSave();
659 void BookmarkModel::ResetDateFolderModified(const BookmarkNode
* node
) {
660 SetDateFolderModified(node
, Time());
663 void BookmarkModel::GetBookmarksMatching(const base::string16
& text
,
665 std::vector
<BookmarkMatch
>* matches
) {
666 GetBookmarksMatching(text
, max_count
,
667 query_parser::MatchingAlgorithm::DEFAULT
, matches
);
670 void BookmarkModel::GetBookmarksMatching(
671 const base::string16
& text
,
673 query_parser::MatchingAlgorithm matching_algorithm
,
674 std::vector
<BookmarkMatch
>* matches
) {
678 index_
->GetBookmarksMatching(text
, max_count
, matching_algorithm
, matches
);
681 void BookmarkModel::ClearStore() {
685 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type
,
687 BookmarkPermanentNode
* node
= AsMutable(PermanentNode(type
));
688 node
->set_visible(value
|| client_
->IsPermanentNodeVisible(node
));
691 const BookmarkPermanentNode
* BookmarkModel::PermanentNode(
692 BookmarkNode::Type type
) {
695 case BookmarkNode::BOOKMARK_BAR
:
696 return bookmark_bar_node_
;
697 case BookmarkNode::OTHER_NODE
:
699 case BookmarkNode::MOBILE
:
707 bool BookmarkModel::IsBookmarkedNoLock(const GURL
& url
) {
708 BookmarkNode
tmp_node(url
);
709 return (nodes_ordered_by_url_set_
.find(&tmp_node
) !=
710 nodes_ordered_by_url_set_
.end());
713 void BookmarkModel::RemoveNode(BookmarkNode
* node
,
714 std::set
<GURL
>* removed_urls
) {
715 if (!loaded_
|| !node
|| is_permanent_node(node
)) {
720 url_lock_
.AssertAcquired();
721 if (node
->is_url()) {
722 RemoveNodeFromInternalMaps(node
);
723 removed_urls
->insert(node
->url());
726 CancelPendingFaviconLoadRequests(node
);
728 // Recurse through children.
729 for (int i
= node
->child_count() - 1; i
>= 0; --i
)
730 RemoveNode(node
->GetChild(i
), removed_urls
);
733 void BookmarkModel::DoneLoading(scoped_ptr
<BookmarkLoadDetails
> details
) {
736 // We should only ever be loaded once.
741 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
743 tracked_objects::ScopedTracker
tracking_profile1(
744 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading1"));
746 next_node_id_
= details
->max_id();
747 if (details
->computed_checksum() != details
->stored_checksum() ||
748 details
->ids_reassigned()) {
749 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
751 tracked_objects::ScopedTracker
tracking_profile2(
752 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading2"));
754 // If bookmarks file changed externally, the IDs may have changed
755 // externally. In that case, the decoder may have reassigned IDs to make
756 // them unique. So when the file has changed externally, we should save the
757 // bookmarks file to persist new IDs.
759 store_
->ScheduleSave();
761 bookmark_bar_node_
= details
->release_bb_node();
762 other_node_
= details
->release_other_folder_node();
763 mobile_node_
= details
->release_mobile_folder_node();
764 index_
.reset(details
->release_index());
766 // Get any extra nodes and take ownership of them at the |root_|.
767 std::vector
<BookmarkPermanentNode
*> extra_nodes
;
768 details
->release_extra_nodes(&extra_nodes
);
770 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
772 tracked_objects::ScopedTracker
tracking_profile3(
773 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading3"));
775 // WARNING: order is important here, various places assume the order is
776 // constant (but can vary between embedders with the initial visibility
777 // of permanent nodes).
778 std::vector
<BookmarkPermanentNode
*> root_children
;
779 root_children
.push_back(bookmark_bar_node_
);
780 root_children
.push_back(other_node_
);
781 root_children
.push_back(mobile_node_
);
782 for (size_t i
= 0; i
< extra_nodes
.size(); ++i
)
783 root_children
.push_back(extra_nodes
[i
]);
785 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
787 tracked_objects::ScopedTracker
tracking_profile4(
788 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading4"));
790 std::stable_sort(root_children
.begin(),
792 VisibilityComparator(client_
));
793 for (size_t i
= 0; i
< root_children
.size(); ++i
)
794 root_
.Add(root_children
[i
], static_cast<int>(i
));
796 root_
.SetMetaInfoMap(details
->model_meta_info_map());
797 root_
.set_sync_transaction_version(details
->model_sync_transaction_version());
799 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
801 tracked_objects::ScopedTracker
tracking_profile5(
802 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading5"));
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 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
816 tracked_objects::ScopedTracker
tracking_profile6(
817 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading6"));
819 // Notify our direct observers.
820 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
821 BookmarkModelLoaded(this, details
->ids_reassigned()));
824 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode
* delete_me
) {
825 scoped_ptr
<BookmarkNode
> node(delete_me
);
827 const BookmarkNode
* parent
= node
->parent();
829 int index
= parent
->GetIndexOf(node
.get());
830 DCHECK_NE(-1, index
);
832 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
833 OnWillRemoveBookmarks(this, parent
, index
, node
.get()));
835 std::set
<GURL
> removed_urls
;
837 base::AutoLock
url_lock(url_lock_
);
838 RemoveNodeAndGetRemovedUrls(node
.get(), &removed_urls
);
842 store_
->ScheduleSave();
845 BookmarkModelObserver
,
847 BookmarkNodeRemoved(this, parent
, index
, node
.get(), removed_urls
));
850 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode
* node
) {
851 index_
->Remove(node
);
852 // NOTE: this is called in such a way that url_lock_ is already held. As
853 // such, this doesn't explicitly grab the lock.
854 url_lock_
.AssertAcquired();
855 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(node
);
856 DCHECK(i
!= nodes_ordered_by_url_set_
.end());
857 // i points to the first node with the URL, advance until we find the
858 // node we're removing.
861 nodes_ordered_by_url_set_
.erase(i
);
864 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode
* node
,
865 std::set
<GURL
>* removed_urls
) {
866 // NOTE: this method should be always called with |url_lock_| held.
867 // This method does not explicitly acquires a lock.
868 url_lock_
.AssertAcquired();
869 DCHECK(removed_urls
);
870 BookmarkNode
* parent
= node
->parent();
872 parent
->Remove(node
);
873 RemoveNode(node
, removed_urls
);
874 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
875 // allow duplicates we need to remove any entries that are still bookmarked.
876 for (std::set
<GURL
>::iterator i
= removed_urls
->begin();
877 i
!= removed_urls
->end();) {
878 if (IsBookmarkedNoLock(*i
)) {
879 // When we erase the iterator pointing at the erasee is
880 // invalidated, so using i++ here within the "erase" call is
881 // important as it advances the iterator before passing the
882 // old value through to erase.
883 removed_urls
->erase(i
++);
890 BookmarkNode
* BookmarkModel::AddNode(BookmarkNode
* parent
,
892 BookmarkNode
* node
) {
893 parent
->Add(node
, index
);
896 store_
->ScheduleSave();
898 if (node
->type() == BookmarkNode::URL
) {
899 base::AutoLock
url_lock(url_lock_
);
900 AddNodeToInternalMaps(node
);
905 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
906 BookmarkNodeAdded(this, parent
, index
));
911 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode
* node
) {
913 url_lock_
.AssertAcquired();
914 nodes_ordered_by_url_set_
.insert(node
);
917 bool BookmarkModel::IsValidIndex(const BookmarkNode
* parent
,
920 return (parent
&& parent
->is_folder() &&
921 (index
>= 0 && (index
< parent
->child_count() ||
922 (allow_end
&& index
== parent
->child_count()))));
925 BookmarkPermanentNode
* BookmarkModel::CreatePermanentNode(
926 BookmarkNode::Type type
) {
927 DCHECK(type
== BookmarkNode::BOOKMARK_BAR
||
928 type
== BookmarkNode::OTHER_NODE
||
929 type
== BookmarkNode::MOBILE
);
930 BookmarkPermanentNode
* node
=
931 new BookmarkPermanentNode(generate_next_node_id());
932 node
->set_type(type
);
933 node
->set_visible(client_
->IsPermanentNodeVisible(node
));
937 case BookmarkNode::BOOKMARK_BAR
:
938 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
940 case BookmarkNode::OTHER_NODE
:
941 title_id
= IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME
;
943 case BookmarkNode::MOBILE
:
944 title_id
= IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME
;
948 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
951 node
->SetTitle(l10n_util::GetStringUTF16(title_id
));
955 void BookmarkModel::OnFaviconDataAvailable(
957 favicon_base::IconType icon_type
,
958 const favicon_base::FaviconImageResult
& image_result
) {
960 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
961 node
->set_favicon_state(BookmarkNode::LOADED_FAVICON
);
962 if (!image_result
.image
.IsEmpty()) {
963 node
->set_favicon_type(icon_type
);
964 node
->set_favicon(image_result
.image
);
965 node
->set_icon_url(image_result
.icon_url
);
967 } else if (icon_type
== favicon_base::TOUCH_ICON
) {
968 // Couldn't load the touch icon, fallback to the regular favicon.
969 DCHECK(client_
->PreferTouchIcon());
970 LoadFavicon(node
, favicon_base::FAVICON
);
974 void BookmarkModel::LoadFavicon(BookmarkNode
* node
,
975 favicon_base::IconType icon_type
) {
976 if (node
->is_folder())
979 DCHECK(node
->url().is_valid());
980 node
->set_favicon_state(BookmarkNode::LOADING_FAVICON
);
981 base::CancelableTaskTracker::TaskId taskId
=
982 client_
->GetFaviconImageForPageURL(
986 &BookmarkModel::OnFaviconDataAvailable
,
987 base::Unretained(this),
990 &cancelable_task_tracker_
);
991 if (taskId
!= base::CancelableTaskTracker::kBadTaskId
)
992 node
->set_favicon_load_task_id(taskId
);
995 void BookmarkModel::FaviconLoaded(const BookmarkNode
* node
) {
996 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
997 BookmarkNodeFaviconChanged(this, node
));
1000 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode
* node
) {
1001 if (node
->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId
) {
1002 cancelable_task_tracker_
.TryCancel(node
->favicon_load_task_id());
1003 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
1007 void BookmarkModel::PopulateNodesByURL(BookmarkNode
* node
) {
1008 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1009 // explicitly grab the lock.
1011 nodes_ordered_by_url_set_
.insert(node
);
1012 for (int i
= 0; i
< node
->child_count(); ++i
)
1013 PopulateNodesByURL(node
->GetChild(i
));
1016 int64
BookmarkModel::generate_next_node_id() {
1017 return next_node_id_
++;
1020 scoped_ptr
<BookmarkLoadDetails
> BookmarkModel::CreateLoadDetails(
1021 const std::string
& accept_languages
) {
1022 BookmarkPermanentNode
* bb_node
=
1023 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR
);
1024 BookmarkPermanentNode
* other_node
=
1025 CreatePermanentNode(BookmarkNode::OTHER_NODE
);
1026 BookmarkPermanentNode
* mobile_node
=
1027 CreatePermanentNode(BookmarkNode::MOBILE
);
1028 return scoped_ptr
<BookmarkLoadDetails
>(new BookmarkLoadDetails(
1032 client_
->GetLoadExtraNodesCallback(),
1033 new BookmarkIndex(client_
, accept_languages
),
1037 } // namespace bookmarks