blob: 35145c08c34f86d07d69bc88ac55d6ce31d08b48 [file] [log] [blame]
[email protected]c47f86fa2014-04-30 02:20:181// Copyright 2014 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]a90c8ca2014-05-20 17:16:045#include "components/bookmarks/browser/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]6a848b52014-04-26 22:06:5413#include "base/logging.h"
[email protected]996dbe82014-05-12 12:32:1814#include "base/macros.h"
[email protected]6a848b52014-04-26 22:06:5415#include "base/strings/string_util.h"
[email protected]a90c8ca2014-05-20 17:16:0416#include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
17#include "components/bookmarks/browser/bookmark_index.h"
18#include "components/bookmarks/browser/bookmark_match.h"
19#include "components/bookmarks/browser/bookmark_model_observer.h"
20#include "components/bookmarks/browser/bookmark_node_data.h"
21#include "components/bookmarks/browser/bookmark_storage.h"
22#include "components/bookmarks/browser/bookmark_utils.h"
[email protected]7627e0b42014-04-17 17:20:5323#include "components/favicon_base/favicon_types.h"
[email protected]426d676a2014-05-28 14:41:0324#include "grit/components_strings.h"
[email protected]c051a1b2011-01-21 23:30:1725#include "ui/base/l10n/l10n_util.h"
[email protected]65baa222012-08-30 15:43:5126#include "ui/gfx/favicon_size.h"
initial.commit09911bf2008-07-26 23:55:2927
[email protected]e1acf6f2008-10-27 20:43:3328using base::Time;
[email protected]5d4832f2014-06-27 01:26:1529using bookmarks::BookmarkClient;
[email protected]05c90652014-06-13 02:42:0030using bookmarks::BookmarkExpandedStateTracker;
[email protected]c45f5622014-06-01 21:35:4831using bookmarks::BookmarkIndex;
[email protected]9e422ecc2014-05-27 22:31:3132using bookmarks::BookmarkLoadDetails;
[email protected]19c602f12014-06-12 07:37:0533using bookmarks::BookmarkMatch;
[email protected]9e422ecc2014-05-27 22:31:3134using bookmarks::BookmarkStorage;
[email protected]e1acf6f2008-10-27 20:43:3335
[email protected]b3c33d462009-06-26 22:29:2036namespace {
37
38// Helper to get a mutable bookmark node.
[email protected]7b084b02011-07-08 16:06:4839BookmarkNode* AsMutable(const BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:2040 return const_cast<BookmarkNode*>(node);
41}
42
[email protected]996dbe82014-05-12 12:32:1843// Helper to get a mutable permanent bookmark node.
44BookmarkPermanentNode* AsMutable(const BookmarkPermanentNode* node) {
45 return const_cast<BookmarkPermanentNode*>(node);
46}
47
48// Comparator used when sorting permanent nodes. Nodes that are initially
49// visible are sorted before nodes that are initially hidden.
50class VisibilityComparator
51 : public std::binary_function<const BookmarkPermanentNode*,
52 const BookmarkPermanentNode*,
53 bool> {
54 public:
55 explicit VisibilityComparator(BookmarkClient* client) : client_(client) {}
56
57 // Returns true if |n1| preceeds |n2|.
58 bool operator()(const BookmarkPermanentNode* n1,
59 const BookmarkPermanentNode* n2) {
[email protected]043a76d2014-06-05 16:36:2460 bool n1_visible = client_->IsPermanentNodeVisible(n1);
61 bool n2_visible = client_->IsPermanentNodeVisible(n2);
[email protected]996dbe82014-05-12 12:32:1862 return n1_visible != n2_visible && n1_visible;
63 }
64
65 private:
66 BookmarkClient* client_;
67};
68
[email protected]ef762642009-03-05 16:30:2569// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:4470// bookmarks.
[email protected]b3c33d462009-06-26 22:29:2071class SortComparator : public std::binary_function<const BookmarkNode*,
72 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:2573 bool> {
74 public:
[email protected]a48f87d2012-10-09 18:06:3375 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
[email protected]ef762642009-03-05 16:30:2576
[email protected]a48f87d2012-10-09 18:06:3377 // Returns true if |n1| preceeds |n2|.
78 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:0879 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:2580 // Types are the same, compare the names.
81 if (!collator_)
[email protected]440b37b22010-08-30 05:31:4082 return n1->GetTitle() < n2->GetTitle();
[email protected]9a08fe82013-04-23 05:06:0583 return base::i18n::CompareString16WithCollator(
[email protected]3abebda2011-01-07 20:17:1584 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
[email protected]ef762642009-03-05 16:30:2585 }
86 // Types differ, sort such that folders come first.
87 return n1->is_folder();
88 }
89
90 private:
[email protected]b5b2385a2009-08-18 05:12:2991 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:2592};
93
[email protected]bc770a032011-12-12 17:35:3094} // namespace
[email protected]97fdd162011-12-03 20:50:1295
[email protected]97fdd162011-12-03 20:50:1296// BookmarkModel --------------------------------------------------------------
97
[email protected]6a848b52014-04-26 22:06:5498BookmarkModel::BookmarkModel(BookmarkClient* client, bool index_urls)
99 : client_(client),
initial.commit09911bf2008-07-26 23:55:29100 loaded_(false),
[email protected]ea2e5aa52009-05-20 18:01:28101 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:29102 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:46103 other_node_(NULL),
[email protected]37bc9132011-12-01 22:29:29104 mobile_node_(NULL),
[email protected]4d89f382009-05-12 06:56:49105 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:06106 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]b3a84892014-04-23 04:28:07107 index_urls_(index_urls),
[email protected]b68a8172012-02-17 00:25:18108 loaded_signal_(true, false),
109 extensive_changes_(0) {
[email protected]6a848b52014-04-26 22:06:54110 DCHECK(client_);
initial.commit09911bf2008-07-26 23:55:29111}
112
[email protected]d8e41ed2008-09-11 15:22:32113BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:32114 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:51115 BookmarkModelBeingDeleted(this));
116
[email protected]dc24976f2013-06-02 21:15:09117 if (store_.get()) {
[email protected]f25387b2008-08-21 15:20:33118 // The store maintains a reference back to us. We need to tell it we're gone
119 // so that it doesn't try and invoke a method back on us again.
120 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29121 }
122}
123
[email protected]f61f4782012-06-08 21:54:21124void BookmarkModel::Shutdown() {
[email protected]88e6a232011-09-14 14:53:20125 if (loaded_)
126 return;
127
[email protected]f61f4782012-06-08 21:54:21128 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
129 // details. It is also called when the BookmarkModel is deleted.
[email protected]88e6a232011-09-14 14:53:20130 loaded_signal_.Signal();
131}
132
[email protected]afecfb72013-04-18 17:17:33133void BookmarkModel::Load(
[email protected]6a848b52014-04-26 22:06:54134 PrefService* pref_service,
135 const std::string& accept_languages,
136 const base::FilePath& profile_path,
137 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
138 const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner) {
[email protected]90ef13132008-08-27 03:27:46139 if (store_.get()) {
140 // If the store is non-null, it means Load was already invoked. Load should
141 // only be invoked once.
142 NOTREACHED();
143 return;
144 }
145
[email protected]6df6ec022013-07-16 03:05:22146 expanded_state_tracker_.reset(
[email protected]6a848b52014-04-26 22:06:54147 new BookmarkExpandedStateTracker(this, pref_service));
[email protected]90ef13132008-08-27 03:27:46148
149 // Load the bookmarks. BookmarkStorage notifies us when done.
[email protected]265e88e2014-07-07 20:45:19150 store_ .reset(new BookmarkStorage(this, profile_path, io_task_runner.get()));
[email protected]6a848b52014-04-26 22:06:54151 store_->LoadBookmarks(CreateLoadDetails(accept_languages), ui_task_runner);
[email protected]90ef13132008-08-27 03:27:46152}
153
[email protected]b3c33d462009-06-26 22:29:20154const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
155 std::vector<const BookmarkNode*> nodes =
[email protected]23e39692014-06-06 21:10:13156 bookmark_utils::GetMostRecentlyModifiedUserFolders(this, 1);
[email protected]82f4655a2011-10-18 13:05:43157 DCHECK(!nodes.empty()); // This list is always padded with default folders.
158 return nodes[0];
initial.commit09911bf2008-07-26 23:55:29159}
160
[email protected]125b234182011-07-08 19:54:41161void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
162 observers_.AddObserver(observer);
163}
164
165void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
166 observers_.RemoveObserver(observer);
167}
168
[email protected]b68a8172012-02-17 00:25:18169void BookmarkModel::BeginExtensiveChanges() {
170 if (++extensive_changes_ == 1) {
171 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
172 ExtensiveBookmarkChangesBeginning(this));
173 }
[email protected]125b234182011-07-08 19:54:41174}
175
[email protected]b68a8172012-02-17 00:25:18176void BookmarkModel::EndExtensiveChanges() {
177 --extensive_changes_;
178 DCHECK_GE(extensive_changes_, 0);
179 if (extensive_changes_ == 0) {
180 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
181 ExtensiveBookmarkChangesEnded(this));
182 }
[email protected]125b234182011-07-08 19:54:41183}
184
[email protected]346453a2014-03-12 10:14:37185void BookmarkModel::BeginGroupedChanges() {
186 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
187 GroupedBookmarkChangesBeginning(this));
188}
189
190void BookmarkModel::EndGroupedChanges() {
191 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
192 GroupedBookmarkChangesEnded(this));
193}
194
[email protected]b3c33d462009-06-26 22:29:20195void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
[email protected]6b4d64c2011-07-29 21:33:24196 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
[email protected]f25387b2008-08-21 15:20:33197 NOTREACHED();
198 return;
199 }
[email protected]b3c33d462009-06-26 22:29:20200 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
[email protected]f25387b2008-08-21 15:20:33201}
202
[email protected]5cd942208a2014-06-11 06:16:46203void BookmarkModel::RemoveAllUserBookmarks() {
[email protected]9109f8a12013-06-12 18:07:48204 std::set<GURL> removed_urls;
[email protected]323dbf72013-03-30 17:08:33205 ScopedVector<BookmarkNode> removed_nodes;
[email protected]472f95e2013-06-10 16:49:18206
207 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]5cd942208a2014-06-11 06:16:46208 OnWillRemoveAllUserBookmarks(this));
[email protected]472f95e2013-06-10 16:49:18209
[email protected]323dbf72013-03-30 17:08:33210 BeginExtensiveChanges();
211 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
212 // its immediate children. For removing all non permanent nodes just remove
213 // all children of non-root permanent nodes.
214 {
215 base::AutoLock url_lock(url_lock_);
216 for (int i = 0; i < root_.child_count(); ++i) {
[email protected]ed18102fa2013-12-26 04:02:00217 BookmarkNode* permanent_node = root_.GetChild(i);
[email protected]043a76d2014-06-05 16:36:24218
[email protected]23e39692014-06-06 21:10:13219 if (!client_->CanBeEditedByUser(permanent_node))
[email protected]043a76d2014-06-05 16:36:24220 continue;
221
[email protected]323dbf72013-03-30 17:08:33222 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
[email protected]ed18102fa2013-12-26 04:02:00223 BookmarkNode* child_node = permanent_node->GetChild(j);
[email protected]323dbf72013-03-30 17:08:33224 removed_nodes.push_back(child_node);
[email protected]9109f8a12013-06-12 18:07:48225 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
[email protected]323dbf72013-03-30 17:08:33226 }
227 }
228 }
229 EndExtensiveChanges();
230 if (store_.get())
231 store_->ScheduleSave();
232
[email protected]323dbf72013-03-30 17:08:33233 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]5cd942208a2014-06-11 06:16:46234 BookmarkAllUserNodesRemoved(this, removed_urls));
235}
236
[email protected]b3c33d462009-06-26 22:29:20237void BookmarkModel::Move(const BookmarkNode* node,
238 const BookmarkNode* new_parent,
[email protected]d8e41ed2008-09-11 15:22:32239 int index) {
[email protected]f25387b2008-08-21 15:20:33240 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24241 is_root_node(new_parent) || is_permanent_node(node)) {
initial.commit09911bf2008-07-26 23:55:29242 NOTREACHED();
243 return;
244 }
245
246 if (new_parent->HasAncestor(node)) {
247 // Can't make an ancestor of the node be a child of the node.
248 NOTREACHED();
249 return;
250 }
251
[email protected]2d48ee842011-03-08 23:27:29252 const BookmarkNode* old_parent = node->parent();
[email protected]368f3a72011-03-08 17:17:48253 int old_index = old_parent->GetIndexOf(node);
initial.commit09911bf2008-07-26 23:55:29254
255 if (old_parent == new_parent &&
256 (index == old_index || index == old_index + 1)) {
257 // Node is already in this position, nothing to do.
258 return;
259 }
260
[email protected]9e583642012-12-05 02:48:32261 SetDateFolderModified(new_parent, Time::Now());
262
initial.commit09911bf2008-07-26 23:55:29263 if (old_parent == new_parent && index > old_index)
264 index--;
[email protected]b3c33d462009-06-26 22:29:20265 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
[email protected]a0dd6a32011-03-18 17:31:37266 mutable_new_parent->Add(AsMutable(node), index);
initial.commit09911bf2008-07-26 23:55:29267
[email protected]f25387b2008-08-21 15:20:33268 if (store_.get())
269 store_->ScheduleSave();
270
[email protected]d8e41ed2008-09-11 15:22:32271 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29272 BookmarkNodeMoved(this, old_parent, old_index,
273 new_parent, index));
274}
275
[email protected]4e187ef652010-03-11 05:21:35276void BookmarkModel::Copy(const BookmarkNode* node,
277 const BookmarkNode* new_parent,
278 int index) {
279 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24280 is_root_node(new_parent) || is_permanent_node(node)) {
[email protected]4e187ef652010-03-11 05:21:35281 NOTREACHED();
282 return;
283 }
284
285 if (new_parent->HasAncestor(node)) {
286 // Can't make an ancestor of the node be a child of the node.
287 NOTREACHED();
288 return;
289 }
290
[email protected]c6a7a3d2011-03-12 01:04:30291 SetDateFolderModified(new_parent, Time::Now());
[email protected]67a8ae42013-05-20 09:42:39292 BookmarkNodeData drag_data(node);
293 std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
[email protected]b1864502010-11-13 00:55:51294 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
[email protected]1a566fae2010-04-16 01:05:06295 // don't need to send notifications here.
[email protected]7be9c7d2013-08-22 01:19:33296 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index, true);
[email protected]4e187ef652010-03-11 05:21:35297
298 if (store_.get())
299 store_->ScheduleSave();
[email protected]4e187ef652010-03-11 05:21:35300}
301
[email protected]6a4e5a02012-06-26 19:47:48302const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
[email protected]ea2e5aa52009-05-20 18:01:28303 DCHECK(node);
[email protected]bcc38612012-10-23 01:10:27304 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
[email protected]b3c33d462009-06-26 22:29:20305 BookmarkNode* mutable_node = AsMutable(node);
[email protected]504fca82014-05-07 22:48:08306 LoadFavicon(
307 mutable_node,
308 client_->PreferTouchIcon() ?
309 favicon_base::TOUCH_ICON :
310 favicon_base::FAVICON);
[email protected]ea2e5aa52009-05-20 18:01:28311 }
312 return node->favicon();
313}
314
[email protected]504fca82014-05-07 22:48:08315favicon_base::IconType BookmarkModel::GetFaviconType(const BookmarkNode* node) {
316 DCHECK(node);
317 return node->favicon_type();
318}
319
[email protected]6a848b52014-04-26 22:06:54320void BookmarkModel::SetTitle(const BookmarkNode* node,
321 const base::string16& title) {
[email protected]f25387b2008-08-21 15:20:33322 if (!node) {
323 NOTREACHED();
324 return;
325 }
[email protected]0491ff72011-12-30 00:45:59326 if (node->GetTitle() == title)
initial.commit09911bf2008-07-26 23:55:29327 return;
[email protected]f25387b2008-08-21 15:20:33328
[email protected]043a76d2014-06-05 16:36:24329 if (is_permanent_node(node) && !client_->CanSetPermanentNodeTitle(node)) {
[email protected]baf4f922009-10-19 16:44:07330 NOTREACHED();
331 return;
332 }
333
[email protected]472f95e2013-06-10 16:49:18334 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
335 OnWillChangeBookmarkNode(this, node));
336
[email protected]85d911c2009-05-19 03:59:42337 // The title index doesn't support changing the title, instead we remove then
338 // add it back.
[email protected]01eec882009-05-22 18:13:28339 index_->Remove(node);
[email protected]0491ff72011-12-30 00:45:59340 AsMutable(node)->SetTitle(title);
[email protected]01eec882009-05-22 18:13:28341 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42342
[email protected]f25387b2008-08-21 15:20:33343 if (store_.get())
344 store_->ScheduleSave();
345
[email protected]d8e41ed2008-09-11 15:22:32346 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29347 BookmarkNodeChanged(this, node));
348}
349
[email protected]e5486602010-02-09 21:27:55350void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
351 if (!node) {
352 NOTREACHED();
353 return;
354 }
355
356 // We cannot change the URL of a folder.
357 if (node->is_folder()) {
358 NOTREACHED();
359 return;
360 }
361
[email protected]5d4077542011-07-21 20:24:07362 if (node->url() == url)
[email protected]e5486602010-02-09 21:27:55363 return;
364
[email protected]5b5c9b7f32011-07-21 01:07:18365 BookmarkNode* mutable_node = AsMutable(node);
366 mutable_node->InvalidateFavicon();
367 CancelPendingFaviconLoadRequests(mutable_node);
[email protected]e5486602010-02-09 21:27:55368
[email protected]472f95e2013-06-10 16:49:18369 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
370 OnWillChangeBookmarkNode(this, node));
371
[email protected]e5486602010-02-09 21:27:55372 {
[email protected]20305ec2011-01-21 04:55:52373 base::AutoLock url_lock(url_lock_);
[email protected]5db00bca2013-12-23 00:43:09374 RemoveNodeFromURLSet(mutable_node);
[email protected]5d4077542011-07-21 20:24:07375 mutable_node->set_url(url);
[email protected]5b5c9b7f32011-07-21 01:07:18376 nodes_ordered_by_url_set_.insert(mutable_node);
[email protected]e5486602010-02-09 21:27:55377 }
378
379 if (store_.get())
380 store_->ScheduleSave();
381
382 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
383 BookmarkNodeChanged(this, node));
384}
385
[email protected]1858410f2012-10-26 05:06:45386void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
387 const std::string& key,
388 const std::string& value) {
[email protected]39e113452013-11-26 00:43:06389 std::string old_value;
390 if (node->GetMetaInfo(key, &old_value) && old_value == value)
391 return;
392
[email protected]cd869ede2013-10-17 12:29:40393 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
394 OnWillChangeBookmarkMetaInfo(this, node));
395
[email protected]1858410f2012-10-26 05:06:45396 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
397 store_->ScheduleSave();
[email protected]cd869ede2013-10-17 12:29:40398
399 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
400 BookmarkMetaInfoChanged(this, node));
[email protected]1858410f2012-10-26 05:06:45401}
402
[email protected]e38a87d2013-12-05 01:35:18403void BookmarkModel::SetNodeMetaInfoMap(
404 const BookmarkNode* node,
405 const BookmarkNode::MetaInfoMap& meta_info_map) {
406 const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
407 if ((!old_meta_info_map && meta_info_map.empty()) ||
408 (old_meta_info_map && meta_info_map == *old_meta_info_map))
409 return;
410
411 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
412 OnWillChangeBookmarkMetaInfo(this, node));
413
414 AsMutable(node)->SetMetaInfoMap(meta_info_map);
415 if (store_.get())
416 store_->ScheduleSave();
417
418 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
419 BookmarkMetaInfoChanged(this, node));
420}
421
[email protected]1858410f2012-10-26 05:06:45422void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
423 const std::string& key) {
[email protected]39e113452013-11-26 00:43:06424 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
425 if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
426 return;
427
[email protected]cd869ede2013-10-17 12:29:40428 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
429 OnWillChangeBookmarkMetaInfo(this, node));
430
[email protected]1858410f2012-10-26 05:06:45431 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
432 store_->ScheduleSave();
[email protected]cd869ede2013-10-17 12:29:40433
434 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
435 BookmarkMetaInfoChanged(this, node));
[email protected]1858410f2012-10-26 05:06:45436}
437
[email protected]39e113452013-11-26 00:43:06438void BookmarkModel::SetNodeSyncTransactionVersion(
439 const BookmarkNode* node,
440 int64 sync_transaction_version) {
[email protected]043a76d2014-06-05 16:36:24441 DCHECK(client_->CanSyncNode(node));
442
[email protected]39e113452013-11-26 00:43:06443 if (sync_transaction_version == node->sync_transaction_version())
444 return;
445
446 AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
447 if (store_.get())
448 store_->ScheduleSave();
449}
450
[email protected]6a848b52014-04-26 22:06:54451void BookmarkModel::OnFaviconChanged(const std::set<GURL>& urls) {
452 // Ignore events if |Load| has not been called yet.
453 if (!store_)
454 return;
455
456 // Prevent the observers from getting confused for multiple favicon loads.
457 for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
458 std::vector<const BookmarkNode*> nodes;
459 GetNodesByURL(*i, &nodes);
460 for (size_t i = 0; i < nodes.size(); ++i) {
461 // Got an updated favicon, for a URL, do a new request.
462 BookmarkNode* node = AsMutable(nodes[i]);
463 node->InvalidateFavicon();
464 CancelPendingFaviconLoadRequests(node);
465 FOR_EACH_OBSERVER(BookmarkModelObserver,
466 observers_,
467 BookmarkNodeFaviconChanged(this, node));
468 }
469 }
470}
471
[email protected]b61445c2012-10-27 00:11:42472void BookmarkModel::SetDateAdded(const BookmarkNode* node,
[email protected]af77ce622014-05-10 11:48:16473 Time date_added) {
[email protected]b61445c2012-10-27 00:11:42474 if (!node) {
475 NOTREACHED();
476 return;
477 }
478
479 if (node->date_added() == date_added)
480 return;
481
482 if (is_permanent_node(node)) {
483 NOTREACHED();
484 return;
485 }
486
487 AsMutable(node)->set_date_added(date_added);
488
489 // Syncing might result in dates newer than the folder's last modified date.
490 if (date_added > node->parent()->date_folder_modified()) {
491 // Will trigger store_->ScheduleSave().
492 SetDateFolderModified(node->parent(), date_added);
493 } else if (store_.get()) {
494 store_->ScheduleSave();
495 }
496}
497
[email protected]848cd05e2008-09-19 18:33:48498void BookmarkModel::GetNodesByURL(const GURL& url,
[email protected]b3c33d462009-06-26 22:29:20499 std::vector<const BookmarkNode*>* nodes) {
[email protected]20305ec2011-01-21 04:55:52500 base::AutoLock url_lock(url_lock_);
[email protected]ea2e5aa52009-05-20 18:01:28501 BookmarkNode tmp_node(url);
initial.commit09911bf2008-07-26 23:55:29502 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]5d4077542011-07-21 20:24:07503 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
[email protected]848cd05e2008-09-19 18:33:48504 nodes->push_back(*i);
505 ++i;
506 }
507}
508
[email protected]23e39692014-06-06 21:10:13509const BookmarkNode* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
[email protected]b3c33d462009-06-26 22:29:20510 const GURL& url) {
511 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48512 GetNodesByURL(url, &nodes);
[email protected]9333f182008-12-09 17:34:17513 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]23e39692014-06-06 21:10:13514
515 // Look for the first node that the user can edit.
516 for (size_t i = 0; i < nodes.size(); ++i) {
517 if (client_->CanBeEditedByUser(nodes[i]))
518 return nodes[i];
519 }
520
521 return NULL;
initial.commit09911bf2008-07-26 23:55:29522}
523
[email protected]cf8e8172011-07-23 00:46:24524bool BookmarkModel::HasBookmarks() {
525 base::AutoLock url_lock(url_lock_);
526 return !nodes_ordered_by_url_set_.empty();
527}
528
529bool BookmarkModel::IsBookmarked(const GURL& url) {
530 base::AutoLock url_lock(url_lock_);
531 return IsBookmarkedNoLock(url);
532}
533
[email protected]0f7bee52012-08-06 20:04:17534void BookmarkModel::GetBookmarks(
[email protected]cef7931c2014-06-06 09:56:09535 std::vector<BookmarkModel::URLAndTitle>* bookmarks) {
[email protected]20305ec2011-01-21 04:55:52536 base::AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48537 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46538 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
539 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]5d4077542011-07-21 20:24:07540 const GURL* url = &((*i)->url());
[email protected]848cd05e2008-09-19 18:33:48541 // Only add unique URLs.
[email protected]0f7bee52012-08-06 20:04:17542 if (!last_url || *url != *last_url) {
[email protected]cef7931c2014-06-06 09:56:09543 BookmarkModel::URLAndTitle bookmark;
[email protected]0f7bee52012-08-06 20:04:17544 bookmark.url = *url;
545 bookmark.title = (*i)->GetTitle();
546 bookmarks->push_back(bookmark);
547 }
[email protected]848cd05e2008-09-19 18:33:48548 last_url = url;
[email protected]90ef13132008-08-27 03:27:46549 }
550}
551
[email protected]cf8e8172011-07-23 00:46:24552void BookmarkModel::BlockTillLoaded() {
553 loaded_signal_.Wait();
[email protected]848cd05e2008-09-19 18:33:48554}
555
[email protected]39703292011-03-18 17:03:40556const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
557 int index,
[email protected]96920152013-12-04 21:00:16558 const base::string16& title) {
[email protected]eb59ad12014-04-24 00:05:08559 return AddFolderWithMetaInfo(parent, index, title, NULL);
560}
561const BookmarkNode* BookmarkModel::AddFolderWithMetaInfo(
562 const BookmarkNode* parent,
563 int index,
564 const base::string16& title,
565 const BookmarkNode::MetaInfoMap* meta_info) {
[email protected]6b4d64c2011-07-29 21:33:24566 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29567 // Can't add to the root.
568 NOTREACHED();
569 return NULL;
570 }
571
[email protected]5b5c9b7f32011-07-21 01:07:18572 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
[email protected]edb63cc2011-03-11 02:00:41573 new_node->set_date_folder_modified(Time::Now());
[email protected]aee236542011-12-01 04:34:03574 // Folders shouldn't have line breaks in their titles.
[email protected]0491ff72011-12-30 00:45:59575 new_node->SetTitle(title);
[email protected]037db002009-10-19 20:06:08576 new_node->set_type(BookmarkNode::FOLDER);
[email protected]eb59ad12014-04-24 00:05:08577 if (meta_info)
578 new_node->SetMetaInfoMap(*meta_info);
initial.commit09911bf2008-07-26 23:55:29579
[email protected]a42ec64a2012-12-20 17:04:24580 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29581}
582
[email protected]e64e9012010-01-11 23:10:55583const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
584 int index,
[email protected]96920152013-12-04 21:00:16585 const base::string16& title,
[email protected]e64e9012010-01-11 23:10:55586 const GURL& url) {
[email protected]eb59ad12014-04-24 00:05:08587 return AddURLWithCreationTimeAndMetaInfo(
588 parent,
589 index,
590 base::CollapseWhitespace(title, false),
591 url,
592 Time::Now(),
593 NULL);
initial.commit09911bf2008-07-26 23:55:29594}
595
[email protected]eb59ad12014-04-24 00:05:08596const BookmarkNode* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
[email protected]e64e9012010-01-11 23:10:55597 const BookmarkNode* parent,
598 int index,
[email protected]96920152013-12-04 21:00:16599 const base::string16& title,
[email protected]e64e9012010-01-11 23:10:55600 const GURL& url,
[email protected]eb59ad12014-04-24 00:05:08601 const Time& creation_time,
602 const BookmarkNode::MetaInfoMap* meta_info) {
[email protected]6b4d64c2011-07-29 21:33:24603 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
[email protected]f25387b2008-08-21 15:20:33604 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29605 NOTREACHED();
606 return NULL;
607 }
initial.commit09911bf2008-07-26 23:55:29608
[email protected]b61445c2012-10-27 00:11:42609 // Syncing may result in dates newer than the last modified date.
[email protected]8ad613b2012-10-12 21:28:45610 if (creation_time > parent->date_folder_modified())
611 SetDateFolderModified(parent, creation_time);
initial.commit09911bf2008-07-26 23:55:29612
[email protected]ea2e5aa52009-05-20 18:01:28613 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
[email protected]0491ff72011-12-30 00:45:59614 new_node->SetTitle(title);
[email protected]b3c33d462009-06-26 22:29:20615 new_node->set_date_added(creation_time);
[email protected]037db002009-10-19 20:06:08616 new_node->set_type(BookmarkNode::URL);
[email protected]eb59ad12014-04-24 00:05:08617 if (meta_info)
618 new_node->SetMetaInfoMap(*meta_info);
initial.commit09911bf2008-07-26 23:55:29619
[email protected]776e7492008-10-23 16:47:41620 {
621 // Only hold the lock for the duration of the insert.
[email protected]20305ec2011-01-21 04:55:52622 base::AutoLock url_lock(url_lock_);
[email protected]776e7492008-10-23 16:47:41623 nodes_ordered_by_url_set_.insert(new_node);
624 }
initial.commit09911bf2008-07-26 23:55:29625
[email protected]a42ec64a2012-12-20 17:04:24626 return AddNode(AsMutable(parent), index, new_node);
initial.commit09911bf2008-07-26 23:55:29627}
628
[email protected]b3c33d462009-06-26 22:29:20629void BookmarkModel::SortChildren(const BookmarkNode* parent) {
[email protected]23e39692014-06-06 21:10:13630 DCHECK(client_->CanBeEditedByUser(parent));
[email protected]043a76d2014-06-05 16:36:24631
[email protected]6b4d64c2011-07-29 21:33:24632 if (!parent || !parent->is_folder() || is_root_node(parent) ||
[email protected]9c1a75a2011-03-10 02:38:12633 parent->child_count() <= 1) {
[email protected]e2f86d92009-02-25 00:22:01634 return;
635 }
636
[email protected]472f95e2013-06-10 16:49:18637 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
638 OnWillReorderBookmarkNode(this, parent));
639
[email protected]ef762642009-03-05 16:30:25640 UErrorCode error = U_ZERO_ERROR;
[email protected]94d914522013-04-02 19:49:47641 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
[email protected]ef762642009-03-05 16:30:25642 if (U_FAILURE(error))
643 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20644 BookmarkNode* mutable_parent = AsMutable(parent);
645 std::sort(mutable_parent->children().begin(),
646 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25647 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01648
[email protected]997a0362009-03-12 03:10:51649 if (store_.get())
650 store_->ScheduleSave();
651
[email protected]e2f86d92009-02-25 00:22:01652 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
653 BookmarkNodeChildrenReordered(this, parent));
654}
655
[email protected]472f95e2013-06-10 16:49:18656void BookmarkModel::ReorderChildren(
657 const BookmarkNode* parent,
[email protected]257d5082013-08-06 07:46:43658 const std::vector<const BookmarkNode*>& ordered_nodes) {
[email protected]23e39692014-06-06 21:10:13659 DCHECK(client_->CanBeEditedByUser(parent));
[email protected]043a76d2014-06-05 16:36:24660
[email protected]472f95e2013-06-10 16:49:18661 // Ensure that all children in |parent| are in |ordered_nodes|.
662 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
663 for (size_t i = 0; i < ordered_nodes.size(); ++i)
664 DCHECK_EQ(parent, ordered_nodes[i]->parent());
665
666 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
667 OnWillReorderBookmarkNode(this, parent));
668
[email protected]257d5082013-08-06 07:46:43669 AsMutable(parent)->SetChildren(
670 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
[email protected]472f95e2013-06-10 16:49:18671
672 if (store_.get())
673 store_->ScheduleSave();
674
675 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
676 BookmarkNodeChildrenReordered(this, parent));
677}
678
[email protected]c6a7a3d2011-03-12 01:04:30679void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
680 const Time time) {
[email protected]eea8fd5532009-12-16 00:08:10681 DCHECK(parent);
[email protected]edb63cc2011-03-11 02:00:41682 AsMutable(parent)->set_date_folder_modified(time);
[email protected]eea8fd5532009-12-16 00:08:10683
684 if (store_.get())
685 store_->ScheduleSave();
686}
687
[email protected]c6a7a3d2011-03-12 01:04:30688void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
689 SetDateFolderModified(node, Time());
initial.commit09911bf2008-07-26 23:55:29690}
691
[email protected]b3a84892014-04-23 04:28:07692void BookmarkModel::GetBookmarksMatching(
[email protected]96920152013-12-04 21:00:16693 const base::string16& text,
[email protected]e64e9012010-01-11 23:10:55694 size_t max_count,
[email protected]b3a84892014-04-23 04:28:07695 std::vector<BookmarkMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28696 if (!loaded_)
697 return;
698
[email protected]b3a84892014-04-23 04:28:07699 index_->GetBookmarksMatching(text, max_count, matches);
[email protected]85d911c2009-05-19 03:59:42700}
701
[email protected]9876bb1c2008-12-16 20:42:25702void BookmarkModel::ClearStore() {
[email protected]265e88e2014-07-07 20:45:19703 store_.reset();
[email protected]9876bb1c2008-12-16 20:42:25704}
705
[email protected]bc770a032011-12-12 17:35:30706void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
707 bool value) {
[email protected]043a76d2014-06-05 16:36:24708 BookmarkPermanentNode* node = AsMutable(PermanentNode(type));
709 node->set_visible(value || client_->IsPermanentNodeVisible(node));
[email protected]996dbe82014-05-12 12:32:18710}
711
712const BookmarkPermanentNode* BookmarkModel::PermanentNode(
713 BookmarkNode::Type type) {
[email protected]1da5f712011-12-06 05:52:26714 DCHECK(loaded_);
[email protected]bc770a032011-12-12 17:35:30715 switch (type) {
716 case BookmarkNode::BOOKMARK_BAR:
[email protected]996dbe82014-05-12 12:32:18717 return bookmark_bar_node_;
[email protected]bc770a032011-12-12 17:35:30718 case BookmarkNode::OTHER_NODE:
[email protected]996dbe82014-05-12 12:32:18719 return other_node_;
[email protected]bc770a032011-12-12 17:35:30720 case BookmarkNode::MOBILE:
[email protected]996dbe82014-05-12 12:32:18721 return mobile_node_;
[email protected]bc770a032011-12-12 17:35:30722 default:
723 NOTREACHED();
[email protected]996dbe82014-05-12 12:32:18724 return NULL;
[email protected]bc770a032011-12-12 17:35:30725 }
[email protected]1da5f712011-12-06 05:52:26726}
727
[email protected]dddc1b42008-10-09 20:56:59728bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28729 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59730 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
731 nodes_ordered_by_url_set_.end());
732}
733
[email protected]d8e41ed2008-09-11 15:22:32734void BookmarkModel::RemoveNode(BookmarkNode* node,
735 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20736 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33737 NOTREACHED();
738 return;
739 }
740
[email protected]323dbf72013-03-30 17:08:33741 url_lock_.AssertAcquired();
[email protected]0890e60e2011-06-27 14:55:21742 if (node->is_url()) {
[email protected]5db00bca2013-12-23 00:43:09743 RemoveNodeFromURLSet(node);
[email protected]5d4077542011-07-21 20:24:07744 removed_urls->insert(node->url());
[email protected]01eec882009-05-22 18:13:28745 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29746 }
747
[email protected]abc2f262011-03-15 21:15:44748 CancelPendingFaviconLoadRequests(node);
initial.commit09911bf2008-07-26 23:55:29749
750 // Recurse through children.
[email protected]9c1a75a2011-03-10 02:38:12751 for (int i = node->child_count() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33752 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29753}
754
[email protected]6a848b52014-04-26 22:06:54755void BookmarkModel::DoneLoading(scoped_ptr<BookmarkLoadDetails> details) {
756 DCHECK(details);
[email protected]01eec882009-05-22 18:13:28757 if (loaded_) {
758 // We should only ever be loaded once.
759 NOTREACHED();
760 return;
761 }
762
[email protected]01eec882009-05-22 18:13:28763 next_node_id_ = details->max_id();
[email protected]367d7072009-07-13 23:27:13764 if (details->computed_checksum() != details->stored_checksum() ||
765 details->ids_reassigned()) {
766 // If bookmarks file changed externally, the IDs may have changed
767 // externally. In that case, the decoder may have reassigned IDs to make
768 // them unique. So when the file has changed externally, we should save the
769 // bookmarks file to persist new IDs.
770 if (store_.get())
771 store_->ScheduleSave();
772 }
[email protected]d22d8732010-05-04 19:24:42773 bookmark_bar_node_ = details->release_bb_node();
774 other_node_ = details->release_other_folder_node();
[email protected]37bc9132011-12-01 22:29:29775 mobile_node_ = details->release_mobile_folder_node();
[email protected]d22d8732010-05-04 19:24:42776 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28777
[email protected]043a76d2014-06-05 16:36:24778 // Get any extra nodes and take ownership of them at the |root_|.
779 std::vector<BookmarkPermanentNode*> extra_nodes;
780 details->release_extra_nodes(&extra_nodes);
781
[email protected]82f4655a2011-10-18 13:05:43782 // WARNING: order is important here, various places assume the order is
[email protected]996dbe82014-05-12 12:32:18783 // constant (but can vary between embedders with the initial visibility
784 // of permanent nodes).
[email protected]043a76d2014-06-05 16:36:24785 std::vector<BookmarkPermanentNode*> root_children;
786 root_children.push_back(bookmark_bar_node_);
787 root_children.push_back(other_node_);
788 root_children.push_back(mobile_node_);
789 for (size_t i = 0; i < extra_nodes.size(); ++i)
790 root_children.push_back(extra_nodes[i]);
791 std::stable_sort(root_children.begin(),
792 root_children.end(),
[email protected]996dbe82014-05-12 12:32:18793 VisibilityComparator(client_));
[email protected]043a76d2014-06-05 16:36:24794 for (size_t i = 0; i < root_children.size(); ++i)
[email protected]996dbe82014-05-12 12:32:18795 root_.Add(root_children[i], static_cast<int>(i));
[email protected]6c1164042009-05-08 14:41:08796
[email protected]39e113452013-11-26 00:43:06797 root_.SetMetaInfoMap(details->model_meta_info_map());
798 root_.set_sync_transaction_version(details->model_sync_transaction_version());
[email protected]1858410f2012-10-26 05:06:45799
[email protected]90ef13132008-08-27 03:27:46800 {
[email protected]20305ec2011-01-21 04:55:52801 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46802 // Update nodes_ordered_by_url_set_ from the nodes.
803 PopulateNodesByURL(&root_);
804 }
[email protected]f25387b2008-08-21 15:20:33805
initial.commit09911bf2008-07-26 23:55:29806 loaded_ = true;
807
[email protected]cbcd6412009-03-09 22:31:39808 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46809
[email protected]f25387b2008-08-21 15:20:33810 // Notify our direct observers.
[email protected]c58c5ea2011-07-13 21:43:16811 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]d1bc7af2013-12-26 05:45:13812 BookmarkModelLoaded(this, details->ids_reassigned()));
initial.commit09911bf2008-07-26 23:55:29813}
814
[email protected]d8e41ed2008-09-11 15:22:32815void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
816 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29817
[email protected]323dbf72013-03-30 17:08:33818 const BookmarkNode* parent = node->parent();
initial.commit09911bf2008-07-26 23:55:29819 DCHECK(parent);
[email protected]368f3a72011-03-08 17:17:48820 int index = parent->GetIndexOf(node.get());
[email protected]323dbf72013-03-30 17:08:33821
[email protected]472f95e2013-06-10 16:49:18822 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
823 OnWillRemoveBookmarks(this, parent, index, node.get()));
824
[email protected]9109f8a12013-06-12 18:07:48825 std::set<GURL> removed_urls;
[email protected]90ef13132008-08-27 03:27:46826 {
[email protected]20305ec2011-01-21 04:55:52827 base::AutoLock url_lock(url_lock_);
[email protected]9109f8a12013-06-12 18:07:48828 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
[email protected]90ef13132008-08-27 03:27:46829 }
initial.commit09911bf2008-07-26 23:55:29830
[email protected]f25387b2008-08-21 15:20:33831 if (store_.get())
832 store_->ScheduleSave();
833
[email protected]ba8b6ac2014-05-02 01:07:34834 FOR_EACH_OBSERVER(
835 BookmarkModelObserver,
836 observers_,
837 BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls));
[email protected]323dbf72013-03-30 17:08:33838}
[email protected]f25387b2008-08-21 15:20:33839
[email protected]5db00bca2013-12-23 00:43:09840void BookmarkModel::RemoveNodeFromURLSet(BookmarkNode* node) {
841 // NOTE: this is called in such a way that url_lock_ is already held. As
842 // such, this doesn't explicitly grab the lock.
843 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
844 DCHECK(i != nodes_ordered_by_url_set_.end());
845 // i points to the first node with the URL, advance until we find the
846 // node we're removing.
847 while (*i != node)
848 ++i;
849 nodes_ordered_by_url_set_.erase(i);
850}
851
[email protected]323dbf72013-03-30 17:08:33852void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
853 std::set<GURL>* removed_urls) {
854 // NOTE: this method should be always called with |url_lock_| held.
855 // This method does not explicitly acquires a lock.
856 url_lock_.AssertAcquired();
857 DCHECK(removed_urls);
858 BookmarkNode* parent = AsMutable(node->parent());
859 DCHECK(parent);
860 parent->Remove(node);
861 RemoveNode(node, removed_urls);
862 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
863 // allow duplicates we need to remove any entries that are still bookmarked.
864 for (std::set<GURL>::iterator i = removed_urls->begin();
865 i != removed_urls->end();) {
866 if (IsBookmarkedNoLock(*i)) {
867 // When we erase the iterator pointing at the erasee is
868 // invalidated, so using i++ here within the "erase" call is
869 // important as it advances the iterator before passing the
870 // old value through to erase.
871 removed_urls->erase(i++);
872 } else {
873 ++i;
874 }
875 }
876}
877
[email protected]d8e41ed2008-09-11 15:22:32878BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
879 int index,
[email protected]a42ec64a2012-12-20 17:04:24880 BookmarkNode* node) {
[email protected]224e1d12014-06-17 03:03:05881 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
882 OnWillAddBookmarkNode(this, node));
883
[email protected]a0dd6a32011-03-18 17:31:37884 parent->Add(node, index);
initial.commit09911bf2008-07-26 23:55:29885
[email protected]f25387b2008-08-21 15:20:33886 if (store_.get())
887 store_->ScheduleSave();
888
[email protected]d8e41ed2008-09-11 15:22:32889 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29890 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33891
[email protected]01eec882009-05-22 18:13:28892 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42893
initial.commit09911bf2008-07-26 23:55:29894 return node;
895}
896
[email protected]b3c33d462009-06-26 22:29:20897bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32898 int index,
899 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41900 return (parent && parent->is_folder() &&
[email protected]9c1a75a2011-03-10 02:38:12901 (index >= 0 && (index < parent->child_count() ||
902 (allow_end && index == parent->child_count()))));
[email protected]bd1b96702009-07-08 21:54:14903}
[email protected]f25387b2008-08-21 15:20:33904
[email protected]bc770a032011-12-12 17:35:30905BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
906 BookmarkNode::Type type) {
[email protected]e1f76c62011-06-30 20:15:39907 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
908 type == BookmarkNode::OTHER_NODE ||
[email protected]37bc9132011-12-01 22:29:29909 type == BookmarkNode::MOBILE);
[email protected]bc770a032011-12-12 17:35:30910 BookmarkPermanentNode* node =
911 new BookmarkPermanentNode(generate_next_node_id());
[email protected]043a76d2014-06-05 16:36:24912 node->set_type(type);
913 node->set_visible(client_->IsPermanentNodeVisible(node));
[email protected]bc770a032011-12-12 17:35:30914
915 int title_id;
916 switch (type) {
917 case BookmarkNode::BOOKMARK_BAR:
918 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
919 break;
920 case BookmarkNode::OTHER_NODE:
921 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
922 break;
923 case BookmarkNode::MOBILE:
924 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
925 break;
926 default:
[email protected]97fdd162011-12-03 20:50:12927 NOTREACHED();
[email protected]bc770a032011-12-12 17:35:30928 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
929 break;
[email protected]7caca8a22010-08-21 18:25:31930 }
[email protected]0491ff72011-12-30 00:45:59931 node->SetTitle(l10n_util::GetStringUTF16(title_id));
initial.commit09911bf2008-07-26 23:55:29932 return node;
933}
934
[email protected]abc2f262011-03-15 21:15:44935void BookmarkModel::OnFaviconDataAvailable(
[email protected]0ea3db52012-12-07 01:32:01936 BookmarkNode* node,
[email protected]504fca82014-05-07 22:48:08937 favicon_base::IconType icon_type,
[email protected]7627e0b42014-04-17 17:20:53938 const favicon_base::FaviconImageResult& image_result) {
[email protected]4167c3a2008-08-21 18:12:20939 DCHECK(node);
[email protected]e95b717f2014-02-06 13:47:13940 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
[email protected]bcc38612012-10-23 01:10:27941 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
[email protected]65baa222012-08-30 15:43:51942 if (!image_result.image.IsEmpty()) {
[email protected]504fca82014-05-07 22:48:08943 node->set_favicon_type(icon_type);
[email protected]65baa222012-08-30 15:43:51944 node->set_favicon(image_result.image);
[email protected]2ad0e872012-11-30 01:24:46945 node->set_icon_url(image_result.icon_url);
[email protected]65baa222012-08-30 15:43:51946 FaviconLoaded(node);
[email protected]504fca82014-05-07 22:48:08947 } else if (icon_type == favicon_base::TOUCH_ICON) {
948 // Couldn't load the touch icon, fallback to the regular favicon.
949 DCHECK(client_->PreferTouchIcon());
950 LoadFavicon(node, favicon_base::FAVICON);
initial.commit09911bf2008-07-26 23:55:29951 }
952}
953
[email protected]504fca82014-05-07 22:48:08954void BookmarkModel::LoadFavicon(
955 BookmarkNode* node,
956 favicon_base::IconType icon_type) {
[email protected]0890e60e2011-06-27 14:55:21957 if (node->is_folder())
initial.commit09911bf2008-07-26 23:55:29958 return;
959
[email protected]5d4077542011-07-21 20:24:07960 DCHECK(node->url().is_valid());
[email protected]504fca82014-05-07 22:48:08961 node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
[email protected]25244a932014-07-12 23:00:24962 base::CancelableTaskTracker::TaskId taskId =
963 client_->GetFaviconImageForPageURL(
964 node->url(),
965 icon_type,
966 base::Bind(
967 &BookmarkModel::OnFaviconDataAvailable,
968 base::Unretained(this),
969 node,
970 icon_type),
971 &cancelable_task_tracker_);
[email protected]6a848b52014-04-26 22:06:54972 if (taskId != base::CancelableTaskTracker::kBadTaskId)
973 node->set_favicon_load_task_id(taskId);
initial.commit09911bf2008-07-26 23:55:29974}
975
[email protected]5b5c9b7f32011-07-21 01:07:18976void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
977 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
978 BookmarkNodeFaviconChanged(this, node));
979}
980
[email protected]abc2f262011-03-15 21:15:44981void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
[email protected]e95b717f2014-02-06 13:47:13982 if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) {
[email protected]0ea3db52012-12-07 01:32:01983 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
[email protected]e95b717f2014-02-06 13:47:13984 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
initial.commit09911bf2008-07-26 23:55:29985 }
986}
987
[email protected]d8e41ed2008-09-11 15:22:32988void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:46989 // NOTE: this is called with url_lock_ already held. As such, this doesn't
990 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:33991 if (node->is_url())
992 nodes_ordered_by_url_set_.insert(node);
[email protected]9c1a75a2011-03-10 02:38:12993 for (int i = 0; i < node->child_count(); ++i)
[email protected]f25387b2008-08-21 15:20:33994 PopulateNodesByURL(node->GetChild(i));
995}
[email protected]4d89f382009-05-12 06:56:49996
[email protected]367d7072009-07-13 23:27:13997int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:49998 return next_node_id_++;
999}
[email protected]01eec882009-05-22 18:13:281000
[email protected]6a848b52014-04-26 22:06:541001scoped_ptr<BookmarkLoadDetails> BookmarkModel::CreateLoadDetails(
1002 const std::string& accept_languages) {
[email protected]bc770a032011-12-12 17:35:301003 BookmarkPermanentNode* bb_node =
1004 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1005 BookmarkPermanentNode* other_node =
1006 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1007 BookmarkPermanentNode* mobile_node =
1008 CreatePermanentNode(BookmarkNode::MOBILE);
[email protected]6a848b52014-04-26 22:06:541009 return scoped_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails(
1010 bb_node,
1011 other_node,
1012 mobile_node,
[email protected]043a76d2014-06-05 16:36:241013 client_->GetLoadExtraNodesCallback(),
[email protected]6a848b52014-04-26 22:06:541014 new BookmarkIndex(client_, index_urls_, accept_languages),
1015 next_node_id_));
[email protected]01eec882009-05-22 18:13:281016}