blob: 6f66bdc8beaf61188225a71c2917f20998136153 [file] [log] [blame]
[email protected]b68a8172012-02-17 00:25:181// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]d8e41ed2008-09-11 15:22:325#include "chrome/browser/bookmarks/bookmark_model.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]4e425be42011-01-15 06:56:097#include <algorithm>
8#include <functional>
9
[email protected]bbdd2982011-10-08 18:14:2410#include "base/bind.h"
11#include "base/bind_helpers.h"
[email protected]9a08fe82013-04-23 05:06:0512#include "base/i18n/string_compare.h"
[email protected]fb441962013-05-08 05:35:2413#include "base/sequenced_task_runner.h"
[email protected]ac62f512013-06-10 19:26:4414#include "base/strings/string_util.h"
[email protected]135cb802013-06-09 16:44:2015#include "base/strings/utf_string_conversions.h"
[email protected]1858410f2012-10-26 05:06:4516#include "base/values.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]dcf5fbb72013-05-30 15:39:5721#include "chrome/browser/bookmarks/bookmark_title_match.h"
[email protected]368f3a72011-03-08 17:17:4822#include "chrome/browser/bookmarks/bookmark_utils.h"
[email protected]fdf40f3e2013-07-11 23:55:4623#include "chrome/browser/chrome_notification_types.h"
[email protected]0ab247d2013-05-28 17:01:0624#include "chrome/browser/favicon/favicon_changed_details.h"
[email protected]dfad18be72012-12-21 00:46:3425#include "chrome/browser/favicon/favicon_service.h"
[email protected]f3d2b312012-08-23 22:27:5926#include "chrome/browser/favicon/favicon_service_factory.h"
[email protected]6a2c09f2013-01-25 04:50:0727#include "chrome/browser/history/history_service.h"
[email protected]9aac66862012-06-19 19:44:3128#include "chrome/browser/history/history_service_factory.h"
[email protected]8ecad5e2010-12-02 21:18:3329#include "chrome/browser/profiles/profile.h"
[email protected]78dfe382013-07-17 18:05:4230#include "chrome/common/favicon/favicon_types.h"
[email protected]dae9f5b2013-02-27 15:29:2331#include "content/public/browser/notification_details.h"
32#include "content/public/browser/notification_source.h"
[email protected]34ac8f32009-02-22 23:03:2733#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1734#include "ui/base/l10n/l10n_util.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]9bfbc212013-06-21 04:59:4347// Helper to recursively determine if a Dictionary has any valid values.
48bool HasValues(const base::DictionaryValue& root) {
49 if (root.empty())
50 return false;
51 for (base::DictionaryValue::Iterator iter(root); !iter.IsAtEnd();
52 iter.Advance()) {
53 const base::Value& value = iter.value();
54 if (value.IsType(base::Value::TYPE_DICTIONARY)) {
55 const base::DictionaryValue* dict_value = NULL;
56 if (value.GetAsDictionary(&dict_value) && HasValues(*dict_value))
57 return true;
58 } else {
59 // A non dictionary type was encountered, assume it's a valid value.
60 return true;
61 }
62 }
63 return false;
64}
65
[email protected]f280db82012-10-20 04:21:0866// Whitespace characters to strip from bookmark titles.
67const char16 kInvalidChars[] = {
68 '\n', '\r', '\t',
69 0x2028, // Line separator
70 0x2029, // Paragraph separator
71 0
72};
73
[email protected]e1f76c62011-06-30 20:15:3974} // namespace
[email protected]b3c33d462009-06-26 22:29:2075
[email protected]d8e41ed2008-09-11 15:22:3276// BookmarkNode ---------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2977
[email protected]39e113452013-11-26 00:43:0678const int64 BookmarkNode::kInvalidSyncTransactionVersion = -1;
79
[email protected]ea2e5aa52009-05-20 18:01:2880BookmarkNode::BookmarkNode(const GURL& url)
[email protected]814a2d32009-04-30 23:09:0181 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2882 Initialize(0);
[email protected]814a2d32009-04-30 23:09:0183}
84
[email protected]367d7072009-07-13 23:27:1385BookmarkNode::BookmarkNode(int64 id, const GURL& url)
[email protected]2c685cc22009-08-28 00:17:4486 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2887 Initialize(id);
[email protected]814a2d32009-04-30 23:09:0188}
89
[email protected]a0835ac2010-09-13 19:40:0890BookmarkNode::~BookmarkNode() {
91}
92
[email protected]0491ff72011-12-30 00:45:5993void BookmarkNode::SetTitle(const string16& title) {
[email protected]f280db82012-10-20 04:21:0894 // Replace newlines and other problematic whitespace characters in
95 // folder/bookmark names with spaces.
96 string16 trimmed_title;
97 ReplaceChars(title, kInvalidChars, ASCIIToUTF16(" "), &trimmed_title);
98 ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
[email protected]0491ff72011-12-30 00:45:5999}
100
[email protected]97fdd162011-12-03 20:50:12101bool BookmarkNode::IsVisible() const {
102 return true;
103}
104
[email protected]1858410f2012-10-26 05:06:45105bool BookmarkNode::GetMetaInfo(const std::string& key,
106 std::string* value) const {
[email protected]39e113452013-11-26 00:43:06107 if (!meta_info_map_)
[email protected]1858410f2012-10-26 05:06:45108 return false;
109
[email protected]39e113452013-11-26 00:43:06110 MetaInfoMap::const_iterator it = meta_info_map_->find(key);
111 if (it == meta_info_map_->end())
112 return false;
113
114 *value = it->second;
115 return true;
[email protected]1858410f2012-10-26 05:06:45116}
117
118bool BookmarkNode::SetMetaInfo(const std::string& key,
119 const std::string& value) {
[email protected]39e113452013-11-26 00:43:06120 if (!meta_info_map_)
121 meta_info_map_.reset(new MetaInfoMap);
122
123 MetaInfoMap::iterator it = meta_info_map_->find(key);
124 if (it == meta_info_map_->end()) {
125 (*meta_info_map_)[key] = value;
126 return true;
[email protected]1858410f2012-10-26 05:06:45127 }
[email protected]39e113452013-11-26 00:43:06128 // Key already in map, check if the value has changed.
129 if (it->second == value)
130 return false;
131 it->second = value;
[email protected]1858410f2012-10-26 05:06:45132 return true;
133}
134
135bool BookmarkNode::DeleteMetaInfo(const std::string& key) {
[email protected]39e113452013-11-26 00:43:06136 if (!meta_info_map_)
[email protected]1858410f2012-10-26 05:06:45137 return false;
[email protected]39e113452013-11-26 00:43:06138 bool erased = meta_info_map_->erase(key) != 0;
139 if (meta_info_map_->empty())
140 meta_info_map_.reset();
141 return erased;
[email protected]1858410f2012-10-26 05:06:45142}
143
[email protected]39e113452013-11-26 00:43:06144void BookmarkNode::SetMetaInfoMap(const MetaInfoMap& meta_info_map) {
145 if (meta_info_map.empty())
146 meta_info_map_.reset();
147 else
148 meta_info_map_.reset(new MetaInfoMap(meta_info_map));
149}
150
151const BookmarkNode::MetaInfoMap* BookmarkNode::GetMetaInfoMap() const {
152 return meta_info_map_.get();
153}
154
155
[email protected]7b084b02011-07-08 16:06:48156void BookmarkNode::Initialize(int64 id) {
157 id_ = id;
158 type_ = url_.is_empty() ? FOLDER : URL;
159 date_added_ = Time::Now();
[email protected]bcc38612012-10-23 01:10:27160 favicon_state_ = INVALID_FAVICON;
[email protected]0ea3db52012-12-07 01:32:01161 favicon_load_task_id_ = CancelableTaskTracker::kBadTaskId;
[email protected]39e113452013-11-26 00:43:06162 meta_info_map_.reset();
163 sync_transaction_version_ = kInvalidSyncTransactionVersion;
[email protected]7b084b02011-07-08 16:06:48164}
165
[email protected]5b5c9b7f32011-07-21 01:07:18166void BookmarkNode::InvalidateFavicon() {
[email protected]2ad0e872012-11-30 01:24:46167 icon_url_ = GURL();
[email protected]6a4e5a02012-06-26 19:47:48168 favicon_ = gfx::Image();
[email protected]bcc38612012-10-23 01:10:27169 favicon_state_ = INVALID_FAVICON;
[email protected]5b5c9b7f32011-07-21 01:07:18170}
171
[email protected]ef762642009-03-05 16:30:25172namespace {
173
174// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:44175// bookmarks.
[email protected]b3c33d462009-06-26 22:29:20176class SortComparator : public std::binary_function<const BookmarkNode*,
177 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:25178 bool> {
179 public:
[email protected]a48f87d2012-10-09 18:06:33180 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
[email protected]ef762642009-03-05 16:30:25181
[email protected]a48f87d2012-10-09 18:06:33182 // Returns true if |n1| preceeds |n2|.
183 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:08184 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:25185 // Types are the same, compare the names.
186 if (!collator_)
[email protected]440b37b22010-08-30 05:31:40187 return n1->GetTitle() < n2->GetTitle();
[email protected]9a08fe82013-04-23 05:06:05188 return base::i18n::CompareString16WithCollator(
[email protected]3abebda2011-01-07 20:17:15189 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
[email protected]ef762642009-03-05 16:30:25190 }
191 // Types differ, sort such that folders come first.
192 return n1->is_folder();
193 }
194
195 private:
[email protected]b5b2385a2009-08-18 05:12:29196 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:25197};
198
[email protected]bc770a032011-12-12 17:35:30199} // namespace
[email protected]97fdd162011-12-03 20:50:12200
[email protected]bc770a032011-12-12 17:35:30201// BookmarkPermanentNode -------------------------------------------------------
[email protected]97fdd162011-12-03 20:50:12202
[email protected]bc770a032011-12-12 17:35:30203BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
[email protected]97fdd162011-12-03 20:50:12204 : BookmarkNode(id, GURL()),
[email protected]bc770a032011-12-12 17:35:30205 visible_(true) {
[email protected]97fdd162011-12-03 20:50:12206}
207
[email protected]bc770a032011-12-12 17:35:30208BookmarkPermanentNode::~BookmarkPermanentNode() {
[email protected]97fdd162011-12-03 20:50:12209}
210
[email protected]bc770a032011-12-12 17:35:30211bool BookmarkPermanentNode::IsVisible() const {
[email protected]97fdd162011-12-03 20:50:12212 return visible_ || !empty();
213}
214
[email protected]97fdd162011-12-03 20:50:12215// BookmarkModel --------------------------------------------------------------
216
[email protected]d8e41ed2008-09-11 15:22:32217BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:29218 : profile_(profile),
219 loaded_(false),
[email protected]ea2e5aa52009-05-20 18:01:28220 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:29221 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:46222 other_node_(NULL),
[email protected]37bc9132011-12-01 22:29:29223 mobile_node_(NULL),
[email protected]4d89f382009-05-12 06:56:49224 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:06225 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]b68a8172012-02-17 00:25:18226 loaded_signal_(true, false),
227 extensive_changes_(0) {
[email protected]f25387b2008-08-21 15:20:33228 if (!profile_) {
229 // Profile is null during testing.
[email protected]01eec882009-05-22 18:13:28230 DoneLoading(CreateLoadDetails());
initial.commit09911bf2008-07-26 23:55:29231 }
initial.commit09911bf2008-07-26 23:55:29232}
233
[email protected]d8e41ed2008-09-11 15:22:32234BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:32235 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:51236 BookmarkModelBeingDeleted(this));
237
[email protected]dc24976f2013-06-02 21:15:09238 if (store_.get()) {
[email protected]f25387b2008-08-21 15:20:33239 // The store maintains a reference back to us. We need to tell it we're gone
240 // so that it doesn't try and invoke a method back on us again.
241 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29242 }
243}
244
[email protected]f61f4782012-06-08 21:54:21245void BookmarkModel::Shutdown() {
[email protected]88e6a232011-09-14 14:53:20246 if (loaded_)
247 return;
248
[email protected]f61f4782012-06-08 21:54:21249 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
250 // details. It is also called when the BookmarkModel is deleted.
[email protected]88e6a232011-09-14 14:53:20251 loaded_signal_.Signal();
252}
253
[email protected]afecfb72013-04-18 17:17:33254void BookmarkModel::Load(
255 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
[email protected]90ef13132008-08-27 03:27:46256 if (store_.get()) {
257 // If the store is non-null, it means Load was already invoked. Load should
258 // only be invoked once.
259 NOTREACHED();
260 return;
261 }
262
[email protected]6df6ec022013-07-16 03:05:22263 expanded_state_tracker_.reset(
264 new BookmarkExpandedStateTracker(this, profile_->GetPrefs()));
[email protected]c58c5ea2011-07-13 21:43:16265
[email protected]90ef13132008-08-27 03:27:46266 // Listen for changes to favicons so that we can update the favicon of the
267 // node appropriately.
[email protected]432115822011-07-10 15:52:27268 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
[email protected]6c2381d2011-10-19 02:52:53269 content::Source<Profile>(profile_));
[email protected]90ef13132008-08-27 03:27:46270
271 // Load the bookmarks. BookmarkStorage notifies us when done.
[email protected]dc24976f2013-06-02 21:15:09272 store_ = new BookmarkStorage(profile_, this, task_runner.get());
[email protected]01eec882009-05-22 18:13:28273 store_->LoadBookmarks(CreateLoadDetails());
[email protected]90ef13132008-08-27 03:27:46274}
275
[email protected]b3c33d462009-06-26 22:29:20276const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
277 std::vector<const BookmarkNode*> nodes =
[email protected]9fcaee72011-03-21 21:53:44278 bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
[email protected]82f4655a2011-10-18 13:05:43279 DCHECK(!nodes.empty()); // This list is always padded with default folders.
280 return nodes[0];
initial.commit09911bf2008-07-26 23:55:29281}
282
[email protected]125b234182011-07-08 19:54:41283void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
284 observers_.AddObserver(observer);
285}
286
287void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
288 observers_.RemoveObserver(observer);
289}
290
[email protected]b68a8172012-02-17 00:25:18291void BookmarkModel::BeginExtensiveChanges() {
292 if (++extensive_changes_ == 1) {
293 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
294 ExtensiveBookmarkChangesBeginning(this));
295 }
[email protected]125b234182011-07-08 19:54:41296}
297
[email protected]b68a8172012-02-17 00:25:18298void BookmarkModel::EndExtensiveChanges() {
299 --extensive_changes_;
300 DCHECK_GE(extensive_changes_, 0);
301 if (extensive_changes_ == 0) {
302 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
303 ExtensiveBookmarkChangesEnded(this));
304 }
[email protected]125b234182011-07-08 19:54:41305}
306
[email protected]b3c33d462009-06-26 22:29:20307void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
[email protected]6b4d64c2011-07-29 21:33:24308 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
[email protected]f25387b2008-08-21 15:20:33309 NOTREACHED();
310 return;
311 }
[email protected]b3c33d462009-06-26 22:29:20312 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
[email protected]f25387b2008-08-21 15:20:33313}
314
[email protected]323dbf72013-03-30 17:08:33315void BookmarkModel::RemoveAll() {
[email protected]9109f8a12013-06-12 18:07:48316 std::set<GURL> removed_urls;
[email protected]323dbf72013-03-30 17:08:33317 ScopedVector<BookmarkNode> removed_nodes;
[email protected]472f95e2013-06-10 16:49:18318
319 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
320 OnWillRemoveAllBookmarks(this));
321
[email protected]323dbf72013-03-30 17:08:33322 BeginExtensiveChanges();
323 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
324 // its immediate children. For removing all non permanent nodes just remove
325 // all children of non-root permanent nodes.
326 {
327 base::AutoLock url_lock(url_lock_);
328 for (int i = 0; i < root_.child_count(); ++i) {
329 const BookmarkNode* permanent_node = root_.GetChild(i);
330 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
331 BookmarkNode* child_node = AsMutable(permanent_node->GetChild(j));
332 removed_nodes.push_back(child_node);
[email protected]9109f8a12013-06-12 18:07:48333 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
[email protected]323dbf72013-03-30 17:08:33334 }
335 }
336 }
337 EndExtensiveChanges();
338 if (store_.get())
339 store_->ScheduleSave();
340
[email protected]9109f8a12013-06-12 18:07:48341 NotifyHistoryAboutRemovedBookmarks(removed_urls);
[email protected]323dbf72013-03-30 17:08:33342
343 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
344 BookmarkAllNodesRemoved(this));
345}
346
[email protected]b3c33d462009-06-26 22:29:20347void BookmarkModel::Move(const BookmarkNode* node,
348 const BookmarkNode* new_parent,
[email protected]d8e41ed2008-09-11 15:22:32349 int index) {
[email protected]f25387b2008-08-21 15:20:33350 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24351 is_root_node(new_parent) || is_permanent_node(node)) {
initial.commit09911bf2008-07-26 23:55:29352 NOTREACHED();
353 return;
354 }
355
356 if (new_parent->HasAncestor(node)) {
357 // Can't make an ancestor of the node be a child of the node.
358 NOTREACHED();
359 return;
360 }
361
[email protected]2d48ee842011-03-08 23:27:29362 const BookmarkNode* old_parent = node->parent();
[email protected]368f3a72011-03-08 17:17:48363 int old_index = old_parent->GetIndexOf(node);
initial.commit09911bf2008-07-26 23:55:29364
365 if (old_parent == new_parent &&
366 (index == old_index || index == old_index + 1)) {
367 // Node is already in this position, nothing to do.
368 return;
369 }
370
[email protected]9e583642012-12-05 02:48:32371 SetDateFolderModified(new_parent, Time::Now());
372
initial.commit09911bf2008-07-26 23:55:29373 if (old_parent == new_parent && index > old_index)
374 index--;
[email protected]b3c33d462009-06-26 22:29:20375 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
[email protected]a0dd6a32011-03-18 17:31:37376 mutable_new_parent->Add(AsMutable(node), index);
initial.commit09911bf2008-07-26 23:55:29377
[email protected]f25387b2008-08-21 15:20:33378 if (store_.get())
379 store_->ScheduleSave();
380
[email protected]d8e41ed2008-09-11 15:22:32381 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29382 BookmarkNodeMoved(this, old_parent, old_index,
383 new_parent, index));
384}
385
[email protected]4e187ef652010-03-11 05:21:35386void BookmarkModel::Copy(const BookmarkNode* node,
387 const BookmarkNode* new_parent,
388 int index) {
389 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24390 is_root_node(new_parent) || is_permanent_node(node)) {
[email protected]4e187ef652010-03-11 05:21:35391 NOTREACHED();
392 return;
393 }
394
395 if (new_parent->HasAncestor(node)) {
396 // Can't make an ancestor of the node be a child of the node.
397 NOTREACHED();
398 return;
399 }
400
[email protected]c6a7a3d2011-03-12 01:04:30401 SetDateFolderModified(new_parent, Time::Now());
[email protected]67a8ae42013-05-20 09:42:39402 BookmarkNodeData drag_data(node);
403 std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
[email protected]b1864502010-11-13 00:55:51404 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
[email protected]1a566fae2010-04-16 01:05:06405 // don't need to send notifications here.
[email protected]7be9c7d2013-08-22 01:19:33406 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index, true);
[email protected]4e187ef652010-03-11 05:21:35407
408 if (store_.get())
409 store_->ScheduleSave();
[email protected]4e187ef652010-03-11 05:21:35410}
411
[email protected]6a4e5a02012-06-26 19:47:48412const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
[email protected]ea2e5aa52009-05-20 18:01:28413 DCHECK(node);
[email protected]bcc38612012-10-23 01:10:27414 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
[email protected]b3c33d462009-06-26 22:29:20415 BookmarkNode* mutable_node = AsMutable(node);
[email protected]bcc38612012-10-23 01:10:27416 mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
[email protected]abc2f262011-03-15 21:15:44417 LoadFavicon(mutable_node);
[email protected]ea2e5aa52009-05-20 18:01:28418 }
419 return node->favicon();
420}
421
[email protected]e64e9012010-01-11 23:10:55422void BookmarkModel::SetTitle(const BookmarkNode* node, const string16& title) {
[email protected]f25387b2008-08-21 15:20:33423 if (!node) {
424 NOTREACHED();
425 return;
426 }
[email protected]0491ff72011-12-30 00:45:59427 if (node->GetTitle() == title)
initial.commit09911bf2008-07-26 23:55:29428 return;
[email protected]f25387b2008-08-21 15:20:33429
[email protected]6892e2e2011-05-26 22:07:17430 if (is_permanent_node(node)) {
[email protected]baf4f922009-10-19 16:44:07431 NOTREACHED();
432 return;
433 }
434
[email protected]472f95e2013-06-10 16:49:18435 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
436 OnWillChangeBookmarkNode(this, node));
437
[email protected]85d911c2009-05-19 03:59:42438 // The title index doesn't support changing the title, instead we remove then
439 // add it back.
[email protected]01eec882009-05-22 18:13:28440 index_->Remove(node);
[email protected]0491ff72011-12-30 00:45:59441 AsMutable(node)->SetTitle(title);
[email protected]01eec882009-05-22 18:13:28442 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42443
[email protected]f25387b2008-08-21 15:20:33444 if (store_.get())
445 store_->ScheduleSave();
446
[email protected]d8e41ed2008-09-11 15:22:32447 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29448 BookmarkNodeChanged(this, node));
449}
450
[email protected]e5486602010-02-09 21:27:55451void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
452 if (!node) {
453 NOTREACHED();
454 return;
455 }
456
457 // We cannot change the URL of a folder.
458 if (node->is_folder()) {
459 NOTREACHED();
460 return;
461 }
462
[email protected]5d4077542011-07-21 20:24:07463 if (node->url() == url)
[email protected]e5486602010-02-09 21:27:55464 return;
465
[email protected]5b5c9b7f32011-07-21 01:07:18466 BookmarkNode* mutable_node = AsMutable(node);
467 mutable_node->InvalidateFavicon();
468 CancelPendingFaviconLoadRequests(mutable_node);
[email protected]e5486602010-02-09 21:27:55469
[email protected]472f95e2013-06-10 16:49:18470 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
471 OnWillChangeBookmarkNode(this, node));
472
[email protected]e5486602010-02-09 21:27:55473 {
[email protected]20305ec2011-01-21 04:55:52474 base::AutoLock url_lock(url_lock_);
[email protected]e5486602010-02-09 21:27:55475 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
[email protected]5b5c9b7f32011-07-21 01:07:18476 mutable_node);
[email protected]e5486602010-02-09 21:27:55477 DCHECK(i != nodes_ordered_by_url_set_.end());
478 // i points to the first node with the URL, advance until we find the
479 // node we're removing.
480 while (*i != node)
481 ++i;
482 nodes_ordered_by_url_set_.erase(i);
483
[email protected]5d4077542011-07-21 20:24:07484 mutable_node->set_url(url);
[email protected]5b5c9b7f32011-07-21 01:07:18485 nodes_ordered_by_url_set_.insert(mutable_node);
[email protected]e5486602010-02-09 21:27:55486 }
487
488 if (store_.get())
489 store_->ScheduleSave();
490
491 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
492 BookmarkNodeChanged(this, node));
493}
494
[email protected]1858410f2012-10-26 05:06:45495void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
496 const std::string& key,
497 const std::string& value) {
[email protected]39e113452013-11-26 00:43:06498 std::string old_value;
499 if (node->GetMetaInfo(key, &old_value) && old_value == value)
500 return;
501
[email protected]cd869ede2013-10-17 12:29:40502 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
503 OnWillChangeBookmarkMetaInfo(this, node));
504
[email protected]1858410f2012-10-26 05:06:45505 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
506 store_->ScheduleSave();
[email protected]cd869ede2013-10-17 12:29:40507
508 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
509 BookmarkMetaInfoChanged(this, node));
[email protected]1858410f2012-10-26 05:06:45510}
511
512void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
513 const std::string& key) {
[email protected]39e113452013-11-26 00:43:06514 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
515 if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
516 return;
517
[email protected]cd869ede2013-10-17 12:29:40518 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
519 OnWillChangeBookmarkMetaInfo(this, node));
520
[email protected]1858410f2012-10-26 05:06:45521 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
522 store_->ScheduleSave();
[email protected]cd869ede2013-10-17 12:29:40523
524 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
525 BookmarkMetaInfoChanged(this, node));
[email protected]1858410f2012-10-26 05:06:45526}
527
[email protected]39e113452013-11-26 00:43:06528void BookmarkModel::SetNodeSyncTransactionVersion(
529 const BookmarkNode* node,
530 int64 sync_transaction_version) {
531 if (sync_transaction_version == node->sync_transaction_version())
532 return;
533
534 AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
535 if (store_.get())
536 store_->ScheduleSave();
537}
538
[email protected]b61445c2012-10-27 00:11:42539void BookmarkModel::SetDateAdded(const BookmarkNode* node,
540 base::Time date_added) {
541 if (!node) {
542 NOTREACHED();
543 return;
544 }
545
546 if (node->date_added() == date_added)
547 return;
548
549 if (is_permanent_node(node)) {
550 NOTREACHED();
551 return;
552 }
553
554 AsMutable(node)->set_date_added(date_added);
555
556 // Syncing might result in dates newer than the folder's last modified date.
557 if (date_added > node->parent()->date_folder_modified()) {
558 // Will trigger store_->ScheduleSave().
559 SetDateFolderModified(node->parent(), date_added);
560 } else if (store_.get()) {
561 store_->ScheduleSave();
562 }
563}
564
[email protected]848cd05e2008-09-19 18:33:48565void BookmarkModel::GetNodesByURL(const GURL& url,
[email protected]b3c33d462009-06-26 22:29:20566 std::vector<const BookmarkNode*>* nodes) {
[email protected]20305ec2011-01-21 04:55:52567 base::AutoLock url_lock(url_lock_);
[email protected]ea2e5aa52009-05-20 18:01:28568 BookmarkNode tmp_node(url);
initial.commit09911bf2008-07-26 23:55:29569 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]5d4077542011-07-21 20:24:07570 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
[email protected]848cd05e2008-09-19 18:33:48571 nodes->push_back(*i);
572 ++i;
573 }
574}
575
[email protected]b3c33d462009-06-26 22:29:20576const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
577 const GURL& url) {
578 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48579 GetNodesByURL(url, &nodes);
580 if (nodes.empty())
581 return NULL;
582
[email protected]9333f182008-12-09 17:34:17583 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]848cd05e2008-09-19 18:33:48584 return nodes.front();
initial.commit09911bf2008-07-26 23:55:29585}
586
[email protected]cf8e8172011-07-23 00:46:24587bool BookmarkModel::HasBookmarks() {
588 base::AutoLock url_lock(url_lock_);
589 return !nodes_ordered_by_url_set_.empty();
590}
591
592bool BookmarkModel::IsBookmarked(const GURL& url) {
593 base::AutoLock url_lock(url_lock_);
594 return IsBookmarkedNoLock(url);
595}
596
[email protected]0f7bee52012-08-06 20:04:17597void BookmarkModel::GetBookmarks(
598 std::vector<BookmarkService::URLAndTitle>* bookmarks) {
[email protected]20305ec2011-01-21 04:55:52599 base::AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48600 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46601 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
602 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]5d4077542011-07-21 20:24:07603 const GURL* url = &((*i)->url());
[email protected]848cd05e2008-09-19 18:33:48604 // Only add unique URLs.
[email protected]0f7bee52012-08-06 20:04:17605 if (!last_url || *url != *last_url) {
606 BookmarkService::URLAndTitle bookmark;
607 bookmark.url = *url;
608 bookmark.title = (*i)->GetTitle();
609 bookmarks->push_back(bookmark);
610 }
[email protected]848cd05e2008-09-19 18:33:48611 last_url = url;
[email protected]90ef13132008-08-27 03:27:46612 }
613}
614
[email protected]cf8e8172011-07-23 00:46:24615void BookmarkModel::BlockTillLoaded() {
616 loaded_signal_.Wait();
[email protected]848cd05e2008-09-19 18:33:48617}
618
[email protected]66bb7dc2011-10-27 15:29:23619const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
initial.commit09911bf2008-07-26 23:55:29620 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33621 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29622}
623
[email protected]39703292011-03-18 17:03:40624const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
625 int index,
626 const string16& title) {
[email protected]6b4d64c2011-07-29 21:33:24627 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29628 // Can't add to the root.
629 NOTREACHED();
630 return NULL;
631 }
632
[email protected]5b5c9b7f32011-07-21 01:07:18633 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
[email protected]edb63cc2011-03-11 02:00:41634 new_node->set_date_folder_modified(Time::Now());
[email protected]aee236542011-12-01 04:34:03635 // Folders shouldn't have line breaks in their titles.
[email protected]0491ff72011-12-30 00:45:59636 new_node->SetTitle(title);
[email protected]037db002009-10-19 20:06:08637 new_node->set_type(BookmarkNode::FOLDER);
initial.commit09911bf2008-07-26 23:55:29638
[email protected]a42ec64a2012-12-20 17:04:24639 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29640}
641
[email protected]e64e9012010-01-11 23:10:55642const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
643 int index,
644 const string16& title,
645 const GURL& url) {
[email protected]aee236542011-12-01 04:34:03646 return AddURLWithCreationTime(parent, index,
647 CollapseWhitespace(title, false),
648 url, Time::Now());
initial.commit09911bf2008-07-26 23:55:29649}
650
[email protected]e64e9012010-01-11 23:10:55651const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
652 const BookmarkNode* parent,
653 int index,
654 const string16& title,
655 const GURL& url,
656 const Time& creation_time) {
[email protected]6b4d64c2011-07-29 21:33:24657 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
[email protected]f25387b2008-08-21 15:20:33658 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29659 NOTREACHED();
660 return NULL;
661 }
initial.commit09911bf2008-07-26 23:55:29662
[email protected]b61445c2012-10-27 00:11:42663 // Syncing may result in dates newer than the last modified date.
[email protected]8ad613b2012-10-12 21:28:45664 if (creation_time > parent->date_folder_modified())
665 SetDateFolderModified(parent, creation_time);
initial.commit09911bf2008-07-26 23:55:29666
[email protected]ea2e5aa52009-05-20 18:01:28667 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
[email protected]0491ff72011-12-30 00:45:59668 new_node->SetTitle(title);
[email protected]b3c33d462009-06-26 22:29:20669 new_node->set_date_added(creation_time);
[email protected]037db002009-10-19 20:06:08670 new_node->set_type(BookmarkNode::URL);
initial.commit09911bf2008-07-26 23:55:29671
[email protected]776e7492008-10-23 16:47:41672 {
673 // Only hold the lock for the duration of the insert.
[email protected]20305ec2011-01-21 04:55:52674 base::AutoLock url_lock(url_lock_);
[email protected]776e7492008-10-23 16:47:41675 nodes_ordered_by_url_set_.insert(new_node);
676 }
initial.commit09911bf2008-07-26 23:55:29677
[email protected]a42ec64a2012-12-20 17:04:24678 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29679}
680
[email protected]b3c33d462009-06-26 22:29:20681void BookmarkModel::SortChildren(const BookmarkNode* parent) {
[email protected]6b4d64c2011-07-29 21:33:24682 if (!parent || !parent->is_folder() || is_root_node(parent) ||
[email protected]9c1a75a2011-03-10 02:38:12683 parent->child_count() <= 1) {
[email protected]e2f86d92009-02-25 00:22:01684 return;
685 }
686
[email protected]472f95e2013-06-10 16:49:18687 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
688 OnWillReorderBookmarkNode(this, parent));
689
[email protected]ef762642009-03-05 16:30:25690 UErrorCode error = U_ZERO_ERROR;
[email protected]94d914522013-04-02 19:49:47691 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
[email protected]ef762642009-03-05 16:30:25692 if (U_FAILURE(error))
693 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20694 BookmarkNode* mutable_parent = AsMutable(parent);
695 std::sort(mutable_parent->children().begin(),
696 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25697 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01698
[email protected]997a0362009-03-12 03:10:51699 if (store_.get())
700 store_->ScheduleSave();
701
[email protected]e2f86d92009-02-25 00:22:01702 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
703 BookmarkNodeChildrenReordered(this, parent));
704}
705
[email protected]472f95e2013-06-10 16:49:18706void BookmarkModel::ReorderChildren(
707 const BookmarkNode* parent,
[email protected]257d5082013-08-06 07:46:43708 const std::vector<const BookmarkNode*>& ordered_nodes) {
[email protected]472f95e2013-06-10 16:49:18709 // Ensure that all children in |parent| are in |ordered_nodes|.
710 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
711 for (size_t i = 0; i < ordered_nodes.size(); ++i)
712 DCHECK_EQ(parent, ordered_nodes[i]->parent());
713
714 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
715 OnWillReorderBookmarkNode(this, parent));
716
[email protected]257d5082013-08-06 07:46:43717 AsMutable(parent)->SetChildren(
718 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
[email protected]472f95e2013-06-10 16:49:18719
720 if (store_.get())
721 store_->ScheduleSave();
722
723 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
724 BookmarkNodeChildrenReordered(this, parent));
725}
726
[email protected]c6a7a3d2011-03-12 01:04:30727void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
728 const Time time) {
[email protected]eea8fd5532009-12-16 00:08:10729 DCHECK(parent);
[email protected]edb63cc2011-03-11 02:00:41730 AsMutable(parent)->set_date_folder_modified(time);
[email protected]eea8fd5532009-12-16 00:08:10731
732 if (store_.get())
733 store_->ScheduleSave();
734}
735
[email protected]c6a7a3d2011-03-12 01:04:30736void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
737 SetDateFolderModified(node, Time());
initial.commit09911bf2008-07-26 23:55:29738}
739
[email protected]e64e9012010-01-11 23:10:55740void BookmarkModel::GetBookmarksWithTitlesMatching(
741 const string16& text,
742 size_t max_count,
[email protected]dcf5fbb72013-05-30 15:39:57743 std::vector<BookmarkTitleMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28744 if (!loaded_)
745 return;
746
[email protected]d0195a62010-08-22 04:40:20747 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
[email protected]85d911c2009-05-19 03:59:42748}
749
[email protected]9876bb1c2008-12-16 20:42:25750void BookmarkModel::ClearStore() {
[email protected]40e0486b2009-05-22 01:47:27751 registrar_.RemoveAll();
[email protected]9876bb1c2008-12-16 20:42:25752 store_ = NULL;
753}
754
[email protected]bc770a032011-12-12 17:35:30755void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
756 bool value) {
[email protected]1da5f712011-12-06 05:52:26757 DCHECK(loaded_);
[email protected]bc770a032011-12-12 17:35:30758 switch (type) {
759 case BookmarkNode::BOOKMARK_BAR:
760 bookmark_bar_node_->set_visible(value);
761 break;
762 case BookmarkNode::OTHER_NODE:
763 other_node_->set_visible(value);
764 break;
765 case BookmarkNode::MOBILE:
766 mobile_node_->set_visible(value);
767 break;
768 default:
769 NOTREACHED();
770 }
[email protected]1da5f712011-12-06 05:52:26771}
772
[email protected]dddc1b42008-10-09 20:56:59773bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28774 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59775 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
776 nodes_ordered_by_url_set_.end());
777}
778
[email protected]d8e41ed2008-09-11 15:22:32779void BookmarkModel::RemoveNode(BookmarkNode* node,
780 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20781 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33782 NOTREACHED();
783 return;
784 }
785
[email protected]323dbf72013-03-30 17:08:33786 url_lock_.AssertAcquired();
[email protected]0890e60e2011-06-27 14:55:21787 if (node->is_url()) {
[email protected]90ef13132008-08-27 03:27:46788 // NOTE: this is called in such a way that url_lock_ is already held. As
789 // such, this doesn't explicitly grab the lock.
initial.commit09911bf2008-07-26 23:55:29790 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
791 DCHECK(i != nodes_ordered_by_url_set_.end());
[email protected]848cd05e2008-09-19 18:33:48792 // i points to the first node with the URL, advance until we find the
793 // node we're removing.
794 while (*i != node)
795 ++i;
initial.commit09911bf2008-07-26 23:55:29796 nodes_ordered_by_url_set_.erase(i);
[email protected]5d4077542011-07-21 20:24:07797 removed_urls->insert(node->url());
[email protected]85d911c2009-05-19 03:59:42798
[email protected]01eec882009-05-22 18:13:28799 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29800 }
801
[email protected]abc2f262011-03-15 21:15:44802 CancelPendingFaviconLoadRequests(node);
initial.commit09911bf2008-07-26 23:55:29803
804 // Recurse through children.
[email protected]9c1a75a2011-03-10 02:38:12805 for (int i = node->child_count() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33806 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29807}
808
[email protected]a48f87d2012-10-09 18:06:33809void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
[email protected]01eec882009-05-22 18:13:28810 DCHECK(details_delete_me);
[email protected]9c92d192009-12-02 08:03:16811 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
[email protected]01eec882009-05-22 18:13:28812 if (loaded_) {
813 // We should only ever be loaded once.
814 NOTREACHED();
815 return;
816 }
817
[email protected]01eec882009-05-22 18:13:28818 next_node_id_ = details->max_id();
[email protected]367d7072009-07-13 23:27:13819 if (details->computed_checksum() != details->stored_checksum() ||
820 details->ids_reassigned()) {
821 // If bookmarks file changed externally, the IDs may have changed
822 // externally. In that case, the decoder may have reassigned IDs to make
823 // them unique. So when the file has changed externally, we should save the
824 // bookmarks file to persist new IDs.
825 if (store_.get())
826 store_->ScheduleSave();
827 }
[email protected]d22d8732010-05-04 19:24:42828 bookmark_bar_node_ = details->release_bb_node();
829 other_node_ = details->release_other_folder_node();
[email protected]37bc9132011-12-01 22:29:29830 mobile_node_ = details->release_mobile_folder_node();
[email protected]d22d8732010-05-04 19:24:42831 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28832
[email protected]82f4655a2011-10-18 13:05:43833 // WARNING: order is important here, various places assume the order is
834 // constant.
[email protected]a0dd6a32011-03-18 17:31:37835 root_.Add(bookmark_bar_node_, 0);
836 root_.Add(other_node_, 1);
[email protected]37bc9132011-12-01 22:29:29837 root_.Add(mobile_node_, 2);
[email protected]6c1164042009-05-08 14:41:08838
[email protected]39e113452013-11-26 00:43:06839 root_.SetMetaInfoMap(details->model_meta_info_map());
840 root_.set_sync_transaction_version(details->model_sync_transaction_version());
[email protected]1858410f2012-10-26 05:06:45841
[email protected]90ef13132008-08-27 03:27:46842 {
[email protected]20305ec2011-01-21 04:55:52843 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46844 // Update nodes_ordered_by_url_set_ from the nodes.
845 PopulateNodesByURL(&root_);
846 }
[email protected]f25387b2008-08-21 15:20:33847
initial.commit09911bf2008-07-26 23:55:29848 loaded_ = true;
849
[email protected]cbcd6412009-03-09 22:31:39850 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46851
[email protected]f25387b2008-08-21 15:20:33852 // Notify our direct observers.
[email protected]c58c5ea2011-07-13 21:43:16853 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
854 Loaded(this, details->ids_reassigned()));
initial.commit09911bf2008-07-26 23:55:29855}
856
[email protected]d8e41ed2008-09-11 15:22:32857void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
858 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29859
[email protected]323dbf72013-03-30 17:08:33860 const BookmarkNode* parent = node->parent();
initial.commit09911bf2008-07-26 23:55:29861 DCHECK(parent);
[email protected]368f3a72011-03-08 17:17:48862 int index = parent->GetIndexOf(node.get());
[email protected]323dbf72013-03-30 17:08:33863
[email protected]472f95e2013-06-10 16:49:18864 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
865 OnWillRemoveBookmarks(this, parent, index, node.get()));
866
[email protected]9109f8a12013-06-12 18:07:48867 std::set<GURL> removed_urls;
[email protected]90ef13132008-08-27 03:27:46868 {
[email protected]20305ec2011-01-21 04:55:52869 base::AutoLock url_lock(url_lock_);
[email protected]9109f8a12013-06-12 18:07:48870 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
[email protected]90ef13132008-08-27 03:27:46871 }
initial.commit09911bf2008-07-26 23:55:29872
[email protected]f25387b2008-08-21 15:20:33873 if (store_.get())
874 store_->ScheduleSave();
875
[email protected]9109f8a12013-06-12 18:07:48876 NotifyHistoryAboutRemovedBookmarks(removed_urls);
[email protected]323dbf72013-03-30 17:08:33877
[email protected]d8e41ed2008-09-11 15:22:32878 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41879 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]323dbf72013-03-30 17:08:33880}
[email protected]f25387b2008-08-21 15:20:33881
[email protected]323dbf72013-03-30 17:08:33882void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
883 std::set<GURL>* removed_urls) {
884 // NOTE: this method should be always called with |url_lock_| held.
885 // This method does not explicitly acquires a lock.
886 url_lock_.AssertAcquired();
887 DCHECK(removed_urls);
888 BookmarkNode* parent = AsMutable(node->parent());
889 DCHECK(parent);
890 parent->Remove(node);
891 RemoveNode(node, removed_urls);
892 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
893 // allow duplicates we need to remove any entries that are still bookmarked.
894 for (std::set<GURL>::iterator i = removed_urls->begin();
895 i != removed_urls->end();) {
896 if (IsBookmarkedNoLock(*i)) {
897 // When we erase the iterator pointing at the erasee is
898 // invalidated, so using i++ here within the "erase" call is
899 // important as it advances the iterator before passing the
900 // old value through to erase.
901 removed_urls->erase(i++);
902 } else {
903 ++i;
904 }
905 }
906}
907
908void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
909 const std::set<GURL>& removed_bookmark_urls) const {
910 if (removed_bookmark_urls.empty()) {
[email protected]848cd05e2008-09-19 18:33:48911 // No point in sending out notification if the starred state didn't change.
912 return;
913 }
914
[email protected]90ef13132008-08-27 03:27:46915 if (profile_) {
916 HistoryService* history =
[email protected]9aac66862012-06-19 19:44:31917 HistoryServiceFactory::GetForProfile(profile_,
918 Profile::EXPLICIT_ACCESS);
[email protected]90ef13132008-08-27 03:27:46919 if (history)
[email protected]323dbf72013-03-30 17:08:33920 history->URLsNoLongerBookmarked(removed_bookmark_urls);
[email protected]90ef13132008-08-27 03:27:46921 }
initial.commit09911bf2008-07-26 23:55:29922}
923
[email protected]d8e41ed2008-09-11 15:22:32924BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
925 int index,
[email protected]a42ec64a2012-12-20 17:04:24926 BookmarkNode* node) {
[email protected]a0dd6a32011-03-18 17:31:37927 parent->Add(node, index);
initial.commit09911bf2008-07-26 23:55:29928
[email protected]f25387b2008-08-21 15:20:33929 if (store_.get())
930 store_->ScheduleSave();
931
[email protected]d8e41ed2008-09-11 15:22:32932 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29933 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33934
[email protected]01eec882009-05-22 18:13:28935 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42936
initial.commit09911bf2008-07-26 23:55:29937 return node;
938}
939
[email protected]b3c33d462009-06-26 22:29:20940const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
[email protected]66bb7dc2011-10-27 15:29:23941 int64 id) const {
[email protected]f25387b2008-08-21 15:20:33942 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29943 return node;
[email protected]f25387b2008-08-21 15:20:33944
[email protected]9c1a75a2011-03-10 02:38:12945 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
[email protected]b3c33d462009-06-26 22:29:20946 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29947 if (result)
948 return result;
949 }
950 return NULL;
951}
952
[email protected]b3c33d462009-06-26 22:29:20953bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32954 int index,
955 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41956 return (parent && parent->is_folder() &&
[email protected]9c1a75a2011-03-10 02:38:12957 (index >= 0 && (index < parent->child_count() ||
958 (allow_end && index == parent->child_count()))));
[email protected]bd1b96702009-07-08 21:54:14959}
[email protected]f25387b2008-08-21 15:20:33960
[email protected]bc770a032011-12-12 17:35:30961BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
962 BookmarkNode::Type type) {
[email protected]e1f76c62011-06-30 20:15:39963 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
964 type == BookmarkNode::OTHER_NODE ||
[email protected]37bc9132011-12-01 22:29:29965 type == BookmarkNode::MOBILE);
[email protected]bc770a032011-12-12 17:35:30966 BookmarkPermanentNode* node =
967 new BookmarkPermanentNode(generate_next_node_id());
968 if (type == BookmarkNode::MOBILE)
969 node->set_visible(false); // Mobile node is initially hidden.
970
971 int title_id;
972 switch (type) {
973 case BookmarkNode::BOOKMARK_BAR:
974 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
975 break;
976 case BookmarkNode::OTHER_NODE:
977 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
978 break;
979 case BookmarkNode::MOBILE:
980 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
981 break;
982 default:
[email protected]97fdd162011-12-03 20:50:12983 NOTREACHED();
[email protected]bc770a032011-12-12 17:35:30984 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
985 break;
[email protected]7caca8a22010-08-21 18:25:31986 }
[email protected]0491ff72011-12-30 00:45:59987 node->SetTitle(l10n_util::GetStringUTF16(title_id));
[email protected]97fdd162011-12-03 20:50:12988 node->set_type(type);
initial.commit09911bf2008-07-26 23:55:29989 return node;
990}
991
[email protected]abc2f262011-03-15 21:15:44992void BookmarkModel::OnFaviconDataAvailable(
[email protected]0ea3db52012-12-07 01:32:01993 BookmarkNode* node,
[email protected]299e4542013-05-16 03:09:06994 const chrome::FaviconImageResult& image_result) {
[email protected]4167c3a2008-08-21 18:12:20995 DCHECK(node);
[email protected]0ea3db52012-12-07 01:32:01996 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
[email protected]bcc38612012-10-23 01:10:27997 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
[email protected]65baa222012-08-30 15:43:51998 if (!image_result.image.IsEmpty()) {
999 node->set_favicon(image_result.image);
[email protected]2ad0e872012-11-30 01:24:461000 node->set_icon_url(image_result.icon_url);
[email protected]65baa222012-08-30 15:43:511001 FaviconLoaded(node);
initial.commit09911bf2008-07-26 23:55:291002 }
1003}
1004
[email protected]abc2f262011-03-15 21:15:441005void BookmarkModel::LoadFavicon(BookmarkNode* node) {
[email protected]0890e60e2011-06-27 14:55:211006 if (node->is_folder())
initial.commit09911bf2008-07-26 23:55:291007 return;
1008
[email protected]5d4077542011-07-21 20:24:071009 DCHECK(node->url().is_valid());
[email protected]f3d2b312012-08-23 22:27:591010 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
1011 profile_, Profile::EXPLICIT_ACCESS);
[email protected]0189bc722009-08-28 21:56:481012 if (!favicon_service)
initial.commit09911bf2008-07-26 23:55:291013 return;
[email protected]65baa222012-08-30 15:43:511014 FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
[email protected]f718290e2013-11-05 07:11:581015 FaviconService::FaviconForURLParams(node->url(),
[email protected]299e4542013-05-16 03:09:061016 chrome::FAVICON,
[email protected]0ea3db52012-12-07 01:32:011017 gfx::kFaviconSize),
[email protected]bbdd2982011-10-08 18:14:241018 base::Bind(&BookmarkModel::OnFaviconDataAvailable,
[email protected]0ea3db52012-12-07 01:32:011019 base::Unretained(this), node),
1020 &cancelable_task_tracker_);
1021 node->set_favicon_load_task_id(handle);
initial.commit09911bf2008-07-26 23:55:291022}
1023
[email protected]5b5c9b7f32011-07-21 01:07:181024void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
1025 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
1026 BookmarkNodeFaviconChanged(this, node));
1027}
1028
[email protected]abc2f262011-03-15 21:15:441029void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
[email protected]0ea3db52012-12-07 01:32:011030 if (node->favicon_load_task_id() != CancelableTaskTracker::kBadTaskId) {
1031 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
1032 node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
initial.commit09911bf2008-07-26 23:55:291033 }
1034}
1035
[email protected]432115822011-07-10 15:52:271036void BookmarkModel::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:531037 const content::NotificationSource& source,
1038 const content::NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:271039 switch (type) {
1040 case chrome::NOTIFICATION_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:291041 // Prevent the observers from getting confused for multiple favicon loads.
[email protected]0ab247d2013-05-28 17:01:061042 content::Details<FaviconChangedDetails> favicon_details(details);
initial.commit09911bf2008-07-26 23:55:291043 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
1044 i != favicon_details->urls.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:201045 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:481046 GetNodesByURL(*i, &nodes);
1047 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:291048 // Got an updated favicon, for a URL, do a new request.
[email protected]b3c33d462009-06-26 22:29:201049 BookmarkNode* node = AsMutable(nodes[i]);
initial.commit09911bf2008-07-26 23:55:291050 node->InvalidateFavicon();
[email protected]abc2f262011-03-15 21:15:441051 CancelPendingFaviconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:321052 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]f12de8302011-05-23 16:12:301053 BookmarkNodeFaviconChanged(this, node));
initial.commit09911bf2008-07-26 23:55:291054 }
1055 }
1056 break;
1057 }
1058
1059 default:
1060 NOTREACHED();
1061 break;
1062 }
1063}
[email protected]f25387b2008-08-21 15:20:331064
[email protected]d8e41ed2008-09-11 15:22:321065void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:461066 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1067 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:331068 if (node->is_url())
1069 nodes_ordered_by_url_set_.insert(node);
[email protected]9c1a75a2011-03-10 02:38:121070 for (int i = 0; i < node->child_count(); ++i)
[email protected]f25387b2008-08-21 15:20:331071 PopulateNodesByURL(node->GetChild(i));
1072}
[email protected]4d89f382009-05-12 06:56:491073
[email protected]367d7072009-07-13 23:27:131074int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:491075 return next_node_id_++;
1076}
[email protected]01eec882009-05-22 18:13:281077
[email protected]9c92d192009-12-02 08:03:161078BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
[email protected]bc770a032011-12-12 17:35:301079 BookmarkPermanentNode* bb_node =
1080 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1081 BookmarkPermanentNode* other_node =
1082 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1083 BookmarkPermanentNode* mobile_node =
1084 CreatePermanentNode(BookmarkNode::MOBILE);
[email protected]37bc9132011-12-01 22:29:291085 return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
[email protected]1858410f2012-10-26 05:06:451086 new BookmarkIndex(profile_),
1087 next_node_id_);
[email protected]01eec882009-05-22 18:13:281088}