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/metrics/histogram_macros.h"
16 #include "base/profiler/scoped_tracker.h"
17 #include "base/strings/string_util.h"
18 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
19 #include "components/bookmarks/browser/bookmark_index.h"
20 #include "components/bookmarks/browser/bookmark_match.h"
21 #include "components/bookmarks/browser/bookmark_model_observer.h"
22 #include "components/bookmarks/browser/bookmark_node_data.h"
23 #include "components/bookmarks/browser/bookmark_storage.h"
24 #include "components/bookmarks/browser/bookmark_utils.h"
25 #include "components/favicon_base/favicon_types.h"
26 #include "grit/components_strings.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/gfx/favicon_size.h"
36 // Helper to get a mutable bookmark node.
37 BookmarkNode
* AsMutable(const BookmarkNode
* node
) {
38 return const_cast<BookmarkNode
*>(node
);
41 // Helper to get a mutable permanent bookmark node.
42 BookmarkPermanentNode
* AsMutable(const BookmarkPermanentNode
* node
) {
43 return const_cast<BookmarkPermanentNode
*>(node
);
46 // Comparator used when sorting permanent nodes. Nodes that are initially
47 // visible are sorted before nodes that are initially hidden.
48 class VisibilityComparator
49 : public std::binary_function
<const BookmarkPermanentNode
*,
50 const BookmarkPermanentNode
*,
53 explicit VisibilityComparator(BookmarkClient
* client
) : client_(client
) {}
55 // Returns true if |n1| preceeds |n2|.
56 bool operator()(const BookmarkPermanentNode
* n1
,
57 const BookmarkPermanentNode
* n2
) {
58 bool n1_visible
= client_
->IsPermanentNodeVisible(n1
);
59 bool n2_visible
= client_
->IsPermanentNodeVisible(n2
);
60 return n1_visible
!= n2_visible
&& n1_visible
;
64 BookmarkClient
* client_
;
67 // Comparator used when sorting bookmarks. Folders are sorted first, then
69 class SortComparator
: public std::binary_function
<const BookmarkNode
*,
73 explicit SortComparator(icu::Collator
* collator
) : collator_(collator
) {}
75 // Returns true if |n1| preceeds |n2|.
76 bool operator()(const BookmarkNode
* n1
, const BookmarkNode
* n2
) {
77 if (n1
->type() == n2
->type()) {
78 // Types are the same, compare the names.
80 return n1
->GetTitle() < n2
->GetTitle();
81 return base::i18n::CompareString16WithCollator(
82 *collator_
, n1
->GetTitle(), n2
->GetTitle()) == UCOL_LESS
;
84 // Types differ, sort such that folders come first.
85 return n1
->is_folder();
89 icu::Collator
* collator_
;
94 // BookmarkModel --------------------------------------------------------------
96 BookmarkModel::BookmarkModel(BookmarkClient
* client
)
100 bookmark_bar_node_(NULL
),
105 base::ObserverList
<BookmarkModelObserver
>::NOTIFY_EXISTING_ONLY
),
106 loaded_signal_(true, false),
107 extensive_changes_(0) {
111 BookmarkModel::~BookmarkModel() {
112 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
113 BookmarkModelBeingDeleted(this));
116 // The store maintains a reference back to us. We need to tell it we're gone
117 // so that it doesn't try and invoke a method back on us again.
118 store_
->BookmarkModelDeleted();
122 void BookmarkModel::Shutdown() {
126 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
127 // details. It is also called when the BookmarkModel is deleted.
128 loaded_signal_
.Signal();
131 void BookmarkModel::Load(
132 PrefService
* pref_service
,
133 const std::string
& accept_languages
,
134 const base::FilePath
& profile_path
,
135 const scoped_refptr
<base::SequencedTaskRunner
>& io_task_runner
,
136 const scoped_refptr
<base::SequencedTaskRunner
>& ui_task_runner
) {
138 // If the store is non-null, it means Load was already invoked. Load should
139 // only be invoked once.
144 expanded_state_tracker_
.reset(
145 new BookmarkExpandedStateTracker(this, pref_service
));
147 // Load the bookmarks. BookmarkStorage notifies us when done.
148 store_
.reset(new BookmarkStorage(this, profile_path
, io_task_runner
.get()));
149 store_
->LoadBookmarks(CreateLoadDetails(accept_languages
), ui_task_runner
);
152 const BookmarkNode
* BookmarkModel::GetParentForNewNodes() {
153 std::vector
<const BookmarkNode
*> nodes
=
154 GetMostRecentlyModifiedUserFolders(this, 1);
155 DCHECK(!nodes
.empty()); // This list is always padded with default folders.
159 void BookmarkModel::AddObserver(BookmarkModelObserver
* observer
) {
160 observers_
.AddObserver(observer
);
163 void BookmarkModel::RemoveObserver(BookmarkModelObserver
* observer
) {
164 observers_
.RemoveObserver(observer
);
167 void BookmarkModel::BeginExtensiveChanges() {
168 if (++extensive_changes_
== 1) {
169 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
170 ExtensiveBookmarkChangesBeginning(this));
174 void BookmarkModel::EndExtensiveChanges() {
175 --extensive_changes_
;
176 DCHECK_GE(extensive_changes_
, 0);
177 if (extensive_changes_
== 0) {
178 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
179 ExtensiveBookmarkChangesEnded(this));
183 void BookmarkModel::BeginGroupedChanges() {
184 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
185 GroupedBookmarkChangesBeginning(this));
188 void BookmarkModel::EndGroupedChanges() {
189 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
190 GroupedBookmarkChangesEnded(this));
193 void BookmarkModel::Remove(const BookmarkNode
* node
) {
196 DCHECK(!is_root_node(node
));
197 RemoveAndDeleteNode(AsMutable(node
));
200 void BookmarkModel::RemoveAllUserBookmarks() {
201 std::set
<GURL
> removed_urls
;
202 ScopedVector
<BookmarkNode
> removed_nodes
;
204 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
205 OnWillRemoveAllUserBookmarks(this));
207 BeginExtensiveChanges();
208 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
209 // its immediate children. For removing all non permanent nodes just remove
210 // all children of non-root permanent nodes.
212 base::AutoLock
url_lock(url_lock_
);
213 for (int i
= 0; i
< root_
.child_count(); ++i
) {
214 BookmarkNode
* permanent_node
= root_
.GetChild(i
);
216 if (!client_
->CanBeEditedByUser(permanent_node
))
219 for (int j
= permanent_node
->child_count() - 1; j
>= 0; --j
) {
220 BookmarkNode
* child_node
= permanent_node
->GetChild(j
);
221 removed_nodes
.push_back(child_node
);
222 RemoveNodeAndGetRemovedUrls(child_node
, &removed_urls
);
226 EndExtensiveChanges();
228 store_
->ScheduleSave();
230 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
231 BookmarkAllUserNodesRemoved(this, removed_urls
));
234 void BookmarkModel::Move(const BookmarkNode
* node
,
235 const BookmarkNode
* new_parent
,
237 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
238 is_root_node(new_parent
) || is_permanent_node(node
)) {
243 if (new_parent
->HasAncestor(node
)) {
244 // Can't make an ancestor of the node be a child of the node.
249 const BookmarkNode
* old_parent
= node
->parent();
250 int old_index
= old_parent
->GetIndexOf(node
);
252 if (old_parent
== new_parent
&&
253 (index
== old_index
|| index
== old_index
+ 1)) {
254 // Node is already in this position, nothing to do.
258 SetDateFolderModified(new_parent
, Time::Now());
260 if (old_parent
== new_parent
&& index
> old_index
)
262 BookmarkNode
* mutable_new_parent
= AsMutable(new_parent
);
263 mutable_new_parent
->Add(AsMutable(node
), index
);
266 store_
->ScheduleSave();
268 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
269 BookmarkNodeMoved(this, old_parent
, old_index
,
273 void BookmarkModel::Copy(const BookmarkNode
* node
,
274 const BookmarkNode
* new_parent
,
276 if (!loaded_
|| !node
|| !IsValidIndex(new_parent
, index
, true) ||
277 is_root_node(new_parent
) || is_permanent_node(node
)) {
282 if (new_parent
->HasAncestor(node
)) {
283 // Can't make an ancestor of the node be a child of the node.
288 SetDateFolderModified(new_parent
, Time::Now());
289 BookmarkNodeData
drag_data(node
);
290 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
291 // don't need to send notifications here.
292 CloneBookmarkNode(this, drag_data
.elements
, new_parent
, index
, true);
295 store_
->ScheduleSave();
298 const gfx::Image
& BookmarkModel::GetFavicon(const BookmarkNode
* node
) {
300 if (node
->favicon_state() == BookmarkNode::INVALID_FAVICON
) {
301 BookmarkNode
* mutable_node
= AsMutable(node
);
302 LoadFavicon(mutable_node
,
303 client_
->PreferTouchIcon() ? favicon_base::TOUCH_ICON
304 : favicon_base::FAVICON
);
306 return node
->favicon();
309 favicon_base::IconType
BookmarkModel::GetFaviconType(const BookmarkNode
* node
) {
311 return node
->favicon_type();
314 void BookmarkModel::SetTitle(const BookmarkNode
* node
,
315 const base::string16
& title
) {
318 if (node
->GetTitle() == title
)
321 if (is_permanent_node(node
) && !client_
->CanSetPermanentNodeTitle(node
)) {
326 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
327 OnWillChangeBookmarkNode(this, node
));
329 // The title index doesn't support changing the title, instead we remove then
331 index_
->Remove(node
);
332 AsMutable(node
)->SetTitle(title
);
336 store_
->ScheduleSave();
338 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
339 BookmarkNodeChanged(this, node
));
342 void BookmarkModel::SetURL(const BookmarkNode
* node
, const GURL
& url
) {
343 DCHECK(node
&& !node
->is_folder());
345 if (node
->url() == url
)
348 BookmarkNode
* mutable_node
= AsMutable(node
);
349 mutable_node
->InvalidateFavicon();
350 CancelPendingFaviconLoadRequests(mutable_node
);
352 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
353 OnWillChangeBookmarkNode(this, node
));
356 base::AutoLock
url_lock(url_lock_
);
357 RemoveNodeFromInternalMaps(mutable_node
);
358 mutable_node
->set_url(url
);
359 AddNodeToInternalMaps(mutable_node
);
363 store_
->ScheduleSave();
365 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
366 BookmarkNodeChanged(this, node
));
369 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode
* node
,
370 const std::string
& key
,
371 const std::string
& value
) {
372 std::string old_value
;
373 if (node
->GetMetaInfo(key
, &old_value
) && old_value
== value
)
376 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
377 OnWillChangeBookmarkMetaInfo(this, node
));
379 if (AsMutable(node
)->SetMetaInfo(key
, value
) && store_
.get())
380 store_
->ScheduleSave();
382 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
383 BookmarkMetaInfoChanged(this, node
));
386 void BookmarkModel::SetNodeMetaInfoMap(
387 const BookmarkNode
* node
,
388 const BookmarkNode::MetaInfoMap
& meta_info_map
) {
389 const BookmarkNode::MetaInfoMap
* old_meta_info_map
= node
->GetMetaInfoMap();
390 if ((!old_meta_info_map
&& meta_info_map
.empty()) ||
391 (old_meta_info_map
&& meta_info_map
== *old_meta_info_map
))
394 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
395 OnWillChangeBookmarkMetaInfo(this, node
));
397 AsMutable(node
)->SetMetaInfoMap(meta_info_map
);
399 store_
->ScheduleSave();
401 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
402 BookmarkMetaInfoChanged(this, node
));
405 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode
* node
,
406 const std::string
& key
) {
407 const BookmarkNode::MetaInfoMap
* meta_info_map
= node
->GetMetaInfoMap();
408 if (!meta_info_map
|| meta_info_map
->find(key
) == meta_info_map
->end())
411 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
412 OnWillChangeBookmarkMetaInfo(this, node
));
414 if (AsMutable(node
)->DeleteMetaInfo(key
) && store_
.get())
415 store_
->ScheduleSave();
417 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
418 BookmarkMetaInfoChanged(this, node
));
421 void BookmarkModel::AddNonClonedKey(const std::string
& key
) {
422 non_cloned_keys_
.insert(key
);
425 void BookmarkModel::SetNodeSyncTransactionVersion(
426 const BookmarkNode
* node
,
427 int64 sync_transaction_version
) {
428 DCHECK(client_
->CanSyncNode(node
));
430 if (sync_transaction_version
== node
->sync_transaction_version())
433 AsMutable(node
)->set_sync_transaction_version(sync_transaction_version
);
435 store_
->ScheduleSave();
438 void BookmarkModel::OnFaviconsChanged(const std::set
<GURL
>& page_urls
,
439 const GURL
& icon_url
) {
440 std::set
<const BookmarkNode
*> to_update
;
441 for (const GURL
& page_url
: page_urls
) {
442 std::vector
<const BookmarkNode
*> nodes
;
443 GetNodesByURL(page_url
, &nodes
);
444 to_update
.insert(nodes
.begin(), nodes
.end());
447 if (!icon_url
.is_empty()) {
448 // Log Histogram to determine how often |icon_url| is non empty in
450 // TODO(pkotwicz): Do something more efficient if |icon_url| is non-empty
451 // many times a day for each user.
452 UMA_HISTOGRAM_BOOLEAN("Bookmarks.OnFaviconsChangedIconURL", true);
454 base::AutoLock
url_lock(url_lock_
);
455 for (const BookmarkNode
* node
: nodes_ordered_by_url_set_
) {
456 if (icon_url
== node
->icon_url())
457 to_update
.insert(node
);
461 for (const BookmarkNode
* node
: to_update
) {
462 // Rerequest the favicon.
463 BookmarkNode
* mutable_node
= AsMutable(node
);
464 mutable_node
->InvalidateFavicon();
465 CancelPendingFaviconLoadRequests(mutable_node
);
466 FOR_EACH_OBSERVER(BookmarkModelObserver
,
468 BookmarkNodeFaviconChanged(this, node
));
472 void BookmarkModel::SetDateAdded(const BookmarkNode
* node
, Time date_added
) {
473 DCHECK(node
&& !is_permanent_node(node
));
475 if (node
->date_added() == date_added
)
478 AsMutable(node
)->set_date_added(date_added
);
480 // Syncing might result in dates newer than the folder's last modified date.
481 if (date_added
> node
->parent()->date_folder_modified()) {
482 // Will trigger store_->ScheduleSave().
483 SetDateFolderModified(node
->parent(), date_added
);
484 } else if (store_
.get()) {
485 store_
->ScheduleSave();
489 void BookmarkModel::GetNodesByURL(const GURL
& url
,
490 std::vector
<const BookmarkNode
*>* nodes
) {
491 base::AutoLock
url_lock(url_lock_
);
492 BookmarkNode
tmp_node(url
);
493 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(&tmp_node
);
494 while (i
!= nodes_ordered_by_url_set_
.end() && (*i
)->url() == url
) {
495 nodes
->push_back(*i
);
500 const BookmarkNode
* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
502 std::vector
<const BookmarkNode
*> nodes
;
503 GetNodesByURL(url
, &nodes
);
504 std::sort(nodes
.begin(), nodes
.end(), &MoreRecentlyAdded
);
506 // Look for the first node that the user can edit.
507 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
508 if (client_
->CanBeEditedByUser(nodes
[i
]))
515 bool BookmarkModel::HasBookmarks() {
516 base::AutoLock
url_lock(url_lock_
);
517 return !nodes_ordered_by_url_set_
.empty();
520 bool BookmarkModel::IsBookmarked(const GURL
& url
) {
521 base::AutoLock
url_lock(url_lock_
);
522 return IsBookmarkedNoLock(url
);
525 void BookmarkModel::GetBookmarks(
526 std::vector
<BookmarkModel::URLAndTitle
>* bookmarks
) {
527 base::AutoLock
url_lock(url_lock_
);
528 const GURL
* last_url
= NULL
;
529 for (NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.begin();
530 i
!= nodes_ordered_by_url_set_
.end(); ++i
) {
531 const GURL
* url
= &((*i
)->url());
532 // Only add unique URLs.
533 if (!last_url
|| *url
!= *last_url
) {
534 BookmarkModel::URLAndTitle bookmark
;
536 bookmark
.title
= (*i
)->GetTitle();
537 bookmarks
->push_back(bookmark
);
543 void BookmarkModel::BlockTillLoaded() {
544 loaded_signal_
.Wait();
547 const BookmarkNode
* BookmarkModel::AddFolder(const BookmarkNode
* parent
,
549 const base::string16
& title
) {
550 return AddFolderWithMetaInfo(parent
, index
, title
, NULL
);
552 const BookmarkNode
* BookmarkModel::AddFolderWithMetaInfo(
553 const BookmarkNode
* parent
,
555 const base::string16
& title
,
556 const BookmarkNode::MetaInfoMap
* meta_info
) {
557 if (!loaded_
|| is_root_node(parent
) || !IsValidIndex(parent
, index
, true)) {
558 // Can't add to the root.
563 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), GURL());
564 new_node
->set_date_folder_modified(Time::Now());
565 // Folders shouldn't have line breaks in their titles.
566 new_node
->SetTitle(title
);
567 new_node
->set_type(BookmarkNode::FOLDER
);
569 new_node
->SetMetaInfoMap(*meta_info
);
571 return AddNode(AsMutable(parent
), index
, new_node
);
574 const BookmarkNode
* BookmarkModel::AddURL(const BookmarkNode
* parent
,
576 const base::string16
& title
,
578 return AddURLWithCreationTimeAndMetaInfo(
581 base::CollapseWhitespace(title
, false),
587 const BookmarkNode
* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
588 const BookmarkNode
* parent
,
590 const base::string16
& title
,
592 const Time
& creation_time
,
593 const BookmarkNode::MetaInfoMap
* meta_info
) {
594 if (!loaded_
|| !url
.is_valid() || is_root_node(parent
) ||
595 !IsValidIndex(parent
, index
, true)) {
600 // Syncing may result in dates newer than the last modified date.
601 if (creation_time
> parent
->date_folder_modified())
602 SetDateFolderModified(parent
, creation_time
);
604 BookmarkNode
* new_node
= new BookmarkNode(generate_next_node_id(), url
);
605 new_node
->SetTitle(title
);
606 new_node
->set_date_added(creation_time
);
607 new_node
->set_type(BookmarkNode::URL
);
609 new_node
->SetMetaInfoMap(*meta_info
);
611 return AddNode(AsMutable(parent
), index
, new_node
);
614 void BookmarkModel::SortChildren(const BookmarkNode
* parent
) {
615 DCHECK(client_
->CanBeEditedByUser(parent
));
617 if (!parent
|| !parent
->is_folder() || is_root_node(parent
) ||
618 parent
->child_count() <= 1) {
622 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
623 OnWillReorderBookmarkNode(this, parent
));
625 UErrorCode error
= U_ZERO_ERROR
;
626 scoped_ptr
<icu::Collator
> collator(icu::Collator::createInstance(error
));
627 if (U_FAILURE(error
))
628 collator
.reset(NULL
);
629 BookmarkNode
* mutable_parent
= AsMutable(parent
);
630 std::sort(mutable_parent
->children().begin(),
631 mutable_parent
->children().end(),
632 SortComparator(collator
.get()));
635 store_
->ScheduleSave();
637 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
638 BookmarkNodeChildrenReordered(this, parent
));
641 void BookmarkModel::ReorderChildren(
642 const BookmarkNode
* parent
,
643 const std::vector
<const BookmarkNode
*>& ordered_nodes
) {
644 DCHECK(client_
->CanBeEditedByUser(parent
));
646 // Ensure that all children in |parent| are in |ordered_nodes|.
647 DCHECK_EQ(static_cast<size_t>(parent
->child_count()), ordered_nodes
.size());
648 for (size_t i
= 0; i
< ordered_nodes
.size(); ++i
)
649 DCHECK_EQ(parent
, ordered_nodes
[i
]->parent());
651 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
652 OnWillReorderBookmarkNode(this, parent
));
654 AsMutable(parent
)->SetChildren(
655 *(reinterpret_cast<const std::vector
<BookmarkNode
*>*>(&ordered_nodes
)));
658 store_
->ScheduleSave();
660 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
661 BookmarkNodeChildrenReordered(this, parent
));
664 void BookmarkModel::SetDateFolderModified(const BookmarkNode
* parent
,
667 AsMutable(parent
)->set_date_folder_modified(time
);
670 store_
->ScheduleSave();
673 void BookmarkModel::ResetDateFolderModified(const BookmarkNode
* node
) {
674 SetDateFolderModified(node
, Time());
677 void BookmarkModel::GetBookmarksMatching(const base::string16
& text
,
679 std::vector
<BookmarkMatch
>* matches
) {
680 GetBookmarksMatching(text
, max_count
,
681 query_parser::MatchingAlgorithm::DEFAULT
, matches
);
684 void BookmarkModel::GetBookmarksMatching(
685 const base::string16
& text
,
687 query_parser::MatchingAlgorithm matching_algorithm
,
688 std::vector
<BookmarkMatch
>* matches
) {
692 index_
->GetBookmarksMatching(text
, max_count
, matching_algorithm
, matches
);
695 void BookmarkModel::ClearStore() {
699 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type
,
701 BookmarkPermanentNode
* node
= AsMutable(PermanentNode(type
));
702 node
->set_visible(value
|| client_
->IsPermanentNodeVisible(node
));
705 const BookmarkPermanentNode
* BookmarkModel::PermanentNode(
706 BookmarkNode::Type type
) {
709 case BookmarkNode::BOOKMARK_BAR
:
710 return bookmark_bar_node_
;
711 case BookmarkNode::OTHER_NODE
:
713 case BookmarkNode::MOBILE
:
721 bool BookmarkModel::IsBookmarkedNoLock(const GURL
& url
) {
722 BookmarkNode
tmp_node(url
);
723 return (nodes_ordered_by_url_set_
.find(&tmp_node
) !=
724 nodes_ordered_by_url_set_
.end());
727 void BookmarkModel::RemoveNode(BookmarkNode
* node
,
728 std::set
<GURL
>* removed_urls
) {
729 if (!loaded_
|| !node
|| is_permanent_node(node
)) {
734 url_lock_
.AssertAcquired();
735 if (node
->is_url()) {
736 RemoveNodeFromInternalMaps(node
);
737 removed_urls
->insert(node
->url());
740 CancelPendingFaviconLoadRequests(node
);
742 // Recurse through children.
743 for (int i
= node
->child_count() - 1; i
>= 0; --i
)
744 RemoveNode(node
->GetChild(i
), removed_urls
);
747 void BookmarkModel::DoneLoading(scoped_ptr
<BookmarkLoadDetails
> details
) {
750 // We should only ever be loaded once.
755 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
757 tracked_objects::ScopedTracker
tracking_profile1(
758 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading1"));
760 next_node_id_
= details
->max_id();
761 if (details
->computed_checksum() != details
->stored_checksum() ||
762 details
->ids_reassigned()) {
763 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
765 tracked_objects::ScopedTracker
tracking_profile2(
766 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading2"));
768 // If bookmarks file changed externally, the IDs may have changed
769 // externally. In that case, the decoder may have reassigned IDs to make
770 // them unique. So when the file has changed externally, we should save the
771 // bookmarks file to persist new IDs.
773 store_
->ScheduleSave();
775 bookmark_bar_node_
= details
->release_bb_node();
776 other_node_
= details
->release_other_folder_node();
777 mobile_node_
= details
->release_mobile_folder_node();
778 index_
.reset(details
->release_index());
780 // Get any extra nodes and take ownership of them at the |root_|.
781 std::vector
<BookmarkPermanentNode
*> extra_nodes
;
782 details
->release_extra_nodes(&extra_nodes
);
784 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
786 tracked_objects::ScopedTracker
tracking_profile3(
787 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading3"));
789 // WARNING: order is important here, various places assume the order is
790 // constant (but can vary between embedders with the initial visibility
791 // of permanent nodes).
792 std::vector
<BookmarkPermanentNode
*> root_children
;
793 root_children
.push_back(bookmark_bar_node_
);
794 root_children
.push_back(other_node_
);
795 root_children
.push_back(mobile_node_
);
796 for (size_t i
= 0; i
< extra_nodes
.size(); ++i
)
797 root_children
.push_back(extra_nodes
[i
]);
799 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
801 tracked_objects::ScopedTracker
tracking_profile4(
802 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading4"));
804 std::stable_sort(root_children
.begin(),
806 VisibilityComparator(client_
));
807 for (size_t i
= 0; i
< root_children
.size(); ++i
)
808 root_
.Add(root_children
[i
], static_cast<int>(i
));
810 root_
.SetMetaInfoMap(details
->model_meta_info_map());
811 root_
.set_sync_transaction_version(details
->model_sync_transaction_version());
813 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
815 tracked_objects::ScopedTracker
tracking_profile5(
816 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading5"));
819 base::AutoLock
url_lock(url_lock_
);
820 // Update nodes_ordered_by_url_set_ from the nodes.
821 PopulateNodesByURL(&root_
);
826 loaded_signal_
.Signal();
828 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
830 tracked_objects::ScopedTracker
tracking_profile6(
831 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading6"));
833 // Notify our direct observers.
834 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
835 BookmarkModelLoaded(this, details
->ids_reassigned()));
838 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode
* delete_me
) {
839 scoped_ptr
<BookmarkNode
> node(delete_me
);
841 const BookmarkNode
* parent
= node
->parent();
843 int index
= parent
->GetIndexOf(node
.get());
844 DCHECK_NE(-1, index
);
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();
859 BookmarkModelObserver
,
861 BookmarkNodeRemoved(this, parent
, index
, node
.get(), removed_urls
));
864 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode
* node
) {
865 index_
->Remove(node
);
866 // NOTE: this is called in such a way that url_lock_ is already held. As
867 // such, this doesn't explicitly grab the lock.
868 url_lock_
.AssertAcquired();
869 NodesOrderedByURLSet::iterator i
= nodes_ordered_by_url_set_
.find(node
);
870 DCHECK(i
!= nodes_ordered_by_url_set_
.end());
871 // i points to the first node with the URL, advance until we find the
872 // node we're removing.
875 nodes_ordered_by_url_set_
.erase(i
);
878 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode
* node
,
879 std::set
<GURL
>* removed_urls
) {
880 // NOTE: this method should be always called with |url_lock_| held.
881 // This method does not explicitly acquires a lock.
882 url_lock_
.AssertAcquired();
883 DCHECK(removed_urls
);
884 BookmarkNode
* parent
= node
->parent();
886 parent
->Remove(node
);
887 RemoveNode(node
, removed_urls
);
888 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
889 // allow duplicates we need to remove any entries that are still bookmarked.
890 for (std::set
<GURL
>::iterator i
= removed_urls
->begin();
891 i
!= removed_urls
->end();) {
892 if (IsBookmarkedNoLock(*i
)) {
893 // When we erase the iterator pointing at the erasee is
894 // invalidated, so using i++ here within the "erase" call is
895 // important as it advances the iterator before passing the
896 // old value through to erase.
897 removed_urls
->erase(i
++);
904 BookmarkNode
* BookmarkModel::AddNode(BookmarkNode
* parent
,
906 BookmarkNode
* node
) {
907 parent
->Add(node
, index
);
910 store_
->ScheduleSave();
912 if (node
->type() == BookmarkNode::URL
) {
913 base::AutoLock
url_lock(url_lock_
);
914 AddNodeToInternalMaps(node
);
919 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
920 BookmarkNodeAdded(this, parent
, index
));
925 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode
* node
) {
927 url_lock_
.AssertAcquired();
928 nodes_ordered_by_url_set_
.insert(node
);
931 bool BookmarkModel::IsValidIndex(const BookmarkNode
* parent
,
934 return (parent
&& parent
->is_folder() &&
935 (index
>= 0 && (index
< parent
->child_count() ||
936 (allow_end
&& index
== parent
->child_count()))));
939 BookmarkPermanentNode
* BookmarkModel::CreatePermanentNode(
940 BookmarkNode::Type type
) {
941 DCHECK(type
== BookmarkNode::BOOKMARK_BAR
||
942 type
== BookmarkNode::OTHER_NODE
||
943 type
== BookmarkNode::MOBILE
);
944 BookmarkPermanentNode
* node
=
945 new BookmarkPermanentNode(generate_next_node_id());
946 node
->set_type(type
);
947 node
->set_visible(client_
->IsPermanentNodeVisible(node
));
951 case BookmarkNode::BOOKMARK_BAR
:
952 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
954 case BookmarkNode::OTHER_NODE
:
955 title_id
= IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME
;
957 case BookmarkNode::MOBILE
:
958 title_id
= IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME
;
962 title_id
= IDS_BOOKMARK_BAR_FOLDER_NAME
;
965 node
->SetTitle(l10n_util::GetStringUTF16(title_id
));
969 void BookmarkModel::OnFaviconDataAvailable(
971 favicon_base::IconType icon_type
,
972 const favicon_base::FaviconImageResult
& image_result
) {
974 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
975 node
->set_favicon_state(BookmarkNode::LOADED_FAVICON
);
976 if (!image_result
.image
.IsEmpty()) {
977 node
->set_favicon_type(icon_type
);
978 node
->set_favicon(image_result
.image
);
979 node
->set_icon_url(image_result
.icon_url
);
981 } else if (icon_type
== favicon_base::TOUCH_ICON
) {
982 // Couldn't load the touch icon, fallback to the regular favicon.
983 DCHECK(client_
->PreferTouchIcon());
984 LoadFavicon(node
, favicon_base::FAVICON
);
988 void BookmarkModel::LoadFavicon(BookmarkNode
* node
,
989 favicon_base::IconType icon_type
) {
990 if (node
->is_folder())
993 DCHECK(node
->url().is_valid());
994 node
->set_favicon_state(BookmarkNode::LOADING_FAVICON
);
995 base::CancelableTaskTracker::TaskId taskId
=
996 client_
->GetFaviconImageForPageURL(
1000 &BookmarkModel::OnFaviconDataAvailable
,
1001 base::Unretained(this),
1004 &cancelable_task_tracker_
);
1005 if (taskId
!= base::CancelableTaskTracker::kBadTaskId
)
1006 node
->set_favicon_load_task_id(taskId
);
1009 void BookmarkModel::FaviconLoaded(const BookmarkNode
* node
) {
1010 FOR_EACH_OBSERVER(BookmarkModelObserver
, observers_
,
1011 BookmarkNodeFaviconChanged(this, node
));
1014 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode
* node
) {
1015 if (node
->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId
) {
1016 cancelable_task_tracker_
.TryCancel(node
->favicon_load_task_id());
1017 node
->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId
);
1021 void BookmarkModel::PopulateNodesByURL(BookmarkNode
* node
) {
1022 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1023 // explicitly grab the lock.
1025 nodes_ordered_by_url_set_
.insert(node
);
1026 for (int i
= 0; i
< node
->child_count(); ++i
)
1027 PopulateNodesByURL(node
->GetChild(i
));
1030 int64
BookmarkModel::generate_next_node_id() {
1031 return next_node_id_
++;
1034 scoped_ptr
<BookmarkLoadDetails
> BookmarkModel::CreateLoadDetails(
1035 const std::string
& accept_languages
) {
1036 BookmarkPermanentNode
* bb_node
=
1037 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR
);
1038 BookmarkPermanentNode
* other_node
=
1039 CreatePermanentNode(BookmarkNode::OTHER_NODE
);
1040 BookmarkPermanentNode
* mobile_node
=
1041 CreatePermanentNode(BookmarkNode::MOBILE
);
1042 return scoped_ptr
<BookmarkLoadDetails
>(new BookmarkLoadDetails(
1046 client_
->GetLoadExtraNodesCallback(),
1047 new BookmarkIndex(client_
, accept_languages
),
1051 } // namespace bookmarks