blob: 995e716c331bb38d0baa0d4c28a08dddc6c87bc0 [file] [log] [blame]
[email protected]6524385ef2010-08-18 06:34:131// Copyright (c) 2010 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]a92b8642009-05-05 23:38:567#include "app/l10n_util.h"
[email protected]8891d142010-01-25 20:38:538#include "app/l10n_util_collator.h"
[email protected]2041cf342010-02-19 03:15:599#include "base/callback.h"
[email protected]807204142009-05-05 03:31:4410#include "base/scoped_vector.h"
[email protected]fc3fc452009-02-10 03:25:4011#include "build/build_config.h"
[email protected]01eec882009-05-22 18:13:2812#include "chrome/browser/bookmarks/bookmark_index.h"
[email protected]9333f182008-12-09 17:34:1713#include "chrome/browser/bookmarks/bookmark_utils.h"
[email protected]68de8b72008-09-09 23:08:1314#include "chrome/browser/bookmarks/bookmark_storage.h"
[email protected]e2f86d92009-02-25 00:22:0115#include "chrome/browser/browser_process.h"
[email protected]9c92d192009-12-02 08:03:1616#include "chrome/browser/history/history_notifications.h"
[email protected]052313b2010-02-19 09:43:0817#include "chrome/browser/pref_service.h"
initial.commit09911bf2008-07-26 23:55:2918#include "chrome/browser/profile.h"
[email protected]bfd04a62009-02-01 18:16:5619#include "chrome/common/notification_service.h"
[email protected]538ddce92010-03-18 04:28:0620#include "gfx/codec/png_codec.h"
[email protected]34ac8f32009-02-22 23:03:2721#include "grit/generated_resources.h"
initial.commit09911bf2008-07-26 23:55:2922
[email protected]e1acf6f2008-10-27 20:43:3323using base::Time;
24
[email protected]b3c33d462009-06-26 22:29:2025namespace {
26
27// Helper to get a mutable bookmark node.
28static BookmarkNode* AsMutable(const BookmarkNode* node) {
29 return const_cast<BookmarkNode*>(node);
30}
31
32} // anonymous namespace
33
[email protected]d8e41ed2008-09-11 15:22:3234// BookmarkNode ---------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2935
[email protected]ea2e5aa52009-05-20 18:01:2836BookmarkNode::BookmarkNode(const GURL& url)
[email protected]814a2d32009-04-30 23:09:0137 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2838 Initialize(0);
[email protected]814a2d32009-04-30 23:09:0139}
40
[email protected]367d7072009-07-13 23:27:1341BookmarkNode::BookmarkNode(int64 id, const GURL& url)
[email protected]2c685cc22009-08-28 00:17:4442 : url_(url) {
[email protected]ea2e5aa52009-05-20 18:01:2843 Initialize(id);
[email protected]814a2d32009-04-30 23:09:0144}
45
[email protected]367d7072009-07-13 23:27:1346void BookmarkNode::Initialize(int64 id) {
[email protected]4d89f382009-05-12 06:56:4947 id_ = id;
[email protected]814a2d32009-04-30 23:09:0148 loaded_favicon_ = false;
49 favicon_load_handle_ = 0;
[email protected]bd1b96702009-07-08 21:54:1450 type_ = !url_.is_empty() ? URL : BOOKMARK_BAR;
[email protected]814a2d32009-04-30 23:09:0151 date_added_ = Time::Now();
initial.commit09911bf2008-07-26 23:55:2952}
53
[email protected]d8e41ed2008-09-11 15:22:3254void BookmarkNode::Reset(const history::StarredEntry& entry) {
[email protected]bd1b96702009-07-08 21:54:1455 DCHECK(entry.type != history::StarredEntry::URL || entry.url == url_);
[email protected]f25387b2008-08-21 15:20:3356
initial.commit09911bf2008-07-26 23:55:2957 favicon_ = SkBitmap();
[email protected]bd1b96702009-07-08 21:54:1458 switch (entry.type) {
59 case history::StarredEntry::URL:
60 type_ = BookmarkNode::URL;
61 break;
62 case history::StarredEntry::USER_GROUP:
63 type_ = BookmarkNode::FOLDER;
64 break;
65 case history::StarredEntry::BOOKMARK_BAR:
66 type_ = BookmarkNode::BOOKMARK_BAR;
67 break;
68 case history::StarredEntry::OTHER:
69 type_ = BookmarkNode::OTHER_NODE;
70 break;
71 default:
72 NOTREACHED();
73 }
initial.commit09911bf2008-07-26 23:55:2974 date_added_ = entry.date_added;
75 date_group_modified_ = entry.date_group_modified;
76 SetTitle(entry.title);
77}
78
[email protected]d8e41ed2008-09-11 15:22:3279// BookmarkModel --------------------------------------------------------------
initial.commit09911bf2008-07-26 23:55:2980
[email protected]ef762642009-03-05 16:30:2581namespace {
82
83// Comparator used when sorting bookmarks. Folders are sorted first, then
[email protected]2c685cc22009-08-28 00:17:4484// bookmarks.
[email protected]b3c33d462009-06-26 22:29:2085class SortComparator : public std::binary_function<const BookmarkNode*,
86 const BookmarkNode*,
[email protected]ef762642009-03-05 16:30:2587 bool> {
88 public:
[email protected]b5b2385a2009-08-18 05:12:2989 explicit SortComparator(icu::Collator* collator) : collator_(collator) { }
[email protected]ef762642009-03-05 16:30:2590
91 // Returns true if lhs preceeds rhs.
[email protected]b3c33d462009-06-26 22:29:2092 bool operator() (const BookmarkNode* n1, const BookmarkNode* n2) {
[email protected]037db002009-10-19 20:06:0893 if (n1->type() == n2->type()) {
[email protected]ef762642009-03-05 16:30:2594 // Types are the same, compare the names.
95 if (!collator_)
96 return n1->GetTitle() < n2->GetTitle();
97 return l10n_util::CompareStringWithCollator(collator_, n1->GetTitle(),
98 n2->GetTitle()) == UCOL_LESS;
99 }
100 // Types differ, sort such that folders come first.
101 return n1->is_folder();
102 }
103
104 private:
[email protected]b5b2385a2009-08-18 05:12:29105 icu::Collator* collator_;
[email protected]ef762642009-03-05 16:30:25106};
107
108} // namespace
109
[email protected]d8e41ed2008-09-11 15:22:32110BookmarkModel::BookmarkModel(Profile* profile)
initial.commit09911bf2008-07-26 23:55:29111 : profile_(profile),
112 loaded_(false),
[email protected]fc7c36a22009-05-28 20:23:33113 file_changed_(false),
[email protected]ea2e5aa52009-05-20 18:01:28114 root_(GURL()),
initial.commit09911bf2008-07-26 23:55:29115 bookmark_bar_node_(NULL),
[email protected]90ef13132008-08-27 03:27:46116 other_node_(NULL),
[email protected]4d89f382009-05-12 06:56:49117 next_node_id_(1),
[email protected]b3e2fad02008-10-31 03:32:06118 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
[email protected]4d89f382009-05-12 06:56:49119 loaded_signal_(TRUE, FALSE) {
[email protected]f25387b2008-08-21 15:20:33120 if (!profile_) {
121 // Profile is null during testing.
[email protected]01eec882009-05-22 18:13:28122 DoneLoading(CreateLoadDetails());
initial.commit09911bf2008-07-26 23:55:29123 }
initial.commit09911bf2008-07-26 23:55:29124}
125
[email protected]d8e41ed2008-09-11 15:22:32126BookmarkModel::~BookmarkModel() {
[email protected]d8e41ed2008-09-11 15:22:32127 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]3de6fd342008-09-05 02:44:51128 BookmarkModelBeingDeleted(this));
129
[email protected]f25387b2008-08-21 15:20:33130 if (store_) {
131 // The store maintains a reference back to us. We need to tell it we're gone
132 // so that it doesn't try and invoke a method back on us again.
133 store_->BookmarkModelDeleted();
initial.commit09911bf2008-07-26 23:55:29134 }
135}
136
[email protected]d8e41ed2008-09-11 15:22:32137void BookmarkModel::Load() {
[email protected]90ef13132008-08-27 03:27:46138 if (store_.get()) {
139 // If the store is non-null, it means Load was already invoked. Load should
140 // only be invoked once.
141 NOTREACHED();
142 return;
143 }
144
145 // Listen for changes to favicons so that we can update the favicon of the
146 // node appropriately.
[email protected]40e0486b2009-05-22 01:47:27147 registrar_.Add(this, NotificationType::FAVICON_CHANGED,
148 Source<Profile>(profile_));
[email protected]90ef13132008-08-27 03:27:46149
150 // Load the bookmarks. BookmarkStorage notifies us when done.
151 store_ = new BookmarkStorage(profile_, this);
[email protected]01eec882009-05-22 18:13:28152 store_->LoadBookmarks(CreateLoadDetails());
[email protected]90ef13132008-08-27 03:27:46153}
154
[email protected]b3c33d462009-06-26 22:29:20155const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
156 std::vector<const BookmarkNode*> nodes =
[email protected]9333f182008-12-09 17:34:17157 bookmark_utils::GetMostRecentlyModifiedGroups(this, 1);
initial.commit09911bf2008-07-26 23:55:29158 return nodes.empty() ? bookmark_bar_node_ : nodes[0];
159}
160
[email protected]b3c33d462009-06-26 22:29:20161void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
162 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root(parent)) {
[email protected]f25387b2008-08-21 15:20:33163 NOTREACHED();
164 return;
165 }
[email protected]b3c33d462009-06-26 22:29:20166 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
[email protected]f25387b2008-08-21 15:20:33167}
168
[email protected]b3c33d462009-06-26 22:29:20169void BookmarkModel::Move(const BookmarkNode* node,
170 const BookmarkNode* new_parent,
[email protected]d8e41ed2008-09-11 15:22:32171 int index) {
[email protected]f25387b2008-08-21 15:20:33172 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
[email protected]b3c33d462009-06-26 22:29:20173 is_root(new_parent) || is_permanent_node(node)) {
initial.commit09911bf2008-07-26 23:55:29174 NOTREACHED();
175 return;
176 }
177
178 if (new_parent->HasAncestor(node)) {
179 // Can't make an ancestor of the node be a child of the node.
180 NOTREACHED();
181 return;
182 }
183
184 SetDateGroupModified(new_parent, Time::Now());
185
[email protected]b3c33d462009-06-26 22:29:20186 const BookmarkNode* old_parent = node->GetParent();
initial.commit09911bf2008-07-26 23:55:29187 int old_index = old_parent->IndexOfChild(node);
188
189 if (old_parent == new_parent &&
190 (index == old_index || index == old_index + 1)) {
191 // Node is already in this position, nothing to do.
192 return;
193 }
194
195 if (old_parent == new_parent && index > old_index)
196 index--;
[email protected]b3c33d462009-06-26 22:29:20197 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
198 mutable_new_parent->Add(index, AsMutable(node));
initial.commit09911bf2008-07-26 23:55:29199
[email protected]f25387b2008-08-21 15:20:33200 if (store_.get())
201 store_->ScheduleSave();
202
[email protected]d8e41ed2008-09-11 15:22:32203 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29204 BookmarkNodeMoved(this, old_parent, old_index,
205 new_parent, index));
206}
207
[email protected]4e187ef652010-03-11 05:21:35208void BookmarkModel::Copy(const BookmarkNode* node,
209 const BookmarkNode* new_parent,
210 int index) {
211 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
212 is_root(new_parent) || is_permanent_node(node)) {
213 NOTREACHED();
214 return;
215 }
216
217 if (new_parent->HasAncestor(node)) {
218 // Can't make an ancestor of the node be a child of the node.
219 NOTREACHED();
220 return;
221 }
222
223 SetDateGroupModified(new_parent, Time::Now());
224 BookmarkDragData drag_data_(node);
225 std::vector<BookmarkDragData::Element> elements(drag_data_.elements);
[email protected]1a566fae2010-04-16 01:05:06226 // CloneDragData will use BookmarkModel methods to do the job, so we
227 // don't need to send notifications here.
[email protected]4e187ef652010-03-11 05:21:35228 bookmark_utils::CloneDragData(this, elements, new_parent, index);
229
230 if (store_.get())
231 store_->ScheduleSave();
[email protected]4e187ef652010-03-11 05:21:35232}
233
[email protected]b3c33d462009-06-26 22:29:20234const SkBitmap& BookmarkModel::GetFavIcon(const BookmarkNode* node) {
[email protected]ea2e5aa52009-05-20 18:01:28235 DCHECK(node);
[email protected]b3c33d462009-06-26 22:29:20236 if (!node->is_favicon_loaded()) {
237 BookmarkNode* mutable_node = AsMutable(node);
238 mutable_node->set_favicon_loaded(true);
239 LoadFavIcon(mutable_node);
[email protected]ea2e5aa52009-05-20 18:01:28240 }
241 return node->favicon();
242}
243
[email protected]e64e9012010-01-11 23:10:55244void BookmarkModel::SetTitle(const BookmarkNode* node, const string16& title) {
[email protected]f25387b2008-08-21 15:20:33245 if (!node) {
246 NOTREACHED();
247 return;
248 }
[email protected]e64e9012010-01-11 23:10:55249 if (node->GetTitleAsString16() == title)
initial.commit09911bf2008-07-26 23:55:29250 return;
[email protected]f25387b2008-08-21 15:20:33251
[email protected]baf4f922009-10-19 16:44:07252 if (node == bookmark_bar_node_ || node == other_node_) {
253 NOTREACHED();
254 return;
255 }
256
[email protected]85d911c2009-05-19 03:59:42257 // The title index doesn't support changing the title, instead we remove then
258 // add it back.
[email protected]01eec882009-05-22 18:13:28259 index_->Remove(node);
[email protected]b3c33d462009-06-26 22:29:20260 AsMutable(node)->SetTitle(title);
[email protected]01eec882009-05-22 18:13:28261 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42262
[email protected]f25387b2008-08-21 15:20:33263 if (store_.get())
264 store_->ScheduleSave();
265
[email protected]d8e41ed2008-09-11 15:22:32266 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29267 BookmarkNodeChanged(this, node));
268}
269
[email protected]e5486602010-02-09 21:27:55270void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
271 if (!node) {
272 NOTREACHED();
273 return;
274 }
275
276 // We cannot change the URL of a folder.
277 if (node->is_folder()) {
278 NOTREACHED();
279 return;
280 }
281
282 if (url == node->GetURL())
283 return;
284
285 AsMutable(node)->InvalidateFavicon();
286 CancelPendingFavIconLoadRequests(AsMutable(node));
287
288 {
289 AutoLock url_lock(url_lock_);
290 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
291 AsMutable(node));
292 DCHECK(i != nodes_ordered_by_url_set_.end());
293 // i points to the first node with the URL, advance until we find the
294 // node we're removing.
295 while (*i != node)
296 ++i;
297 nodes_ordered_by_url_set_.erase(i);
298
299 AsMutable(node)->SetURL(url);
300 nodes_ordered_by_url_set_.insert(AsMutable(node));
301 }
302
303 if (store_.get())
304 store_->ScheduleSave();
305
306 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
307 BookmarkNodeChanged(this, node));
308}
309
[email protected]848cd05e2008-09-19 18:33:48310void BookmarkModel::GetNodesByURL(const GURL& url,
[email protected]b3c33d462009-06-26 22:29:20311 std::vector<const BookmarkNode*>* nodes) {
[email protected]90ef13132008-08-27 03:27:46312 AutoLock url_lock(url_lock_);
[email protected]ea2e5aa52009-05-20 18:01:28313 BookmarkNode tmp_node(url);
initial.commit09911bf2008-07-26 23:55:29314 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
[email protected]848cd05e2008-09-19 18:33:48315 while (i != nodes_ordered_by_url_set_.end() && (*i)->GetURL() == url) {
316 nodes->push_back(*i);
317 ++i;
318 }
319}
320
[email protected]b3c33d462009-06-26 22:29:20321const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
322 const GURL& url) {
323 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48324 GetNodesByURL(url, &nodes);
325 if (nodes.empty())
326 return NULL;
327
[email protected]9333f182008-12-09 17:34:17328 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
[email protected]848cd05e2008-09-19 18:33:48329 return nodes.front();
initial.commit09911bf2008-07-26 23:55:29330}
331
[email protected]d8e41ed2008-09-11 15:22:32332void BookmarkModel::GetBookmarks(std::vector<GURL>* urls) {
[email protected]90ef13132008-08-27 03:27:46333 AutoLock url_lock(url_lock_);
[email protected]848cd05e2008-09-19 18:33:48334 const GURL* last_url = NULL;
[email protected]90ef13132008-08-27 03:27:46335 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
336 i != nodes_ordered_by_url_set_.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20337 const GURL* url = &((*i)->GetURL());
[email protected]848cd05e2008-09-19 18:33:48338 // Only add unique URLs.
339 if (!last_url || *url != *last_url)
340 urls->push_back(*url);
341 last_url = url;
[email protected]90ef13132008-08-27 03:27:46342 }
343}
344
[email protected]848cd05e2008-09-19 18:33:48345bool BookmarkModel::IsBookmarked(const GURL& url) {
346 AutoLock url_lock(url_lock_);
[email protected]dddc1b42008-10-09 20:56:59347 return IsBookmarkedNoLock(url);
[email protected]848cd05e2008-09-19 18:33:48348}
349
[email protected]367d7072009-07-13 23:27:13350const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) {
initial.commit09911bf2008-07-26 23:55:29351 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
[email protected]f25387b2008-08-21 15:20:33352 return GetNodeByID(&root_, id);
initial.commit09911bf2008-07-26 23:55:29353}
354
[email protected]e64e9012010-01-11 23:10:55355const BookmarkNode* BookmarkModel::AddGroup(const BookmarkNode* parent,
356 int index,
357 const string16& title) {
[email protected]f25387b2008-08-21 15:20:33358 if (!loaded_ || parent == &root_ || !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29359 // Can't add to the root.
360 NOTREACHED();
361 return NULL;
362 }
363
[email protected]ea2e5aa52009-05-20 18:01:28364 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(),
[email protected]4d89f382009-05-12 06:56:49365 GURL());
[email protected]b3c33d462009-06-26 22:29:20366 new_node->set_date_group_modified(Time::Now());
initial.commit09911bf2008-07-26 23:55:29367 new_node->SetTitle(title);
[email protected]037db002009-10-19 20:06:08368 new_node->set_type(BookmarkNode::FOLDER);
initial.commit09911bf2008-07-26 23:55:29369
[email protected]b3c33d462009-06-26 22:29:20370 return AddNode(AsMutable(parent), index, new_node, false);
initial.commit09911bf2008-07-26 23:55:29371}
372
[email protected]e64e9012010-01-11 23:10:55373const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
374 int index,
375 const string16& title,
376 const GURL& url) {
initial.commit09911bf2008-07-26 23:55:29377 return AddURLWithCreationTime(parent, index, title, url, Time::Now());
378}
379
[email protected]e64e9012010-01-11 23:10:55380const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
381 const BookmarkNode* parent,
382 int index,
383 const string16& title,
384 const GURL& url,
385 const Time& creation_time) {
[email protected]b3c33d462009-06-26 22:29:20386 if (!loaded_ || !url.is_valid() || is_root(parent) ||
[email protected]f25387b2008-08-21 15:20:33387 !IsValidIndex(parent, index, true)) {
initial.commit09911bf2008-07-26 23:55:29388 NOTREACHED();
389 return NULL;
390 }
initial.commit09911bf2008-07-26 23:55:29391
[email protected]848cd05e2008-09-19 18:33:48392 bool was_bookmarked = IsBookmarked(url);
initial.commit09911bf2008-07-26 23:55:29393
394 SetDateGroupModified(parent, creation_time);
395
[email protected]ea2e5aa52009-05-20 18:01:28396 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
initial.commit09911bf2008-07-26 23:55:29397 new_node->SetTitle(title);
[email protected]b3c33d462009-06-26 22:29:20398 new_node->set_date_added(creation_time);
[email protected]037db002009-10-19 20:06:08399 new_node->set_type(BookmarkNode::URL);
initial.commit09911bf2008-07-26 23:55:29400
[email protected]776e7492008-10-23 16:47:41401 {
402 // Only hold the lock for the duration of the insert.
403 AutoLock url_lock(url_lock_);
404 nodes_ordered_by_url_set_.insert(new_node);
405 }
initial.commit09911bf2008-07-26 23:55:29406
[email protected]b3c33d462009-06-26 22:29:20407 return AddNode(AsMutable(parent), index, new_node, was_bookmarked);
initial.commit09911bf2008-07-26 23:55:29408}
409
[email protected]b3c33d462009-06-26 22:29:20410void BookmarkModel::SortChildren(const BookmarkNode* parent) {
411 if (!parent || !parent->is_folder() || is_root(parent) ||
[email protected]e2f86d92009-02-25 00:22:01412 parent->GetChildCount() <= 1) {
413 return;
414 }
415
[email protected]ef762642009-03-05 16:30:25416 UErrorCode error = U_ZERO_ERROR;
[email protected]b5b2385a2009-08-18 05:12:29417 scoped_ptr<icu::Collator> collator(
418 icu::Collator::createInstance(
419 icu::Locale(g_browser_process->GetApplicationLocale().c_str()),
[email protected]ef762642009-03-05 16:30:25420 error));
421 if (U_FAILURE(error))
422 collator.reset(NULL);
[email protected]b3c33d462009-06-26 22:29:20423 BookmarkNode* mutable_parent = AsMutable(parent);
424 std::sort(mutable_parent->children().begin(),
425 mutable_parent->children().end(),
[email protected]ef762642009-03-05 16:30:25426 SortComparator(collator.get()));
[email protected]e2f86d92009-02-25 00:22:01427
[email protected]997a0362009-03-12 03:10:51428 if (store_.get())
429 store_->ScheduleSave();
430
[email protected]e2f86d92009-02-25 00:22:01431 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
432 BookmarkNodeChildrenReordered(this, parent));
433}
434
[email protected]e64e9012010-01-11 23:10:55435void BookmarkModel::SetURLStarred(const GURL& url,
436 const string16& title,
437 bool is_starred) {
[email protected]b3c33d462009-06-26 22:29:20438 std::vector<const BookmarkNode*> bookmarks;
[email protected]848cd05e2008-09-19 18:33:48439 GetNodesByURL(url, &bookmarks);
440 bool bookmarks_exist = !bookmarks.empty();
441 if (is_starred == bookmarks_exist)
442 return; // Nothing to do, state already matches.
443
444 if (is_starred) {
445 // Create a bookmark.
[email protected]b3c33d462009-06-26 22:29:20446 const BookmarkNode* parent = GetParentForNewNodes();
initial.commit09911bf2008-07-26 23:55:29447 AddURL(parent, parent->GetChildCount(), title, url);
[email protected]848cd05e2008-09-19 18:33:48448 } else {
449 // Remove all the bookmarks.
450 for (size_t i = 0; i < bookmarks.size(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20451 const BookmarkNode* node = bookmarks[i];
[email protected]848cd05e2008-09-19 18:33:48452 Remove(node->GetParent(), node->GetParent()->IndexOfChild(node));
453 }
initial.commit09911bf2008-07-26 23:55:29454 }
455}
456
[email protected]eea8fd5532009-12-16 00:08:10457void BookmarkModel::SetDateGroupModified(const BookmarkNode* parent,
458 const Time time) {
459 DCHECK(parent);
460 AsMutable(parent)->set_date_group_modified(time);
461
462 if (store_.get())
463 store_->ScheduleSave();
464}
465
[email protected]b3c33d462009-06-26 22:29:20466void BookmarkModel::ResetDateGroupModified(const BookmarkNode* node) {
initial.commit09911bf2008-07-26 23:55:29467 SetDateGroupModified(node, Time());
468}
469
[email protected]e64e9012010-01-11 23:10:55470void BookmarkModel::GetBookmarksWithTitlesMatching(
471 const string16& text,
472 size_t max_count,
473 std::vector<bookmark_utils::TitleMatch>* matches) {
[email protected]01eec882009-05-22 18:13:28474 if (!loaded_)
475 return;
476
[email protected]81415222010-08-18 16:17:20477 index_->GetBookmarksWithTitlesMatching(UTF16ToWideHack(text), max_count,
478 matches);
[email protected]85d911c2009-05-19 03:59:42479}
480
[email protected]9876bb1c2008-12-16 20:42:25481void BookmarkModel::ClearStore() {
[email protected]40e0486b2009-05-22 01:47:27482 registrar_.RemoveAll();
[email protected]9876bb1c2008-12-16 20:42:25483 store_ = NULL;
484}
485
[email protected]dddc1b42008-10-09 20:56:59486bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
[email protected]ea2e5aa52009-05-20 18:01:28487 BookmarkNode tmp_node(url);
[email protected]dddc1b42008-10-09 20:56:59488 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
489 nodes_ordered_by_url_set_.end());
490}
491
[email protected]b3c33d462009-06-26 22:29:20492void BookmarkModel::FavIconLoaded(const BookmarkNode* node) {
initial.commit09911bf2008-07-26 23:55:29493 // Send out notification to the observer.
[email protected]d8e41ed2008-09-11 15:22:32494 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29495 BookmarkNodeFavIconLoaded(this, node));
496}
497
[email protected]d8e41ed2008-09-11 15:22:32498void BookmarkModel::RemoveNode(BookmarkNode* node,
499 std::set<GURL>* removed_urls) {
[email protected]b3c33d462009-06-26 22:29:20500 if (!loaded_ || !node || is_permanent_node(node)) {
[email protected]f25387b2008-08-21 15:20:33501 NOTREACHED();
502 return;
503 }
504
[email protected]037db002009-10-19 20:06:08505 if (node->type() == BookmarkNode::URL) {
[email protected]90ef13132008-08-27 03:27:46506 // NOTE: this is called in such a way that url_lock_ is already held. As
507 // such, this doesn't explicitly grab the lock.
initial.commit09911bf2008-07-26 23:55:29508 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
509 DCHECK(i != nodes_ordered_by_url_set_.end());
[email protected]848cd05e2008-09-19 18:33:48510 // i points to the first node with the URL, advance until we find the
511 // node we're removing.
512 while (*i != node)
513 ++i;
initial.commit09911bf2008-07-26 23:55:29514 nodes_ordered_by_url_set_.erase(i);
[email protected]f25387b2008-08-21 15:20:33515 removed_urls->insert(node->GetURL());
[email protected]85d911c2009-05-19 03:59:42516
[email protected]01eec882009-05-22 18:13:28517 index_->Remove(node);
initial.commit09911bf2008-07-26 23:55:29518 }
519
520 CancelPendingFavIconLoadRequests(node);
521
522 // Recurse through children.
523 for (int i = node->GetChildCount() - 1; i >= 0; --i)
[email protected]f25387b2008-08-21 15:20:33524 RemoveNode(node->GetChild(i), removed_urls);
initial.commit09911bf2008-07-26 23:55:29525}
526
[email protected]01eec882009-05-22 18:13:28527void BookmarkModel::DoneLoading(
[email protected]9c92d192009-12-02 08:03:16528 BookmarkLoadDetails* details_delete_me) {
[email protected]01eec882009-05-22 18:13:28529 DCHECK(details_delete_me);
[email protected]9c92d192009-12-02 08:03:16530 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
[email protected]01eec882009-05-22 18:13:28531 if (loaded_) {
532 // We should only ever be loaded once.
533 NOTREACHED();
534 return;
535 }
536
[email protected]01eec882009-05-22 18:13:28537 next_node_id_ = details->max_id();
[email protected]fc7c36a22009-05-28 20:23:33538 if (details->computed_checksum() != details->stored_checksum())
539 SetFileChanged();
[email protected]367d7072009-07-13 23:27:13540 if (details->computed_checksum() != details->stored_checksum() ||
541 details->ids_reassigned()) {
542 // If bookmarks file changed externally, the IDs may have changed
543 // externally. In that case, the decoder may have reassigned IDs to make
544 // them unique. So when the file has changed externally, we should save the
545 // bookmarks file to persist new IDs.
546 if (store_.get())
547 store_->ScheduleSave();
548 }
[email protected]d22d8732010-05-04 19:24:42549 bookmark_bar_node_ = details->release_bb_node();
550 other_node_ = details->release_other_folder_node();
551 index_.reset(details->release_index());
[email protected]01eec882009-05-22 18:13:28552
553 // WARNING: order is important here, various places assume bookmark bar then
554 // other node.
555 root_.Add(0, bookmark_bar_node_);
556 root_.Add(1, other_node_);
[email protected]6c1164042009-05-08 14:41:08557
[email protected]90ef13132008-08-27 03:27:46558 {
559 AutoLock url_lock(url_lock_);
560 // Update nodes_ordered_by_url_set_ from the nodes.
561 PopulateNodesByURL(&root_);
562 }
[email protected]f25387b2008-08-21 15:20:33563
initial.commit09911bf2008-07-26 23:55:29564 loaded_ = true;
565
[email protected]cbcd6412009-03-09 22:31:39566 loaded_signal_.Signal();
[email protected]90ef13132008-08-27 03:27:46567
[email protected]f25387b2008-08-21 15:20:33568 // Notify our direct observers.
[email protected]d8e41ed2008-09-11 15:22:32569 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, Loaded(this));
initial.commit09911bf2008-07-26 23:55:29570
[email protected]f25387b2008-08-21 15:20:33571 // And generic notification.
initial.commit09911bf2008-07-26 23:55:29572 NotificationService::current()->Notify(
[email protected]bfd04a62009-02-01 18:16:56573 NotificationType::BOOKMARK_MODEL_LOADED,
initial.commit09911bf2008-07-26 23:55:29574 Source<Profile>(profile_),
575 NotificationService::NoDetails());
576}
577
[email protected]d8e41ed2008-09-11 15:22:32578void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
579 scoped_ptr<BookmarkNode> node(delete_me);
initial.commit09911bf2008-07-26 23:55:29580
[email protected]b3c33d462009-06-26 22:29:20581 BookmarkNode* parent = AsMutable(node->GetParent());
initial.commit09911bf2008-07-26 23:55:29582 DCHECK(parent);
583 int index = parent->IndexOfChild(node.get());
584 parent->Remove(index);
[email protected]f25387b2008-08-21 15:20:33585 history::URLsStarredDetails details(false);
[email protected]90ef13132008-08-27 03:27:46586 {
587 AutoLock url_lock(url_lock_);
588 RemoveNode(node.get(), &details.changed_urls);
[email protected]848cd05e2008-09-19 18:33:48589
590 // RemoveNode adds an entry to changed_urls for each node of type URL. As we
591 // allow duplicates we need to remove any entries that are still bookmarked.
592 for (std::set<GURL>::iterator i = details.changed_urls.begin();
[email protected]2c685cc22009-08-28 00:17:44593 i != details.changed_urls.end(); ) {
[email protected]fc3fc452009-02-10 03:25:40594 if (IsBookmarkedNoLock(*i)) {
595 // When we erase the iterator pointing at the erasee is
596 // invalidated, so using i++ here within the "erase" call is
597 // important as it advances the iterator before passing the
598 // old value through to erase.
599 details.changed_urls.erase(i++);
600 } else {
[email protected]848cd05e2008-09-19 18:33:48601 ++i;
[email protected]fc3fc452009-02-10 03:25:40602 }
[email protected]848cd05e2008-09-19 18:33:48603 }
[email protected]90ef13132008-08-27 03:27:46604 }
initial.commit09911bf2008-07-26 23:55:29605
[email protected]f25387b2008-08-21 15:20:33606 if (store_.get())
607 store_->ScheduleSave();
608
[email protected]d8e41ed2008-09-11 15:22:32609 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
[email protected]776e7492008-10-23 16:47:41610 BookmarkNodeRemoved(this, parent, index, node.get()));
[email protected]f25387b2008-08-21 15:20:33611
[email protected]848cd05e2008-09-19 18:33:48612 if (details.changed_urls.empty()) {
613 // No point in sending out notification if the starred state didn't change.
614 return;
615 }
616
[email protected]90ef13132008-08-27 03:27:46617 if (profile_) {
618 HistoryService* history =
619 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
620 if (history)
621 history->URLsNoLongerBookmarked(details.changed_urls);
622 }
623
[email protected]bfd04a62009-02-01 18:16:56624 NotificationService::current()->Notify(
625 NotificationType::URLS_STARRED,
[email protected]f25387b2008-08-21 15:20:33626 Source<Profile>(profile_),
627 Details<history::URLsStarredDetails>(&details));
initial.commit09911bf2008-07-26 23:55:29628}
629
[email protected]cb6cf792010-01-28 00:04:56630void BookmarkModel::BeginImportMode() {
631 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
632 BookmarkImportBeginning(this));
633}
634
635void BookmarkModel::EndImportMode() {
636 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
637 BookmarkImportEnding(this));
638}
639
[email protected]d8e41ed2008-09-11 15:22:32640BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
641 int index,
[email protected]848cd05e2008-09-19 18:33:48642 BookmarkNode* node,
643 bool was_bookmarked) {
initial.commit09911bf2008-07-26 23:55:29644 parent->Add(index, node);
645
[email protected]f25387b2008-08-21 15:20:33646 if (store_.get())
647 store_->ScheduleSave();
648
[email protected]d8e41ed2008-09-11 15:22:32649 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29650 BookmarkNodeAdded(this, parent, index));
[email protected]f25387b2008-08-21 15:20:33651
[email protected]01eec882009-05-22 18:13:28652 index_->Add(node);
[email protected]85d911c2009-05-19 03:59:42653
[email protected]037db002009-10-19 20:06:08654 if (node->type() == BookmarkNode::URL && !was_bookmarked) {
[email protected]f25387b2008-08-21 15:20:33655 history::URLsStarredDetails details(true);
656 details.changed_urls.insert(node->GetURL());
[email protected]bfd04a62009-02-01 18:16:56657 NotificationService::current()->Notify(
658 NotificationType::URLS_STARRED,
[email protected]f25387b2008-08-21 15:20:33659 Source<Profile>(profile_),
660 Details<history::URLsStarredDetails>(&details));
661 }
initial.commit09911bf2008-07-26 23:55:29662 return node;
663}
664
[email protected]d8e41ed2008-09-11 15:22:32665void BookmarkModel::BlockTillLoaded() {
[email protected]cbcd6412009-03-09 22:31:39666 loaded_signal_.Wait();
[email protected]90ef13132008-08-27 03:27:46667}
668
[email protected]b3c33d462009-06-26 22:29:20669const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
[email protected]367d7072009-07-13 23:27:13670 int64 id) {
[email protected]f25387b2008-08-21 15:20:33671 if (node->id() == id)
initial.commit09911bf2008-07-26 23:55:29672 return node;
[email protected]f25387b2008-08-21 15:20:33673
[email protected]bd1b96702009-07-08 21:54:14674 for (int i = 0, child_count = node->GetChildCount(); i < child_count; ++i) {
[email protected]b3c33d462009-06-26 22:29:20675 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
initial.commit09911bf2008-07-26 23:55:29676 if (result)
677 return result;
678 }
679 return NULL;
680}
681
[email protected]b3c33d462009-06-26 22:29:20682bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
[email protected]d8e41ed2008-09-11 15:22:32683 int index,
684 bool allow_end) {
[email protected]776e7492008-10-23 16:47:41685 return (parent && parent->is_folder() &&
[email protected]f25387b2008-08-21 15:20:33686 (index >= 0 && (index < parent->GetChildCount() ||
687 (allow_end && index == parent->GetChildCount()))));
[email protected]bd1b96702009-07-08 21:54:14688}
[email protected]f25387b2008-08-21 15:20:33689
[email protected]01eec882009-05-22 18:13:28690BookmarkNode* BookmarkModel::CreateBookmarkNode() {
initial.commit09911bf2008-07-26 23:55:29691 history::StarredEntry entry;
initial.commit09911bf2008-07-26 23:55:29692 entry.type = history::StarredEntry::BOOKMARK_BAR;
[email protected]01eec882009-05-22 18:13:28693 return CreateRootNodeFromStarredEntry(entry);
initial.commit09911bf2008-07-26 23:55:29694}
695
[email protected]01eec882009-05-22 18:13:28696BookmarkNode* BookmarkModel::CreateOtherBookmarksNode() {
initial.commit09911bf2008-07-26 23:55:29697 history::StarredEntry entry;
initial.commit09911bf2008-07-26 23:55:29698 entry.type = history::StarredEntry::OTHER;
[email protected]01eec882009-05-22 18:13:28699 return CreateRootNodeFromStarredEntry(entry);
initial.commit09911bf2008-07-26 23:55:29700}
701
[email protected]d8e41ed2008-09-11 15:22:32702BookmarkNode* BookmarkModel::CreateRootNodeFromStarredEntry(
initial.commit09911bf2008-07-26 23:55:29703 const history::StarredEntry& entry) {
704 DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR ||
705 entry.type == history::StarredEntry::OTHER);
[email protected]ea2e5aa52009-05-20 18:01:28706 BookmarkNode* node = new BookmarkNode(generate_next_node_id(), GURL());
initial.commit09911bf2008-07-26 23:55:29707 node->Reset(entry);
[email protected]7caca8a22010-08-21 18:25:31708 if (entry.type == history::StarredEntry::BOOKMARK_BAR) {
709 node->SetTitle(l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_FOLDER_NAME));
710 } else {
711 node->SetTitle(
712 l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
713 }
initial.commit09911bf2008-07-26 23:55:29714 return node;
715}
716
[email protected]d8e41ed2008-09-11 15:22:32717void BookmarkModel::OnFavIconDataAvailable(
[email protected]0189bc722009-08-28 21:56:48718 FaviconService::Handle handle,
initial.commit09911bf2008-07-26 23:55:29719 bool know_favicon,
[email protected]790879f72010-03-17 20:19:57720 scoped_refptr<RefCountedMemory> data,
initial.commit09911bf2008-07-26 23:55:29721 bool expired,
[email protected]3e377c52009-08-06 07:46:37722 GURL icon_url) {
initial.commit09911bf2008-07-26 23:55:29723 SkBitmap fav_icon;
[email protected]d8e41ed2008-09-11 15:22:32724 BookmarkNode* node =
[email protected]4167c3a2008-08-21 18:12:20725 load_consumer_.GetClientData(
[email protected]0189bc722009-08-28 21:56:48726 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), handle);
[email protected]4167c3a2008-08-21 18:12:20727 DCHECK(node);
[email protected]b3c33d462009-06-26 22:29:20728 node->set_favicon_load_handle(0);
[email protected]7dc7f032009-10-27 06:09:33729 if (know_favicon && data.get() && data->size() &&
[email protected]52d70a7c2009-10-22 17:36:17730 gfx::PNGCodec::Decode(data->front(), data->size(), &fav_icon)) {
[email protected]b3c33d462009-06-26 22:29:20731 node->set_favicon(fav_icon);
initial.commit09911bf2008-07-26 23:55:29732 FavIconLoaded(node);
733 }
734}
735
[email protected]d8e41ed2008-09-11 15:22:32736void BookmarkModel::LoadFavIcon(BookmarkNode* node) {
[email protected]037db002009-10-19 20:06:08737 if (node->type() != BookmarkNode::URL)
initial.commit09911bf2008-07-26 23:55:29738 return;
739
740 DCHECK(node->GetURL().is_valid());
[email protected]0189bc722009-08-28 21:56:48741 FaviconService* favicon_service =
742 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
743 if (!favicon_service)
initial.commit09911bf2008-07-26 23:55:29744 return;
[email protected]0189bc722009-08-28 21:56:48745 FaviconService::Handle handle = favicon_service->GetFaviconForURL(
initial.commit09911bf2008-07-26 23:55:29746 node->GetURL(), &load_consumer_,
[email protected]d8e41ed2008-09-11 15:22:32747 NewCallback(this, &BookmarkModel::OnFavIconDataAvailable));
[email protected]0189bc722009-08-28 21:56:48748 load_consumer_.SetClientData(favicon_service, handle, node);
[email protected]b3c33d462009-06-26 22:29:20749 node->set_favicon_load_handle(handle);
initial.commit09911bf2008-07-26 23:55:29750}
751
[email protected]d8e41ed2008-09-11 15:22:32752void BookmarkModel::CancelPendingFavIconLoadRequests(BookmarkNode* node) {
[email protected]b3c33d462009-06-26 22:29:20753 if (node->favicon_load_handle()) {
[email protected]0189bc722009-08-28 21:56:48754 FaviconService* favicon_service =
755 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
756 if (favicon_service)
757 favicon_service->CancelRequest(node->favicon_load_handle());
[email protected]b3c33d462009-06-26 22:29:20758 node->set_favicon_load_handle(0);
initial.commit09911bf2008-07-26 23:55:29759 }
760}
761
[email protected]d8e41ed2008-09-11 15:22:32762void BookmarkModel::Observe(NotificationType type,
763 const NotificationSource& source,
764 const NotificationDetails& details) {
[email protected]bfd04a62009-02-01 18:16:56765 switch (type.value) {
766 case NotificationType::FAVICON_CHANGED: {
initial.commit09911bf2008-07-26 23:55:29767 // Prevent the observers from getting confused for multiple favicon loads.
768 Details<history::FavIconChangeDetails> favicon_details(details);
769 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
770 i != favicon_details->urls.end(); ++i) {
[email protected]b3c33d462009-06-26 22:29:20771 std::vector<const BookmarkNode*> nodes;
[email protected]848cd05e2008-09-19 18:33:48772 GetNodesByURL(*i, &nodes);
773 for (size_t i = 0; i < nodes.size(); ++i) {
initial.commit09911bf2008-07-26 23:55:29774 // Got an updated favicon, for a URL, do a new request.
[email protected]b3c33d462009-06-26 22:29:20775 BookmarkNode* node = AsMutable(nodes[i]);
initial.commit09911bf2008-07-26 23:55:29776 node->InvalidateFavicon();
777 CancelPendingFavIconLoadRequests(node);
[email protected]d8e41ed2008-09-11 15:22:32778 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
initial.commit09911bf2008-07-26 23:55:29779 BookmarkNodeChanged(this, node));
780 }
781 }
782 break;
783 }
784
785 default:
786 NOTREACHED();
787 break;
788 }
789}
[email protected]f25387b2008-08-21 15:20:33790
[email protected]d8e41ed2008-09-11 15:22:32791void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
[email protected]90ef13132008-08-27 03:27:46792 // NOTE: this is called with url_lock_ already held. As such, this doesn't
793 // explicitly grab the lock.
[email protected]f25387b2008-08-21 15:20:33794 if (node->is_url())
795 nodes_ordered_by_url_set_.insert(node);
796 for (int i = 0; i < node->GetChildCount(); ++i)
797 PopulateNodesByURL(node->GetChild(i));
798}
[email protected]4d89f382009-05-12 06:56:49799
[email protected]367d7072009-07-13 23:27:13800int64 BookmarkModel::generate_next_node_id() {
[email protected]4d89f382009-05-12 06:56:49801 return next_node_id_++;
802}
[email protected]01eec882009-05-22 18:13:28803
[email protected]fc7c36a22009-05-28 20:23:33804void BookmarkModel::SetFileChanged() {
805 file_changed_ = true;
[email protected]fc7c36a22009-05-28 20:23:33806}
807
[email protected]9c92d192009-12-02 08:03:16808BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
[email protected]01eec882009-05-22 18:13:28809 BookmarkNode* bb_node = CreateBookmarkNode();
810 BookmarkNode* other_folder_node = CreateOtherBookmarksNode();
[email protected]9c92d192009-12-02 08:03:16811 return new BookmarkLoadDetails(
[email protected]2c685cc22009-08-28 00:17:44812 bb_node, other_folder_node, new BookmarkIndex(profile()), next_node_id_);
[email protected]01eec882009-05-22 18:13:28813}