blob: d23f64581fb78002a93a7b50316dcfa219450daf [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]1858410f2012-10-26 05:06:4512#include "base/json/json_string_value_serializer.h"
[email protected]0de615a2012-11-08 04:40:5913#include "base/sequenced_task_runner.h"
[email protected]aee236542011-12-01 04:34:0314#include "base/string_util.h"
[email protected]1858410f2012-10-26 05:06:4515#include "base/values.h"
[email protected]fc3fc452009-02-10 03:25:4016#include "build/build_config.h"
[email protected]c58c5ea2011-07-13 21:43:1617#include "chrome/browser/bookmarks/bookmark_expanded_state_tracker.h"
[email protected]01eec882009-05-22 18:13:2818#include "chrome/browser/bookmarks/bookmark_index.h"
[email protected]125b234182011-07-08 19:54:4119#include "chrome/browser/bookmarks/bookmark_model_observer.h"
[email protected]68de8b72008-09-09 23:08:1320#include "chrome/browser/bookmarks/bookmark_storage.h"
[email protected]368f3a72011-03-08 17:17:4821#include "chrome/browser/bookmarks/bookmark_utils.h"
[email protected]dfad18be72012-12-21 00:46:3422#include "chrome/browser/favicon/favicon_service.h"
[email protected]f3d2b312012-08-23 22:27:5923#include "chrome/browser/favicon/favicon_service_factory.h"
[email protected]9c92d192009-12-02 08:03:1624#include "chrome/browser/history/history_notifications.h"
[email protected]6a2c09f2013-01-25 04:50:0725#include "chrome/browser/history/history_service.h"
[email protected]9aac66862012-06-19 19:44:3126#include "chrome/browser/history/history_service_factory.h"
[email protected]8ecad5e2010-12-02 21:18:3327#include "chrome/browser/profiles/profile.h"
[email protected]432115822011-07-10 15:52:2728#include "chrome/common/chrome_notification_types.h"
[email protected]dae9f5b2013-02-27 15:29:2329#include "content/public/browser/notification_details.h"
30#include "content/public/browser/notification_source.h"
[email protected]34ac8f32009-02-22 23:03:2731#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1732#include "ui/base/l10n/l10n_util.h"
33#include "ui/base/l10n/l10n_util_collator.h"
[email protected]65baa222012-08-30 15:43:5134#include "ui/gfx/favicon_size.h"
[email protected]6a4e5a02012-06-26 19:47:4835#include "ui/gfx/image/image_util.h"
initial.commit09911bf2008-07-26 23:55:2936
[email protected]e1acf6f2008-10-27 20:43:3337using base::Time;
38
[email protected]b3c33d462009-06-26 22:29:2039namespace {
40
41// Helper to get a mutable bookmark node.
[email protected]7b084b02011-07-08 16:06:4842BookmarkNode* AsMutable(const BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:2043 return const_cast<BookmarkNode*>(node);
44}
45
[email protected]f280db82012-10-20 04:21:0846// Whitespace characters to strip from bookmark titles.
47const char16 kInvalidChars[] = {
48 '\n', '\r', '\t',
49 0x2028, // Line separator
50 0x2029, // Paragraph separator
51 0
52};
53
[email protected]e1f76c62011-06-30 20:15:3954} // namespace
[email protected]b3c33d462009-06-26 22:29:2055
[email protected]d8e41ed2008-09-11 15:22:3256// BookmarkNode ---------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2957
[email protected]ea2e5aa52009-05-20 18:01:2858BookmarkNode::BookmarkNode(const GURL& url)
[email protected]814a2d32009-04-30 23:09:0159 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2860 Initialize(0);
[email protected]814a2d32009-04-30 23:09:0161}
62
[email protected]367d7072009-07-13 23:27:1363BookmarkNode::BookmarkNode(int64 id, const GURL& url)
[email protected]2c685cc22009-08-28 00:17:4464 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2865 Initialize(id);
[email protected]814a2d32009-04-30 23:09:0166}
67
[email protected]a0835ac2010-09-13 19:40:0868BookmarkNode::~BookmarkNode() {
69}
70
[email protected]0491ff72011-12-30 00:45:5971void BookmarkNode::SetTitle(const string16& title) {
[email protected]f280db82012-10-20 04:21:0872 // Replace newlines and other problematic whitespace characters in
73 // folder/bookmark names with spaces.
74 string16 trimmed_title;
75 ReplaceChars(title, kInvalidChars, ASCIIToUTF16(" "), &trimmed_title);
76 ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
[email protected]0491ff72011-12-30 00:45:5977}
78
[email protected]97fdd162011-12-03 20:50:1279bool BookmarkNode::IsVisible() const {
80 return true;
81}
82
[email protected]1858410f2012-10-26 05:06:4583bool BookmarkNode::GetMetaInfo(const std::string& key,
84 std::string* value) const {
85 if (meta_info_str_.empty())
86 return false;
87
88 JSONStringValueSerializer serializer(meta_info_str_);
89 scoped_ptr<DictionaryValue> meta_dict(
90 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
91 return meta_dict.get() ? meta_dict->GetString(key, value) : false;
92}
93
94bool BookmarkNode::SetMetaInfo(const std::string& key,
95 const std::string& value) {
96 JSONStringValueSerializer serializer(&meta_info_str_);
97 scoped_ptr<DictionaryValue> meta_dict;
98 if (!meta_info_str_.empty()) {
99 meta_dict.reset(
100 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
101 }
102 if (!meta_dict.get()) {
103 meta_dict.reset(new DictionaryValue);
104 } else {
105 std::string old_value;
106 if (meta_dict->GetString(key, &old_value) && old_value == value)
107 return false;
108 }
109 meta_dict->SetString(key, value);
110 serializer.Serialize(*meta_dict);
111 std::string(meta_info_str_.data(), meta_info_str_.size()).swap(
112 meta_info_str_);
113 return true;
114}
115
116bool BookmarkNode::DeleteMetaInfo(const std::string& key) {
117 if (meta_info_str_.empty())
118 return false;
119
120 JSONStringValueSerializer serializer(&meta_info_str_);
121 scoped_ptr<DictionaryValue> meta_dict(
122 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
123 if (meta_dict.get() && meta_dict->HasKey(key)) {
124 meta_dict->Remove(key, NULL);
125 if (meta_dict->empty()) {
126 meta_info_str_.clear();
127 } else {
128 serializer.Serialize(*meta_dict);
129 std::string(meta_info_str_.data(), meta_info_str_.size()).swap(
130 meta_info_str_);
131 }
132 return true;
133 } else {
134 return false;
135 }
136}
137
[email protected]7b084b02011-07-08 16:06:48138void BookmarkNode::Initialize(int64 id) {
139 id_ = id;
140 type_ = url_.is_empty() ? FOLDER : URL;
141 date_added_ = Time::Now();
[email protected]bcc38612012-10-23 01:10:27142 favicon_state_ = INVALID_FAVICON;
[email protected]0ea3db52012-12-07 01:32:01143 favicon_load_task_id_ = CancelableTaskTracker::kBadTaskId;
[email protected]1858410f2012-10-26 05:06:45144 meta_info_str_.clear();
[email protected]7b084b02011-07-08 16:06:48145}
146
[email protected]5b5c9b7f32011-07-21 01:07:18147void BookmarkNode::InvalidateFavicon() {
[email protected]2ad0e872012-11-30 01:24:46148 icon_url_ = GURL();
[email protected]6a4e5a02012-06-26 19:47:48149 favicon_ = gfx::Image();
[email protected]bcc38612012-10-23 01:10:27150 favicon_state_ = INVALID_FAVICON;
[email protected]5b5c9b7f32011-07-21 01:07:18151}
152
[email protected]ef762642009-03-05 16:30:25153namespace {
154
155// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:44156// bookmarks.
[email protected]b3c33d462009-06-26 22:29:20157class SortComparator : public std::binary_function<const BookmarkNode*,
158 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:25159 bool> {
160 public:
[email protected]a48f87d2012-10-09 18:06:33161 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
[email protected]ef762642009-03-05 16:30:25162
[email protected]a48f87d2012-10-09 18:06:33163 // Returns true if |n1| preceeds |n2|.
164 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:08165 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:25166 // Types are the same, compare the names.
167 if (!collator_)
[email protected]440b37b22010-08-30 05:31:40168 return n1->GetTitle() < n2->GetTitle();
[email protected]3abebda2011-01-07 20:17:15169 return l10n_util::CompareString16WithCollator(
170 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
[email protected]ef762642009-03-05 16:30:25171 }
172 // Types differ, sort such that folders come first.
173 return n1->is_folder();
174 }
175
176 private:
[email protected]b5b2385a2009-08-18 05:12:29177 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:25178};
179
[email protected]bc770a032011-12-12 17:35:30180} // namespace
[email protected]97fdd162011-12-03 20:50:12181
[email protected]bc770a032011-12-12 17:35:30182// BookmarkPermanentNode -------------------------------------------------------
[email protected]97fdd162011-12-03 20:50:12183
[email protected]bc770a032011-12-12 17:35:30184BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
[email protected]97fdd162011-12-03 20:50:12185 : BookmarkNode(id, GURL()),
[email protected]bc770a032011-12-12 17:35:30186 visible_(true) {
[email protected]97fdd162011-12-03 20:50:12187}
188
[email protected]bc770a032011-12-12 17:35:30189BookmarkPermanentNode::~BookmarkPermanentNode() {
[email protected]97fdd162011-12-03 20:50:12190}
191
[email protected]bc770a032011-12-12 17:35:30192bool BookmarkPermanentNode::IsVisible() const {
[email protected]97fdd162011-12-03 20:50:12193 return visible_ || !empty();
194}
195
[email protected]97fdd162011-12-03 20:50:12196// BookmarkModel --------------------------------------------------------------
197
[email protected]d8e41ed2008-09-11 15:22:32198BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:29199 : profile_(profile),
200 loaded_(false),
[email protected]ea2e5aa52009-05-20 18:01:28201 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:29202 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:46203 other_node_(NULL),
[email protected]37bc9132011-12-01 22:29:29204 mobile_node_(NULL),
[email protected]4d89f382009-05-12 06:56:49205 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:06206 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]b68a8172012-02-17 00:25:18207 loaded_signal_(true, false),
208 extensive_changes_(0) {
[email protected]f25387b2008-08-21 15:20:33209 if (!profile_) {
210 // Profile is null during testing.
[email protected]01eec882009-05-22 18:13:28211 DoneLoading(CreateLoadDetails());
initial.commit09911bf2008-07-26 23:55:29212 }
initial.commit09911bf2008-07-26 23:55:29213}
214
[email protected]d8e41ed2008-09-11 15:22:32215BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:32216 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:51217 BookmarkModelBeingDeleted(this));
218
[email protected]f25387b2008-08-21 15:20:33219 if (store_) {
220 // The store maintains a reference back to us. We need to tell it we're gone
221 // so that it doesn't try and invoke a method back on us again.
222 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29223 }
224}
225
[email protected]f61f4782012-06-08 21:54:21226void BookmarkModel::Shutdown() {
[email protected]88e6a232011-09-14 14:53:20227 if (loaded_)
228 return;
229
[email protected]f61f4782012-06-08 21:54:21230 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
231 // details. It is also called when the BookmarkModel is deleted.
[email protected]88e6a232011-09-14 14:53:20232 loaded_signal_.Signal();
233}
234
[email protected]afecfb72013-04-18 17:17:33235void BookmarkModel::Load(
236 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
[email protected]90ef13132008-08-27 03:27:46237 if (store_.get()) {
238 // If the store is non-null, it means Load was already invoked. Load should
239 // only be invoked once.
240 NOTREACHED();
241 return;
242 }
243
[email protected]c58c5ea2011-07-13 21:43:16244 expanded_state_tracker_.reset(new BookmarkExpandedStateTracker(
[email protected]25ee85302012-10-12 17:27:41245 profile_, this));
[email protected]c58c5ea2011-07-13 21:43:16246
[email protected]90ef13132008-08-27 03:27:46247 // Listen for changes to favicons so that we can update the favicon of the
248 // node appropriately.
[email protected]432115822011-07-10 15:52:27249 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
[email protected]6c2381d2011-10-19 02:52:53250 content::Source<Profile>(profile_));
[email protected]90ef13132008-08-27 03:27:46251
252 // Load the bookmarks. BookmarkStorage notifies us when done.
[email protected]afecfb72013-04-18 17:17:33253 store_ = new BookmarkStorage(profile_, this, task_runner);
[email protected]01eec882009-05-22 18:13:28254 store_->LoadBookmarks(CreateLoadDetails());
[email protected]90ef13132008-08-27 03:27:46255}
256
[email protected]61b8c782011-07-25 18:11:16257bool BookmarkModel::IsLoaded() const {
258 return loaded_;
259}
260
[email protected]b3c33d462009-06-26 22:29:20261const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
262 std::vector<const BookmarkNode*> nodes =
[email protected]9fcaee72011-03-21 21:53:44263 bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
[email protected]82f4655a2011-10-18 13:05:43264 DCHECK(!nodes.empty()); // This list is always padded with default folders.
265 return nodes[0];
initial.commit09911bf2008-07-26 23:55:29266}
267
[email protected]125b234182011-07-08 19:54:41268void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
269 observers_.AddObserver(observer);
270}
271
272void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
273 observers_.RemoveObserver(observer);
274}
275
[email protected]b68a8172012-02-17 00:25:18276void BookmarkModel::BeginExtensiveChanges() {
277 if (++extensive_changes_ == 1) {
278 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
279 ExtensiveBookmarkChangesBeginning(this));
280 }
[email protected]125b234182011-07-08 19:54:41281}
282
[email protected]b68a8172012-02-17 00:25:18283void BookmarkModel::EndExtensiveChanges() {
284 --extensive_changes_;
285 DCHECK_GE(extensive_changes_, 0);
286 if (extensive_changes_ == 0) {
287 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
288 ExtensiveBookmarkChangesEnded(this));
289 }
[email protected]125b234182011-07-08 19:54:41290}
291
[email protected]b3c33d462009-06-26 22:29:20292void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
[email protected]6b4d64c2011-07-29 21:33:24293 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
[email protected]f25387b2008-08-21 15:20:33294 NOTREACHED();
295 return;
296 }
[email protected]b3c33d462009-06-26 22:29:20297 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
[email protected]f25387b2008-08-21 15:20:33298}
299
[email protected]323dbf72013-03-30 17:08:33300void BookmarkModel::RemoveAll() {
301 std::set<GURL> changed_urls;
302 ScopedVector<BookmarkNode> removed_nodes;
303 BeginExtensiveChanges();
304 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
305 // its immediate children. For removing all non permanent nodes just remove
306 // all children of non-root permanent nodes.
307 {
308 base::AutoLock url_lock(url_lock_);
309 for (int i = 0; i < root_.child_count(); ++i) {
310 const BookmarkNode* permanent_node = root_.GetChild(i);
311 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
312 BookmarkNode* child_node = AsMutable(permanent_node->GetChild(j));
313 removed_nodes.push_back(child_node);
314 RemoveNodeAndGetRemovedUrls(child_node, &changed_urls);
315 }
316 }
317 }
318 EndExtensiveChanges();
319 if (store_.get())
320 store_->ScheduleSave();
321
322 NotifyHistoryAboutRemovedBookmarks(changed_urls);
323
324 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
325 BookmarkAllNodesRemoved(this));
326}
327
[email protected]b3c33d462009-06-26 22:29:20328void BookmarkModel::Move(const BookmarkNode* node,
329 const BookmarkNode* new_parent,
[email protected]d8e41ed2008-09-11 15:22:32330 int index) {
[email protected]f25387b2008-08-21 15:20:33331 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24332 is_root_node(new_parent) || is_permanent_node(node)) {
initial.commit09911bf2008-07-26 23:55:29333 NOTREACHED();
334 return;
335 }
336
337 if (new_parent->HasAncestor(node)) {
338 // Can't make an ancestor of the node be a child of the node.
339 NOTREACHED();
340 return;
341 }
342
[email protected]2d48ee842011-03-08 23:27:29343 const BookmarkNode* old_parent = node->parent();
[email protected]368f3a72011-03-08 17:17:48344 int old_index = old_parent->GetIndexOf(node);
initial.commit09911bf2008-07-26 23:55:29345
346 if (old_parent == new_parent &&
347 (index == old_index || index == old_index + 1)) {
348 // Node is already in this position, nothing to do.
349 return;
350 }
351
[email protected]9e583642012-12-05 02:48:32352 SetDateFolderModified(new_parent, Time::Now());
353
initial.commit09911bf2008-07-26 23:55:29354 if (old_parent == new_parent && index > old_index)
355 index--;
[email protected]b3c33d462009-06-26 22:29:20356 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
[email protected]a0dd6a32011-03-18 17:31:37357 mutable_new_parent->Add(AsMutable(node), index);
initial.commit09911bf2008-07-26 23:55:29358
[email protected]f25387b2008-08-21 15:20:33359 if (store_.get())
360 store_->ScheduleSave();
361
[email protected]d8e41ed2008-09-11 15:22:32362 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29363 BookmarkNodeMoved(this, old_parent, old_index,
364 new_parent, index));
365}
366
[email protected]4e187ef652010-03-11 05:21:35367void BookmarkModel::Copy(const BookmarkNode* node,
368 const BookmarkNode* new_parent,
369 int index) {
370 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24371 is_root_node(new_parent) || is_permanent_node(node)) {
[email protected]4e187ef652010-03-11 05:21:35372 NOTREACHED();
373 return;
374 }
375
376 if (new_parent->HasAncestor(node)) {
377 // Can't make an ancestor of the node be a child of the node.
378 NOTREACHED();
379 return;
380 }
381
[email protected]c6a7a3d2011-03-12 01:04:30382 SetDateFolderModified(new_parent, Time::Now());
[email protected]14eb15af2010-11-20 01:03:26383 BookmarkNodeData drag_data_(node);
384 std::vector<BookmarkNodeData::Element> elements(drag_data_.elements);
[email protected]b1864502010-11-13 00:55:51385 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
[email protected]1a566fae2010-04-16 01:05:06386 // don't need to send notifications here.
[email protected]b1864502010-11-13 00:55:51387 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index);
[email protected]4e187ef652010-03-11 05:21:35388
389 if (store_.get())
390 store_->ScheduleSave();
[email protected]4e187ef652010-03-11 05:21:35391}
392
[email protected]6a4e5a02012-06-26 19:47:48393const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
[email protected]ea2e5aa52009-05-20 18:01:28394 DCHECK(node);
[email protected]bcc38612012-10-23 01:10:27395 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
[email protected]b3c33d462009-06-26 22:29:20396 BookmarkNode* mutable_node = AsMutable(node);
[email protected]bcc38612012-10-23 01:10:27397 mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
[email protected]abc2f262011-03-15 21:15:44398 LoadFavicon(mutable_node);
[email protected]ea2e5aa52009-05-20 18:01:28399 }
400 return node->favicon();
401}
402
[email protected]e64e9012010-01-11 23:10:55403void BookmarkModel::SetTitle(const BookmarkNode* node, const string16& title) {
[email protected]f25387b2008-08-21 15:20:33404 if (!node) {
405 NOTREACHED();
406 return;
407 }
[email protected]0491ff72011-12-30 00:45:59408 if (node->GetTitle() == title)
initial.commit09911bf2008-07-26 23:55:29409 return;
[email protected]f25387b2008-08-21 15:20:33410
[email protected]6892e2e2011-05-26 22:07:17411 if (is_permanent_node(node)) {
[email protected]baf4f922009-10-19 16:44:07412 NOTREACHED();
413 return;
414 }
415
[email protected]85d911c2009-05-19 03:59:42416 // The title index doesn't support changing the title, instead we remove then
417 // add it back.
[email protected]01eec882009-05-22 18:13:28418 index_->Remove(node);
[email protected]0491ff72011-12-30 00:45:59419 AsMutable(node)->SetTitle(title);
[email protected]01eec882009-05-22 18:13:28420 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42421
[email protected]f25387b2008-08-21 15:20:33422 if (store_.get())
423 store_->ScheduleSave();
424
[email protected]d8e41ed2008-09-11 15:22:32425 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29426 BookmarkNodeChanged(this, node));
427}
428
[email protected]e5486602010-02-09 21:27:55429void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
430 if (!node) {
431 NOTREACHED();
432 return;
433 }
434
435 // We cannot change the URL of a folder.
436 if (node->is_folder()) {
437 NOTREACHED();
438 return;
439 }
440
[email protected]5d4077542011-07-21 20:24:07441 if (node->url() == url)
[email protected]e5486602010-02-09 21:27:55442 return;
443
[email protected]5b5c9b7f32011-07-21 01:07:18444 BookmarkNode* mutable_node = AsMutable(node);
445 mutable_node->InvalidateFavicon();
446 CancelPendingFaviconLoadRequests(mutable_node);
[email protected]e5486602010-02-09 21:27:55447
448 {
[email protected]20305ec2011-01-21 04:55:52449 base::AutoLock url_lock(url_lock_);
[email protected]e5486602010-02-09 21:27:55450 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
[email protected]5b5c9b7f32011-07-21 01:07:18451 mutable_node);
[email protected]e5486602010-02-09 21:27:55452 DCHECK(i != nodes_ordered_by_url_set_.end());
453 // i points to the first node with the URL, advance until we find the
454 // node we're removing.
455 while (*i != node)
456 ++i;
457 nodes_ordered_by_url_set_.erase(i);
458
[email protected]5d4077542011-07-21 20:24:07459 mutable_node->set_url(url);
[email protected]5b5c9b7f32011-07-21 01:07:18460 nodes_ordered_by_url_set_.insert(mutable_node);
[email protected]e5486602010-02-09 21:27:55461 }
462
463 if (store_.get())
464 store_->ScheduleSave();
465
466 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
467 BookmarkNodeChanged(this, node));
468}
469
[email protected]1858410f2012-10-26 05:06:45470void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
471 const std::string& key,
472 const std::string& value) {
473 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
474 store_->ScheduleSave();
475}
476
477void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
478 const std::string& key) {
479 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
480 store_->ScheduleSave();
481}
482
[email protected]b61445c2012-10-27 00:11:42483void BookmarkModel::SetDateAdded(const BookmarkNode* node,
484 base::Time date_added) {
485 if (!node) {
486 NOTREACHED();
487 return;
488 }
489
490 if (node->date_added() == date_added)
491 return;
492
493 if (is_permanent_node(node)) {
494 NOTREACHED();
495 return;
496 }
497
498 AsMutable(node)->set_date_added(date_added);
499
500 // Syncing might result in dates newer than the folder's last modified date.
501 if (date_added > node->parent()->date_folder_modified()) {
502 // Will trigger store_->ScheduleSave().
503 SetDateFolderModified(node->parent(), date_added);
504 } else if (store_.get()) {
505 store_->ScheduleSave();
506 }
507}
508
[email protected]848cd05e2008-09-19 18:33:48509void BookmarkModel::GetNodesByURL(const GURL& url,
[email protected]b3c33d462009-06-26 22:29:20510 std::vector<const BookmarkNode*>* nodes) {
[email protected]20305ec2011-01-21 04:55:52511 base::AutoLock url_lock(url_lock_);
[email protected]ea2e5aa52009-05-20 18:01:28512 BookmarkNode tmp_node(url);
initial.commit09911bf2008-07-26 23:55:29513 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]5d4077542011-07-21 20:24:07514 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
[email protected]848cd05e2008-09-19 18:33:48515 nodes->push_back(*i);
516 ++i;
517 }
518}
519
[email protected]b3c33d462009-06-26 22:29:20520const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
521 const GURL& url) {
522 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48523 GetNodesByURL(url, &nodes);
524 if (nodes.empty())
525 return NULL;
526
[email protected]9333f182008-12-09 17:34:17527 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]848cd05e2008-09-19 18:33:48528 return nodes.front();
initial.commit09911bf2008-07-26 23:55:29529}
530
[email protected]cf8e8172011-07-23 00:46:24531bool BookmarkModel::HasBookmarks() {
532 base::AutoLock url_lock(url_lock_);
533 return !nodes_ordered_by_url_set_.empty();
534}
535
536bool BookmarkModel::IsBookmarked(const GURL& url) {
537 base::AutoLock url_lock(url_lock_);
538 return IsBookmarkedNoLock(url);
539}
540
[email protected]0f7bee52012-08-06 20:04:17541void BookmarkModel::GetBookmarks(
542 std::vector<BookmarkService::URLAndTitle>* bookmarks) {
[email protected]20305ec2011-01-21 04:55:52543 base::AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48544 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46545 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
546 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]5d4077542011-07-21 20:24:07547 const GURL* url = &((*i)->url());
[email protected]848cd05e2008-09-19 18:33:48548 // Only add unique URLs.
[email protected]0f7bee52012-08-06 20:04:17549 if (!last_url || *url != *last_url) {
550 BookmarkService::URLAndTitle bookmark;
551 bookmark.url = *url;
552 bookmark.title = (*i)->GetTitle();
553 bookmarks->push_back(bookmark);
554 }
[email protected]848cd05e2008-09-19 18:33:48555 last_url = url;
[email protected]90ef13132008-08-27 03:27:46556 }
557}
558
[email protected]cf8e8172011-07-23 00:46:24559void BookmarkModel::BlockTillLoaded() {
560 loaded_signal_.Wait();
[email protected]848cd05e2008-09-19 18:33:48561}
562
[email protected]66bb7dc2011-10-27 15:29:23563const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
initial.commit09911bf2008-07-26 23:55:29564 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33565 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29566}
567
[email protected]39703292011-03-18 17:03:40568const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
569 int index,
570 const string16& title) {
[email protected]6b4d64c2011-07-29 21:33:24571 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29572 // Can't add to the root.
573 NOTREACHED();
574 return NULL;
575 }
576
[email protected]5b5c9b7f32011-07-21 01:07:18577 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
[email protected]edb63cc2011-03-11 02:00:41578 new_node->set_date_folder_modified(Time::Now());
[email protected]aee236542011-12-01 04:34:03579 // Folders shouldn't have line breaks in their titles.
[email protected]0491ff72011-12-30 00:45:59580 new_node->SetTitle(title);
[email protected]037db002009-10-19 20:06:08581 new_node->set_type(BookmarkNode::FOLDER);
initial.commit09911bf2008-07-26 23:55:29582
[email protected]a42ec64a2012-12-20 17:04:24583 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29584}
585
[email protected]e64e9012010-01-11 23:10:55586const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
587 int index,
588 const string16& title,
589 const GURL& url) {
[email protected]aee236542011-12-01 04:34:03590 return AddURLWithCreationTime(parent, index,
591 CollapseWhitespace(title, false),
592 url, Time::Now());
initial.commit09911bf2008-07-26 23:55:29593}
594
[email protected]e64e9012010-01-11 23:10:55595const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
596 const BookmarkNode* parent,
597 int index,
598 const string16& title,
599 const GURL& url,
600 const Time& creation_time) {
[email protected]6b4d64c2011-07-29 21:33:24601 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
[email protected]f25387b2008-08-21 15:20:33602 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29603 NOTREACHED();
604 return NULL;
605 }
initial.commit09911bf2008-07-26 23:55:29606
[email protected]b61445c2012-10-27 00:11:42607 // Syncing may result in dates newer than the last modified date.
[email protected]8ad613b2012-10-12 21:28:45608 if (creation_time > parent->date_folder_modified())
609 SetDateFolderModified(parent, creation_time);
initial.commit09911bf2008-07-26 23:55:29610
[email protected]ea2e5aa52009-05-20 18:01:28611 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
[email protected]0491ff72011-12-30 00:45:59612 new_node->SetTitle(title);
[email protected]b3c33d462009-06-26 22:29:20613 new_node->set_date_added(creation_time);
[email protected]037db002009-10-19 20:06:08614 new_node->set_type(BookmarkNode::URL);
initial.commit09911bf2008-07-26 23:55:29615
[email protected]776e7492008-10-23 16:47:41616 {
617 // Only hold the lock for the duration of the insert.
[email protected]20305ec2011-01-21 04:55:52618 base::AutoLock url_lock(url_lock_);
[email protected]776e7492008-10-23 16:47:41619 nodes_ordered_by_url_set_.insert(new_node);
620 }
initial.commit09911bf2008-07-26 23:55:29621
[email protected]a42ec64a2012-12-20 17:04:24622 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29623}
624
[email protected]b3c33d462009-06-26 22:29:20625void BookmarkModel::SortChildren(const BookmarkNode* parent) {
[email protected]6b4d64c2011-07-29 21:33:24626 if (!parent || !parent->is_folder() || is_root_node(parent) ||
[email protected]9c1a75a2011-03-10 02:38:12627 parent->child_count() <= 1) {
[email protected]e2f86d92009-02-25 00:22:01628 return;
629 }
630
[email protected]ef762642009-03-05 16:30:25631 UErrorCode error = U_ZERO_ERROR;
[email protected]94d914522013-04-02 19:49:47632 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
[email protected]ef762642009-03-05 16:30:25633 if (U_FAILURE(error))
634 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20635 BookmarkNode* mutable_parent = AsMutable(parent);
636 std::sort(mutable_parent->children().begin(),
637 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25638 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01639
[email protected]997a0362009-03-12 03:10:51640 if (store_.get())
641 store_->ScheduleSave();
642
[email protected]e2f86d92009-02-25 00:22:01643 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
644 BookmarkNodeChildrenReordered(this, parent));
645}
646
[email protected]c6a7a3d2011-03-12 01:04:30647void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
648 const Time time) {
[email protected]eea8fd5532009-12-16 00:08:10649 DCHECK(parent);
[email protected]edb63cc2011-03-11 02:00:41650 AsMutable(parent)->set_date_folder_modified(time);
[email protected]eea8fd5532009-12-16 00:08:10651
652 if (store_.get())
653 store_->ScheduleSave();
654}
655
[email protected]c6a7a3d2011-03-12 01:04:30656void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
657 SetDateFolderModified(node, Time());
initial.commit09911bf2008-07-26 23:55:29658}
659
[email protected]e64e9012010-01-11 23:10:55660void BookmarkModel::GetBookmarksWithTitlesMatching(
661 const string16& text,
662 size_t max_count,
663 std::vector<bookmark_utils::TitleMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28664 if (!loaded_)
665 return;
666
[email protected]d0195a62010-08-22 04:40:20667 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
[email protected]85d911c2009-05-19 03:59:42668}
669
[email protected]9876bb1c2008-12-16 20:42:25670void BookmarkModel::ClearStore() {
[email protected]40e0486b2009-05-22 01:47:27671 registrar_.RemoveAll();
[email protected]9876bb1c2008-12-16 20:42:25672 store_ = NULL;
673}
674
[email protected]bc770a032011-12-12 17:35:30675void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
676 bool value) {
[email protected]1da5f712011-12-06 05:52:26677 DCHECK(loaded_);
[email protected]bc770a032011-12-12 17:35:30678 switch (type) {
679 case BookmarkNode::BOOKMARK_BAR:
680 bookmark_bar_node_->set_visible(value);
681 break;
682 case BookmarkNode::OTHER_NODE:
683 other_node_->set_visible(value);
684 break;
685 case BookmarkNode::MOBILE:
686 mobile_node_->set_visible(value);
687 break;
688 default:
689 NOTREACHED();
690 }
[email protected]1da5f712011-12-06 05:52:26691}
692
[email protected]dddc1b42008-10-09 20:56:59693bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28694 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59695 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
696 nodes_ordered_by_url_set_.end());
697}
698
[email protected]d8e41ed2008-09-11 15:22:32699void BookmarkModel::RemoveNode(BookmarkNode* node,
700 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20701 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33702 NOTREACHED();
703 return;
704 }
705
[email protected]323dbf72013-03-30 17:08:33706 url_lock_.AssertAcquired();
[email protected]0890e60e2011-06-27 14:55:21707 if (node->is_url()) {
[email protected]90ef13132008-08-27 03:27:46708 // NOTE: this is called in such a way that url_lock_ is already held. As
709 // such, this doesn't explicitly grab the lock.
initial.commit09911bf2008-07-26 23:55:29710 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
711 DCHECK(i != nodes_ordered_by_url_set_.end());
[email protected]848cd05e2008-09-19 18:33:48712 // i points to the first node with the URL, advance until we find the
713 // node we're removing.
714 while (*i != node)
715 ++i;
initial.commit09911bf2008-07-26 23:55:29716 nodes_ordered_by_url_set_.erase(i);
[email protected]5d4077542011-07-21 20:24:07717 removed_urls->insert(node->url());
[email protected]85d911c2009-05-19 03:59:42718
[email protected]01eec882009-05-22 18:13:28719 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29720 }
721
[email protected]abc2f262011-03-15 21:15:44722 CancelPendingFaviconLoadRequests(node);
initial.commit09911bf2008-07-26 23:55:29723
724 // Recurse through children.
[email protected]9c1a75a2011-03-10 02:38:12725 for (int i = node->child_count() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33726 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29727}
728
[email protected]a48f87d2012-10-09 18:06:33729void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
[email protected]01eec882009-05-22 18:13:28730 DCHECK(details_delete_me);
[email protected]9c92d192009-12-02 08:03:16731 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
[email protected]01eec882009-05-22 18:13:28732 if (loaded_) {
733 // We should only ever be loaded once.
734 NOTREACHED();
735 return;
736 }
737
[email protected]01eec882009-05-22 18:13:28738 next_node_id_ = details->max_id();
[email protected]367d7072009-07-13 23:27:13739 if (details->computed_checksum() != details->stored_checksum() ||
740 details->ids_reassigned()) {
741 // If bookmarks file changed externally, the IDs may have changed
742 // externally. In that case, the decoder may have reassigned IDs to make
743 // them unique. So when the file has changed externally, we should save the
744 // bookmarks file to persist new IDs.
745 if (store_.get())
746 store_->ScheduleSave();
747 }
[email protected]d22d8732010-05-04 19:24:42748 bookmark_bar_node_ = details->release_bb_node();
749 other_node_ = details->release_other_folder_node();
[email protected]37bc9132011-12-01 22:29:29750 mobile_node_ = details->release_mobile_folder_node();
[email protected]d22d8732010-05-04 19:24:42751 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28752
[email protected]82f4655a2011-10-18 13:05:43753 // WARNING: order is important here, various places assume the order is
754 // constant.
[email protected]a0dd6a32011-03-18 17:31:37755 root_.Add(bookmark_bar_node_, 0);
756 root_.Add(other_node_, 1);
[email protected]37bc9132011-12-01 22:29:29757 root_.Add(mobile_node_, 2);
[email protected]6c1164042009-05-08 14:41:08758
[email protected]1858410f2012-10-26 05:06:45759 root_.set_meta_info_str(details->model_meta_info());
760
[email protected]90ef13132008-08-27 03:27:46761 {
[email protected]20305ec2011-01-21 04:55:52762 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46763 // Update nodes_ordered_by_url_set_ from the nodes.
764 PopulateNodesByURL(&root_);
765 }
[email protected]f25387b2008-08-21 15:20:33766
initial.commit09911bf2008-07-26 23:55:29767 loaded_ = true;
768
[email protected]cbcd6412009-03-09 22:31:39769 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46770
[email protected]f25387b2008-08-21 15:20:33771 // Notify our direct observers.
[email protected]c58c5ea2011-07-13 21:43:16772 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
773 Loaded(this, details->ids_reassigned()));
initial.commit09911bf2008-07-26 23:55:29774}
775
[email protected]d8e41ed2008-09-11 15:22:32776void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
777 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29778
[email protected]323dbf72013-03-30 17:08:33779 const BookmarkNode* parent = node->parent();
initial.commit09911bf2008-07-26 23:55:29780 DCHECK(parent);
[email protected]368f3a72011-03-08 17:17:48781 int index = parent->GetIndexOf(node.get());
[email protected]323dbf72013-03-30 17:08:33782
[email protected]86335312012-12-09 21:16:40783 std::set<GURL> changed_urls;
[email protected]90ef13132008-08-27 03:27:46784 {
[email protected]20305ec2011-01-21 04:55:52785 base::AutoLock url_lock(url_lock_);
[email protected]323dbf72013-03-30 17:08:33786 RemoveNodeAndGetRemovedUrls(node.get(), &changed_urls);
[email protected]90ef13132008-08-27 03:27:46787 }
initial.commit09911bf2008-07-26 23:55:29788
[email protected]f25387b2008-08-21 15:20:33789 if (store_.get())
790 store_->ScheduleSave();
791
[email protected]323dbf72013-03-30 17:08:33792 NotifyHistoryAboutRemovedBookmarks(changed_urls);
793
[email protected]d8e41ed2008-09-11 15:22:32794 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41795 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]323dbf72013-03-30 17:08:33796}
[email protected]f25387b2008-08-21 15:20:33797
[email protected]323dbf72013-03-30 17:08:33798void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
799 std::set<GURL>* removed_urls) {
800 // NOTE: this method should be always called with |url_lock_| held.
801 // This method does not explicitly acquires a lock.
802 url_lock_.AssertAcquired();
803 DCHECK(removed_urls);
804 BookmarkNode* parent = AsMutable(node->parent());
805 DCHECK(parent);
806 parent->Remove(node);
807 RemoveNode(node, removed_urls);
808 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
809 // allow duplicates we need to remove any entries that are still bookmarked.
810 for (std::set<GURL>::iterator i = removed_urls->begin();
811 i != removed_urls->end();) {
812 if (IsBookmarkedNoLock(*i)) {
813 // When we erase the iterator pointing at the erasee is
814 // invalidated, so using i++ here within the "erase" call is
815 // important as it advances the iterator before passing the
816 // old value through to erase.
817 removed_urls->erase(i++);
818 } else {
819 ++i;
820 }
821 }
822}
823
824void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
825 const std::set<GURL>& removed_bookmark_urls) const {
826 if (removed_bookmark_urls.empty()) {
[email protected]848cd05e2008-09-19 18:33:48827 // No point in sending out notification if the starred state didn't change.
828 return;
829 }
830
[email protected]90ef13132008-08-27 03:27:46831 if (profile_) {
832 HistoryService* history =
[email protected]9aac66862012-06-19 19:44:31833 HistoryServiceFactory::GetForProfile(profile_,
834 Profile::EXPLICIT_ACCESS);
[email protected]90ef13132008-08-27 03:27:46835 if (history)
[email protected]323dbf72013-03-30 17:08:33836 history->URLsNoLongerBookmarked(removed_bookmark_urls);
[email protected]90ef13132008-08-27 03:27:46837 }
initial.commit09911bf2008-07-26 23:55:29838}
839
[email protected]d8e41ed2008-09-11 15:22:32840BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
841 int index,
[email protected]a42ec64a2012-12-20 17:04:24842 BookmarkNode* node) {
[email protected]a0dd6a32011-03-18 17:31:37843 parent->Add(node, index);
initial.commit09911bf2008-07-26 23:55:29844
[email protected]f25387b2008-08-21 15:20:33845 if (store_.get())
846 store_->ScheduleSave();
847
[email protected]d8e41ed2008-09-11 15:22:32848 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29849 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33850
[email protected]01eec882009-05-22 18:13:28851 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42852
initial.commit09911bf2008-07-26 23:55:29853 return node;
854}
855
[email protected]b3c33d462009-06-26 22:29:20856const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
[email protected]66bb7dc2011-10-27 15:29:23857 int64 id) const {
[email protected]f25387b2008-08-21 15:20:33858 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29859 return node;
[email protected]f25387b2008-08-21 15:20:33860
[email protected]9c1a75a2011-03-10 02:38:12861 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
[email protected]b3c33d462009-06-26 22:29:20862 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29863 if (result)
864 return result;
865 }
866 return NULL;
867}
868
[email protected]b3c33d462009-06-26 22:29:20869bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32870 int index,
871 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41872 return (parent && parent->is_folder() &&
[email protected]9c1a75a2011-03-10 02:38:12873 (index >= 0 && (index < parent->child_count() ||
874 (allow_end && index == parent->child_count()))));
[email protected]bd1b96702009-07-08 21:54:14875}
[email protected]f25387b2008-08-21 15:20:33876
[email protected]bc770a032011-12-12 17:35:30877BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
878 BookmarkNode::Type type) {
[email protected]e1f76c62011-06-30 20:15:39879 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
880 type == BookmarkNode::OTHER_NODE ||
[email protected]37bc9132011-12-01 22:29:29881 type == BookmarkNode::MOBILE);
[email protected]bc770a032011-12-12 17:35:30882 BookmarkPermanentNode* node =
883 new BookmarkPermanentNode(generate_next_node_id());
884 if (type == BookmarkNode::MOBILE)
885 node->set_visible(false); // Mobile node is initially hidden.
886
887 int title_id;
888 switch (type) {
889 case BookmarkNode::BOOKMARK_BAR:
890 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
891 break;
892 case BookmarkNode::OTHER_NODE:
893 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
894 break;
895 case BookmarkNode::MOBILE:
896 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
897 break;
898 default:
[email protected]97fdd162011-12-03 20:50:12899 NOTREACHED();
[email protected]bc770a032011-12-12 17:35:30900 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
901 break;
[email protected]7caca8a22010-08-21 18:25:31902 }
[email protected]0491ff72011-12-30 00:45:59903 node->SetTitle(l10n_util::GetStringUTF16(title_id));
[email protected]97fdd162011-12-03 20:50:12904 node->set_type(type);
initial.commit09911bf2008-07-26 23:55:29905 return node;
906}
907
[email protected]abc2f262011-03-15 21:15:44908void BookmarkModel::OnFaviconDataAvailable(
[email protected]0ea3db52012-12-07 01:32:01909 BookmarkNode* node,
[email protected]65baa222012-08-30 15:43:51910 const history::FaviconImageResult& image_result) {
[email protected]4167c3a2008-08-21 18:12:20911 DCHECK(node);
[email protected]0ea3db52012-12-07 01:32:01912 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
[email protected]bcc38612012-10-23 01:10:27913 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
[email protected]65baa222012-08-30 15:43:51914 if (!image_result.image.IsEmpty()) {
915 node->set_favicon(image_result.image);
[email protected]2ad0e872012-11-30 01:24:46916 node->set_icon_url(image_result.icon_url);
[email protected]65baa222012-08-30 15:43:51917 FaviconLoaded(node);
initial.commit09911bf2008-07-26 23:55:29918 }
919}
920
[email protected]abc2f262011-03-15 21:15:44921void BookmarkModel::LoadFavicon(BookmarkNode* node) {
[email protected]0890e60e2011-06-27 14:55:21922 if (node->is_folder())
initial.commit09911bf2008-07-26 23:55:29923 return;
924
[email protected]5d4077542011-07-21 20:24:07925 DCHECK(node->url().is_valid());
[email protected]f3d2b312012-08-23 22:27:59926 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
927 profile_, Profile::EXPLICIT_ACCESS);
[email protected]0189bc722009-08-28 21:56:48928 if (!favicon_service)
initial.commit09911bf2008-07-26 23:55:29929 return;
[email protected]65baa222012-08-30 15:43:51930 FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
[email protected]0ea3db52012-12-07 01:32:01931 FaviconService::FaviconForURLParams(profile_,
932 node->url(),
933 history::FAVICON,
934 gfx::kFaviconSize),
[email protected]bbdd2982011-10-08 18:14:24935 base::Bind(&BookmarkModel::OnFaviconDataAvailable,
[email protected]0ea3db52012-12-07 01:32:01936 base::Unretained(this), node),
937 &cancelable_task_tracker_);
938 node->set_favicon_load_task_id(handle);
initial.commit09911bf2008-07-26 23:55:29939}
940
[email protected]5b5c9b7f32011-07-21 01:07:18941void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
942 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
943 BookmarkNodeFaviconChanged(this, node));
944}
945
[email protected]abc2f262011-03-15 21:15:44946void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
[email protected]0ea3db52012-12-07 01:32:01947 if (node->favicon_load_task_id() != CancelableTaskTracker::kBadTaskId) {
948 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
949 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
initial.commit09911bf2008-07-26 23:55:29950 }
951}
952
[email protected]432115822011-07-10 15:52:27953void BookmarkModel::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53954 const content::NotificationSource& source,
955 const content::NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27956 switch (type) {
957 case chrome::NOTIFICATION_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29958 // Prevent the observers from getting confused for multiple favicon loads.
[email protected]6c2381d2011-10-19 02:52:53959 content::Details<history::FaviconChangeDetails> favicon_details(details);
initial.commit09911bf2008-07-26 23:55:29960 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
961 i != favicon_details->urls.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20962 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48963 GetNodesByURL(*i, &nodes);
964 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:29965 // Got an updated favicon, for a URL, do a new request.
[email protected]b3c33d462009-06-26 22:29:20966 BookmarkNode* node = AsMutable(nodes[i]);
initial.commit09911bf2008-07-26 23:55:29967 node->InvalidateFavicon();
[email protected]abc2f262011-03-15 21:15:44968 CancelPendingFaviconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:32969 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]f12de8302011-05-23 16:12:30970 BookmarkNodeFaviconChanged(this, node));
initial.commit09911bf2008-07-26 23:55:29971 }
972 }
973 break;
974 }
975
976 default:
977 NOTREACHED();
978 break;
979 }
980}
[email protected]f25387b2008-08-21 15:20:33981
[email protected]d8e41ed2008-09-11 15:22:32982void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:46983 // NOTE: this is called with url_lock_ already held. As such, this doesn't
984 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:33985 if (node->is_url())
986 nodes_ordered_by_url_set_.insert(node);
[email protected]9c1a75a2011-03-10 02:38:12987 for (int i = 0; i < node->child_count(); ++i)
[email protected]f25387b2008-08-21 15:20:33988 PopulateNodesByURL(node->GetChild(i));
989}
[email protected]4d89f382009-05-12 06:56:49990
[email protected]367d7072009-07-13 23:27:13991int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:49992 return next_node_id_++;
993}
[email protected]01eec882009-05-22 18:13:28994
[email protected]9c92d192009-12-02 08:03:16995BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
[email protected]bc770a032011-12-12 17:35:30996 BookmarkPermanentNode* bb_node =
997 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
998 BookmarkPermanentNode* other_node =
999 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1000 BookmarkPermanentNode* mobile_node =
1001 CreatePermanentNode(BookmarkNode::MOBILE);
[email protected]37bc9132011-12-01 22:29:291002 return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
[email protected]1858410f2012-10-26 05:06:451003 new BookmarkIndex(profile_),
1004 next_node_id_);
[email protected]01eec882009-05-22 18:13:281005}