blob: cdd91f5a973a1d0d8e0ba2b28e3f0da935a5f4b3 [file] [log] [blame]
[email protected]b68a8172012-02-17 00:25:181// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]d8e41ed2008-09-11 15:22:325#include "chrome/browser/bookmarks/bookmark_model.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]4e425be42011-01-15 06:56:097#include <algorithm>
8#include <functional>
9
[email protected]bbdd2982011-10-08 18:14:2410#include "base/bind.h"
11#include "base/bind_helpers.h"
[email protected]9a08fe82013-04-23 05:06:0512#include "base/i18n/string_compare.h"
[email protected]fb441962013-05-08 05:35:2413#include "base/sequenced_task_runner.h"
[email protected]c58c5ea2011-07-13 21:43:1614#include "chrome/browser/bookmarks/bookmark_expanded_state_tracker.h"
[email protected]01eec882009-05-22 18:13:2815#include "chrome/browser/bookmarks/bookmark_index.h"
[email protected]125b234182011-07-08 19:54:4116#include "chrome/browser/bookmarks/bookmark_model_observer.h"
[email protected]68de8b72008-09-09 23:08:1317#include "chrome/browser/bookmarks/bookmark_storage.h"
[email protected]368f3a72011-03-08 17:17:4818#include "chrome/browser/bookmarks/bookmark_utils.h"
[email protected]fdf40f3e2013-07-11 23:55:4619#include "chrome/browser/chrome_notification_types.h"
[email protected]0ab247d2013-05-28 17:01:0620#include "chrome/browser/favicon/favicon_changed_details.h"
[email protected]dfad18be72012-12-21 00:46:3421#include "chrome/browser/favicon/favicon_service.h"
[email protected]f3d2b312012-08-23 22:27:5922#include "chrome/browser/favicon/favicon_service_factory.h"
[email protected]6a2c09f2013-01-25 04:50:0723#include "chrome/browser/history/history_service.h"
[email protected]9aac66862012-06-19 19:44:3124#include "chrome/browser/history/history_service_factory.h"
[email protected]8ecad5e2010-12-02 21:18:3325#include "chrome/browser/profiles/profile.h"
[email protected]707dc162014-04-16 17:00:3126#include "components/bookmarks/core/browser/bookmark_title_match.h"
[email protected]7627e0b42014-04-17 17:20:5327#include "components/favicon_base/favicon_types.h"
[email protected]dae9f5b2013-02-27 15:29:2328#include "content/public/browser/notification_details.h"
29#include "content/public/browser/notification_source.h"
[email protected]34ac8f32009-02-22 23:03:2730#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1731#include "ui/base/l10n/l10n_util.h"
[email protected]65baa222012-08-30 15:43:5132#include "ui/gfx/favicon_size.h"
[email protected]6a4e5a02012-06-26 19:47:4833#include "ui/gfx/image/image_util.h"
initial.commit09911bf2008-07-26 23:55:2934
[email protected]e1acf6f2008-10-27 20:43:3335using base::Time;
36
[email protected]b3c33d462009-06-26 22:29:2037namespace {
38
39// Helper to get a mutable bookmark node.
[email protected]7b084b02011-07-08 16:06:4840BookmarkNode* AsMutable(const BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:2041 return const_cast<BookmarkNode*>(node);
42}
43
[email protected]ef762642009-03-05 16:30:2544// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:4445// bookmarks.
[email protected]b3c33d462009-06-26 22:29:2046class SortComparator : public std::binary_function<const BookmarkNode*,
47 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:2548 bool> {
49 public:
[email protected]a48f87d2012-10-09 18:06:3350 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
[email protected]ef762642009-03-05 16:30:2551
[email protected]a48f87d2012-10-09 18:06:3352 // Returns true if |n1| preceeds |n2|.
53 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:0854 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:2555 // Types are the same, compare the names.
56 if (!collator_)
[email protected]440b37b22010-08-30 05:31:4057 return n1->GetTitle() < n2->GetTitle();
[email protected]9a08fe82013-04-23 05:06:0558 return base::i18n::CompareString16WithCollator(
[email protected]3abebda2011-01-07 20:17:1559 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
[email protected]ef762642009-03-05 16:30:2560 }
61 // Types differ, sort such that folders come first.
62 return n1->is_folder();
63 }
64
65 private:
[email protected]b5b2385a2009-08-18 05:12:2966 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:2567};
68
[email protected]bc770a032011-12-12 17:35:3069} // namespace
[email protected]97fdd162011-12-03 20:50:1270
[email protected]97fdd162011-12-03 20:50:1271// BookmarkModel --------------------------------------------------------------
72
[email protected]d8e41ed2008-09-11 15:22:3273BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:2974 : profile_(profile),
75 loaded_(false),
[email protected]ea2e5aa52009-05-20 18:01:2876 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:2977 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:4678 other_node_(NULL),
[email protected]37bc9132011-12-01 22:29:2979 mobile_node_(NULL),
[email protected]4d89f382009-05-12 06:56:4980 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:0681 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]b68a8172012-02-17 00:25:1882 loaded_signal_(true, false),
83 extensive_changes_(0) {
[email protected]f25387b2008-08-21 15:20:3384 if (!profile_) {
85 // Profile is null during testing.
[email protected]01eec882009-05-22 18:13:2886 DoneLoading(CreateLoadDetails());
initial.commit09911bf2008-07-26 23:55:2987 }
initial.commit09911bf2008-07-26 23:55:2988}
89
[email protected]d8e41ed2008-09-11 15:22:3290BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:3291 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:5192 BookmarkModelBeingDeleted(this));
93
[email protected]dc24976f2013-06-02 21:15:0994 if (store_.get()) {
[email protected]f25387b2008-08-21 15:20:3395 // The store maintains a reference back to us. We need to tell it we're gone
96 // so that it doesn't try and invoke a method back on us again.
97 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:2998 }
99}
100
[email protected]f61f4782012-06-08 21:54:21101void BookmarkModel::Shutdown() {
[email protected]88e6a232011-09-14 14:53:20102 if (loaded_)
103 return;
104
[email protected]f61f4782012-06-08 21:54:21105 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
106 // details. It is also called when the BookmarkModel is deleted.
[email protected]88e6a232011-09-14 14:53:20107 loaded_signal_.Signal();
108}
109
[email protected]afecfb72013-04-18 17:17:33110void BookmarkModel::Load(
111 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
[email protected]90ef13132008-08-27 03:27:46112 if (store_.get()) {
113 // If the store is non-null, it means Load was already invoked. Load should
114 // only be invoked once.
115 NOTREACHED();
116 return;
117 }
118
[email protected]6df6ec022013-07-16 03:05:22119 expanded_state_tracker_.reset(
120 new BookmarkExpandedStateTracker(this, profile_->GetPrefs()));
[email protected]c58c5ea2011-07-13 21:43:16121
[email protected]90ef13132008-08-27 03:27:46122 // Listen for changes to favicons so that we can update the favicon of the
123 // node appropriately.
[email protected]432115822011-07-10 15:52:27124 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
[email protected]6c2381d2011-10-19 02:52:53125 content::Source<Profile>(profile_));
[email protected]90ef13132008-08-27 03:27:46126
127 // Load the bookmarks. BookmarkStorage notifies us when done.
[email protected]dc24976f2013-06-02 21:15:09128 store_ = new BookmarkStorage(profile_, this, task_runner.get());
[email protected]01eec882009-05-22 18:13:28129 store_->LoadBookmarks(CreateLoadDetails());
[email protected]90ef13132008-08-27 03:27:46130}
131
[email protected]b3c33d462009-06-26 22:29:20132const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
133 std::vector<const BookmarkNode*> nodes =
[email protected]9fcaee72011-03-21 21:53:44134 bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
[email protected]82f4655a2011-10-18 13:05:43135 DCHECK(!nodes.empty()); // This list is always padded with default folders.
136 return nodes[0];
initial.commit09911bf2008-07-26 23:55:29137}
138
[email protected]125b234182011-07-08 19:54:41139void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
140 observers_.AddObserver(observer);
141}
142
143void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
144 observers_.RemoveObserver(observer);
145}
146
[email protected]b68a8172012-02-17 00:25:18147void BookmarkModel::BeginExtensiveChanges() {
148 if (++extensive_changes_ == 1) {
149 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
150 ExtensiveBookmarkChangesBeginning(this));
151 }
[email protected]125b234182011-07-08 19:54:41152}
153
[email protected]b68a8172012-02-17 00:25:18154void BookmarkModel::EndExtensiveChanges() {
155 --extensive_changes_;
156 DCHECK_GE(extensive_changes_, 0);
157 if (extensive_changes_ == 0) {
158 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
159 ExtensiveBookmarkChangesEnded(this));
160 }
[email protected]125b234182011-07-08 19:54:41161}
162
[email protected]346453a2014-03-12 10:14:37163void BookmarkModel::BeginGroupedChanges() {
164 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
165 GroupedBookmarkChangesBeginning(this));
166}
167
168void BookmarkModel::EndGroupedChanges() {
169 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
170 GroupedBookmarkChangesEnded(this));
171}
172
[email protected]b3c33d462009-06-26 22:29:20173void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
[email protected]6b4d64c2011-07-29 21:33:24174 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
[email protected]f25387b2008-08-21 15:20:33175 NOTREACHED();
176 return;
177 }
[email protected]b3c33d462009-06-26 22:29:20178 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
[email protected]f25387b2008-08-21 15:20:33179}
180
[email protected]323dbf72013-03-30 17:08:33181void BookmarkModel::RemoveAll() {
[email protected]9109f8a12013-06-12 18:07:48182 std::set<GURL> removed_urls;
[email protected]323dbf72013-03-30 17:08:33183 ScopedVector<BookmarkNode> removed_nodes;
[email protected]472f95e2013-06-10 16:49:18184
185 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
186 OnWillRemoveAllBookmarks(this));
187
[email protected]323dbf72013-03-30 17:08:33188 BeginExtensiveChanges();
189 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
190 // its immediate children. For removing all non permanent nodes just remove
191 // all children of non-root permanent nodes.
192 {
193 base::AutoLock url_lock(url_lock_);
194 for (int i = 0; i < root_.child_count(); ++i) {
[email protected]ed18102fa2013-12-26 04:02:00195 BookmarkNode* permanent_node = root_.GetChild(i);
[email protected]323dbf72013-03-30 17:08:33196 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
[email protected]ed18102fa2013-12-26 04:02:00197 BookmarkNode* child_node = permanent_node->GetChild(j);
[email protected]323dbf72013-03-30 17:08:33198 removed_nodes.push_back(child_node);
[email protected]9109f8a12013-06-12 18:07:48199 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
[email protected]323dbf72013-03-30 17:08:33200 }
201 }
202 }
203 EndExtensiveChanges();
204 if (store_.get())
205 store_->ScheduleSave();
206
[email protected]9109f8a12013-06-12 18:07:48207 NotifyHistoryAboutRemovedBookmarks(removed_urls);
[email protected]323dbf72013-03-30 17:08:33208
209 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
210 BookmarkAllNodesRemoved(this));
211}
212
[email protected]b3c33d462009-06-26 22:29:20213void BookmarkModel::Move(const BookmarkNode* node,
214 const BookmarkNode* new_parent,
[email protected]d8e41ed2008-09-11 15:22:32215 int index) {
[email protected]f25387b2008-08-21 15:20:33216 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24217 is_root_node(new_parent) || is_permanent_node(node)) {
initial.commit09911bf2008-07-26 23:55:29218 NOTREACHED();
219 return;
220 }
221
222 if (new_parent->HasAncestor(node)) {
223 // Can't make an ancestor of the node be a child of the node.
224 NOTREACHED();
225 return;
226 }
227
[email protected]2d48ee842011-03-08 23:27:29228 const BookmarkNode* old_parent = node->parent();
[email protected]368f3a72011-03-08 17:17:48229 int old_index = old_parent->GetIndexOf(node);
initial.commit09911bf2008-07-26 23:55:29230
231 if (old_parent == new_parent &&
232 (index == old_index || index == old_index + 1)) {
233 // Node is already in this position, nothing to do.
234 return;
235 }
236
[email protected]9e583642012-12-05 02:48:32237 SetDateFolderModified(new_parent, Time::Now());
238
initial.commit09911bf2008-07-26 23:55:29239 if (old_parent == new_parent && index > old_index)
240 index--;
[email protected]b3c33d462009-06-26 22:29:20241 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
[email protected]a0dd6a32011-03-18 17:31:37242 mutable_new_parent->Add(AsMutable(node), index);
initial.commit09911bf2008-07-26 23:55:29243
[email protected]f25387b2008-08-21 15:20:33244 if (store_.get())
245 store_->ScheduleSave();
246
[email protected]d8e41ed2008-09-11 15:22:32247 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29248 BookmarkNodeMoved(this, old_parent, old_index,
249 new_parent, index));
250}
251
[email protected]4e187ef652010-03-11 05:21:35252void BookmarkModel::Copy(const BookmarkNode* node,
253 const BookmarkNode* new_parent,
254 int index) {
255 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24256 is_root_node(new_parent) || is_permanent_node(node)) {
[email protected]4e187ef652010-03-11 05:21:35257 NOTREACHED();
258 return;
259 }
260
261 if (new_parent->HasAncestor(node)) {
262 // Can't make an ancestor of the node be a child of the node.
263 NOTREACHED();
264 return;
265 }
266
[email protected]c6a7a3d2011-03-12 01:04:30267 SetDateFolderModified(new_parent, Time::Now());
[email protected]67a8ae42013-05-20 09:42:39268 BookmarkNodeData drag_data(node);
269 std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
[email protected]b1864502010-11-13 00:55:51270 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
[email protected]1a566fae2010-04-16 01:05:06271 // don't need to send notifications here.
[email protected]7be9c7d2013-08-22 01:19:33272 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index, true);
[email protected]4e187ef652010-03-11 05:21:35273
274 if (store_.get())
275 store_->ScheduleSave();
[email protected]4e187ef652010-03-11 05:21:35276}
277
[email protected]6a4e5a02012-06-26 19:47:48278const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
[email protected]ea2e5aa52009-05-20 18:01:28279 DCHECK(node);
[email protected]bcc38612012-10-23 01:10:27280 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
[email protected]b3c33d462009-06-26 22:29:20281 BookmarkNode* mutable_node = AsMutable(node);
[email protected]bcc38612012-10-23 01:10:27282 mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
[email protected]abc2f262011-03-15 21:15:44283 LoadFavicon(mutable_node);
[email protected]ea2e5aa52009-05-20 18:01:28284 }
285 return node->favicon();
286}
287
[email protected]96920152013-12-04 21:00:16288void BookmarkModel::SetTitle(const BookmarkNode* node, const base::string16& title) {
[email protected]f25387b2008-08-21 15:20:33289 if (!node) {
290 NOTREACHED();
291 return;
292 }
[email protected]0491ff72011-12-30 00:45:59293 if (node->GetTitle() == title)
initial.commit09911bf2008-07-26 23:55:29294 return;
[email protected]f25387b2008-08-21 15:20:33295
[email protected]6892e2e2011-05-26 22:07:17296 if (is_permanent_node(node)) {
[email protected]baf4f922009-10-19 16:44:07297 NOTREACHED();
298 return;
299 }
300
[email protected]472f95e2013-06-10 16:49:18301 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
302 OnWillChangeBookmarkNode(this, node));
303
[email protected]85d911c2009-05-19 03:59:42304 // The title index doesn't support changing the title, instead we remove then
305 // add it back.
[email protected]01eec882009-05-22 18:13:28306 index_->Remove(node);
[email protected]0491ff72011-12-30 00:45:59307 AsMutable(node)->SetTitle(title);
[email protected]01eec882009-05-22 18:13:28308 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42309
[email protected]f25387b2008-08-21 15:20:33310 if (store_.get())
311 store_->ScheduleSave();
312
[email protected]d8e41ed2008-09-11 15:22:32313 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29314 BookmarkNodeChanged(this, node));
315}
316
[email protected]e5486602010-02-09 21:27:55317void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
318 if (!node) {
319 NOTREACHED();
320 return;
321 }
322
323 // We cannot change the URL of a folder.
324 if (node->is_folder()) {
325 NOTREACHED();
326 return;
327 }
328
[email protected]5d4077542011-07-21 20:24:07329 if (node->url() == url)
[email protected]e5486602010-02-09 21:27:55330 return;
331
[email protected]5b5c9b7f32011-07-21 01:07:18332 BookmarkNode* mutable_node = AsMutable(node);
333 mutable_node->InvalidateFavicon();
334 CancelPendingFaviconLoadRequests(mutable_node);
[email protected]e5486602010-02-09 21:27:55335
[email protected]472f95e2013-06-10 16:49:18336 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
337 OnWillChangeBookmarkNode(this, node));
338
[email protected]e5486602010-02-09 21:27:55339 {
[email protected]20305ec2011-01-21 04:55:52340 base::AutoLock url_lock(url_lock_);
[email protected]5db00bca2013-12-23 00:43:09341 RemoveNodeFromURLSet(mutable_node);
[email protected]5d4077542011-07-21 20:24:07342 mutable_node->set_url(url);
[email protected]5b5c9b7f32011-07-21 01:07:18343 nodes_ordered_by_url_set_.insert(mutable_node);
[email protected]e5486602010-02-09 21:27:55344 }
345
346 if (store_.get())
347 store_->ScheduleSave();
348
349 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
350 BookmarkNodeChanged(this, node));
351}
352
[email protected]1858410f2012-10-26 05:06:45353void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
354 const std::string& key,
355 const std::string& value) {
[email protected]39e113452013-11-26 00:43:06356 std::string old_value;
357 if (node->GetMetaInfo(key, &old_value) && old_value == value)
358 return;
359
[email protected]cd869ede2013-10-17 12:29:40360 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
361 OnWillChangeBookmarkMetaInfo(this, node));
362
[email protected]1858410f2012-10-26 05:06:45363 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
364 store_->ScheduleSave();
[email protected]cd869ede2013-10-17 12:29:40365
366 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
367 BookmarkMetaInfoChanged(this, node));
[email protected]1858410f2012-10-26 05:06:45368}
369
[email protected]e38a87d2013-12-05 01:35:18370void BookmarkModel::SetNodeMetaInfoMap(
371 const BookmarkNode* node,
372 const BookmarkNode::MetaInfoMap& meta_info_map) {
373 const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
374 if ((!old_meta_info_map && meta_info_map.empty()) ||
375 (old_meta_info_map && meta_info_map == *old_meta_info_map))
376 return;
377
378 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
379 OnWillChangeBookmarkMetaInfo(this, node));
380
381 AsMutable(node)->SetMetaInfoMap(meta_info_map);
382 if (store_.get())
383 store_->ScheduleSave();
384
385 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
386 BookmarkMetaInfoChanged(this, node));
387}
388
[email protected]1858410f2012-10-26 05:06:45389void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
390 const std::string& key) {
[email protected]39e113452013-11-26 00:43:06391 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
392 if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
393 return;
394
[email protected]cd869ede2013-10-17 12:29:40395 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
396 OnWillChangeBookmarkMetaInfo(this, node));
397
[email protected]1858410f2012-10-26 05:06:45398 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
399 store_->ScheduleSave();
[email protected]cd869ede2013-10-17 12:29:40400
401 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
402 BookmarkMetaInfoChanged(this, node));
[email protected]1858410f2012-10-26 05:06:45403}
404
[email protected]39e113452013-11-26 00:43:06405void BookmarkModel::SetNodeSyncTransactionVersion(
406 const BookmarkNode* node,
407 int64 sync_transaction_version) {
408 if (sync_transaction_version == node->sync_transaction_version())
409 return;
410
411 AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
412 if (store_.get())
413 store_->ScheduleSave();
414}
415
[email protected]b61445c2012-10-27 00:11:42416void BookmarkModel::SetDateAdded(const BookmarkNode* node,
417 base::Time date_added) {
418 if (!node) {
419 NOTREACHED();
420 return;
421 }
422
423 if (node->date_added() == date_added)
424 return;
425
426 if (is_permanent_node(node)) {
427 NOTREACHED();
428 return;
429 }
430
431 AsMutable(node)->set_date_added(date_added);
432
433 // Syncing might result in dates newer than the folder's last modified date.
434 if (date_added > node->parent()->date_folder_modified()) {
435 // Will trigger store_->ScheduleSave().
436 SetDateFolderModified(node->parent(), date_added);
437 } else if (store_.get()) {
438 store_->ScheduleSave();
439 }
440}
441
[email protected]848cd05e2008-09-19 18:33:48442void BookmarkModel::GetNodesByURL(const GURL& url,
[email protected]b3c33d462009-06-26 22:29:20443 std::vector<const BookmarkNode*>* nodes) {
[email protected]20305ec2011-01-21 04:55:52444 base::AutoLock url_lock(url_lock_);
[email protected]ea2e5aa52009-05-20 18:01:28445 BookmarkNode tmp_node(url);
initial.commit09911bf2008-07-26 23:55:29446 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]5d4077542011-07-21 20:24:07447 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
[email protected]848cd05e2008-09-19 18:33:48448 nodes->push_back(*i);
449 ++i;
450 }
451}
452
[email protected]b3c33d462009-06-26 22:29:20453const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
454 const GURL& url) {
455 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48456 GetNodesByURL(url, &nodes);
457 if (nodes.empty())
458 return NULL;
459
[email protected]9333f182008-12-09 17:34:17460 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]848cd05e2008-09-19 18:33:48461 return nodes.front();
initial.commit09911bf2008-07-26 23:55:29462}
463
[email protected]cf8e8172011-07-23 00:46:24464bool BookmarkModel::HasBookmarks() {
465 base::AutoLock url_lock(url_lock_);
466 return !nodes_ordered_by_url_set_.empty();
467}
468
469bool BookmarkModel::IsBookmarked(const GURL& url) {
470 base::AutoLock url_lock(url_lock_);
471 return IsBookmarkedNoLock(url);
472}
473
[email protected]0f7bee52012-08-06 20:04:17474void BookmarkModel::GetBookmarks(
475 std::vector<BookmarkService::URLAndTitle>* bookmarks) {
[email protected]20305ec2011-01-21 04:55:52476 base::AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48477 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46478 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
479 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]5d4077542011-07-21 20:24:07480 const GURL* url = &((*i)->url());
[email protected]848cd05e2008-09-19 18:33:48481 // Only add unique URLs.
[email protected]0f7bee52012-08-06 20:04:17482 if (!last_url || *url != *last_url) {
483 BookmarkService::URLAndTitle bookmark;
484 bookmark.url = *url;
485 bookmark.title = (*i)->GetTitle();
486 bookmarks->push_back(bookmark);
487 }
[email protected]848cd05e2008-09-19 18:33:48488 last_url = url;
[email protected]90ef13132008-08-27 03:27:46489 }
490}
491
[email protected]cf8e8172011-07-23 00:46:24492void BookmarkModel::BlockTillLoaded() {
493 loaded_signal_.Wait();
[email protected]848cd05e2008-09-19 18:33:48494}
495
[email protected]66bb7dc2011-10-27 15:29:23496const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
initial.commit09911bf2008-07-26 23:55:29497 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33498 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29499}
500
[email protected]39703292011-03-18 17:03:40501const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
502 int index,
[email protected]96920152013-12-04 21:00:16503 const base::string16& title) {
[email protected]6b4d64c2011-07-29 21:33:24504 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29505 // Can't add to the root.
506 NOTREACHED();
507 return NULL;
508 }
509
[email protected]5b5c9b7f32011-07-21 01:07:18510 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
[email protected]edb63cc2011-03-11 02:00:41511 new_node->set_date_folder_modified(Time::Now());
[email protected]aee236542011-12-01 04:34:03512 // Folders shouldn't have line breaks in their titles.
[email protected]0491ff72011-12-30 00:45:59513 new_node->SetTitle(title);
[email protected]037db002009-10-19 20:06:08514 new_node->set_type(BookmarkNode::FOLDER);
initial.commit09911bf2008-07-26 23:55:29515
[email protected]a42ec64a2012-12-20 17:04:24516 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29517}
518
[email protected]e64e9012010-01-11 23:10:55519const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
520 int index,
[email protected]96920152013-12-04 21:00:16521 const base::string16& title,
[email protected]e64e9012010-01-11 23:10:55522 const GURL& url) {
[email protected]aee236542011-12-01 04:34:03523 return AddURLWithCreationTime(parent, index,
[email protected]1e1229a12014-03-11 23:16:24524 base::CollapseWhitespace(title, false),
[email protected]aee236542011-12-01 04:34:03525 url, Time::Now());
initial.commit09911bf2008-07-26 23:55:29526}
527
[email protected]e64e9012010-01-11 23:10:55528const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
529 const BookmarkNode* parent,
530 int index,
[email protected]96920152013-12-04 21:00:16531 const base::string16& title,
[email protected]e64e9012010-01-11 23:10:55532 const GURL& url,
533 const Time& creation_time) {
[email protected]6b4d64c2011-07-29 21:33:24534 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
[email protected]f25387b2008-08-21 15:20:33535 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29536 NOTREACHED();
537 return NULL;
538 }
initial.commit09911bf2008-07-26 23:55:29539
[email protected]b61445c2012-10-27 00:11:42540 // Syncing may result in dates newer than the last modified date.
[email protected]8ad613b2012-10-12 21:28:45541 if (creation_time > parent->date_folder_modified())
542 SetDateFolderModified(parent, creation_time);
initial.commit09911bf2008-07-26 23:55:29543
[email protected]ea2e5aa52009-05-20 18:01:28544 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
[email protected]0491ff72011-12-30 00:45:59545 new_node->SetTitle(title);
[email protected]b3c33d462009-06-26 22:29:20546 new_node->set_date_added(creation_time);
[email protected]037db002009-10-19 20:06:08547 new_node->set_type(BookmarkNode::URL);
initial.commit09911bf2008-07-26 23:55:29548
[email protected]776e7492008-10-23 16:47:41549 {
550 // Only hold the lock for the duration of the insert.
[email protected]20305ec2011-01-21 04:55:52551 base::AutoLock url_lock(url_lock_);
[email protected]776e7492008-10-23 16:47:41552 nodes_ordered_by_url_set_.insert(new_node);
553 }
initial.commit09911bf2008-07-26 23:55:29554
[email protected]a42ec64a2012-12-20 17:04:24555 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29556}
557
[email protected]b3c33d462009-06-26 22:29:20558void BookmarkModel::SortChildren(const BookmarkNode* parent) {
[email protected]6b4d64c2011-07-29 21:33:24559 if (!parent || !parent->is_folder() || is_root_node(parent) ||
[email protected]9c1a75a2011-03-10 02:38:12560 parent->child_count() <= 1) {
[email protected]e2f86d92009-02-25 00:22:01561 return;
562 }
563
[email protected]472f95e2013-06-10 16:49:18564 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
565 OnWillReorderBookmarkNode(this, parent));
566
[email protected]ef762642009-03-05 16:30:25567 UErrorCode error = U_ZERO_ERROR;
[email protected]94d914522013-04-02 19:49:47568 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
[email protected]ef762642009-03-05 16:30:25569 if (U_FAILURE(error))
570 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20571 BookmarkNode* mutable_parent = AsMutable(parent);
572 std::sort(mutable_parent->children().begin(),
573 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25574 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01575
[email protected]997a0362009-03-12 03:10:51576 if (store_.get())
577 store_->ScheduleSave();
578
[email protected]e2f86d92009-02-25 00:22:01579 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
580 BookmarkNodeChildrenReordered(this, parent));
581}
582
[email protected]472f95e2013-06-10 16:49:18583void BookmarkModel::ReorderChildren(
584 const BookmarkNode* parent,
[email protected]257d5082013-08-06 07:46:43585 const std::vector<const BookmarkNode*>& ordered_nodes) {
[email protected]472f95e2013-06-10 16:49:18586 // Ensure that all children in |parent| are in |ordered_nodes|.
587 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
588 for (size_t i = 0; i < ordered_nodes.size(); ++i)
589 DCHECK_EQ(parent, ordered_nodes[i]->parent());
590
591 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
592 OnWillReorderBookmarkNode(this, parent));
593
[email protected]257d5082013-08-06 07:46:43594 AsMutable(parent)->SetChildren(
595 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
[email protected]472f95e2013-06-10 16:49:18596
597 if (store_.get())
598 store_->ScheduleSave();
599
600 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
601 BookmarkNodeChildrenReordered(this, parent));
602}
603
[email protected]c6a7a3d2011-03-12 01:04:30604void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
605 const Time time) {
[email protected]eea8fd5532009-12-16 00:08:10606 DCHECK(parent);
[email protected]edb63cc2011-03-11 02:00:41607 AsMutable(parent)->set_date_folder_modified(time);
[email protected]eea8fd5532009-12-16 00:08:10608
609 if (store_.get())
610 store_->ScheduleSave();
611}
612
[email protected]c6a7a3d2011-03-12 01:04:30613void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
614 SetDateFolderModified(node, Time());
initial.commit09911bf2008-07-26 23:55:29615}
616
[email protected]e64e9012010-01-11 23:10:55617void BookmarkModel::GetBookmarksWithTitlesMatching(
[email protected]96920152013-12-04 21:00:16618 const base::string16& text,
[email protected]e64e9012010-01-11 23:10:55619 size_t max_count,
[email protected]dcf5fbb72013-05-30 15:39:57620 std::vector<BookmarkTitleMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28621 if (!loaded_)
622 return;
623
[email protected]d0195a62010-08-22 04:40:20624 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
[email protected]85d911c2009-05-19 03:59:42625}
626
[email protected]9876bb1c2008-12-16 20:42:25627void BookmarkModel::ClearStore() {
[email protected]40e0486b2009-05-22 01:47:27628 registrar_.RemoveAll();
[email protected]9876bb1c2008-12-16 20:42:25629 store_ = NULL;
630}
631
[email protected]bc770a032011-12-12 17:35:30632void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
633 bool value) {
[email protected]1da5f712011-12-06 05:52:26634 DCHECK(loaded_);
[email protected]bc770a032011-12-12 17:35:30635 switch (type) {
636 case BookmarkNode::BOOKMARK_BAR:
637 bookmark_bar_node_->set_visible(value);
638 break;
639 case BookmarkNode::OTHER_NODE:
640 other_node_->set_visible(value);
641 break;
642 case BookmarkNode::MOBILE:
643 mobile_node_->set_visible(value);
644 break;
645 default:
646 NOTREACHED();
647 }
[email protected]1da5f712011-12-06 05:52:26648}
649
[email protected]dddc1b42008-10-09 20:56:59650bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28651 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59652 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
653 nodes_ordered_by_url_set_.end());
654}
655
[email protected]d8e41ed2008-09-11 15:22:32656void BookmarkModel::RemoveNode(BookmarkNode* node,
657 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20658 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33659 NOTREACHED();
660 return;
661 }
662
[email protected]323dbf72013-03-30 17:08:33663 url_lock_.AssertAcquired();
[email protected]0890e60e2011-06-27 14:55:21664 if (node->is_url()) {
[email protected]5db00bca2013-12-23 00:43:09665 RemoveNodeFromURLSet(node);
[email protected]5d4077542011-07-21 20:24:07666 removed_urls->insert(node->url());
[email protected]01eec882009-05-22 18:13:28667 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29668 }
669
[email protected]abc2f262011-03-15 21:15:44670 CancelPendingFaviconLoadRequests(node);
initial.commit09911bf2008-07-26 23:55:29671
672 // Recurse through children.
[email protected]9c1a75a2011-03-10 02:38:12673 for (int i = node->child_count() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33674 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29675}
676
[email protected]a48f87d2012-10-09 18:06:33677void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
[email protected]01eec882009-05-22 18:13:28678 DCHECK(details_delete_me);
[email protected]9c92d192009-12-02 08:03:16679 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
[email protected]01eec882009-05-22 18:13:28680 if (loaded_) {
681 // We should only ever be loaded once.
682 NOTREACHED();
683 return;
684 }
685
[email protected]01eec882009-05-22 18:13:28686 next_node_id_ = details->max_id();
[email protected]367d7072009-07-13 23:27:13687 if (details->computed_checksum() != details->stored_checksum() ||
688 details->ids_reassigned()) {
689 // If bookmarks file changed externally, the IDs may have changed
690 // externally. In that case, the decoder may have reassigned IDs to make
691 // them unique. So when the file has changed externally, we should save the
692 // bookmarks file to persist new IDs.
693 if (store_.get())
694 store_->ScheduleSave();
695 }
[email protected]d22d8732010-05-04 19:24:42696 bookmark_bar_node_ = details->release_bb_node();
697 other_node_ = details->release_other_folder_node();
[email protected]37bc9132011-12-01 22:29:29698 mobile_node_ = details->release_mobile_folder_node();
[email protected]d22d8732010-05-04 19:24:42699 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28700
[email protected]82f4655a2011-10-18 13:05:43701 // WARNING: order is important here, various places assume the order is
702 // constant.
[email protected]a0dd6a32011-03-18 17:31:37703 root_.Add(bookmark_bar_node_, 0);
704 root_.Add(other_node_, 1);
[email protected]37bc9132011-12-01 22:29:29705 root_.Add(mobile_node_, 2);
[email protected]6c1164042009-05-08 14:41:08706
[email protected]39e113452013-11-26 00:43:06707 root_.SetMetaInfoMap(details->model_meta_info_map());
708 root_.set_sync_transaction_version(details->model_sync_transaction_version());
[email protected]1858410f2012-10-26 05:06:45709
[email protected]90ef13132008-08-27 03:27:46710 {
[email protected]20305ec2011-01-21 04:55:52711 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46712 // Update nodes_ordered_by_url_set_ from the nodes.
713 PopulateNodesByURL(&root_);
714 }
[email protected]f25387b2008-08-21 15:20:33715
initial.commit09911bf2008-07-26 23:55:29716 loaded_ = true;
717
[email protected]cbcd6412009-03-09 22:31:39718 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46719
[email protected]f25387b2008-08-21 15:20:33720 // Notify our direct observers.
[email protected]c58c5ea2011-07-13 21:43:16721 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]d1bc7af2013-12-26 05:45:13722 BookmarkModelLoaded(this, details->ids_reassigned()));
initial.commit09911bf2008-07-26 23:55:29723}
724
[email protected]d8e41ed2008-09-11 15:22:32725void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
726 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29727
[email protected]323dbf72013-03-30 17:08:33728 const BookmarkNode* parent = node->parent();
initial.commit09911bf2008-07-26 23:55:29729 DCHECK(parent);
[email protected]368f3a72011-03-08 17:17:48730 int index = parent->GetIndexOf(node.get());
[email protected]323dbf72013-03-30 17:08:33731
[email protected]472f95e2013-06-10 16:49:18732 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
733 OnWillRemoveBookmarks(this, parent, index, node.get()));
734
[email protected]9109f8a12013-06-12 18:07:48735 std::set<GURL> removed_urls;
[email protected]90ef13132008-08-27 03:27:46736 {
[email protected]20305ec2011-01-21 04:55:52737 base::AutoLock url_lock(url_lock_);
[email protected]9109f8a12013-06-12 18:07:48738 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
[email protected]90ef13132008-08-27 03:27:46739 }
initial.commit09911bf2008-07-26 23:55:29740
[email protected]f25387b2008-08-21 15:20:33741 if (store_.get())
742 store_->ScheduleSave();
743
[email protected]9109f8a12013-06-12 18:07:48744 NotifyHistoryAboutRemovedBookmarks(removed_urls);
[email protected]323dbf72013-03-30 17:08:33745
[email protected]d8e41ed2008-09-11 15:22:32746 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41747 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]323dbf72013-03-30 17:08:33748}
[email protected]f25387b2008-08-21 15:20:33749
[email protected]5db00bca2013-12-23 00:43:09750void BookmarkModel::RemoveNodeFromURLSet(BookmarkNode* node) {
751 // NOTE: this is called in such a way that url_lock_ is already held. As
752 // such, this doesn't explicitly grab the lock.
753 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
754 DCHECK(i != nodes_ordered_by_url_set_.end());
755 // i points to the first node with the URL, advance until we find the
756 // node we're removing.
757 while (*i != node)
758 ++i;
759 nodes_ordered_by_url_set_.erase(i);
760}
761
[email protected]323dbf72013-03-30 17:08:33762void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
763 std::set<GURL>* removed_urls) {
764 // NOTE: this method should be always called with |url_lock_| held.
765 // This method does not explicitly acquires a lock.
766 url_lock_.AssertAcquired();
767 DCHECK(removed_urls);
768 BookmarkNode* parent = AsMutable(node->parent());
769 DCHECK(parent);
770 parent->Remove(node);
771 RemoveNode(node, removed_urls);
772 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
773 // allow duplicates we need to remove any entries that are still bookmarked.
774 for (std::set<GURL>::iterator i = removed_urls->begin();
775 i != removed_urls->end();) {
776 if (IsBookmarkedNoLock(*i)) {
777 // When we erase the iterator pointing at the erasee is
778 // invalidated, so using i++ here within the "erase" call is
779 // important as it advances the iterator before passing the
780 // old value through to erase.
781 removed_urls->erase(i++);
782 } else {
783 ++i;
784 }
785 }
786}
787
788void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
789 const std::set<GURL>& removed_bookmark_urls) const {
790 if (removed_bookmark_urls.empty()) {
[email protected]848cd05e2008-09-19 18:33:48791 // No point in sending out notification if the starred state didn't change.
792 return;
793 }
794
[email protected]90ef13132008-08-27 03:27:46795 if (profile_) {
796 HistoryService* history =
[email protected]9aac66862012-06-19 19:44:31797 HistoryServiceFactory::GetForProfile(profile_,
798 Profile::EXPLICIT_ACCESS);
[email protected]90ef13132008-08-27 03:27:46799 if (history)
[email protected]323dbf72013-03-30 17:08:33800 history->URLsNoLongerBookmarked(removed_bookmark_urls);
[email protected]90ef13132008-08-27 03:27:46801 }
initial.commit09911bf2008-07-26 23:55:29802}
803
[email protected]d8e41ed2008-09-11 15:22:32804BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
805 int index,
[email protected]a42ec64a2012-12-20 17:04:24806 BookmarkNode* node) {
[email protected]a0dd6a32011-03-18 17:31:37807 parent->Add(node, index);
initial.commit09911bf2008-07-26 23:55:29808
[email protected]f25387b2008-08-21 15:20:33809 if (store_.get())
810 store_->ScheduleSave();
811
[email protected]d8e41ed2008-09-11 15:22:32812 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29813 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33814
[email protected]01eec882009-05-22 18:13:28815 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42816
initial.commit09911bf2008-07-26 23:55:29817 return node;
818}
819
[email protected]b3c33d462009-06-26 22:29:20820const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
[email protected]66bb7dc2011-10-27 15:29:23821 int64 id) const {
[email protected]f25387b2008-08-21 15:20:33822 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29823 return node;
[email protected]f25387b2008-08-21 15:20:33824
[email protected]9c1a75a2011-03-10 02:38:12825 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
[email protected]b3c33d462009-06-26 22:29:20826 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29827 if (result)
828 return result;
829 }
830 return NULL;
831}
832
[email protected]b3c33d462009-06-26 22:29:20833bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32834 int index,
835 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41836 return (parent && parent->is_folder() &&
[email protected]9c1a75a2011-03-10 02:38:12837 (index >= 0 && (index < parent->child_count() ||
838 (allow_end && index == parent->child_count()))));
[email protected]bd1b96702009-07-08 21:54:14839}
[email protected]f25387b2008-08-21 15:20:33840
[email protected]bc770a032011-12-12 17:35:30841BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
842 BookmarkNode::Type type) {
[email protected]e1f76c62011-06-30 20:15:39843 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
844 type == BookmarkNode::OTHER_NODE ||
[email protected]37bc9132011-12-01 22:29:29845 type == BookmarkNode::MOBILE);
[email protected]bc770a032011-12-12 17:35:30846 BookmarkPermanentNode* node =
847 new BookmarkPermanentNode(generate_next_node_id());
848 if (type == BookmarkNode::MOBILE)
849 node->set_visible(false); // Mobile node is initially hidden.
850
851 int title_id;
852 switch (type) {
853 case BookmarkNode::BOOKMARK_BAR:
854 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
855 break;
856 case BookmarkNode::OTHER_NODE:
857 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
858 break;
859 case BookmarkNode::MOBILE:
860 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
861 break;
862 default:
[email protected]97fdd162011-12-03 20:50:12863 NOTREACHED();
[email protected]bc770a032011-12-12 17:35:30864 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
865 break;
[email protected]7caca8a22010-08-21 18:25:31866 }
[email protected]0491ff72011-12-30 00:45:59867 node->SetTitle(l10n_util::GetStringUTF16(title_id));
[email protected]97fdd162011-12-03 20:50:12868 node->set_type(type);
initial.commit09911bf2008-07-26 23:55:29869 return node;
870}
871
[email protected]abc2f262011-03-15 21:15:44872void BookmarkModel::OnFaviconDataAvailable(
[email protected]0ea3db52012-12-07 01:32:01873 BookmarkNode* node,
[email protected]7627e0b42014-04-17 17:20:53874 const favicon_base::FaviconImageResult& image_result) {
[email protected]4167c3a2008-08-21 18:12:20875 DCHECK(node);
[email protected]e95b717f2014-02-06 13:47:13876 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
[email protected]bcc38612012-10-23 01:10:27877 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
[email protected]65baa222012-08-30 15:43:51878 if (!image_result.image.IsEmpty()) {
879 node->set_favicon(image_result.image);
[email protected]2ad0e872012-11-30 01:24:46880 node->set_icon_url(image_result.icon_url);
[email protected]65baa222012-08-30 15:43:51881 FaviconLoaded(node);
initial.commit09911bf2008-07-26 23:55:29882 }
883}
884
[email protected]abc2f262011-03-15 21:15:44885void BookmarkModel::LoadFavicon(BookmarkNode* node) {
[email protected]0890e60e2011-06-27 14:55:21886 if (node->is_folder())
initial.commit09911bf2008-07-26 23:55:29887 return;
888
[email protected]5d4077542011-07-21 20:24:07889 DCHECK(node->url().is_valid());
[email protected]f3d2b312012-08-23 22:27:59890 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
891 profile_, Profile::EXPLICIT_ACCESS);
[email protected]0189bc722009-08-28 21:56:48892 if (!favicon_service)
initial.commit09911bf2008-07-26 23:55:29893 return;
[email protected]325a02092014-04-10 20:21:18894 base::CancelableTaskTracker::TaskId taskId =
895 favicon_service->GetFaviconImageForURL(
896 FaviconService::FaviconForURLParams(
[email protected]7627e0b42014-04-17 17:20:53897 node->url(), favicon_base::FAVICON, gfx::kFaviconSize),
[email protected]325a02092014-04-10 20:21:18898 base::Bind(&BookmarkModel::OnFaviconDataAvailable,
899 base::Unretained(this),
900 node),
901 &cancelable_task_tracker_);
902 node->set_favicon_load_task_id(taskId);
initial.commit09911bf2008-07-26 23:55:29903}
904
[email protected]5b5c9b7f32011-07-21 01:07:18905void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
906 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
907 BookmarkNodeFaviconChanged(this, node));
908}
909
[email protected]abc2f262011-03-15 21:15:44910void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
[email protected]e95b717f2014-02-06 13:47:13911 if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) {
[email protected]0ea3db52012-12-07 01:32:01912 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
[email protected]e95b717f2014-02-06 13:47:13913 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
initial.commit09911bf2008-07-26 23:55:29914 }
915}
916
[email protected]432115822011-07-10 15:52:27917void BookmarkModel::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53918 const content::NotificationSource& source,
919 const content::NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27920 switch (type) {
921 case chrome::NOTIFICATION_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29922 // Prevent the observers from getting confused for multiple favicon loads.
[email protected]0ab247d2013-05-28 17:01:06923 content::Details<FaviconChangedDetails> favicon_details(details);
initial.commit09911bf2008-07-26 23:55:29924 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
925 i != favicon_details->urls.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20926 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48927 GetNodesByURL(*i, &nodes);
928 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:29929 // Got an updated favicon, for a URL, do a new request.
[email protected]b3c33d462009-06-26 22:29:20930 BookmarkNode* node = AsMutable(nodes[i]);
initial.commit09911bf2008-07-26 23:55:29931 node->InvalidateFavicon();
[email protected]abc2f262011-03-15 21:15:44932 CancelPendingFaviconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:32933 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]f12de8302011-05-23 16:12:30934 BookmarkNodeFaviconChanged(this, node));
initial.commit09911bf2008-07-26 23:55:29935 }
936 }
937 break;
938 }
939
940 default:
941 NOTREACHED();
942 break;
943 }
944}
[email protected]f25387b2008-08-21 15:20:33945
[email protected]d8e41ed2008-09-11 15:22:32946void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:46947 // NOTE: this is called with url_lock_ already held. As such, this doesn't
948 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:33949 if (node->is_url())
950 nodes_ordered_by_url_set_.insert(node);
[email protected]9c1a75a2011-03-10 02:38:12951 for (int i = 0; i < node->child_count(); ++i)
[email protected]f25387b2008-08-21 15:20:33952 PopulateNodesByURL(node->GetChild(i));
953}
[email protected]4d89f382009-05-12 06:56:49954
[email protected]367d7072009-07-13 23:27:13955int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:49956 return next_node_id_++;
957}
[email protected]01eec882009-05-22 18:13:28958
[email protected]9c92d192009-12-02 08:03:16959BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
[email protected]bc770a032011-12-12 17:35:30960 BookmarkPermanentNode* bb_node =
961 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
962 BookmarkPermanentNode* other_node =
963 CreatePermanentNode(BookmarkNode::OTHER_NODE);
964 BookmarkPermanentNode* mobile_node =
965 CreatePermanentNode(BookmarkNode::MOBILE);
[email protected]37bc9132011-12-01 22:29:29966 return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
[email protected]1858410f2012-10-26 05:06:45967 new BookmarkIndex(profile_),
968 next_node_id_);
[email protected]01eec882009-05-22 18:13:28969}