OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/bookmarks/browser/bookmark_model.h" | 5 #include "components/bookmarks/browser/bookmark_model.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <functional> | 8 #include <functional> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
12 #include "base/i18n/string_compare.h" | 12 #include "base/i18n/string_compare.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/macros.h" | 14 #include "base/macros.h" |
15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
16 #include "base/profiler/scoped_tracker.h" | 16 #include "base/profiler/scoped_tracker.h" |
17 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
18 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h" | 18 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h" |
19 #include "components/bookmarks/browser/bookmark_index.h" | 19 #include "components/bookmarks/browser/bookmark_index.h" |
20 #include "components/bookmarks/browser/bookmark_match.h" | 20 #include "components/bookmarks/browser/bookmark_match.h" |
21 #include "components/bookmarks/browser/bookmark_model_observer.h" | 21 #include "components/bookmarks/browser/bookmark_model_observer.h" |
22 #include "components/bookmarks/browser/bookmark_node_data.h" | 22 #include "components/bookmarks/browser/bookmark_node_data.h" |
23 #include "components/bookmarks/browser/bookmark_storage.h" | 23 #include "components/bookmarks/browser/bookmark_storage.h" |
| 24 #include "components/bookmarks/browser/bookmark_undo_delegate.h" |
24 #include "components/bookmarks/browser/bookmark_utils.h" | 25 #include "components/bookmarks/browser/bookmark_utils.h" |
25 #include "components/favicon_base/favicon_types.h" | 26 #include "components/favicon_base/favicon_types.h" |
26 #include "grit/components_strings.h" | 27 #include "grit/components_strings.h" |
27 #include "ui/base/l10n/l10n_util.h" | 28 #include "ui/base/l10n/l10n_util.h" |
28 #include "ui/gfx/favicon_size.h" | 29 #include "ui/gfx/favicon_size.h" |
29 | 30 |
30 using base::Time; | 31 using base::Time; |
31 | 32 |
32 namespace bookmarks { | 33 namespace bookmarks { |
33 | 34 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 *collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS; | 83 *collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS; |
83 } | 84 } |
84 // Types differ, sort such that folders come first. | 85 // Types differ, sort such that folders come first. |
85 return n1->is_folder(); | 86 return n1->is_folder(); |
86 } | 87 } |
87 | 88 |
88 private: | 89 private: |
89 icu::Collator* collator_; | 90 icu::Collator* collator_; |
90 }; | 91 }; |
91 | 92 |
| 93 // Delegate that does nothing. |
| 94 class EmptyUndoDelegate : public BookmarkUndoDelegate { |
| 95 public: |
| 96 EmptyUndoDelegate() {} |
| 97 ~EmptyUndoDelegate() override {} |
| 98 |
| 99 private: |
| 100 // BookmarkUndoDelegate: |
| 101 void SetUndoProvider(BookmarkUndoProvider* provider) override {} |
| 102 void OnBookmarkNodeRemoved(BookmarkModel* model, |
| 103 const BookmarkNode* parent, |
| 104 int index, |
| 105 scoped_ptr<BookmarkNode> node) override {} |
| 106 |
| 107 DISALLOW_COPY_AND_ASSIGN(EmptyUndoDelegate); |
| 108 }; |
| 109 |
92 } // namespace | 110 } // namespace |
93 | 111 |
94 // BookmarkModel -------------------------------------------------------------- | 112 // BookmarkModel -------------------------------------------------------------- |
95 | 113 |
96 BookmarkModel::BookmarkModel(BookmarkClient* client) | 114 BookmarkModel::BookmarkModel(BookmarkClient* client) |
97 : client_(client), | 115 : client_(client), |
98 loaded_(false), | 116 loaded_(false), |
99 root_(GURL()), | 117 root_(GURL()), |
100 bookmark_bar_node_(NULL), | 118 bookmark_bar_node_(NULL), |
101 other_node_(NULL), | 119 other_node_(NULL), |
102 mobile_node_(NULL), | 120 mobile_node_(NULL), |
103 next_node_id_(1), | 121 next_node_id_(1), |
104 observers_( | 122 observers_( |
105 base::ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY), | 123 base::ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY), |
106 loaded_signal_(true, false), | 124 loaded_signal_(true, false), |
107 extensive_changes_(0) { | 125 extensive_changes_(0), |
| 126 undo_delegate_(nullptr), |
| 127 empty_undo_delegate_(new EmptyUndoDelegate) { |
108 DCHECK(client_); | 128 DCHECK(client_); |
109 } | 129 } |
110 | 130 |
111 BookmarkModel::~BookmarkModel() { | 131 BookmarkModel::~BookmarkModel() { |
112 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, | 132 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, |
113 BookmarkModelBeingDeleted(this)); | 133 BookmarkModelBeingDeleted(this)); |
114 | 134 |
115 if (store_.get()) { | 135 if (store_.get()) { |
116 // The store maintains a reference back to us. We need to tell it we're gone | 136 // 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. | 137 // so that it doesn't try and invoke a method back on us again. |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 | 212 |
193 void BookmarkModel::Remove(const BookmarkNode* node) { | 213 void BookmarkModel::Remove(const BookmarkNode* node) { |
194 DCHECK(loaded_); | 214 DCHECK(loaded_); |
195 DCHECK(node); | 215 DCHECK(node); |
196 DCHECK(!is_root_node(node)); | 216 DCHECK(!is_root_node(node)); |
197 RemoveAndDeleteNode(AsMutable(node)); | 217 RemoveAndDeleteNode(AsMutable(node)); |
198 } | 218 } |
199 | 219 |
200 void BookmarkModel::RemoveAllUserBookmarks() { | 220 void BookmarkModel::RemoveAllUserBookmarks() { |
201 std::set<GURL> removed_urls; | 221 std::set<GURL> removed_urls; |
202 ScopedVector<BookmarkNode> removed_nodes; | 222 struct RemoveNodeData { |
| 223 RemoveNodeData(const BookmarkNode* parent, int index, BookmarkNode* node) |
| 224 : parent(parent), index(index), node(node) {} |
| 225 |
| 226 const BookmarkNode* parent; |
| 227 int index; |
| 228 BookmarkNode* node; |
| 229 }; |
| 230 std::vector<RemoveNodeData> removed_node_data_list; |
203 | 231 |
204 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, | 232 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, |
205 OnWillRemoveAllUserBookmarks(this)); | 233 OnWillRemoveAllUserBookmarks(this)); |
206 | 234 |
207 BeginExtensiveChanges(); | 235 BeginExtensiveChanges(); |
208 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and | 236 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and |
209 // its immediate children. For removing all non permanent nodes just remove | 237 // its immediate children. For removing all non permanent nodes just remove |
210 // all children of non-root permanent nodes. | 238 // all children of non-root permanent nodes. |
211 { | 239 { |
212 base::AutoLock url_lock(url_lock_); | 240 base::AutoLock url_lock(url_lock_); |
213 for (int i = 0; i < root_.child_count(); ++i) { | 241 for (int i = 0; i < root_.child_count(); ++i) { |
214 BookmarkNode* permanent_node = root_.GetChild(i); | 242 const BookmarkNode* permanent_node = root_.GetChild(i); |
215 | 243 |
216 if (!client_->CanBeEditedByUser(permanent_node)) | 244 if (!client_->CanBeEditedByUser(permanent_node)) |
217 continue; | 245 continue; |
218 | 246 |
219 for (int j = permanent_node->child_count() - 1; j >= 0; --j) { | 247 for (int j = permanent_node->child_count() - 1; j >= 0; --j) { |
220 BookmarkNode* child_node = permanent_node->GetChild(j); | 248 BookmarkNode* child_node = AsMutable(permanent_node->GetChild(j)); |
221 removed_nodes.push_back(child_node); | |
222 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls); | 249 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls); |
| 250 removed_node_data_list.push_back( |
| 251 RemoveNodeData(permanent_node, j, child_node)); |
223 } | 252 } |
224 } | 253 } |
225 } | 254 } |
226 EndExtensiveChanges(); | 255 EndExtensiveChanges(); |
227 if (store_.get()) | 256 if (store_.get()) |
228 store_->ScheduleSave(); | 257 store_->ScheduleSave(); |
229 | 258 |
230 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, | 259 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, |
231 BookmarkAllUserNodesRemoved(this, removed_urls)); | 260 BookmarkAllUserNodesRemoved(this, removed_urls)); |
| 261 |
| 262 BeginGroupedChanges(); |
| 263 for (const auto& removed_node_data : removed_node_data_list) { |
| 264 undo_delegate()->OnBookmarkNodeRemoved( |
| 265 this, |
| 266 removed_node_data.parent, |
| 267 removed_node_data.index, |
| 268 scoped_ptr<BookmarkNode>(removed_node_data.node)); |
| 269 } |
| 270 EndGroupedChanges(); |
232 } | 271 } |
233 | 272 |
234 void BookmarkModel::Move(const BookmarkNode* node, | 273 void BookmarkModel::Move(const BookmarkNode* node, |
235 const BookmarkNode* new_parent, | 274 const BookmarkNode* new_parent, |
236 int index) { | 275 int index) { |
237 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) || | 276 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) || |
238 is_root_node(new_parent) || is_permanent_node(node)) { | 277 is_root_node(new_parent) || is_permanent_node(node)) { |
239 NOTREACHED(); | 278 NOTREACHED(); |
240 return; | 279 return; |
241 } | 280 } |
(...skipping 469 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 case BookmarkNode::OTHER_NODE: | 750 case BookmarkNode::OTHER_NODE: |
712 return other_node_; | 751 return other_node_; |
713 case BookmarkNode::MOBILE: | 752 case BookmarkNode::MOBILE: |
714 return mobile_node_; | 753 return mobile_node_; |
715 default: | 754 default: |
716 NOTREACHED(); | 755 NOTREACHED(); |
717 return NULL; | 756 return NULL; |
718 } | 757 } |
719 } | 758 } |
720 | 759 |
| 760 void BookmarkModel::RestoreRemovedNode(const BookmarkNode* parent, |
| 761 int index, |
| 762 scoped_ptr<BookmarkNode> scoped_node) { |
| 763 BookmarkNode* node = scoped_node.release(); |
| 764 AddNode(AsMutable(parent), index, node); |
| 765 |
| 766 // We might be restoring a folder node that have already contained a set of |
| 767 // child nodes. We need to notify all of them. |
| 768 NotifyNodeAddedForAllDescendents(node); |
| 769 } |
| 770 |
| 771 void BookmarkModel::NotifyNodeAddedForAllDescendents(const BookmarkNode* node) { |
| 772 for (int i = 0; i < node->child_count(); ++i) { |
| 773 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, |
| 774 BookmarkNodeAdded(this, node, i)); |
| 775 NotifyNodeAddedForAllDescendents(node->GetChild(i)); |
| 776 } |
| 777 } |
| 778 |
721 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) { | 779 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) { |
722 BookmarkNode tmp_node(url); | 780 BookmarkNode tmp_node(url); |
723 return (nodes_ordered_by_url_set_.find(&tmp_node) != | 781 return (nodes_ordered_by_url_set_.find(&tmp_node) != |
724 nodes_ordered_by_url_set_.end()); | 782 nodes_ordered_by_url_set_.end()); |
725 } | 783 } |
726 | 784 |
727 void BookmarkModel::RemoveNode(BookmarkNode* node, | 785 void BookmarkModel::RemoveNode(BookmarkNode* node, |
728 std::set<GURL>* removed_urls) { | 786 std::set<GURL>* removed_urls) { |
729 if (!loaded_ || !node || is_permanent_node(node)) { | 787 if (!loaded_ || !node || is_permanent_node(node)) { |
730 NOTREACHED(); | 788 NOTREACHED(); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
852 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls); | 910 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls); |
853 } | 911 } |
854 | 912 |
855 if (store_.get()) | 913 if (store_.get()) |
856 store_->ScheduleSave(); | 914 store_->ScheduleSave(); |
857 | 915 |
858 FOR_EACH_OBSERVER( | 916 FOR_EACH_OBSERVER( |
859 BookmarkModelObserver, | 917 BookmarkModelObserver, |
860 observers_, | 918 observers_, |
861 BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls)); | 919 BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls)); |
| 920 |
| 921 undo_delegate()->OnBookmarkNodeRemoved(this, parent, index, node.Pass()); |
862 } | 922 } |
863 | 923 |
864 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode* node) { | 924 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode* node) { |
865 index_->Remove(node); | 925 index_->Remove(node); |
866 // NOTE: this is called in such a way that url_lock_ is already held. As | 926 // NOTE: this is called in such a way that url_lock_ is already held. As |
867 // such, this doesn't explicitly grab the lock. | 927 // such, this doesn't explicitly grab the lock. |
868 url_lock_.AssertAcquired(); | 928 url_lock_.AssertAcquired(); |
869 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node); | 929 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node); |
870 DCHECK(i != nodes_ordered_by_url_set_.end()); | 930 DCHECK(i != nodes_ordered_by_url_set_.end()); |
871 // i points to the first node with the URL, advance until we find the | 931 // i points to the first node with the URL, advance until we find the |
(...skipping 30 matching lines...) Expand all Loading... |
902 } | 962 } |
903 | 963 |
904 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent, | 964 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent, |
905 int index, | 965 int index, |
906 BookmarkNode* node) { | 966 BookmarkNode* node) { |
907 parent->Add(node, index); | 967 parent->Add(node, index); |
908 | 968 |
909 if (store_.get()) | 969 if (store_.get()) |
910 store_->ScheduleSave(); | 970 store_->ScheduleSave(); |
911 | 971 |
912 if (node->type() == BookmarkNode::URL) { | 972 { |
913 base::AutoLock url_lock(url_lock_); | 973 base::AutoLock url_lock(url_lock_); |
914 AddNodeToInternalMaps(node); | 974 AddNodeToInternalMaps(node); |
915 } else { | |
916 index_->Add(node); | |
917 } | 975 } |
918 | 976 |
919 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, | 977 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, |
920 BookmarkNodeAdded(this, parent, index)); | 978 BookmarkNodeAdded(this, parent, index)); |
921 | 979 |
922 return node; | 980 return node; |
923 } | 981 } |
924 | 982 |
925 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode* node) { | 983 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode* node) { |
926 index_->Add(node); | |
927 url_lock_.AssertAcquired(); | 984 url_lock_.AssertAcquired(); |
928 nodes_ordered_by_url_set_.insert(node); | 985 if (node->is_url()) { |
| 986 index_->Add(node); |
| 987 nodes_ordered_by_url_set_.insert(node); |
| 988 } |
| 989 for (int i = 0; i < node->child_count(); ++i) |
| 990 AddNodeToInternalMaps(node->GetChild(i)); |
929 } | 991 } |
930 | 992 |
931 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent, | 993 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent, |
932 int index, | 994 int index, |
933 bool allow_end) { | 995 bool allow_end) { |
934 return (parent && parent->is_folder() && | 996 return (parent && parent->is_folder() && |
935 (index >= 0 && (index < parent->child_count() || | 997 (index >= 0 && (index < parent->child_count() || |
936 (allow_end && index == parent->child_count())))); | 998 (allow_end && index == parent->child_count())))); |
937 } | 999 } |
938 | 1000 |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1041 CreatePermanentNode(BookmarkNode::MOBILE); | 1103 CreatePermanentNode(BookmarkNode::MOBILE); |
1042 return scoped_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails( | 1104 return scoped_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails( |
1043 bb_node, | 1105 bb_node, |
1044 other_node, | 1106 other_node, |
1045 mobile_node, | 1107 mobile_node, |
1046 client_->GetLoadExtraNodesCallback(), | 1108 client_->GetLoadExtraNodesCallback(), |
1047 new BookmarkIndex(client_, accept_languages), | 1109 new BookmarkIndex(client_, accept_languages), |
1048 next_node_id_)); | 1110 next_node_id_)); |
1049 } | 1111 } |
1050 | 1112 |
| 1113 void BookmarkModel::SetUndoDelegate(BookmarkUndoDelegate* undo_delegate) { |
| 1114 undo_delegate_ = undo_delegate; |
| 1115 if (undo_delegate_) |
| 1116 undo_delegate_->SetUndoProvider(this); |
| 1117 } |
| 1118 |
| 1119 BookmarkUndoDelegate* BookmarkModel::undo_delegate() const { |
| 1120 return undo_delegate_ ? undo_delegate_ : empty_undo_delegate_.get(); |
| 1121 } |
| 1122 |
1051 } // namespace bookmarks | 1123 } // namespace bookmarks |
OLD | NEW |