blob: d4bdb2c12c1274a30f01d48b7b2da2747a7463ef [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]3b63f8f42011-03-28 01:54:1512#include "base/memory/scoped_vector.h"
[email protected]aee236542011-12-01 04:34:0313#include "base/string_util.h"
[email protected]fc3fc452009-02-10 03:25:4014#include "build/build_config.h"
[email protected]c58c5ea2011-07-13 21:43:1615#include "chrome/browser/bookmarks/bookmark_expanded_state_tracker.h"
[email protected]01eec882009-05-22 18:13:2816#include "chrome/browser/bookmarks/bookmark_index.h"
[email protected]125b234182011-07-08 19:54:4117#include "chrome/browser/bookmarks/bookmark_model_observer.h"
[email protected]68de8b72008-09-09 23:08:1318#include "chrome/browser/bookmarks/bookmark_storage.h"
[email protected]368f3a72011-03-08 17:17:4819#include "chrome/browser/bookmarks/bookmark_utils.h"
[email protected]f3d2b312012-08-23 22:27:5920#include "chrome/browser/favicon/favicon_service_factory.h"
[email protected]9c92d192009-12-02 08:03:1621#include "chrome/browser/history/history_notifications.h"
[email protected]9aac66862012-06-19 19:44:3122#include "chrome/browser/history/history_service_factory.h"
[email protected]8ecad5e2010-12-02 21:18:3323#include "chrome/browser/profiles/profile.h"
[email protected]432115822011-07-10 15:52:2724#include "chrome/common/chrome_notification_types.h"
[email protected]bc61e722012-09-07 03:48:0125#include "content/public/browser/content_browser_client.h"
[email protected]ad50def52011-10-19 23:17:0726#include "content/public/browser/notification_service.h"
[email protected]34ac8f32009-02-22 23:03:2727#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1728#include "ui/base/l10n/l10n_util.h"
29#include "ui/base/l10n/l10n_util_collator.h"
[email protected]65baa222012-08-30 15:43:5130#include "ui/gfx/favicon_size.h"
[email protected]6a4e5a02012-06-26 19:47:4831#include "ui/gfx/image/image_util.h"
initial.commit09911bf2008-07-26 23:55:2932
[email protected]e1acf6f2008-10-27 20:43:3333using base::Time;
34
[email protected]b3c33d462009-06-26 22:29:2035namespace {
36
37// Helper to get a mutable bookmark node.
[email protected]7b084b02011-07-08 16:06:4838BookmarkNode* AsMutable(const BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:2039 return const_cast<BookmarkNode*>(node);
40}
41
[email protected]f280db82012-10-20 04:21:0842// Whitespace characters to strip from bookmark titles.
43const char16 kInvalidChars[] = {
44 '\n', '\r', '\t',
45 0x2028, // Line separator
46 0x2029, // Paragraph separator
47 0
48};
49
[email protected]e1f76c62011-06-30 20:15:3950} // namespace
[email protected]b3c33d462009-06-26 22:29:2051
[email protected]d8e41ed2008-09-11 15:22:3252// BookmarkNode ---------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2953
[email protected]ea2e5aa52009-05-20 18:01:2854BookmarkNode::BookmarkNode(const GURL& url)
[email protected]814a2d32009-04-30 23:09:0155 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2856 Initialize(0);
[email protected]814a2d32009-04-30 23:09:0157}
58
[email protected]367d7072009-07-13 23:27:1359BookmarkNode::BookmarkNode(int64 id, const GURL& url)
[email protected]2c685cc22009-08-28 00:17:4460 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2861 Initialize(id);
[email protected]814a2d32009-04-30 23:09:0162}
63
[email protected]a0835ac2010-09-13 19:40:0864BookmarkNode::~BookmarkNode() {
65}
66
[email protected]0491ff72011-12-30 00:45:5967void BookmarkNode::SetTitle(const string16& title) {
[email protected]f280db82012-10-20 04:21:0868 // Replace newlines and other problematic whitespace characters in
69 // folder/bookmark names with spaces.
70 string16 trimmed_title;
71 ReplaceChars(title, kInvalidChars, ASCIIToUTF16(" "), &trimmed_title);
72 ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
[email protected]0491ff72011-12-30 00:45:5973}
74
[email protected]97fdd162011-12-03 20:50:1275bool BookmarkNode::IsVisible() const {
76 return true;
77}
78
[email protected]7b084b02011-07-08 16:06:4879void BookmarkNode::Initialize(int64 id) {
80 id_ = id;
81 type_ = url_.is_empty() ? FOLDER : URL;
82 date_added_ = Time::Now();
[email protected]bcc38612012-10-23 01:10:2783 favicon_state_ = INVALID_FAVICON;
[email protected]7b084b02011-07-08 16:06:4884 favicon_load_handle_ = 0;
85}
86
[email protected]5b5c9b7f32011-07-21 01:07:1887void BookmarkNode::InvalidateFavicon() {
[email protected]6a4e5a02012-06-26 19:47:4888 favicon_ = gfx::Image();
[email protected]bcc38612012-10-23 01:10:2789 favicon_state_ = INVALID_FAVICON;
[email protected]5b5c9b7f32011-07-21 01:07:1890}
91
[email protected]ef762642009-03-05 16:30:2592namespace {
93
94// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:4495// bookmarks.
[email protected]b3c33d462009-06-26 22:29:2096class SortComparator : public std::binary_function<const BookmarkNode*,
97 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:2598 bool> {
99 public:
[email protected]a48f87d2012-10-09 18:06:33100 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
[email protected]ef762642009-03-05 16:30:25101
[email protected]a48f87d2012-10-09 18:06:33102 // Returns true if |n1| preceeds |n2|.
103 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:08104 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:25105 // Types are the same, compare the names.
106 if (!collator_)
[email protected]440b37b22010-08-30 05:31:40107 return n1->GetTitle() < n2->GetTitle();
[email protected]3abebda2011-01-07 20:17:15108 return l10n_util::CompareString16WithCollator(
109 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
[email protected]ef762642009-03-05 16:30:25110 }
111 // Types differ, sort such that folders come first.
112 return n1->is_folder();
113 }
114
115 private:
[email protected]b5b2385a2009-08-18 05:12:29116 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:25117};
118
[email protected]bc770a032011-12-12 17:35:30119} // namespace
[email protected]97fdd162011-12-03 20:50:12120
[email protected]bc770a032011-12-12 17:35:30121// BookmarkPermanentNode -------------------------------------------------------
[email protected]97fdd162011-12-03 20:50:12122
[email protected]bc770a032011-12-12 17:35:30123BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
[email protected]97fdd162011-12-03 20:50:12124 : BookmarkNode(id, GURL()),
[email protected]bc770a032011-12-12 17:35:30125 visible_(true) {
[email protected]97fdd162011-12-03 20:50:12126}
127
[email protected]bc770a032011-12-12 17:35:30128BookmarkPermanentNode::~BookmarkPermanentNode() {
[email protected]97fdd162011-12-03 20:50:12129}
130
[email protected]bc770a032011-12-12 17:35:30131bool BookmarkPermanentNode::IsVisible() const {
[email protected]97fdd162011-12-03 20:50:12132 return visible_ || !empty();
133}
134
[email protected]97fdd162011-12-03 20:50:12135// BookmarkModel --------------------------------------------------------------
136
[email protected]d8e41ed2008-09-11 15:22:32137BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:29138 : profile_(profile),
139 loaded_(false),
[email protected]fc7c36a22009-05-28 20:23:33140 file_changed_(false),
[email protected]ea2e5aa52009-05-20 18:01:28141 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:29142 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:46143 other_node_(NULL),
[email protected]37bc9132011-12-01 22:29:29144 mobile_node_(NULL),
[email protected]4d89f382009-05-12 06:56:49145 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:06146 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]b68a8172012-02-17 00:25:18147 loaded_signal_(true, false),
148 extensive_changes_(0) {
[email protected]f25387b2008-08-21 15:20:33149 if (!profile_) {
150 // Profile is null during testing.
[email protected]01eec882009-05-22 18:13:28151 DoneLoading(CreateLoadDetails());
initial.commit09911bf2008-07-26 23:55:29152 }
initial.commit09911bf2008-07-26 23:55:29153}
154
[email protected]d8e41ed2008-09-11 15:22:32155BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:32156 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:51157 BookmarkModelBeingDeleted(this));
158
[email protected]f25387b2008-08-21 15:20:33159 if (store_) {
160 // The store maintains a reference back to us. We need to tell it we're gone
161 // so that it doesn't try and invoke a method back on us again.
162 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29163 }
164}
165
[email protected]f61f4782012-06-08 21:54:21166void BookmarkModel::Shutdown() {
[email protected]88e6a232011-09-14 14:53:20167 if (loaded_)
168 return;
169
[email protected]f61f4782012-06-08 21:54:21170 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
171 // details. It is also called when the BookmarkModel is deleted.
[email protected]88e6a232011-09-14 14:53:20172 loaded_signal_.Signal();
173}
174
[email protected]d8e41ed2008-09-11 15:22:32175void BookmarkModel::Load() {
[email protected]90ef13132008-08-27 03:27:46176 if (store_.get()) {
177 // If the store is non-null, it means Load was already invoked. Load should
178 // only be invoked once.
179 NOTREACHED();
180 return;
181 }
182
[email protected]c58c5ea2011-07-13 21:43:16183 expanded_state_tracker_.reset(new BookmarkExpandedStateTracker(
[email protected]25ee85302012-10-12 17:27:41184 profile_, this));
[email protected]c58c5ea2011-07-13 21:43:16185
[email protected]90ef13132008-08-27 03:27:46186 // Listen for changes to favicons so that we can update the favicon of the
187 // node appropriately.
[email protected]432115822011-07-10 15:52:27188 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
[email protected]6c2381d2011-10-19 02:52:53189 content::Source<Profile>(profile_));
[email protected]90ef13132008-08-27 03:27:46190
191 // Load the bookmarks. BookmarkStorage notifies us when done.
192 store_ = new BookmarkStorage(profile_, this);
[email protected]01eec882009-05-22 18:13:28193 store_->LoadBookmarks(CreateLoadDetails());
[email protected]90ef13132008-08-27 03:27:46194}
195
[email protected]61b8c782011-07-25 18:11:16196bool BookmarkModel::IsLoaded() const {
197 return loaded_;
198}
199
[email protected]b3c33d462009-06-26 22:29:20200const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
201 std::vector<const BookmarkNode*> nodes =
[email protected]9fcaee72011-03-21 21:53:44202 bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
[email protected]82f4655a2011-10-18 13:05:43203 DCHECK(!nodes.empty()); // This list is always padded with default folders.
204 return nodes[0];
initial.commit09911bf2008-07-26 23:55:29205}
206
[email protected]125b234182011-07-08 19:54:41207void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
208 observers_.AddObserver(observer);
209}
210
211void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
212 observers_.RemoveObserver(observer);
213}
214
[email protected]b68a8172012-02-17 00:25:18215void BookmarkModel::BeginExtensiveChanges() {
216 if (++extensive_changes_ == 1) {
217 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
218 ExtensiveBookmarkChangesBeginning(this));
219 }
[email protected]125b234182011-07-08 19:54:41220}
221
[email protected]b68a8172012-02-17 00:25:18222void BookmarkModel::EndExtensiveChanges() {
223 --extensive_changes_;
224 DCHECK_GE(extensive_changes_, 0);
225 if (extensive_changes_ == 0) {
226 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
227 ExtensiveBookmarkChangesEnded(this));
228 }
[email protected]125b234182011-07-08 19:54:41229}
230
[email protected]b3c33d462009-06-26 22:29:20231void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
[email protected]6b4d64c2011-07-29 21:33:24232 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
[email protected]f25387b2008-08-21 15:20:33233 NOTREACHED();
234 return;
235 }
[email protected]b3c33d462009-06-26 22:29:20236 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
[email protected]f25387b2008-08-21 15:20:33237}
238
[email protected]b3c33d462009-06-26 22:29:20239void BookmarkModel::Move(const BookmarkNode* node,
240 const BookmarkNode* new_parent,
[email protected]d8e41ed2008-09-11 15:22:32241 int index) {
[email protected]f25387b2008-08-21 15:20:33242 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24243 is_root_node(new_parent) || is_permanent_node(node)) {
initial.commit09911bf2008-07-26 23:55:29244 NOTREACHED();
245 return;
246 }
247
248 if (new_parent->HasAncestor(node)) {
249 // Can't make an ancestor of the node be a child of the node.
250 NOTREACHED();
251 return;
252 }
253
[email protected]c6a7a3d2011-03-12 01:04:30254 SetDateFolderModified(new_parent, Time::Now());
initial.commit09911bf2008-07-26 23:55:29255
[email protected]2d48ee842011-03-08 23:27:29256 const BookmarkNode* old_parent = node->parent();
[email protected]368f3a72011-03-08 17:17:48257 int old_index = old_parent->GetIndexOf(node);
initial.commit09911bf2008-07-26 23:55:29258
259 if (old_parent == new_parent &&
260 (index == old_index || index == old_index + 1)) {
261 // Node is already in this position, nothing to do.
262 return;
263 }
264
265 if (old_parent == new_parent && index > old_index)
266 index--;
[email protected]b3c33d462009-06-26 22:29:20267 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
[email protected]a0dd6a32011-03-18 17:31:37268 mutable_new_parent->Add(AsMutable(node), index);
initial.commit09911bf2008-07-26 23:55:29269
[email protected]f25387b2008-08-21 15:20:33270 if (store_.get())
271 store_->ScheduleSave();
272
[email protected]d8e41ed2008-09-11 15:22:32273 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29274 BookmarkNodeMoved(this, old_parent, old_index,
275 new_parent, index));
276}
277
[email protected]4e187ef652010-03-11 05:21:35278void BookmarkModel::Copy(const BookmarkNode* node,
279 const BookmarkNode* new_parent,
280 int index) {
281 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]6b4d64c2011-07-29 21:33:24282 is_root_node(new_parent) || is_permanent_node(node)) {
[email protected]4e187ef652010-03-11 05:21:35283 NOTREACHED();
284 return;
285 }
286
287 if (new_parent->HasAncestor(node)) {
288 // Can't make an ancestor of the node be a child of the node.
289 NOTREACHED();
290 return;
291 }
292
[email protected]c6a7a3d2011-03-12 01:04:30293 SetDateFolderModified(new_parent, Time::Now());
[email protected]14eb15af2010-11-20 01:03:26294 BookmarkNodeData drag_data_(node);
295 std::vector<BookmarkNodeData::Element> elements(drag_data_.elements);
[email protected]b1864502010-11-13 00:55:51296 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
[email protected]1a566fae2010-04-16 01:05:06297 // don't need to send notifications here.
[email protected]b1864502010-11-13 00:55:51298 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index);
[email protected]4e187ef652010-03-11 05:21:35299
300 if (store_.get())
301 store_->ScheduleSave();
[email protected]4e187ef652010-03-11 05:21:35302}
303
[email protected]6a4e5a02012-06-26 19:47:48304const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
[email protected]ea2e5aa52009-05-20 18:01:28305 DCHECK(node);
[email protected]bcc38612012-10-23 01:10:27306 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
[email protected]b3c33d462009-06-26 22:29:20307 BookmarkNode* mutable_node = AsMutable(node);
[email protected]bcc38612012-10-23 01:10:27308 mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
[email protected]abc2f262011-03-15 21:15:44309 LoadFavicon(mutable_node);
[email protected]ea2e5aa52009-05-20 18:01:28310 }
311 return node->favicon();
312}
313
[email protected]e64e9012010-01-11 23:10:55314void BookmarkModel::SetTitle(const BookmarkNode* node, const string16& title) {
[email protected]f25387b2008-08-21 15:20:33315 if (!node) {
316 NOTREACHED();
317 return;
318 }
[email protected]0491ff72011-12-30 00:45:59319 if (node->GetTitle() == title)
initial.commit09911bf2008-07-26 23:55:29320 return;
[email protected]f25387b2008-08-21 15:20:33321
[email protected]6892e2e2011-05-26 22:07:17322 if (is_permanent_node(node)) {
[email protected]baf4f922009-10-19 16:44:07323 NOTREACHED();
324 return;
325 }
326
[email protected]85d911c2009-05-19 03:59:42327 // The title index doesn't support changing the title, instead we remove then
328 // add it back.
[email protected]01eec882009-05-22 18:13:28329 index_->Remove(node);
[email protected]0491ff72011-12-30 00:45:59330 AsMutable(node)->SetTitle(title);
[email protected]01eec882009-05-22 18:13:28331 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42332
[email protected]f25387b2008-08-21 15:20:33333 if (store_.get())
334 store_->ScheduleSave();
335
[email protected]d8e41ed2008-09-11 15:22:32336 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29337 BookmarkNodeChanged(this, node));
338}
339
[email protected]e5486602010-02-09 21:27:55340void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
341 if (!node) {
342 NOTREACHED();
343 return;
344 }
345
346 // We cannot change the URL of a folder.
347 if (node->is_folder()) {
348 NOTREACHED();
349 return;
350 }
351
[email protected]5d4077542011-07-21 20:24:07352 if (node->url() == url)
[email protected]e5486602010-02-09 21:27:55353 return;
354
[email protected]5b5c9b7f32011-07-21 01:07:18355 BookmarkNode* mutable_node = AsMutable(node);
356 mutable_node->InvalidateFavicon();
357 CancelPendingFaviconLoadRequests(mutable_node);
[email protected]e5486602010-02-09 21:27:55358
359 {
[email protected]20305ec2011-01-21 04:55:52360 base::AutoLock url_lock(url_lock_);
[email protected]e5486602010-02-09 21:27:55361 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
[email protected]5b5c9b7f32011-07-21 01:07:18362 mutable_node);
[email protected]e5486602010-02-09 21:27:55363 DCHECK(i != nodes_ordered_by_url_set_.end());
364 // i points to the first node with the URL, advance until we find the
365 // node we're removing.
366 while (*i != node)
367 ++i;
368 nodes_ordered_by_url_set_.erase(i);
369
[email protected]5d4077542011-07-21 20:24:07370 mutable_node->set_url(url);
[email protected]5b5c9b7f32011-07-21 01:07:18371 nodes_ordered_by_url_set_.insert(mutable_node);
[email protected]e5486602010-02-09 21:27:55372 }
373
374 if (store_.get())
375 store_->ScheduleSave();
376
377 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
378 BookmarkNodeChanged(this, node));
379}
380
[email protected]848cd05e2008-09-19 18:33:48381void BookmarkModel::GetNodesByURL(const GURL& url,
[email protected]b3c33d462009-06-26 22:29:20382 std::vector<const BookmarkNode*>* nodes) {
[email protected]20305ec2011-01-21 04:55:52383 base::AutoLock url_lock(url_lock_);
[email protected]ea2e5aa52009-05-20 18:01:28384 BookmarkNode tmp_node(url);
initial.commit09911bf2008-07-26 23:55:29385 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]5d4077542011-07-21 20:24:07386 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
[email protected]848cd05e2008-09-19 18:33:48387 nodes->push_back(*i);
388 ++i;
389 }
390}
391
[email protected]b3c33d462009-06-26 22:29:20392const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
393 const GURL& url) {
394 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48395 GetNodesByURL(url, &nodes);
396 if (nodes.empty())
397 return NULL;
398
[email protected]9333f182008-12-09 17:34:17399 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]848cd05e2008-09-19 18:33:48400 return nodes.front();
initial.commit09911bf2008-07-26 23:55:29401}
402
[email protected]cf8e8172011-07-23 00:46:24403bool BookmarkModel::HasBookmarks() {
404 base::AutoLock url_lock(url_lock_);
405 return !nodes_ordered_by_url_set_.empty();
406}
407
408bool BookmarkModel::IsBookmarked(const GURL& url) {
409 base::AutoLock url_lock(url_lock_);
410 return IsBookmarkedNoLock(url);
411}
412
[email protected]0f7bee52012-08-06 20:04:17413void BookmarkModel::GetBookmarks(
414 std::vector<BookmarkService::URLAndTitle>* bookmarks) {
[email protected]20305ec2011-01-21 04:55:52415 base::AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48416 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46417 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
418 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]5d4077542011-07-21 20:24:07419 const GURL* url = &((*i)->url());
[email protected]848cd05e2008-09-19 18:33:48420 // Only add unique URLs.
[email protected]0f7bee52012-08-06 20:04:17421 if (!last_url || *url != *last_url) {
422 BookmarkService::URLAndTitle bookmark;
423 bookmark.url = *url;
424 bookmark.title = (*i)->GetTitle();
425 bookmarks->push_back(bookmark);
426 }
[email protected]848cd05e2008-09-19 18:33:48427 last_url = url;
[email protected]90ef13132008-08-27 03:27:46428 }
429}
430
[email protected]cf8e8172011-07-23 00:46:24431void BookmarkModel::BlockTillLoaded() {
432 loaded_signal_.Wait();
[email protected]848cd05e2008-09-19 18:33:48433}
434
[email protected]66bb7dc2011-10-27 15:29:23435const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
initial.commit09911bf2008-07-26 23:55:29436 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33437 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29438}
439
[email protected]39703292011-03-18 17:03:40440const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
441 int index,
442 const string16& title) {
[email protected]6b4d64c2011-07-29 21:33:24443 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29444 // Can't add to the root.
445 NOTREACHED();
446 return NULL;
447 }
448
[email protected]5b5c9b7f32011-07-21 01:07:18449 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
[email protected]edb63cc2011-03-11 02:00:41450 new_node->set_date_folder_modified(Time::Now());
[email protected]aee236542011-12-01 04:34:03451 // Folders shouldn't have line breaks in their titles.
[email protected]0491ff72011-12-30 00:45:59452 new_node->SetTitle(title);
[email protected]037db002009-10-19 20:06:08453 new_node->set_type(BookmarkNode::FOLDER);
initial.commit09911bf2008-07-26 23:55:29454
[email protected]b3c33d462009-06-26 22:29:20455 return AddNode(AsMutable(parent), index, new_node, false);
initial.commit09911bf2008-07-26 23:55:29456}
457
[email protected]e64e9012010-01-11 23:10:55458const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
459 int index,
460 const string16& title,
461 const GURL& url) {
[email protected]aee236542011-12-01 04:34:03462 return AddURLWithCreationTime(parent, index,
463 CollapseWhitespace(title, false),
464 url, Time::Now());
initial.commit09911bf2008-07-26 23:55:29465}
466
[email protected]e64e9012010-01-11 23:10:55467const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
468 const BookmarkNode* parent,
469 int index,
470 const string16& title,
471 const GURL& url,
472 const Time& creation_time) {
[email protected]6b4d64c2011-07-29 21:33:24473 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
[email protected]f25387b2008-08-21 15:20:33474 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29475 NOTREACHED();
476 return NULL;
477 }
initial.commit09911bf2008-07-26 23:55:29478
[email protected]848cd05e2008-09-19 18:33:48479 bool was_bookmarked = IsBookmarked(url);
initial.commit09911bf2008-07-26 23:55:29480
[email protected]8ad613b2012-10-12 21:28:45481 // Syncing may result in dates older than the last modified date.
482 if (creation_time > parent->date_folder_modified())
483 SetDateFolderModified(parent, creation_time);
initial.commit09911bf2008-07-26 23:55:29484
[email protected]ea2e5aa52009-05-20 18:01:28485 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
[email protected]0491ff72011-12-30 00:45:59486 new_node->SetTitle(title);
[email protected]b3c33d462009-06-26 22:29:20487 new_node->set_date_added(creation_time);
[email protected]037db002009-10-19 20:06:08488 new_node->set_type(BookmarkNode::URL);
initial.commit09911bf2008-07-26 23:55:29489
[email protected]776e7492008-10-23 16:47:41490 {
491 // Only hold the lock for the duration of the insert.
[email protected]20305ec2011-01-21 04:55:52492 base::AutoLock url_lock(url_lock_);
[email protected]776e7492008-10-23 16:47:41493 nodes_ordered_by_url_set_.insert(new_node);
494 }
initial.commit09911bf2008-07-26 23:55:29495
[email protected]b3c33d462009-06-26 22:29:20496 return AddNode(AsMutable(parent), index, new_node, was_bookmarked);
initial.commit09911bf2008-07-26 23:55:29497}
498
[email protected]b3c33d462009-06-26 22:29:20499void BookmarkModel::SortChildren(const BookmarkNode* parent) {
[email protected]6b4d64c2011-07-29 21:33:24500 if (!parent || !parent->is_folder() || is_root_node(parent) ||
[email protected]9c1a75a2011-03-10 02:38:12501 parent->child_count() <= 1) {
[email protected]e2f86d92009-02-25 00:22:01502 return;
503 }
504
[email protected]ef762642009-03-05 16:30:25505 UErrorCode error = U_ZERO_ERROR;
[email protected]543889ca2012-10-03 00:21:10506 icu::Locale application_locale(
507 content::GetContentClient()->browser()->GetApplicationLocale().c_str());
[email protected]b5b2385a2009-08-18 05:12:29508 scoped_ptr<icu::Collator> collator(
[email protected]543889ca2012-10-03 00:21:10509 icu::Collator::createInstance(application_locale, error));
[email protected]ef762642009-03-05 16:30:25510 if (U_FAILURE(error))
511 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20512 BookmarkNode* mutable_parent = AsMutable(parent);
513 std::sort(mutable_parent->children().begin(),
514 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25515 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01516
[email protected]997a0362009-03-12 03:10:51517 if (store_.get())
518 store_->ScheduleSave();
519
[email protected]e2f86d92009-02-25 00:22:01520 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
521 BookmarkNodeChildrenReordered(this, parent));
522}
523
[email protected]c6a7a3d2011-03-12 01:04:30524void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
525 const Time time) {
[email protected]eea8fd5532009-12-16 00:08:10526 DCHECK(parent);
[email protected]edb63cc2011-03-11 02:00:41527 AsMutable(parent)->set_date_folder_modified(time);
[email protected]eea8fd5532009-12-16 00:08:10528
529 if (store_.get())
530 store_->ScheduleSave();
531}
532
[email protected]c6a7a3d2011-03-12 01:04:30533void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
534 SetDateFolderModified(node, Time());
initial.commit09911bf2008-07-26 23:55:29535}
536
[email protected]e64e9012010-01-11 23:10:55537void BookmarkModel::GetBookmarksWithTitlesMatching(
538 const string16& text,
539 size_t max_count,
540 std::vector<bookmark_utils::TitleMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28541 if (!loaded_)
542 return;
543
[email protected]d0195a62010-08-22 04:40:20544 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
[email protected]85d911c2009-05-19 03:59:42545}
546
[email protected]9876bb1c2008-12-16 20:42:25547void BookmarkModel::ClearStore() {
[email protected]40e0486b2009-05-22 01:47:27548 registrar_.RemoveAll();
[email protected]9876bb1c2008-12-16 20:42:25549 store_ = NULL;
550}
551
[email protected]bc770a032011-12-12 17:35:30552void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
553 bool value) {
[email protected]1da5f712011-12-06 05:52:26554 DCHECK(loaded_);
[email protected]bc770a032011-12-12 17:35:30555 switch (type) {
556 case BookmarkNode::BOOKMARK_BAR:
557 bookmark_bar_node_->set_visible(value);
558 break;
559 case BookmarkNode::OTHER_NODE:
560 other_node_->set_visible(value);
561 break;
562 case BookmarkNode::MOBILE:
563 mobile_node_->set_visible(value);
564 break;
565 default:
566 NOTREACHED();
567 }
[email protected]1da5f712011-12-06 05:52:26568}
569
[email protected]dddc1b42008-10-09 20:56:59570bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28571 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59572 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
573 nodes_ordered_by_url_set_.end());
574}
575
[email protected]d8e41ed2008-09-11 15:22:32576void BookmarkModel::RemoveNode(BookmarkNode* node,
577 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20578 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33579 NOTREACHED();
580 return;
581 }
582
[email protected]0890e60e2011-06-27 14:55:21583 if (node->is_url()) {
[email protected]90ef13132008-08-27 03:27:46584 // NOTE: this is called in such a way that url_lock_ is already held. As
585 // such, this doesn't explicitly grab the lock.
initial.commit09911bf2008-07-26 23:55:29586 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
587 DCHECK(i != nodes_ordered_by_url_set_.end());
[email protected]848cd05e2008-09-19 18:33:48588 // i points to the first node with the URL, advance until we find the
589 // node we're removing.
590 while (*i != node)
591 ++i;
initial.commit09911bf2008-07-26 23:55:29592 nodes_ordered_by_url_set_.erase(i);
[email protected]5d4077542011-07-21 20:24:07593 removed_urls->insert(node->url());
[email protected]85d911c2009-05-19 03:59:42594
[email protected]01eec882009-05-22 18:13:28595 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29596 }
597
[email protected]abc2f262011-03-15 21:15:44598 CancelPendingFaviconLoadRequests(node);
initial.commit09911bf2008-07-26 23:55:29599
600 // Recurse through children.
[email protected]9c1a75a2011-03-10 02:38:12601 for (int i = node->child_count() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33602 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29603}
604
[email protected]a48f87d2012-10-09 18:06:33605void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
[email protected]01eec882009-05-22 18:13:28606 DCHECK(details_delete_me);
[email protected]9c92d192009-12-02 08:03:16607 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
[email protected]01eec882009-05-22 18:13:28608 if (loaded_) {
609 // We should only ever be loaded once.
610 NOTREACHED();
611 return;
612 }
613
[email protected]01eec882009-05-22 18:13:28614 next_node_id_ = details->max_id();
[email protected]fc7c36a22009-05-28 20:23:33615 if (details->computed_checksum() != details->stored_checksum())
[email protected]b29cfed2011-11-11 19:20:18616 file_changed_ = true;
[email protected]367d7072009-07-13 23:27:13617 if (details->computed_checksum() != details->stored_checksum() ||
618 details->ids_reassigned()) {
619 // If bookmarks file changed externally, the IDs may have changed
620 // externally. In that case, the decoder may have reassigned IDs to make
621 // them unique. So when the file has changed externally, we should save the
622 // bookmarks file to persist new IDs.
623 if (store_.get())
624 store_->ScheduleSave();
625 }
[email protected]d22d8732010-05-04 19:24:42626 bookmark_bar_node_ = details->release_bb_node();
627 other_node_ = details->release_other_folder_node();
[email protected]37bc9132011-12-01 22:29:29628 mobile_node_ = details->release_mobile_folder_node();
[email protected]d22d8732010-05-04 19:24:42629 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28630
[email protected]82f4655a2011-10-18 13:05:43631 // WARNING: order is important here, various places assume the order is
632 // constant.
[email protected]a0dd6a32011-03-18 17:31:37633 root_.Add(bookmark_bar_node_, 0);
634 root_.Add(other_node_, 1);
[email protected]37bc9132011-12-01 22:29:29635 root_.Add(mobile_node_, 2);
[email protected]6c1164042009-05-08 14:41:08636
[email protected]90ef13132008-08-27 03:27:46637 {
[email protected]20305ec2011-01-21 04:55:52638 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46639 // Update nodes_ordered_by_url_set_ from the nodes.
640 PopulateNodesByURL(&root_);
641 }
[email protected]f25387b2008-08-21 15:20:33642
initial.commit09911bf2008-07-26 23:55:29643 loaded_ = true;
644
[email protected]cbcd6412009-03-09 22:31:39645 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46646
[email protected]f25387b2008-08-21 15:20:33647 // Notify our direct observers.
[email protected]c58c5ea2011-07-13 21:43:16648 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
649 Loaded(this, details->ids_reassigned()));
initial.commit09911bf2008-07-26 23:55:29650
[email protected]f25387b2008-08-21 15:20:33651 // And generic notification.
[email protected]ad50def52011-10-19 23:17:07652 content::NotificationService::current()->Notify(
[email protected]432115822011-07-10 15:52:27653 chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED,
[email protected]25ee85302012-10-12 17:27:41654 content::Source<content::BrowserContext>(profile_),
[email protected]ad50def52011-10-19 23:17:07655 content::NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:29656}
657
[email protected]d8e41ed2008-09-11 15:22:32658void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
659 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29660
[email protected]2d48ee842011-03-08 23:27:29661 BookmarkNode* parent = AsMutable(node->parent());
initial.commit09911bf2008-07-26 23:55:29662 DCHECK(parent);
[email protected]368f3a72011-03-08 17:17:48663 int index = parent->GetIndexOf(node.get());
[email protected]18cc5ff2011-03-22 01:05:23664 parent->Remove(node.get());
[email protected]f25387b2008-08-21 15:20:33665 history::URLsStarredDetails details(false);
[email protected]90ef13132008-08-27 03:27:46666 {
[email protected]20305ec2011-01-21 04:55:52667 base::AutoLock url_lock(url_lock_);
[email protected]90ef13132008-08-27 03:27:46668 RemoveNode(node.get(), &details.changed_urls);
[email protected]848cd05e2008-09-19 18:33:48669
670 // RemoveNode adds an entry to changed_urls for each node of type URL. As we
671 // allow duplicates we need to remove any entries that are still bookmarked.
672 for (std::set<GURL>::iterator i = details.changed_urls.begin();
[email protected]2c685cc22009-08-28 00:17:44673 i != details.changed_urls.end(); ) {
[email protected]fc3fc452009-02-10 03:25:40674 if (IsBookmarkedNoLock(*i)) {
675 // When we erase the iterator pointing at the erasee is
676 // invalidated, so using i++ here within the "erase" call is
677 // important as it advances the iterator before passing the
678 // old value through to erase.
679 details.changed_urls.erase(i++);
680 } else {
[email protected]848cd05e2008-09-19 18:33:48681 ++i;
[email protected]fc3fc452009-02-10 03:25:40682 }
[email protected]848cd05e2008-09-19 18:33:48683 }
[email protected]90ef13132008-08-27 03:27:46684 }
initial.commit09911bf2008-07-26 23:55:29685
[email protected]f25387b2008-08-21 15:20:33686 if (store_.get())
687 store_->ScheduleSave();
688
[email protected]d8e41ed2008-09-11 15:22:32689 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41690 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]f25387b2008-08-21 15:20:33691
[email protected]848cd05e2008-09-19 18:33:48692 if (details.changed_urls.empty()) {
693 // No point in sending out notification if the starred state didn't change.
694 return;
695 }
696
[email protected]90ef13132008-08-27 03:27:46697 if (profile_) {
698 HistoryService* history =
[email protected]9aac66862012-06-19 19:44:31699 HistoryServiceFactory::GetForProfile(profile_,
700 Profile::EXPLICIT_ACCESS);
[email protected]90ef13132008-08-27 03:27:46701 if (history)
702 history->URLsNoLongerBookmarked(details.changed_urls);
703 }
704
[email protected]ad50def52011-10-19 23:17:07705 content::NotificationService::current()->Notify(
[email protected]432115822011-07-10 15:52:27706 chrome::NOTIFICATION_URLS_STARRED,
[email protected]25ee85302012-10-12 17:27:41707 content::Source<content::BrowserContext>(profile_),
[email protected]6c2381d2011-10-19 02:52:53708 content::Details<history::URLsStarredDetails>(&details));
initial.commit09911bf2008-07-26 23:55:29709}
710
[email protected]d8e41ed2008-09-11 15:22:32711BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
712 int index,
[email protected]848cd05e2008-09-19 18:33:48713 BookmarkNode* node,
714 bool was_bookmarked) {
[email protected]a0dd6a32011-03-18 17:31:37715 parent->Add(node, index);
initial.commit09911bf2008-07-26 23:55:29716
[email protected]f25387b2008-08-21 15:20:33717 if (store_.get())
718 store_->ScheduleSave();
719
[email protected]d8e41ed2008-09-11 15:22:32720 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29721 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33722
[email protected]01eec882009-05-22 18:13:28723 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42724
[email protected]0890e60e2011-06-27 14:55:21725 if (node->is_url() && !was_bookmarked) {
[email protected]f25387b2008-08-21 15:20:33726 history::URLsStarredDetails details(true);
[email protected]5d4077542011-07-21 20:24:07727 details.changed_urls.insert(node->url());
[email protected]ad50def52011-10-19 23:17:07728 content::NotificationService::current()->Notify(
[email protected]432115822011-07-10 15:52:27729 chrome::NOTIFICATION_URLS_STARRED,
[email protected]6c2381d2011-10-19 02:52:53730 content::Source<Profile>(profile_),
731 content::Details<history::URLsStarredDetails>(&details));
[email protected]f25387b2008-08-21 15:20:33732 }
initial.commit09911bf2008-07-26 23:55:29733 return node;
734}
735
[email protected]b3c33d462009-06-26 22:29:20736const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
[email protected]66bb7dc2011-10-27 15:29:23737 int64 id) const {
[email protected]f25387b2008-08-21 15:20:33738 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29739 return node;
[email protected]f25387b2008-08-21 15:20:33740
[email protected]9c1a75a2011-03-10 02:38:12741 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
[email protected]b3c33d462009-06-26 22:29:20742 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29743 if (result)
744 return result;
745 }
746 return NULL;
747}
748
[email protected]b3c33d462009-06-26 22:29:20749bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32750 int index,
751 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41752 return (parent && parent->is_folder() &&
[email protected]9c1a75a2011-03-10 02:38:12753 (index >= 0 && (index < parent->child_count() ||
754 (allow_end && index == parent->child_count()))));
[email protected]bd1b96702009-07-08 21:54:14755}
[email protected]f25387b2008-08-21 15:20:33756
[email protected]bc770a032011-12-12 17:35:30757BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
758 BookmarkNode::Type type) {
[email protected]e1f76c62011-06-30 20:15:39759 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
760 type == BookmarkNode::OTHER_NODE ||
[email protected]37bc9132011-12-01 22:29:29761 type == BookmarkNode::MOBILE);
[email protected]bc770a032011-12-12 17:35:30762 BookmarkPermanentNode* node =
763 new BookmarkPermanentNode(generate_next_node_id());
764 if (type == BookmarkNode::MOBILE)
765 node->set_visible(false); // Mobile node is initially hidden.
766
767 int title_id;
768 switch (type) {
769 case BookmarkNode::BOOKMARK_BAR:
770 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
771 break;
772 case BookmarkNode::OTHER_NODE:
773 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
774 break;
775 case BookmarkNode::MOBILE:
776 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
777 break;
778 default:
[email protected]97fdd162011-12-03 20:50:12779 NOTREACHED();
[email protected]bc770a032011-12-12 17:35:30780 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
781 break;
[email protected]7caca8a22010-08-21 18:25:31782 }
[email protected]0491ff72011-12-30 00:45:59783 node->SetTitle(l10n_util::GetStringUTF16(title_id));
[email protected]97fdd162011-12-03 20:50:12784 node->set_type(type);
initial.commit09911bf2008-07-26 23:55:29785 return node;
786}
787
[email protected]abc2f262011-03-15 21:15:44788void BookmarkModel::OnFaviconDataAvailable(
[email protected]0189bc722009-08-28 21:56:48789 FaviconService::Handle handle,
[email protected]65baa222012-08-30 15:43:51790 const history::FaviconImageResult& image_result) {
[email protected]d8e41ed2008-09-11 15:22:32791 BookmarkNode* node =
[email protected]4167c3a2008-08-21 18:12:20792 load_consumer_.GetClientData(
[email protected]f3d2b312012-08-23 22:27:59793 FaviconServiceFactory::GetForProfile(
794 profile_, Profile::EXPLICIT_ACCESS), handle);
[email protected]4167c3a2008-08-21 18:12:20795 DCHECK(node);
[email protected]b3c33d462009-06-26 22:29:20796 node->set_favicon_load_handle(0);
[email protected]bcc38612012-10-23 01:10:27797 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
[email protected]65baa222012-08-30 15:43:51798 if (!image_result.image.IsEmpty()) {
799 node->set_favicon(image_result.image);
800 FaviconLoaded(node);
initial.commit09911bf2008-07-26 23:55:29801 }
802}
803
[email protected]abc2f262011-03-15 21:15:44804void BookmarkModel::LoadFavicon(BookmarkNode* node) {
[email protected]0890e60e2011-06-27 14:55:21805 if (node->is_folder())
initial.commit09911bf2008-07-26 23:55:29806 return;
807
[email protected]5d4077542011-07-21 20:24:07808 DCHECK(node->url().is_valid());
[email protected]f3d2b312012-08-23 22:27:59809 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
810 profile_, Profile::EXPLICIT_ACCESS);
[email protected]0189bc722009-08-28 21:56:48811 if (!favicon_service)
initial.commit09911bf2008-07-26 23:55:29812 return;
[email protected]65baa222012-08-30 15:43:51813 FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
[email protected]682286372012-09-06 03:42:34814 FaviconService::FaviconForURLParams(profile_, node->url(),
815 history::FAVICON, gfx::kFaviconSize, &load_consumer_),
[email protected]bbdd2982011-10-08 18:14:24816 base::Bind(&BookmarkModel::OnFaviconDataAvailable,
817 base::Unretained(this)));
[email protected]0189bc722009-08-28 21:56:48818 load_consumer_.SetClientData(favicon_service, handle, node);
[email protected]b3c33d462009-06-26 22:29:20819 node->set_favicon_load_handle(handle);
initial.commit09911bf2008-07-26 23:55:29820}
821
[email protected]5b5c9b7f32011-07-21 01:07:18822void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
823 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
824 BookmarkNodeFaviconChanged(this, node));
825}
826
[email protected]abc2f262011-03-15 21:15:44827void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:20828 if (node->favicon_load_handle()) {
[email protected]f3d2b312012-08-23 22:27:59829 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
830 profile_, Profile::EXPLICIT_ACCESS);
[email protected]0189bc722009-08-28 21:56:48831 if (favicon_service)
832 favicon_service->CancelRequest(node->favicon_load_handle());
[email protected]b3c33d462009-06-26 22:29:20833 node->set_favicon_load_handle(0);
initial.commit09911bf2008-07-26 23:55:29834 }
835}
836
[email protected]432115822011-07-10 15:52:27837void BookmarkModel::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53838 const content::NotificationSource& source,
839 const content::NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27840 switch (type) {
841 case chrome::NOTIFICATION_FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29842 // Prevent the observers from getting confused for multiple favicon loads.
[email protected]6c2381d2011-10-19 02:52:53843 content::Details<history::FaviconChangeDetails> favicon_details(details);
initial.commit09911bf2008-07-26 23:55:29844 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
845 i != favicon_details->urls.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20846 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48847 GetNodesByURL(*i, &nodes);
848 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:29849 // Got an updated favicon, for a URL, do a new request.
[email protected]b3c33d462009-06-26 22:29:20850 BookmarkNode* node = AsMutable(nodes[i]);
initial.commit09911bf2008-07-26 23:55:29851 node->InvalidateFavicon();
[email protected]abc2f262011-03-15 21:15:44852 CancelPendingFaviconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:32853 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]f12de8302011-05-23 16:12:30854 BookmarkNodeFaviconChanged(this, node));
initial.commit09911bf2008-07-26 23:55:29855 }
856 }
857 break;
858 }
859
860 default:
861 NOTREACHED();
862 break;
863 }
864}
[email protected]f25387b2008-08-21 15:20:33865
[email protected]d8e41ed2008-09-11 15:22:32866void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:46867 // NOTE: this is called with url_lock_ already held. As such, this doesn't
868 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:33869 if (node->is_url())
870 nodes_ordered_by_url_set_.insert(node);
[email protected]9c1a75a2011-03-10 02:38:12871 for (int i = 0; i < node->child_count(); ++i)
[email protected]f25387b2008-08-21 15:20:33872 PopulateNodesByURL(node->GetChild(i));
873}
[email protected]4d89f382009-05-12 06:56:49874
[email protected]367d7072009-07-13 23:27:13875int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:49876 return next_node_id_++;
877}
[email protected]01eec882009-05-22 18:13:28878
[email protected]9c92d192009-12-02 08:03:16879BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
[email protected]bc770a032011-12-12 17:35:30880 BookmarkPermanentNode* bb_node =
881 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
882 BookmarkPermanentNode* other_node =
883 CreatePermanentNode(BookmarkNode::OTHER_NODE);
884 BookmarkPermanentNode* mobile_node =
885 CreatePermanentNode(BookmarkNode::MOBILE);
[email protected]37bc9132011-12-01 22:29:29886 return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
[email protected]6b4d64c2011-07-29 21:33:24887 new BookmarkIndex(profile_), next_node_id_);
[email protected]01eec882009-05-22 18:13:28888}