blob: ec88acffefb152516b67b0c9d6a4400b416f8eb7 [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]1858410f2012-10-26 05:06:4513#include "base/json/json_string_value_serializer.h"
[email protected]fb441962013-05-08 05:35:2414#include "base/sequenced_task_runner.h"
[email protected]ac62f512013-06-10 19:26:4415#include "base/strings/string_util.h"
[email protected]135cb802013-06-09 16:44:2016#include "base/strings/utf_string_conversions.h"
[email protected]1858410f2012-10-26 05:06:4517#include "base/values.h"
[email protected]fc3fc452009-02-10 03:25:4018#include "build/build_config.h"
[email protected]c58c5ea2011-07-13 21:43:1619#include "chrome/browser/bookmarks/bookmark_expanded_state_tracker.h"
[email protected]01eec882009-05-22 18:13:2820#include "chrome/browser/bookmarks/bookmark_index.h"
[email protected]125b234182011-07-08 19:54:4121#include "chrome/browser/bookmarks/bookmark_model_observer.h"
[email protected]68de8b72008-09-09 23:08:1322#include "chrome/browser/bookmarks/bookmark_storage.h"
[email protected]dcf5fbb72013-05-30 15:39:5723#include "chrome/browser/bookmarks/bookmark_title_match.h"
[email protected]368f3a72011-03-08 17:17:4824#include "chrome/browser/bookmarks/bookmark_utils.h"
[email protected]0ab247d2013-05-28 17:01:0625#include "chrome/browser/favicon/favicon_changed_details.h"
[email protected]dfad18be72012-12-21 00:46:3426#include "chrome/browser/favicon/favicon_service.h"
[email protected]f3d2b312012-08-23 22:27:5927#include "chrome/browser/favicon/favicon_service_factory.h"
[email protected]299e4542013-05-16 03:09:0628#include "chrome/browser/favicon/favicon_types.h"
[email protected]6a2c09f2013-01-25 04:50:0729#include "chrome/browser/history/history_service.h"
[email protected]9aac66862012-06-19 19:44:3130#include "chrome/browser/history/history_service_factory.h"
[email protected]8ecad5e2010-12-02 21:18:3331#include "chrome/browser/profiles/profile.h"
[email protected]432115822011-07-10 15:52:2732#include "chrome/common/chrome_notification_types.h"
[email protected]dae9f5b2013-02-27 15:29:2333#include "content/public/browser/notification_details.h"
34#include "content/public/browser/notification_source.h"
[email protected]34ac8f32009-02-22 23:03:2735#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1736#include "ui/base/l10n/l10n_util.h"
[email protected]65baa222012-08-30 15:43:5137#include "ui/gfx/favicon_size.h"
[email protected]6a4e5a02012-06-26 19:47:4838#include "ui/gfx/image/image_util.h"
initial.commit09911bf2008-07-26 23:55:2939
[email protected]e1acf6f2008-10-27 20:43:3340using base::Time;
41
[email protected]b3c33d462009-06-26 22:29:2042namespace {
43
44// Helper to get a mutable bookmark node.
[email protected]7b084b02011-07-08 16:06:4845BookmarkNode* AsMutable(const BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:2046 return const_cast<BookmarkNode*>(node);
47}
48
[email protected]f280db82012-10-20 04:21:0849// Whitespace characters to strip from bookmark titles.
50const char16 kInvalidChars[] = {
51 '\n', '\r', '\t',
52 0x2028, // Line separator
53 0x2029, // Paragraph separator
54 0
55};
56
[email protected]e1f76c62011-06-30 20:15:3957} // namespace
[email protected]b3c33d462009-06-26 22:29:2058
[email protected]d8e41ed2008-09-11 15:22:3259// BookmarkNode ---------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2960
[email protected]ea2e5aa52009-05-20 18:01:2861BookmarkNode::BookmarkNode(const GURL& url)
[email protected]814a2d32009-04-30 23:09:0162 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2863 Initialize(0);
[email protected]814a2d32009-04-30 23:09:0164}
65
[email protected]367d7072009-07-13 23:27:1366BookmarkNode::BookmarkNode(int64 id, const GURL& url)
[email protected]2c685cc22009-08-28 00:17:4467 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2868 Initialize(id);
[email protected]814a2d32009-04-30 23:09:0169}
70
[email protected]a0835ac2010-09-13 19:40:0871BookmarkNode::~BookmarkNode() {
72}
73
[email protected]0491ff72011-12-30 00:45:5974void BookmarkNode::SetTitle(const string16& title) {
[email protected]f280db82012-10-20 04:21:0875 // Replace newlines and other problematic whitespace characters in
76 // folder/bookmark names with spaces.
77 string16 trimmed_title;
78 ReplaceChars(title, kInvalidChars, ASCIIToUTF16(" "), &trimmed_title);
79 ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
[email protected]0491ff72011-12-30 00:45:5980}
81
[email protected]97fdd162011-12-03 20:50:1282bool BookmarkNode::IsVisible() const {
83 return true;
84}
85
[email protected]1858410f2012-10-26 05:06:4586bool BookmarkNode::GetMetaInfo(const std::string& key,
87 std::string* value) const {
88 if (meta_info_str_.empty())
89 return false;
90
91 JSONStringValueSerializer serializer(meta_info_str_);
92 scoped_ptr<DictionaryValue> meta_dict(
93 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
94 return meta_dict.get() ? meta_dict->GetString(key, value) : false;
95}
96
97bool BookmarkNode::SetMetaInfo(const std::string& key,
98 const std::string& value) {
99 JSONStringValueSerializer serializer(&meta_info_str_);
100 scoped_ptr<DictionaryValue> meta_dict;
101 if (!meta_info_str_.empty()) {
102 meta_dict.reset(
103 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
104 }
105 if (!meta_dict.get()) {
106 meta_dict.reset(new DictionaryValue);
107 } else {
108 std::string old_value;
109 if (meta_dict->GetString(key, &old_value) && old_value == value)
110 return false;
111 }
112 meta_dict->SetString(key, value);
113 serializer.Serialize(*meta_dict);
114 std::string(meta_info_str_.data(), meta_info_str_.size()).swap(
115 meta_info_str_);
116 return true;
117}
118
119bool BookmarkNode::DeleteMetaInfo(const std::string& key) {
120 if (meta_info_str_.empty())
121 return false;
122
123 JSONStringValueSerializer serializer(&meta_info_str_);
124 scoped_ptr<DictionaryValue> meta_dict(
125 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
126 if (meta_dict.get() && meta_dict->HasKey(key)) {
127 meta_dict->Remove(key, NULL);
128 if (meta_dict->empty()) {
129 meta_info_str_.clear();
130 } else {
131 serializer.Serialize(*meta_dict);
132 std::string(meta_info_str_.data(), meta_info_str_.size()).swap(
133 meta_info_str_);
134 }
135 return true;
136 } else {
137 return false;
138 }
139}
140
[email protected]7b084b02011-07-08 16:06:48141void BookmarkNode::Initialize(int64 id) {
142 id_ = id;
143 type_ = url_.is_empty() ? FOLDER : URL;
144 date_added_ = Time::Now();
[email protected]bcc38612012-10-23 01:10:27145 favicon_state_ = INVALID_FAVICON;
[email protected]0ea3db52012-12-07 01:32:01146 favicon_load_task_id_ = CancelableTaskTracker::kBadTaskId;
[email protected]1858410f2012-10-26 05:06:45147 meta_info_str_.clear();
[email protected]7b084b02011-07-08 16:06:48148}
149
[email protected]5b5c9b7f32011-07-21 01:07:18150void BookmarkNode::InvalidateFavicon() {
[email protected]2ad0e872012-11-30 01:24:46151 icon_url_ = GURL();
[email protected]6a4e5a02012-06-26 19:47:48152 favicon_ = gfx::Image();
[email protected]bcc38612012-10-23 01:10:27153 favicon_state_ = INVALID_FAVICON;
[email protected]5b5c9b7f32011-07-21 01:07:18154}
155
[email protected]ef762642009-03-05 16:30:25156namespace {
157
158// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:44159// bookmarks.
[email protected]b3c33d462009-06-26 22:29:20160class SortComparator : public std::binary_function<const BookmarkNode*,
161 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:25162 bool> {
163 public:
[email protected]a48f87d2012-10-09 18:06:33164 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
[email protected]ef762642009-03-05 16:30:25165
[email protected]a48f87d2012-10-09 18:06:33166 // Returns true if |n1| preceeds |n2|.
167 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:08168 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:25169 // Types are the same, compare the names.
170 if (!collator_)
[email protected]440b37b22010-08-30 05:31:40171 return n1->GetTitle() < n2->GetTitle();
[email protected]9a08fe82013-04-23 05:06:05172 return base::i18n::CompareString16WithCollator(
[email protected]3abebda2011-01-07 20:17:15173 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
[email protected]ef762642009-03-05 16:30:25174 }
175 // Types differ, sort such that folders come first.
176 return n1->is_folder();
177 }
178
179 private:
[email protected]b5b2385a2009-08-18 05:12:29180 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:25181};
182
[email protected]bc770a032011-12-12 17:35:30183} // namespace
[email protected]97fdd162011-12-03 20:50:12184
[email protected]bc770a032011-12-12 17:35:30185// BookmarkPermanentNode -------------------------------------------------------
[email protected]97fdd162011-12-03 20:50:12186
[email protected]bc770a032011-12-12 17:35:30187BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
[email protected]97fdd162011-12-03 20:50:12188 : BookmarkNode(id, GURL()),
[email protected]bc770a032011-12-12 17:35:30189 visible_(true) {
[email protected]97fdd162011-12-03 20:50:12190}
191
[email protected]bc770a032011-12-12 17:35:30192BookmarkPermanentNode::~BookmarkPermanentNode() {
[email protected]97fdd162011-12-03 20:50:12193}
194
[email protected]bc770a032011-12-12 17:35:30195bool BookmarkPermanentNode::IsVisible() const {
[email protected]97fdd162011-12-03 20:50:12196 return visible_ || !empty();
197}
198
[email protected]97fdd162011-12-03 20:50:12199// BookmarkModel --------------------------------------------------------------
200
[email protected]d8e41ed2008-09-11 15:22:32201BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:29202 : profile_(profile),
203 loaded_(false),
[email protected]ea2e5aa52009-05-20 18:01:28204 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:29205 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:46206 other_node_(NULL),
[email protected]37bc9132011-12-01 22:29:29207 mobile_node_(NULL),
[email protected]4d89f382009-05-12 06:56:49208 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:06209 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]b68a8172012-02-17 00:25:18210 loaded_signal_(true, false),
211 extensive_changes_(0) {
[email protected]f25387b2008-08-21 15:20:33212 if (!profile_) {
213 // Profile is null during testing.
[email protected]01eec882009-05-22 18:13:28214 DoneLoading(CreateLoadDetails());
initial.commit09911bf2008-07-26 23:55:29215 }
initial.commit09911bf2008-07-26 23:55:29216}
217
[email protected]d8e41ed2008-09-11 15:22:32218BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:32219 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:51220 BookmarkModelBeingDeleted(this));
221
[email protected]dc24976f2013-06-02 21:15:09222 if (store_.get()) {
[email protected]f25387b2008-08-21 15:20:33223 // The store maintains a reference back to us. We need to tell it we're gone
224 // so that it doesn't try and invoke a method back on us again.
225 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29226 }
227}
228
[email protected]f61f4782012-06-08 21:54:21229void BookmarkModel::Shutdown() {
[email protected]88e6a232011-09-14 14:53:20230 if (loaded_)
231 return;
232
[email protected]f61f4782012-06-08 21:54:21233 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
234 // details. It is also called when the BookmarkModel is deleted.
[email protected]88e6a232011-09-14 14:53:20235 loaded_signal_.Signal();
236}
237
[email protected]afecfb72013-04-18 17:17:33238void BookmarkModel::Load(
239 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
[email protected]90ef13132008-08-27 03:27:46240 if (store_.get()) {
241 // If the store is non-null, it means Load was already invoked. Load should
242 // only be invoked once.
243 NOTREACHED();
244 return;
245 }
246
[email protected]c58c5ea2011-07-13 21:43:16247 expanded_state_tracker_.reset(new BookmarkExpandedStateTracker(
[email protected]25ee85302012-10-12 17:27:41248 profile_, this));
[email protected]c58c5ea2011-07-13 21:43:16249
[email protected]90ef13132008-08-27 03:27:46250 // Listen for changes to favicons so that we can update the favicon of the
251 // node appropriately.
[email protected]432115822011-07-10 15:52:27252 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
[email protected]6c2381d2011-10-19 02:52:53253 content::Source<Profile>(profile_));
[email protected]90ef13132008-08-27 03:27:46254
255 // Load the bookmarks. BookmarkStorage notifies us when done.
[email protected]dc24976f2013-06-02 21:15:09256 store_ = new BookmarkStorage(profile_, this, task_runner.get());
[email protected]01eec882009-05-22 18:13:28257 store_->LoadBookmarks(CreateLoadDetails());
[email protected]90ef13132008-08-27 03:27:46258}
259
[email protected]b3c33d462009-06-26 22:29:20260const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
261 std::vector<const BookmarkNode*> nodes =
[email protected]9fcaee72011-03-21 21:53:44262 bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
[email protected]82f4655a2011-10-18 13:05:43263 DCHECK(!nodes.empty()); // This list is always padded with default folders.
264 return nodes[0];
initial.commit09911bf2008-07-26 23:55:29265}
266
[email protected]125b234182011-07-08 19:54:41267void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
268 observers_.AddObserver(observer);
269}
270
271void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
272 observers_.RemoveObserver(observer);
273}
274
[email protected]b68a8172012-02-17 00:25:18275void BookmarkModel::BeginExtensiveChanges() {
276 if (++extensive_changes_ == 1) {
277 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
278 ExtensiveBookmarkChangesBeginning(this));
279 }
[email protected]125b234182011-07-08 19:54:41280}
281
[email protected]b68a8172012-02-17 00:25:18282void BookmarkModel::EndExtensiveChanges() {
283 --extensive_changes_;
284 DCHECK_GE(extensive_changes_, 0);
285 if (extensive_changes_ == 0) {
286 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
287 ExtensiveBookmarkChangesEnded(this));
288 }
[email protected]125b234182011-07-08 19:54:41289}
290
[email protected]b3c33d462009-06-26 22:29:20291void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
[email protected]6b4d64c2011-07-29 21:33:24292 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
[email protected]f25387b2008-08-21 15:20:33293 NOTREACHED();
294 return;
295 }
[email protected]b3c33d462009-06-26 22:29:20296 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
[email protected]f25387b2008-08-21 15:20:33297}
298
[email protected]323dbf72013-03-30 17:08:33299void BookmarkModel::RemoveAll() {
300 std::set<GURL> changed_urls;
301 ScopedVector<BookmarkNode> removed_nodes;
[email protected]472f95e2013-06-10 16:49:18302
303 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
304 OnWillRemoveAllBookmarks(this));
305
[email protected]323dbf72013-03-30 17:08:33306 BeginExtensiveChanges();
307 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
308 // its immediate children. For removing all non permanent nodes just remove
309 // all children of non-root permanent nodes.
310 {
311 base::AutoLock url_lock(url_lock_);
312 for (int i = 0; i < root_.child_count(); ++i) {
313 const BookmarkNode* permanent_node = root_.GetChild(i);
314 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
315 BookmarkNode* child_node = AsMutable(permanent_node->GetChild(j));
316 removed_nodes.push_back(child_node);
317 RemoveNodeAndGetRemovedUrls(child_node, &changed_urls);
318 }
319 }
320 }
321 EndExtensiveChanges();
322 if (store_.get())
323 store_->ScheduleSave();
324
325 NotifyHistoryAboutRemovedBookmarks(changed_urls);
326
327 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
328 BookmarkAllNodesRemoved(this));
329}
330
[email protected]b3c33d462009-06-26 22:29:20331void BookmarkModel::Move(const BookmarkNode* node,
332 const BookmarkNode* new_parent,
[email protected]d8e41ed2008-09-11 15:22:32333 int index) {
[email protected]f25387b2008-08-21 15:20:33334 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24335 is_root_node(new_parent) || is_permanent_node(node)) {
initial.commit09911bf2008-07-26 23:55:29336 NOTREACHED();
337 return;
338 }
339
340 if (new_parent->HasAncestor(node)) {
341 // Can't make an ancestor of the node be a child of the node.
342 NOTREACHED();
343 return;
344 }
345
[email protected]2d48ee842011-03-08 23:27:29346 const BookmarkNode* old_parent = node->parent();
[email protected]368f3a72011-03-08 17:17:48347 int old_index = old_parent->GetIndexOf(node);
initial.commit09911bf2008-07-26 23:55:29348
349 if (old_parent == new_parent &&
350 (index == old_index || index == old_index + 1)) {
351 // Node is already in this position, nothing to do.
352 return;
353 }
354
[email protected]9e583642012-12-05 02:48:32355 SetDateFolderModified(new_parent, Time::Now());
356
initial.commit09911bf2008-07-26 23:55:29357 if (old_parent == new_parent && index > old_index)
358 index--;
[email protected]b3c33d462009-06-26 22:29:20359 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
[email protected]a0dd6a32011-03-18 17:31:37360 mutable_new_parent->Add(AsMutable(node), index);
initial.commit09911bf2008-07-26 23:55:29361
[email protected]f25387b2008-08-21 15:20:33362 if (store_.get())
363 store_->ScheduleSave();
364
[email protected]d8e41ed2008-09-11 15:22:32365 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29366 BookmarkNodeMoved(this, old_parent, old_index,
367 new_parent, index));
368}
369
[email protected]4e187ef652010-03-11 05:21:35370void BookmarkModel::Copy(const BookmarkNode* node,
371 const BookmarkNode* new_parent,
372 int index) {
373 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24374 is_root_node(new_parent) || is_permanent_node(node)) {
[email protected]4e187ef652010-03-11 05:21:35375 NOTREACHED();
376 return;
377 }
378
379 if (new_parent->HasAncestor(node)) {
380 // Can't make an ancestor of the node be a child of the node.
381 NOTREACHED();
382 return;
383 }
384
[email protected]c6a7a3d2011-03-12 01:04:30385 SetDateFolderModified(new_parent, Time::Now());
[email protected]67a8ae42013-05-20 09:42:39386 BookmarkNodeData drag_data(node);
387 std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
[email protected]b1864502010-11-13 00:55:51388 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
[email protected]1a566fae2010-04-16 01:05:06389 // don't need to send notifications here.
[email protected]b1864502010-11-13 00:55:51390 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index);
[email protected]4e187ef652010-03-11 05:21:35391
392 if (store_.get())
393 store_->ScheduleSave();
[email protected]4e187ef652010-03-11 05:21:35394}
395
[email protected]6a4e5a02012-06-26 19:47:48396const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
[email protected]ea2e5aa52009-05-20 18:01:28397 DCHECK(node);
[email protected]bcc38612012-10-23 01:10:27398 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
[email protected]b3c33d462009-06-26 22:29:20399 BookmarkNode* mutable_node = AsMutable(node);
[email protected]bcc38612012-10-23 01:10:27400 mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
[email protected]abc2f262011-03-15 21:15:44401 LoadFavicon(mutable_node);
[email protected]ea2e5aa52009-05-20 18:01:28402 }
403 return node->favicon();
404}
405
[email protected]e64e9012010-01-11 23:10:55406void BookmarkModel::SetTitle(const BookmarkNode* node, const string16& title) {
[email protected]f25387b2008-08-21 15:20:33407 if (!node) {
408 NOTREACHED();
409 return;
410 }
[email protected]0491ff72011-12-30 00:45:59411 if (node->GetTitle() == title)
initial.commit09911bf2008-07-26 23:55:29412 return;
[email protected]f25387b2008-08-21 15:20:33413
[email protected]6892e2e2011-05-26 22:07:17414 if (is_permanent_node(node)) {
[email protected]baf4f922009-10-19 16:44:07415 NOTREACHED();
416 return;
417 }
418
[email protected]472f95e2013-06-10 16:49:18419 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
420 OnWillChangeBookmarkNode(this, node));
421
[email protected]85d911c2009-05-19 03:59:42422 // The title index doesn't support changing the title, instead we remove then
423 // add it back.
[email protected]01eec882009-05-22 18:13:28424 index_->Remove(node);
[email protected]0491ff72011-12-30 00:45:59425 AsMutable(node)->SetTitle(title);
[email protected]01eec882009-05-22 18:13:28426 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42427
[email protected]f25387b2008-08-21 15:20:33428 if (store_.get())
429 store_->ScheduleSave();
430
[email protected]d8e41ed2008-09-11 15:22:32431 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29432 BookmarkNodeChanged(this, node));
433}
434
[email protected]e5486602010-02-09 21:27:55435void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
436 if (!node) {
437 NOTREACHED();
438 return;
439 }
440
441 // We cannot change the URL of a folder.
442 if (node->is_folder()) {
443 NOTREACHED();
444 return;
445 }
446
[email protected]5d4077542011-07-21 20:24:07447 if (node->url() == url)
[email protected]e5486602010-02-09 21:27:55448 return;
449
[email protected]5b5c9b7f32011-07-21 01:07:18450 BookmarkNode* mutable_node = AsMutable(node);
451 mutable_node->InvalidateFavicon();
452 CancelPendingFaviconLoadRequests(mutable_node);
[email protected]e5486602010-02-09 21:27:55453
[email protected]472f95e2013-06-10 16:49:18454 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
455 OnWillChangeBookmarkNode(this, node));
456
[email protected]e5486602010-02-09 21:27:55457 {
[email protected]20305ec2011-01-21 04:55:52458 base::AutoLock url_lock(url_lock_);
[email protected]e5486602010-02-09 21:27:55459 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
[email protected]5b5c9b7f32011-07-21 01:07:18460 mutable_node);
[email protected]e5486602010-02-09 21:27:55461 DCHECK(i != nodes_ordered_by_url_set_.end());
462 // i points to the first node with the URL, advance until we find the
463 // node we're removing.
464 while (*i != node)
465 ++i;
466 nodes_ordered_by_url_set_.erase(i);
467
[email protected]5d4077542011-07-21 20:24:07468 mutable_node->set_url(url);
[email protected]5b5c9b7f32011-07-21 01:07:18469 nodes_ordered_by_url_set_.insert(mutable_node);
[email protected]e5486602010-02-09 21:27:55470 }
471
472 if (store_.get())
473 store_->ScheduleSave();
474
475 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
476 BookmarkNodeChanged(this, node));
477}
478
[email protected]1858410f2012-10-26 05:06:45479void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
480 const std::string& key,
481 const std::string& value) {
482 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
483 store_->ScheduleSave();
484}
485
486void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
487 const std::string& key) {
488 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
489 store_->ScheduleSave();
490}
491
[email protected]b61445c2012-10-27 00:11:42492void BookmarkModel::SetDateAdded(const BookmarkNode* node,
493 base::Time date_added) {
494 if (!node) {
495 NOTREACHED();
496 return;
497 }
498
499 if (node->date_added() == date_added)
500 return;
501
502 if (is_permanent_node(node)) {
503 NOTREACHED();
504 return;
505 }
506
507 AsMutable(node)->set_date_added(date_added);
508
509 // Syncing might result in dates newer than the folder's last modified date.
510 if (date_added > node->parent()->date_folder_modified()) {
511 // Will trigger store_->ScheduleSave().
512 SetDateFolderModified(node->parent(), date_added);
513 } else if (store_.get()) {
514 store_->ScheduleSave();
515 }
516}
517
[email protected]848cd05e2008-09-19 18:33:48518void BookmarkModel::GetNodesByURL(const GURL& url,
[email protected]b3c33d462009-06-26 22:29:20519 std::vector<const BookmarkNode*>* nodes) {
[email protected]20305ec2011-01-21 04:55:52520 base::AutoLock url_lock(url_lock_);
[email protected]ea2e5aa52009-05-20 18:01:28521 BookmarkNode tmp_node(url);
initial.commit09911bf2008-07-26 23:55:29522 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]5d4077542011-07-21 20:24:07523 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
[email protected]848cd05e2008-09-19 18:33:48524 nodes->push_back(*i);
525 ++i;
526 }
527}
528
[email protected]b3c33d462009-06-26 22:29:20529const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
530 const GURL& url) {
531 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48532 GetNodesByURL(url, &nodes);
533 if (nodes.empty())
534 return NULL;
535
[email protected]9333f182008-12-09 17:34:17536 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]848cd05e2008-09-19 18:33:48537 return nodes.front();
initial.commit09911bf2008-07-26 23:55:29538}
539
[email protected]cf8e8172011-07-23 00:46:24540bool BookmarkModel::HasBookmarks() {
541 base::AutoLock url_lock(url_lock_);
542 return !nodes_ordered_by_url_set_.empty();
543}
544
545bool BookmarkModel::IsBookmarked(const GURL& url) {
546 base::AutoLock url_lock(url_lock_);
547 return IsBookmarkedNoLock(url);
548}
549
[email protected]0f7bee52012-08-06 20:04:17550void BookmarkModel::GetBookmarks(
551 std::vector<BookmarkService::URLAndTitle>* bookmarks) {
[email protected]20305ec2011-01-21 04:55:52552 base::AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48553 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46554 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
555 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]5d4077542011-07-21 20:24:07556 const GURL* url = &((*i)->url());
[email protected]848cd05e2008-09-19 18:33:48557 // Only add unique URLs.
[email protected]0f7bee52012-08-06 20:04:17558 if (!last_url || *url != *last_url) {
559 BookmarkService::URLAndTitle bookmark;
560 bookmark.url = *url;
561 bookmark.title = (*i)->GetTitle();
562 bookmarks->push_back(bookmark);
563 }
[email protected]848cd05e2008-09-19 18:33:48564 last_url = url;
[email protected]90ef13132008-08-27 03:27:46565 }
566}
567
[email protected]cf8e8172011-07-23 00:46:24568void BookmarkModel::BlockTillLoaded() {
569 loaded_signal_.Wait();
[email protected]848cd05e2008-09-19 18:33:48570}
571
[email protected]66bb7dc2011-10-27 15:29:23572const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
initial.commit09911bf2008-07-26 23:55:29573 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33574 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29575}
576
[email protected]39703292011-03-18 17:03:40577const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
578 int index,
579 const string16& title) {
[email protected]6b4d64c2011-07-29 21:33:24580 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29581 // Can't add to the root.
582 NOTREACHED();
583 return NULL;
584 }
585
[email protected]5b5c9b7f32011-07-21 01:07:18586 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
[email protected]edb63cc2011-03-11 02:00:41587 new_node->set_date_folder_modified(Time::Now());
[email protected]aee236542011-12-01 04:34:03588 // Folders shouldn't have line breaks in their titles.
[email protected]0491ff72011-12-30 00:45:59589 new_node->SetTitle(title);
[email protected]037db002009-10-19 20:06:08590 new_node->set_type(BookmarkNode::FOLDER);
initial.commit09911bf2008-07-26 23:55:29591
[email protected]a42ec64a2012-12-20 17:04:24592 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29593}
594
[email protected]e64e9012010-01-11 23:10:55595const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
596 int index,
597 const string16& title,
598 const GURL& url) {
[email protected]aee236542011-12-01 04:34:03599 return AddURLWithCreationTime(parent, index,
600 CollapseWhitespace(title, false),
601 url, Time::Now());
initial.commit09911bf2008-07-26 23:55:29602}
603
[email protected]e64e9012010-01-11 23:10:55604const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
605 const BookmarkNode* parent,
606 int index,
607 const string16& title,
608 const GURL& url,
609 const Time& creation_time) {
[email protected]6b4d64c2011-07-29 21:33:24610 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
[email protected]f25387b2008-08-21 15:20:33611 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29612 NOTREACHED();
613 return NULL;
614 }
initial.commit09911bf2008-07-26 23:55:29615
[email protected]b61445c2012-10-27 00:11:42616 // Syncing may result in dates newer than the last modified date.
[email protected]8ad613b2012-10-12 21:28:45617 if (creation_time > parent->date_folder_modified())
618 SetDateFolderModified(parent, creation_time);
initial.commit09911bf2008-07-26 23:55:29619
[email protected]ea2e5aa52009-05-20 18:01:28620 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
[email protected]0491ff72011-12-30 00:45:59621 new_node->SetTitle(title);
[email protected]b3c33d462009-06-26 22:29:20622 new_node->set_date_added(creation_time);
[email protected]037db002009-10-19 20:06:08623 new_node->set_type(BookmarkNode::URL);
initial.commit09911bf2008-07-26 23:55:29624
[email protected]776e7492008-10-23 16:47:41625 {
626 // Only hold the lock for the duration of the insert.
[email protected]20305ec2011-01-21 04:55:52627 base::AutoLock url_lock(url_lock_);
[email protected]776e7492008-10-23 16:47:41628 nodes_ordered_by_url_set_.insert(new_node);
629 }
initial.commit09911bf2008-07-26 23:55:29630
[email protected]a42ec64a2012-12-20 17:04:24631 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29632}
633
[email protected]b3c33d462009-06-26 22:29:20634void BookmarkModel::SortChildren(const BookmarkNode* parent) {
[email protected]6b4d64c2011-07-29 21:33:24635 if (!parent || !parent->is_folder() || is_root_node(parent) ||
[email protected]9c1a75a2011-03-10 02:38:12636 parent->child_count() <= 1) {
[email protected]e2f86d92009-02-25 00:22:01637 return;
638 }
639
[email protected]472f95e2013-06-10 16:49:18640 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
641 OnWillReorderBookmarkNode(this, parent));
642
[email protected]ef762642009-03-05 16:30:25643 UErrorCode error = U_ZERO_ERROR;
[email protected]94d914522013-04-02 19:49:47644 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
[email protected]ef762642009-03-05 16:30:25645 if (U_FAILURE(error))
646 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20647 BookmarkNode* mutable_parent = AsMutable(parent);
648 std::sort(mutable_parent->children().begin(),
649 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25650 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01651
[email protected]997a0362009-03-12 03:10:51652 if (store_.get())
653 store_->ScheduleSave();
654
[email protected]e2f86d92009-02-25 00:22:01655 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
656 BookmarkNodeChildrenReordered(this, parent));
657}
658
[email protected]472f95e2013-06-10 16:49:18659void BookmarkModel::ReorderChildren(
660 const BookmarkNode* parent,
661 const std::vector<BookmarkNode*>& ordered_nodes) {
662 // Ensure that all children in |parent| are in |ordered_nodes|.
663 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
664 for (size_t i = 0; i < ordered_nodes.size(); ++i)
665 DCHECK_EQ(parent, ordered_nodes[i]->parent());
666
667 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
668 OnWillReorderBookmarkNode(this, parent));
669
670 AsMutable(parent)->SetChildren(ordered_nodes);
671
672 if (store_.get())
673 store_->ScheduleSave();
674
675 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
676 BookmarkNodeChildrenReordered(this, parent));
677}
678
[email protected]c6a7a3d2011-03-12 01:04:30679void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
680 const Time time) {
[email protected]eea8fd5532009-12-16 00:08:10681 DCHECK(parent);
[email protected]edb63cc2011-03-11 02:00:41682 AsMutable(parent)->set_date_folder_modified(time);
[email protected]eea8fd5532009-12-16 00:08:10683
684 if (store_.get())
685 store_->ScheduleSave();
686}
687
[email protected]c6a7a3d2011-03-12 01:04:30688void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
689 SetDateFolderModified(node, Time());
initial.commit09911bf2008-07-26 23:55:29690}
691
[email protected]e64e9012010-01-11 23:10:55692void BookmarkModel::GetBookmarksWithTitlesMatching(
693 const string16& text,
694 size_t max_count,
[email protected]dcf5fbb72013-05-30 15:39:57695 std::vector<BookmarkTitleMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28696 if (!loaded_)
697 return;
698
[email protected]d0195a62010-08-22 04:40:20699 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
[email protected]85d911c2009-05-19 03:59:42700}
701
[email protected]9876bb1c2008-12-16 20:42:25702void BookmarkModel::ClearStore() {
[email protected]40e0486b2009-05-22 01:47:27703 registrar_.RemoveAll();
[email protected]9876bb1c2008-12-16 20:42:25704 store_ = NULL;
705}
706
[email protected]bc770a032011-12-12 17:35:30707void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
708 bool value) {
[email protected]1da5f712011-12-06 05:52:26709 DCHECK(loaded_);
[email protected]bc770a032011-12-12 17:35:30710 switch (type) {
711 case BookmarkNode::BOOKMARK_BAR:
712 bookmark_bar_node_->set_visible(value);
713 break;
714 case BookmarkNode::OTHER_NODE:
715 other_node_->set_visible(value);
716 break;
717 case BookmarkNode::MOBILE:
718 mobile_node_->set_visible(value);
719 break;
720 default:
721 NOTREACHED();
722 }
[email protected]1da5f712011-12-06 05:52:26723}
724
[email protected]dddc1b42008-10-09 20:56:59725bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28726 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59727 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
728 nodes_ordered_by_url_set_.end());
729}
730
[email protected]d8e41ed2008-09-11 15:22:32731void BookmarkModel::RemoveNode(BookmarkNode* node,
732 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20733 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33734 NOTREACHED();
735 return;
736 }
737
[email protected]323dbf72013-03-30 17:08:33738 url_lock_.AssertAcquired();
[email protected]0890e60e2011-06-27 14:55:21739 if (node->is_url()) {
[email protected]90ef13132008-08-27 03:27:46740 // NOTE: this is called in such a way that url_lock_ is already held. As
741 // such, this doesn't explicitly grab the lock.
initial.commit09911bf2008-07-26 23:55:29742 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
743 DCHECK(i != nodes_ordered_by_url_set_.end());
[email protected]848cd05e2008-09-19 18:33:48744 // i points to the first node with the URL, advance until we find the
745 // node we're removing.
746 while (*i != node)
747 ++i;
initial.commit09911bf2008-07-26 23:55:29748 nodes_ordered_by_url_set_.erase(i);
[email protected]5d4077542011-07-21 20:24:07749 removed_urls->insert(node->url());
[email protected]85d911c2009-05-19 03:59:42750
[email protected]01eec882009-05-22 18:13:28751 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29752 }
753
[email protected]abc2f262011-03-15 21:15:44754 CancelPendingFaviconLoadRequests(node);
initial.commit09911bf2008-07-26 23:55:29755
756 // Recurse through children.
[email protected]9c1a75a2011-03-10 02:38:12757 for (int i = node->child_count() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33758 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29759}
760
[email protected]a48f87d2012-10-09 18:06:33761void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
[email protected]01eec882009-05-22 18:13:28762 DCHECK(details_delete_me);
[email protected]9c92d192009-12-02 08:03:16763 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
[email protected]01eec882009-05-22 18:13:28764 if (loaded_) {
765 // We should only ever be loaded once.
766 NOTREACHED();
767 return;
768 }
769
[email protected]01eec882009-05-22 18:13:28770 next_node_id_ = details->max_id();
[email protected]367d7072009-07-13 23:27:13771 if (details->computed_checksum() != details->stored_checksum() ||
772 details->ids_reassigned()) {
773 // If bookmarks file changed externally, the IDs may have changed
774 // externally. In that case, the decoder may have reassigned IDs to make
775 // them unique. So when the file has changed externally, we should save the
776 // bookmarks file to persist new IDs.
777 if (store_.get())
778 store_->ScheduleSave();
779 }
[email protected]d22d8732010-05-04 19:24:42780 bookmark_bar_node_ = details->release_bb_node();
781 other_node_ = details->release_other_folder_node();
[email protected]37bc9132011-12-01 22:29:29782 mobile_node_ = details->release_mobile_folder_node();
[email protected]d22d8732010-05-04 19:24:42783 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28784
[email protected]82f4655a2011-10-18 13:05:43785 // WARNING: order is important here, various places assume the order is
786 // constant.
[email protected]a0dd6a32011-03-18 17:31:37787 root_.Add(bookmark_bar_node_, 0);
788 root_.Add(other_node_, 1);
[email protected]37bc9132011-12-01 22:29:29789 root_.Add(mobile_node_, 2);
[email protected]6c1164042009-05-08 14:41:08790
[email protected]1858410f2012-10-26 05:06:45791 root_.set_meta_info_str(details->model_meta_info());
792
[email protected]90ef13132008-08-27 03:27:46793 {
[email protected]20305ec2011-01-21 04:55:52794 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46795 // Update nodes_ordered_by_url_set_ from the nodes.
796 PopulateNodesByURL(&root_);
797 }
[email protected]f25387b2008-08-21 15:20:33798
initial.commit09911bf2008-07-26 23:55:29799 loaded_ = true;
800
[email protected]cbcd6412009-03-09 22:31:39801 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46802
[email protected]f25387b2008-08-21 15:20:33803 // Notify our direct observers.
[email protected]c58c5ea2011-07-13 21:43:16804 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
805 Loaded(this, details->ids_reassigned()));
initial.commit09911bf2008-07-26 23:55:29806}
807
[email protected]d8e41ed2008-09-11 15:22:32808void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
809 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29810
[email protected]323dbf72013-03-30 17:08:33811 const BookmarkNode* parent = node->parent();
initial.commit09911bf2008-07-26 23:55:29812 DCHECK(parent);
[email protected]368f3a72011-03-08 17:17:48813 int index = parent->GetIndexOf(node.get());
[email protected]323dbf72013-03-30 17:08:33814
[email protected]472f95e2013-06-10 16:49:18815 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
816 OnWillRemoveBookmarks(this, parent, index, node.get()));
817
[email protected]86335312012-12-09 21:16:40818 std::set<GURL> changed_urls;
[email protected]90ef13132008-08-27 03:27:46819 {
[email protected]20305ec2011-01-21 04:55:52820 base::AutoLock url_lock(url_lock_);
[email protected]323dbf72013-03-30 17:08:33821 RemoveNodeAndGetRemovedUrls(node.get(), &changed_urls);
[email protected]90ef13132008-08-27 03:27:46822 }
initial.commit09911bf2008-07-26 23:55:29823
[email protected]f25387b2008-08-21 15:20:33824 if (store_.get())
825 store_->ScheduleSave();
826
[email protected]323dbf72013-03-30 17:08:33827 NotifyHistoryAboutRemovedBookmarks(changed_urls);
828
[email protected]d8e41ed2008-09-11 15:22:32829 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41830 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]323dbf72013-03-30 17:08:33831}
[email protected]f25387b2008-08-21 15:20:33832
[email protected]323dbf72013-03-30 17:08:33833void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
834 std::set<GURL>* removed_urls) {
835 // NOTE: this method should be always called with |url_lock_| held.
836 // This method does not explicitly acquires a lock.
837 url_lock_.AssertAcquired();
838 DCHECK(removed_urls);
839 BookmarkNode* parent = AsMutable(node->parent());
840 DCHECK(parent);
841 parent->Remove(node);
842 RemoveNode(node, removed_urls);
843 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
844 // allow duplicates we need to remove any entries that are still bookmarked.
845 for (std::set<GURL>::iterator i = removed_urls->begin();
846 i != removed_urls->end();) {
847 if (IsBookmarkedNoLock(*i)) {
848 // When we erase the iterator pointing at the erasee is
849 // invalidated, so using i++ here within the "erase" call is
850 // important as it advances the iterator before passing the
851 // old value through to erase.
852 removed_urls->erase(i++);
853 } else {
854 ++i;
855 }
856 }
857}
858
859void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
860 const std::set<GURL>& removed_bookmark_urls) const {
861 if (removed_bookmark_urls.empty()) {
[email protected]848cd05e2008-09-19 18:33:48862 // No point in sending out notification if the starred state didn't change.
863 return;
864 }
865
[email protected]90ef13132008-08-27 03:27:46866 if (profile_) {
867 HistoryService* history =
[email protected]9aac66862012-06-19 19:44:31868 HistoryServiceFactory::GetForProfile(profile_,
869 Profile::EXPLICIT_ACCESS);
[email protected]90ef13132008-08-27 03:27:46870 if (history)
[email protected]323dbf72013-03-30 17:08:33871 history->URLsNoLongerBookmarked(removed_bookmark_urls);
[email protected]90ef13132008-08-27 03:27:46872 }
initial.commit09911bf2008-07-26 23:55:29873}
874
[email protected]d8e41ed2008-09-11 15:22:32875BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
876 int index,
[email protected]a42ec64a2012-12-20 17:04:24877 BookmarkNode* node) {
[email protected]a0dd6a32011-03-18 17:31:37878 parent->Add(node, index);
initial.commit09911bf2008-07-26 23:55:29879
[email protected]f25387b2008-08-21 15:20:33880 if (store_.get())
881 store_->ScheduleSave();
882
[email protected]d8e41ed2008-09-11 15:22:32883 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29884 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33885
[email protected]01eec882009-05-22 18:13:28886 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42887
initial.commit09911bf2008-07-26 23:55:29888 return node;
889}
890
[email protected]b3c33d462009-06-26 22:29:20891const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
[email protected]66bb7dc2011-10-27 15:29:23892 int64 id) const {
[email protected]f25387b2008-08-21 15:20:33893 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29894 return node;
[email protected]f25387b2008-08-21 15:20:33895
[email protected]9c1a75a2011-03-10 02:38:12896 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
[email protected]b3c33d462009-06-26 22:29:20897 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29898 if (result)
899 return result;
900 }
901 return NULL;
902}
903
[email protected]b3c33d462009-06-26 22:29:20904bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32905 int index,
906 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41907 return (parent && parent->is_folder() &&
[email protected]9c1a75a2011-03-10 02:38:12908 (index >= 0 && (index < parent->child_count() ||
909 (allow_end && index == parent->child_count()))));
[email protected]bd1b96702009-07-08 21:54:14910}
[email protected]f25387b2008-08-21 15:20:33911
[email protected]bc770a032011-12-12 17:35:30912BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
913 BookmarkNode::Type type) {
[email protected]e1f76c62011-06-30 20:15:39914 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
915 type == BookmarkNode::OTHER_NODE ||
[email protected]37bc9132011-12-01 22:29:29916 type == BookmarkNode::MOBILE);
[email protected]bc770a032011-12-12 17:35:30917 BookmarkPermanentNode* node =
918 new BookmarkPermanentNode(generate_next_node_id());
919 if (type == BookmarkNode::MOBILE)
920 node->set_visible(false); // Mobile node is initially hidden.
921
922 int title_id;
923 switch (type) {
924 case BookmarkNode::BOOKMARK_BAR:
925 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
926 break;
927 case BookmarkNode::OTHER_NODE:
928 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
929 break;
930 case BookmarkNode::MOBILE:
931 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
932 break;
933 default:
[email protected]97fdd162011-12-03 20:50:12934 NOTREACHED();
[email protected]bc770a032011-12-12 17:35:30935 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
936 break;
[email protected]7caca8a22010-08-21 18:25:31937 }
[email protected]0491ff72011-12-30 00:45:59938 node->SetTitle(l10n_util::GetStringUTF16(title_id));
[email protected]97fdd162011-12-03 20:50:12939 node->set_type(type);
initial.commit09911bf2008-07-26 23:55:29940 return node;
941}
942
[email protected]abc2f262011-03-15 21:15:44943void BookmarkModel::OnFaviconDataAvailable(
[email protected]0ea3db52012-12-07 01:32:01944 BookmarkNode* node,
[email protected]299e4542013-05-16 03:09:06945 const chrome::FaviconImageResult& image_result) {
[email protected]4167c3a2008-08-21 18:12:20946 DCHECK(node);
[email protected]0ea3db52012-12-07 01:32:01947 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
[email protected]bcc38612012-10-23 01:10:27948 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
[email protected]65baa222012-08-30 15:43:51949 if (!image_result.image.IsEmpty()) {
950 node->set_favicon(image_result.image);
[email protected]2ad0e872012-11-30 01:24:46951 node->set_icon_url(image_result.icon_url);
[email protected]65baa222012-08-30 15:43:51952 FaviconLoaded(node);
initial.commit09911bf2008-07-26 23:55:29953 }
954}
955
[email protected]abc2f262011-03-15 21:15:44956void BookmarkModel::LoadFavicon(BookmarkNode* node) {
[email protected]0890e60e2011-06-27 14:55:21957 if (node->is_folder())
initial.commit09911bf2008-07-26 23:55:29958 return;
959
[email protected]5d4077542011-07-21 20:24:07960 DCHECK(node->url().is_valid());
[email protected]f3d2b312012-08-23 22:27:59961 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
962 profile_, Profile::EXPLICIT_ACCESS);
[email protected]0189bc722009-08-28 21:56:48963 if (!favicon_service)
initial.commit09911bf2008-07-26 23:55:29964 return;
[email protected]65baa222012-08-30 15:43:51965 FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
[email protected]0ea3db52012-12-07 01:32:01966 FaviconService::FaviconForURLParams(profile_,
967 node->url(),
[email protected]299e4542013-05-16 03:09:06968 chrome::FAVICON,
[email protected]0ea3db52012-12-07 01:32:01969 gfx::kFaviconSize),
[email protected]bbdd2982011-10-08 18:14:24970 base::Bind(&BookmarkModel::OnFaviconDataAvailable,
[email protected]0ea3db52012-12-07 01:32:01971 base::Unretained(this), node),
972 &cancelable_task_tracker_);
973 node->set_favicon_load_task_id(handle);
initial.commit09911bf2008-07-26 23:55:29974}
975
[email protected]5b5c9b7f32011-07-21 01:07:18976void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
977 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
978 BookmarkNodeFaviconChanged(this, node));
979}
980
[email protected]abc2f262011-03-15 21:15:44981void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
[email protected]0ea3db52012-12-07 01:32:01982 if (node->favicon_load_task_id() != CancelableTaskTracker::kBadTaskId) {
983 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
984 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
initial.commit09911bf2008-07-26 23:55:29985 }
986}
987
[email protected]432115822011-07-10 15:52:27988void BookmarkModel::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53989 const content::NotificationSource& source,
990 const content::NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27991 switch (type) {
992 case chrome::NOTIFICATION_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29993 // Prevent the observers from getting confused for multiple favicon loads.
[email protected]0ab247d2013-05-28 17:01:06994 content::Details<FaviconChangedDetails> favicon_details(details);
initial.commit09911bf2008-07-26 23:55:29995 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
996 i != favicon_details->urls.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20997 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48998 GetNodesByURL(*i, &nodes);
999 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:291000 // Got an updated favicon, for a URL, do a new request.
[email protected]b3c33d462009-06-26 22:29:201001 BookmarkNode* node = AsMutable(nodes[i]);
initial.commit09911bf2008-07-26 23:55:291002 node->InvalidateFavicon();
[email protected]abc2f262011-03-15 21:15:441003 CancelPendingFaviconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:321004 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]f12de8302011-05-23 16:12:301005 BookmarkNodeFaviconChanged(this, node));
initial.commit09911bf2008-07-26 23:55:291006 }
1007 }
1008 break;
1009 }
1010
1011 default:
1012 NOTREACHED();
1013 break;
1014 }
1015}
[email protected]f25387b2008-08-21 15:20:331016
[email protected]d8e41ed2008-09-11 15:22:321017void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:461018 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1019 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:331020 if (node->is_url())
1021 nodes_ordered_by_url_set_.insert(node);
[email protected]9c1a75a2011-03-10 02:38:121022 for (int i = 0; i < node->child_count(); ++i)
[email protected]f25387b2008-08-21 15:20:331023 PopulateNodesByURL(node->GetChild(i));
1024}
[email protected]4d89f382009-05-12 06:56:491025
[email protected]367d7072009-07-13 23:27:131026int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:491027 return next_node_id_++;
1028}
[email protected]01eec882009-05-22 18:13:281029
[email protected]9c92d192009-12-02 08:03:161030BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
[email protected]bc770a032011-12-12 17:35:301031 BookmarkPermanentNode* bb_node =
1032 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1033 BookmarkPermanentNode* other_node =
1034 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1035 BookmarkPermanentNode* mobile_node =
1036 CreatePermanentNode(BookmarkNode::MOBILE);
[email protected]37bc9132011-12-01 22:29:291037 return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
[email protected]1858410f2012-10-26 05:06:451038 new BookmarkIndex(profile_),
1039 next_node_id_);
[email protected]01eec882009-05-22 18:13:281040}