blob: d4d8040af3b37e641dce96aea3735f579e72cf06 [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]bc61e722012-09-07 03:48:0129#include "content/public/browser/content_browser_client.h"
[email protected]dae9f5b2013-02-27 15:29:2330#include "content/public/browser/notification_details.h"
31#include "content/public/browser/notification_source.h"
[email protected]34ac8f32009-02-22 23:03:2732#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1733#include "ui/base/l10n/l10n_util.h"
34#include "ui/base/l10n/l10n_util_collator.h"
[email protected]65baa222012-08-30 15:43:5135#include "ui/gfx/favicon_size.h"
[email protected]6a4e5a02012-06-26 19:47:4836#include "ui/gfx/image/image_util.h"
initial.commit09911bf2008-07-26 23:55:2937
[email protected]e1acf6f2008-10-27 20:43:3338using base::Time;
39
[email protected]b3c33d462009-06-26 22:29:2040namespace {
41
42// Helper to get a mutable bookmark node.
[email protected]7b084b02011-07-08 16:06:4843BookmarkNode* AsMutable(const BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:2044 return const_cast<BookmarkNode*>(node);
45}
46
[email protected]f280db82012-10-20 04:21:0847// Whitespace characters to strip from bookmark titles.
48const char16 kInvalidChars[] = {
49 '\n', '\r', '\t',
50 0x2028, // Line separator
51 0x2029, // Paragraph separator
52 0
53};
54
[email protected]e1f76c62011-06-30 20:15:3955} // namespace
[email protected]b3c33d462009-06-26 22:29:2056
[email protected]d8e41ed2008-09-11 15:22:3257// BookmarkNode ---------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2958
[email protected]ea2e5aa52009-05-20 18:01:2859BookmarkNode::BookmarkNode(const GURL& url)
[email protected]814a2d32009-04-30 23:09:0160 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2861 Initialize(0);
[email protected]814a2d32009-04-30 23:09:0162}
63
[email protected]367d7072009-07-13 23:27:1364BookmarkNode::BookmarkNode(int64 id, const GURL& url)
[email protected]2c685cc22009-08-28 00:17:4465 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2866 Initialize(id);
[email protected]814a2d32009-04-30 23:09:0167}
68
[email protected]a0835ac2010-09-13 19:40:0869BookmarkNode::~BookmarkNode() {
70}
71
[email protected]0491ff72011-12-30 00:45:5972void BookmarkNode::SetTitle(const string16& title) {
[email protected]f280db82012-10-20 04:21:0873 // Replace newlines and other problematic whitespace characters in
74 // folder/bookmark names with spaces.
75 string16 trimmed_title;
76 ReplaceChars(title, kInvalidChars, ASCIIToUTF16(" "), &trimmed_title);
77 ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
[email protected]0491ff72011-12-30 00:45:5978}
79
[email protected]97fdd162011-12-03 20:50:1280bool BookmarkNode::IsVisible() const {
81 return true;
82}
83
[email protected]1858410f2012-10-26 05:06:4584bool BookmarkNode::GetMetaInfo(const std::string& key,
85 std::string* value) const {
86 if (meta_info_str_.empty())
87 return false;
88
89 JSONStringValueSerializer serializer(meta_info_str_);
90 scoped_ptr<DictionaryValue> meta_dict(
91 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
92 return meta_dict.get() ? meta_dict->GetString(key, value) : false;
93}
94
95bool BookmarkNode::SetMetaInfo(const std::string& key,
96 const std::string& value) {
97 JSONStringValueSerializer serializer(&meta_info_str_);
98 scoped_ptr<DictionaryValue> meta_dict;
99 if (!meta_info_str_.empty()) {
100 meta_dict.reset(
101 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
102 }
103 if (!meta_dict.get()) {
104 meta_dict.reset(new DictionaryValue);
105 } else {
106 std::string old_value;
107 if (meta_dict->GetString(key, &old_value) && old_value == value)
108 return false;
109 }
110 meta_dict->SetString(key, value);
111 serializer.Serialize(*meta_dict);
112 std::string(meta_info_str_.data(), meta_info_str_.size()).swap(
113 meta_info_str_);
114 return true;
115}
116
117bool BookmarkNode::DeleteMetaInfo(const std::string& key) {
118 if (meta_info_str_.empty())
119 return false;
120
121 JSONStringValueSerializer serializer(&meta_info_str_);
122 scoped_ptr<DictionaryValue> meta_dict(
123 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, NULL)));
124 if (meta_dict.get() && meta_dict->HasKey(key)) {
125 meta_dict->Remove(key, NULL);
126 if (meta_dict->empty()) {
127 meta_info_str_.clear();
128 } else {
129 serializer.Serialize(*meta_dict);
130 std::string(meta_info_str_.data(), meta_info_str_.size()).swap(
131 meta_info_str_);
132 }
133 return true;
134 } else {
135 return false;
136 }
137}
138
[email protected]7b084b02011-07-08 16:06:48139void BookmarkNode::Initialize(int64 id) {
140 id_ = id;
141 type_ = url_.is_empty() ? FOLDER : URL;
142 date_added_ = Time::Now();
[email protected]bcc38612012-10-23 01:10:27143 favicon_state_ = INVALID_FAVICON;
[email protected]0ea3db52012-12-07 01:32:01144 favicon_load_task_id_ = CancelableTaskTracker::kBadTaskId;
[email protected]1858410f2012-10-26 05:06:45145 meta_info_str_.clear();
[email protected]7b084b02011-07-08 16:06:48146}
147
[email protected]5b5c9b7f32011-07-21 01:07:18148void BookmarkNode::InvalidateFavicon() {
[email protected]2ad0e872012-11-30 01:24:46149 icon_url_ = GURL();
[email protected]6a4e5a02012-06-26 19:47:48150 favicon_ = gfx::Image();
[email protected]bcc38612012-10-23 01:10:27151 favicon_state_ = INVALID_FAVICON;
[email protected]5b5c9b7f32011-07-21 01:07:18152}
153
[email protected]ef762642009-03-05 16:30:25154namespace {
155
156// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:44157// bookmarks.
[email protected]b3c33d462009-06-26 22:29:20158class SortComparator : public std::binary_function<const BookmarkNode*,
159 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:25160 bool> {
161 public:
[email protected]a48f87d2012-10-09 18:06:33162 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
[email protected]ef762642009-03-05 16:30:25163
[email protected]a48f87d2012-10-09 18:06:33164 // Returns true if |n1| preceeds |n2|.
165 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:08166 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:25167 // Types are the same, compare the names.
168 if (!collator_)
[email protected]440b37b22010-08-30 05:31:40169 return n1->GetTitle() < n2->GetTitle();
[email protected]3abebda2011-01-07 20:17:15170 return l10n_util::CompareString16WithCollator(
171 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
[email protected]ef762642009-03-05 16:30:25172 }
173 // Types differ, sort such that folders come first.
174 return n1->is_folder();
175 }
176
177 private:
[email protected]b5b2385a2009-08-18 05:12:29178 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:25179};
180
[email protected]bc770a032011-12-12 17:35:30181} // namespace
[email protected]97fdd162011-12-03 20:50:12182
[email protected]bc770a032011-12-12 17:35:30183// BookmarkPermanentNode -------------------------------------------------------
[email protected]97fdd162011-12-03 20:50:12184
[email protected]bc770a032011-12-12 17:35:30185BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
[email protected]97fdd162011-12-03 20:50:12186 : BookmarkNode(id, GURL()),
[email protected]bc770a032011-12-12 17:35:30187 visible_(true) {
[email protected]97fdd162011-12-03 20:50:12188}
189
[email protected]bc770a032011-12-12 17:35:30190BookmarkPermanentNode::~BookmarkPermanentNode() {
[email protected]97fdd162011-12-03 20:50:12191}
192
[email protected]bc770a032011-12-12 17:35:30193bool BookmarkPermanentNode::IsVisible() const {
[email protected]97fdd162011-12-03 20:50:12194 return visible_ || !empty();
195}
196
[email protected]97fdd162011-12-03 20:50:12197// BookmarkModel --------------------------------------------------------------
198
[email protected]d8e41ed2008-09-11 15:22:32199BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:29200 : profile_(profile),
201 loaded_(false),
[email protected]ea2e5aa52009-05-20 18:01:28202 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:29203 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:46204 other_node_(NULL),
[email protected]37bc9132011-12-01 22:29:29205 mobile_node_(NULL),
[email protected]4d89f382009-05-12 06:56:49206 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:06207 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]b68a8172012-02-17 00:25:18208 loaded_signal_(true, false),
209 extensive_changes_(0) {
[email protected]f25387b2008-08-21 15:20:33210 if (!profile_) {
211 // Profile is null during testing.
[email protected]01eec882009-05-22 18:13:28212 DoneLoading(CreateLoadDetails());
initial.commit09911bf2008-07-26 23:55:29213 }
initial.commit09911bf2008-07-26 23:55:29214}
215
[email protected]d8e41ed2008-09-11 15:22:32216BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:32217 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:51218 BookmarkModelBeingDeleted(this));
219
[email protected]f25387b2008-08-21 15:20:33220 if (store_) {
221 // The store maintains a reference back to us. We need to tell it we're gone
222 // so that it doesn't try and invoke a method back on us again.
223 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29224 }
225}
226
[email protected]f61f4782012-06-08 21:54:21227void BookmarkModel::Shutdown() {
[email protected]88e6a232011-09-14 14:53:20228 if (loaded_)
229 return;
230
[email protected]f61f4782012-06-08 21:54:21231 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
232 // details. It is also called when the BookmarkModel is deleted.
[email protected]88e6a232011-09-14 14:53:20233 loaded_signal_.Signal();
234}
235
[email protected]d8e41ed2008-09-11 15:22:32236void BookmarkModel::Load() {
[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]0de615a2012-11-08 04:40:59253 store_ = new BookmarkStorage(profile_, this, profile_->GetIOTaskRunner());
[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]543889ca2012-10-03 00:21:10632 icu::Locale application_locale(
633 content::GetContentClient()->browser()->GetApplicationLocale().c_str());
[email protected]b5b2385a2009-08-18 05:12:29634 scoped_ptr<icu::Collator> collator(
[email protected]543889ca2012-10-03 00:21:10635 icu::Collator::createInstance(application_locale, error));
[email protected]ef762642009-03-05 16:30:25636 if (U_FAILURE(error))
637 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20638 BookmarkNode* mutable_parent = AsMutable(parent);
639 std::sort(mutable_parent->children().begin(),
640 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25641 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01642
[email protected]997a0362009-03-12 03:10:51643 if (store_.get())
644 store_->ScheduleSave();
645
[email protected]e2f86d92009-02-25 00:22:01646 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
647 BookmarkNodeChildrenReordered(this, parent));
648}
649
[email protected]c6a7a3d2011-03-12 01:04:30650void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
651 const Time time) {
[email protected]eea8fd5532009-12-16 00:08:10652 DCHECK(parent);
[email protected]edb63cc2011-03-11 02:00:41653 AsMutable(parent)->set_date_folder_modified(time);
[email protected]eea8fd5532009-12-16 00:08:10654
655 if (store_.get())
656 store_->ScheduleSave();
657}
658
[email protected]c6a7a3d2011-03-12 01:04:30659void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
660 SetDateFolderModified(node, Time());
initial.commit09911bf2008-07-26 23:55:29661}
662
[email protected]e64e9012010-01-11 23:10:55663void BookmarkModel::GetBookmarksWithTitlesMatching(
664 const string16& text,
665 size_t max_count,
666 std::vector<bookmark_utils::TitleMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28667 if (!loaded_)
668 return;
669
[email protected]d0195a62010-08-22 04:40:20670 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
[email protected]85d911c2009-05-19 03:59:42671}
672
[email protected]9876bb1c2008-12-16 20:42:25673void BookmarkModel::ClearStore() {
[email protected]40e0486b2009-05-22 01:47:27674 registrar_.RemoveAll();
[email protected]9876bb1c2008-12-16 20:42:25675 store_ = NULL;
676}
677
[email protected]bc770a032011-12-12 17:35:30678void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
679 bool value) {
[email protected]1da5f712011-12-06 05:52:26680 DCHECK(loaded_);
[email protected]bc770a032011-12-12 17:35:30681 switch (type) {
682 case BookmarkNode::BOOKMARK_BAR:
683 bookmark_bar_node_->set_visible(value);
684 break;
685 case BookmarkNode::OTHER_NODE:
686 other_node_->set_visible(value);
687 break;
688 case BookmarkNode::MOBILE:
689 mobile_node_->set_visible(value);
690 break;
691 default:
692 NOTREACHED();
693 }
[email protected]1da5f712011-12-06 05:52:26694}
695
[email protected]dddc1b42008-10-09 20:56:59696bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28697 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59698 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
699 nodes_ordered_by_url_set_.end());
700}
701
[email protected]d8e41ed2008-09-11 15:22:32702void BookmarkModel::RemoveNode(BookmarkNode* node,
703 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20704 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33705 NOTREACHED();
706 return;
707 }
708
[email protected]323dbf72013-03-30 17:08:33709 url_lock_.AssertAcquired();
[email protected]0890e60e2011-06-27 14:55:21710 if (node->is_url()) {
[email protected]90ef13132008-08-27 03:27:46711 // NOTE: this is called in such a way that url_lock_ is already held. As
712 // such, this doesn't explicitly grab the lock.
initial.commit09911bf2008-07-26 23:55:29713 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
714 DCHECK(i != nodes_ordered_by_url_set_.end());
[email protected]848cd05e2008-09-19 18:33:48715 // i points to the first node with the URL, advance until we find the
716 // node we're removing.
717 while (*i != node)
718 ++i;
initial.commit09911bf2008-07-26 23:55:29719 nodes_ordered_by_url_set_.erase(i);
[email protected]5d4077542011-07-21 20:24:07720 removed_urls->insert(node->url());
[email protected]85d911c2009-05-19 03:59:42721
[email protected]01eec882009-05-22 18:13:28722 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29723 }
724
[email protected]abc2f262011-03-15 21:15:44725 CancelPendingFaviconLoadRequests(node);
initial.commit09911bf2008-07-26 23:55:29726
727 // Recurse through children.
[email protected]9c1a75a2011-03-10 02:38:12728 for (int i = node->child_count() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33729 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29730}
731
[email protected]a48f87d2012-10-09 18:06:33732void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
[email protected]01eec882009-05-22 18:13:28733 DCHECK(details_delete_me);
[email protected]9c92d192009-12-02 08:03:16734 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
[email protected]01eec882009-05-22 18:13:28735 if (loaded_) {
736 // We should only ever be loaded once.
737 NOTREACHED();
738 return;
739 }
740
[email protected]01eec882009-05-22 18:13:28741 next_node_id_ = details->max_id();
[email protected]367d7072009-07-13 23:27:13742 if (details->computed_checksum() != details->stored_checksum() ||
743 details->ids_reassigned()) {
744 // If bookmarks file changed externally, the IDs may have changed
745 // externally. In that case, the decoder may have reassigned IDs to make
746 // them unique. So when the file has changed externally, we should save the
747 // bookmarks file to persist new IDs.
748 if (store_.get())
749 store_->ScheduleSave();
750 }
[email protected]d22d8732010-05-04 19:24:42751 bookmark_bar_node_ = details->release_bb_node();
752 other_node_ = details->release_other_folder_node();
[email protected]37bc9132011-12-01 22:29:29753 mobile_node_ = details->release_mobile_folder_node();
[email protected]d22d8732010-05-04 19:24:42754 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28755
[email protected]82f4655a2011-10-18 13:05:43756 // WARNING: order is important here, various places assume the order is
757 // constant.
[email protected]a0dd6a32011-03-18 17:31:37758 root_.Add(bookmark_bar_node_, 0);
759 root_.Add(other_node_, 1);
[email protected]37bc9132011-12-01 22:29:29760 root_.Add(mobile_node_, 2);
[email protected]6c1164042009-05-08 14:41:08761
[email protected]1858410f2012-10-26 05:06:45762 root_.set_meta_info_str(details->model_meta_info());
763
[email protected]90ef13132008-08-27 03:27:46764 {
[email protected]20305ec2011-01-21 04:55:52765 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46766 // Update nodes_ordered_by_url_set_ from the nodes.
767 PopulateNodesByURL(&root_);
768 }
[email protected]f25387b2008-08-21 15:20:33769
initial.commit09911bf2008-07-26 23:55:29770 loaded_ = true;
771
[email protected]cbcd6412009-03-09 22:31:39772 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46773
[email protected]f25387b2008-08-21 15:20:33774 // Notify our direct observers.
[email protected]c58c5ea2011-07-13 21:43:16775 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
776 Loaded(this, details->ids_reassigned()));
initial.commit09911bf2008-07-26 23:55:29777}
778
[email protected]d8e41ed2008-09-11 15:22:32779void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
780 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29781
[email protected]323dbf72013-03-30 17:08:33782 const BookmarkNode* parent = node->parent();
initial.commit09911bf2008-07-26 23:55:29783 DCHECK(parent);
[email protected]368f3a72011-03-08 17:17:48784 int index = parent->GetIndexOf(node.get());
[email protected]323dbf72013-03-30 17:08:33785
[email protected]86335312012-12-09 21:16:40786 std::set<GURL> changed_urls;
[email protected]90ef13132008-08-27 03:27:46787 {
[email protected]20305ec2011-01-21 04:55:52788 base::AutoLock url_lock(url_lock_);
[email protected]323dbf72013-03-30 17:08:33789 RemoveNodeAndGetRemovedUrls(node.get(), &changed_urls);
[email protected]90ef13132008-08-27 03:27:46790 }
initial.commit09911bf2008-07-26 23:55:29791
[email protected]f25387b2008-08-21 15:20:33792 if (store_.get())
793 store_->ScheduleSave();
794
[email protected]323dbf72013-03-30 17:08:33795 NotifyHistoryAboutRemovedBookmarks(changed_urls);
796
[email protected]d8e41ed2008-09-11 15:22:32797 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41798 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]323dbf72013-03-30 17:08:33799}
[email protected]f25387b2008-08-21 15:20:33800
[email protected]323dbf72013-03-30 17:08:33801void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
802 std::set<GURL>* removed_urls) {
803 // NOTE: this method should be always called with |url_lock_| held.
804 // This method does not explicitly acquires a lock.
805 url_lock_.AssertAcquired();
806 DCHECK(removed_urls);
807 BookmarkNode* parent = AsMutable(node->parent());
808 DCHECK(parent);
809 parent->Remove(node);
810 RemoveNode(node, removed_urls);
811 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
812 // allow duplicates we need to remove any entries that are still bookmarked.
813 for (std::set<GURL>::iterator i = removed_urls->begin();
814 i != removed_urls->end();) {
815 if (IsBookmarkedNoLock(*i)) {
816 // When we erase the iterator pointing at the erasee is
817 // invalidated, so using i++ here within the "erase" call is
818 // important as it advances the iterator before passing the
819 // old value through to erase.
820 removed_urls->erase(i++);
821 } else {
822 ++i;
823 }
824 }
825}
826
827void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
828 const std::set<GURL>& removed_bookmark_urls) const {
829 if (removed_bookmark_urls.empty()) {
[email protected]848cd05e2008-09-19 18:33:48830 // No point in sending out notification if the starred state didn't change.
831 return;
832 }
833
[email protected]90ef13132008-08-27 03:27:46834 if (profile_) {
835 HistoryService* history =
[email protected]9aac66862012-06-19 19:44:31836 HistoryServiceFactory::GetForProfile(profile_,
837 Profile::EXPLICIT_ACCESS);
[email protected]90ef13132008-08-27 03:27:46838 if (history)
[email protected]323dbf72013-03-30 17:08:33839 history->URLsNoLongerBookmarked(removed_bookmark_urls);
[email protected]90ef13132008-08-27 03:27:46840 }
initial.commit09911bf2008-07-26 23:55:29841}
842
[email protected]d8e41ed2008-09-11 15:22:32843BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
844 int index,
[email protected]a42ec64a2012-12-20 17:04:24845 BookmarkNode* node) {
[email protected]a0dd6a32011-03-18 17:31:37846 parent->Add(node, index);
initial.commit09911bf2008-07-26 23:55:29847
[email protected]f25387b2008-08-21 15:20:33848 if (store_.get())
849 store_->ScheduleSave();
850
[email protected]d8e41ed2008-09-11 15:22:32851 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29852 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33853
[email protected]01eec882009-05-22 18:13:28854 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42855
initial.commit09911bf2008-07-26 23:55:29856 return node;
857}
858
[email protected]b3c33d462009-06-26 22:29:20859const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
[email protected]66bb7dc2011-10-27 15:29:23860 int64 id) const {
[email protected]f25387b2008-08-21 15:20:33861 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29862 return node;
[email protected]f25387b2008-08-21 15:20:33863
[email protected]9c1a75a2011-03-10 02:38:12864 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
[email protected]b3c33d462009-06-26 22:29:20865 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29866 if (result)
867 return result;
868 }
869 return NULL;
870}
871
[email protected]b3c33d462009-06-26 22:29:20872bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32873 int index,
874 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41875 return (parent && parent->is_folder() &&
[email protected]9c1a75a2011-03-10 02:38:12876 (index >= 0 && (index < parent->child_count() ||
877 (allow_end && index == parent->child_count()))));
[email protected]bd1b96702009-07-08 21:54:14878}
[email protected]f25387b2008-08-21 15:20:33879
[email protected]bc770a032011-12-12 17:35:30880BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
881 BookmarkNode::Type type) {
[email protected]e1f76c62011-06-30 20:15:39882 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
883 type == BookmarkNode::OTHER_NODE ||
[email protected]37bc9132011-12-01 22:29:29884 type == BookmarkNode::MOBILE);
[email protected]bc770a032011-12-12 17:35:30885 BookmarkPermanentNode* node =
886 new BookmarkPermanentNode(generate_next_node_id());
887 if (type == BookmarkNode::MOBILE)
888 node->set_visible(false); // Mobile node is initially hidden.
889
890 int title_id;
891 switch (type) {
892 case BookmarkNode::BOOKMARK_BAR:
893 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
894 break;
895 case BookmarkNode::OTHER_NODE:
896 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
897 break;
898 case BookmarkNode::MOBILE:
899 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
900 break;
901 default:
[email protected]97fdd162011-12-03 20:50:12902 NOTREACHED();
[email protected]bc770a032011-12-12 17:35:30903 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
904 break;
[email protected]7caca8a22010-08-21 18:25:31905 }
[email protected]0491ff72011-12-30 00:45:59906 node->SetTitle(l10n_util::GetStringUTF16(title_id));
[email protected]97fdd162011-12-03 20:50:12907 node->set_type(type);
initial.commit09911bf2008-07-26 23:55:29908 return node;
909}
910
[email protected]abc2f262011-03-15 21:15:44911void BookmarkModel::OnFaviconDataAvailable(
[email protected]0ea3db52012-12-07 01:32:01912 BookmarkNode* node,
[email protected]65baa222012-08-30 15:43:51913 const history::FaviconImageResult& image_result) {
[email protected]4167c3a2008-08-21 18:12:20914 DCHECK(node);
[email protected]0ea3db52012-12-07 01:32:01915 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
[email protected]bcc38612012-10-23 01:10:27916 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
[email protected]65baa222012-08-30 15:43:51917 if (!image_result.image.IsEmpty()) {
918 node->set_favicon(image_result.image);
[email protected]2ad0e872012-11-30 01:24:46919 node->set_icon_url(image_result.icon_url);
[email protected]65baa222012-08-30 15:43:51920 FaviconLoaded(node);
initial.commit09911bf2008-07-26 23:55:29921 }
922}
923
[email protected]abc2f262011-03-15 21:15:44924void BookmarkModel::LoadFavicon(BookmarkNode* node) {
[email protected]0890e60e2011-06-27 14:55:21925 if (node->is_folder())
initial.commit09911bf2008-07-26 23:55:29926 return;
927
[email protected]5d4077542011-07-21 20:24:07928 DCHECK(node->url().is_valid());
[email protected]f3d2b312012-08-23 22:27:59929 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
930 profile_, Profile::EXPLICIT_ACCESS);
[email protected]0189bc722009-08-28 21:56:48931 if (!favicon_service)
initial.commit09911bf2008-07-26 23:55:29932 return;
[email protected]65baa222012-08-30 15:43:51933 FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
[email protected]0ea3db52012-12-07 01:32:01934 FaviconService::FaviconForURLParams(profile_,
935 node->url(),
936 history::FAVICON,
937 gfx::kFaviconSize),
[email protected]bbdd2982011-10-08 18:14:24938 base::Bind(&BookmarkModel::OnFaviconDataAvailable,
[email protected]0ea3db52012-12-07 01:32:01939 base::Unretained(this), node),
940 &cancelable_task_tracker_);
941 node->set_favicon_load_task_id(handle);
initial.commit09911bf2008-07-26 23:55:29942}
943
[email protected]5b5c9b7f32011-07-21 01:07:18944void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
945 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
946 BookmarkNodeFaviconChanged(this, node));
947}
948
[email protected]abc2f262011-03-15 21:15:44949void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
[email protected]0ea3db52012-12-07 01:32:01950 if (node->favicon_load_task_id() != CancelableTaskTracker::kBadTaskId) {
951 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
952 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
initial.commit09911bf2008-07-26 23:55:29953 }
954}
955
[email protected]432115822011-07-10 15:52:27956void BookmarkModel::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53957 const content::NotificationSource& source,
958 const content::NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27959 switch (type) {
960 case chrome::NOTIFICATION_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29961 // Prevent the observers from getting confused for multiple favicon loads.
[email protected]6c2381d2011-10-19 02:52:53962 content::Details<history::FaviconChangeDetails> favicon_details(details);
initial.commit09911bf2008-07-26 23:55:29963 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
964 i != favicon_details->urls.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20965 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48966 GetNodesByURL(*i, &nodes);
967 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:29968 // Got an updated favicon, for a URL, do a new request.
[email protected]b3c33d462009-06-26 22:29:20969 BookmarkNode* node = AsMutable(nodes[i]);
initial.commit09911bf2008-07-26 23:55:29970 node->InvalidateFavicon();
[email protected]abc2f262011-03-15 21:15:44971 CancelPendingFaviconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:32972 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]f12de8302011-05-23 16:12:30973 BookmarkNodeFaviconChanged(this, node));
initial.commit09911bf2008-07-26 23:55:29974 }
975 }
976 break;
977 }
978
979 default:
980 NOTREACHED();
981 break;
982 }
983}
[email protected]f25387b2008-08-21 15:20:33984
[email protected]d8e41ed2008-09-11 15:22:32985void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:46986 // NOTE: this is called with url_lock_ already held. As such, this doesn't
987 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:33988 if (node->is_url())
989 nodes_ordered_by_url_set_.insert(node);
[email protected]9c1a75a2011-03-10 02:38:12990 for (int i = 0; i < node->child_count(); ++i)
[email protected]f25387b2008-08-21 15:20:33991 PopulateNodesByURL(node->GetChild(i));
992}
[email protected]4d89f382009-05-12 06:56:49993
[email protected]367d7072009-07-13 23:27:13994int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:49995 return next_node_id_++;
996}
[email protected]01eec882009-05-22 18:13:28997
[email protected]9c92d192009-12-02 08:03:16998BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
[email protected]bc770a032011-12-12 17:35:30999 BookmarkPermanentNode* bb_node =
1000 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1001 BookmarkPermanentNode* other_node =
1002 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1003 BookmarkPermanentNode* mobile_node =
1004 CreatePermanentNode(BookmarkNode::MOBILE);
[email protected]37bc9132011-12-01 22:29:291005 return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
[email protected]1858410f2012-10-26 05:06:451006 new BookmarkIndex(profile_),
1007 next_node_id_);
[email protected]01eec882009-05-22 18:13:281008}