1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/bookmarks/bookmark_model.h"
6
7 #include <algorithm>
8 #include <functional>
9
10 #include "base/callback.h"
11 #include "base/memory/scoped_vector.h"
12 #include "build/build_config.h"
13 #include "chrome/browser/bookmarks/bookmark_index.h"
14 #include "chrome/browser/bookmarks/bookmark_storage.h"
15 #include "chrome/browser/bookmarks/bookmark_utils.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/history/history_notifications.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "content/common/notification_service.h"
20 #include "grit/generated_resources.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/l10n/l10n_util_collator.h"
23 #include "ui/gfx/codec/png_codec.h"
24
25 using base::Time;
26
27 namespace {
28
29 // Helper to get a mutable bookmark node.
AsMutable(const BookmarkNode * node)30 static BookmarkNode* AsMutable(const BookmarkNode* node) {
31 return const_cast<BookmarkNode*>(node);
32 }
33
34 } // anonymous namespace
35
36 // BookmarkNode ---------------------------------------------------------------
37
BookmarkNode(const GURL & url)38 BookmarkNode::BookmarkNode(const GURL& url)
39 : url_(url) {
40 Initialize(0);
41 }
42
BookmarkNode(int64 id,const GURL & url)43 BookmarkNode::BookmarkNode(int64 id, const GURL& url)
44 : url_(url) {
45 Initialize(id);
46 }
47
~BookmarkNode()48 BookmarkNode::~BookmarkNode() {
49 }
50
Initialize(int64 id)51 void BookmarkNode::Initialize(int64 id) {
52 id_ = id;
53 loaded_favicon_ = false;
54 favicon_load_handle_ = 0;
55 type_ = !url_.is_empty() ? URL : BOOKMARK_BAR;
56 date_added_ = Time::Now();
57 }
58
InvalidateFavicon()59 void BookmarkNode::InvalidateFavicon() {
60 loaded_favicon_ = false;
61 favicon_ = SkBitmap();
62 }
63
Reset(const history::StarredEntry & entry)64 void BookmarkNode::Reset(const history::StarredEntry& entry) {
65 DCHECK(entry.type != history::StarredEntry::URL || entry.url == url_);
66
67 favicon_ = SkBitmap();
68 switch (entry.type) {
69 case history::StarredEntry::URL:
70 type_ = BookmarkNode::URL;
71 break;
72 case history::StarredEntry::USER_FOLDER:
73 type_ = BookmarkNode::FOLDER;
74 break;
75 case history::StarredEntry::BOOKMARK_BAR:
76 type_ = BookmarkNode::BOOKMARK_BAR;
77 break;
78 case history::StarredEntry::OTHER:
79 type_ = BookmarkNode::OTHER_NODE;
80 break;
81 default:
82 NOTREACHED();
83 }
84 date_added_ = entry.date_added;
85 date_folder_modified_ = entry.date_folder_modified;
86 set_title(entry.title);
87 }
88
89 // BookmarkModel --------------------------------------------------------------
90
91 namespace {
92
93 // Comparator used when sorting bookmarks. Folders are sorted first, then
94 // bookmarks.
95 class SortComparator : public std::binary_function<const BookmarkNode*,
96 const BookmarkNode*,
97 bool> {
98 public:
SortComparator(icu::Collator * collator)99 explicit SortComparator(icu::Collator* collator) : collator_(collator) { }
100
101 // Returns true if lhs preceeds rhs.
operator ()(const BookmarkNode * n1,const BookmarkNode * n2)102 bool operator() (const BookmarkNode* n1, const BookmarkNode* n2) {
103 if (n1->type() == n2->type()) {
104 // Types are the same, compare the names.
105 if (!collator_)
106 return n1->GetTitle() < n2->GetTitle();
107 return l10n_util::CompareString16WithCollator(
108 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
109 }
110 // Types differ, sort such that folders come first.
111 return n1->is_folder();
112 }
113
114 private:
115 icu::Collator* collator_;
116 };
117
118 } // namespace
119
BookmarkModel(Profile * profile)120 BookmarkModel::BookmarkModel(Profile* profile)
121 : profile_(profile),
122 loaded_(false),
123 file_changed_(false),
124 root_(GURL()),
125 bookmark_bar_node_(NULL),
126 other_node_(NULL),
127 next_node_id_(1),
128 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
129 loaded_signal_(TRUE, FALSE) {
130 if (!profile_) {
131 // Profile is null during testing.
132 DoneLoading(CreateLoadDetails());
133 }
134 }
135
~BookmarkModel()136 BookmarkModel::~BookmarkModel() {
137 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
138 BookmarkModelBeingDeleted(this));
139
140 if (store_) {
141 // The store maintains a reference back to us. We need to tell it we're gone
142 // so that it doesn't try and invoke a method back on us again.
143 store_->BookmarkModelDeleted();
144 }
145 }
146
Load()147 void BookmarkModel::Load() {
148 if (store_.get()) {
149 // If the store is non-null, it means Load was already invoked. Load should
150 // only be invoked once.
151 NOTREACHED();
152 return;
153 }
154
155 // Listen for changes to favicons so that we can update the favicon of the
156 // node appropriately.
157 registrar_.Add(this, NotificationType::FAVICON_CHANGED,
158 Source<Profile>(profile_));
159
160 // Load the bookmarks. BookmarkStorage notifies us when done.
161 store_ = new BookmarkStorage(profile_, this);
162 store_->LoadBookmarks(CreateLoadDetails());
163 }
164
GetParentForNewNodes()165 const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
166 std::vector<const BookmarkNode*> nodes =
167 bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
168 return nodes.empty() ? bookmark_bar_node_ : nodes[0];
169 }
170
Remove(const BookmarkNode * parent,int index)171 void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
172 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root(parent)) {
173 NOTREACHED();
174 return;
175 }
176 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
177 }
178
Move(const BookmarkNode * node,const BookmarkNode * new_parent,int index)179 void BookmarkModel::Move(const BookmarkNode* node,
180 const BookmarkNode* new_parent,
181 int index) {
182 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
183 is_root(new_parent) || is_permanent_node(node)) {
184 NOTREACHED();
185 return;
186 }
187
188 if (new_parent->HasAncestor(node)) {
189 // Can't make an ancestor of the node be a child of the node.
190 NOTREACHED();
191 return;
192 }
193
194 SetDateFolderModified(new_parent, Time::Now());
195
196 const BookmarkNode* old_parent = node->parent();
197 int old_index = old_parent->GetIndexOf(node);
198
199 if (old_parent == new_parent &&
200 (index == old_index || index == old_index + 1)) {
201 // Node is already in this position, nothing to do.
202 return;
203 }
204
205 if (old_parent == new_parent && index > old_index)
206 index--;
207 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
208 mutable_new_parent->Add(AsMutable(node), index);
209
210 if (store_.get())
211 store_->ScheduleSave();
212
213 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
214 BookmarkNodeMoved(this, old_parent, old_index,
215 new_parent, index));
216 }
217
Copy(const BookmarkNode * node,const BookmarkNode * new_parent,int index)218 void BookmarkModel::Copy(const BookmarkNode* node,
219 const BookmarkNode* new_parent,
220 int index) {
221 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
222 is_root(new_parent) || is_permanent_node(node)) {
223 NOTREACHED();
224 return;
225 }
226
227 if (new_parent->HasAncestor(node)) {
228 // Can't make an ancestor of the node be a child of the node.
229 NOTREACHED();
230 return;
231 }
232
233 SetDateFolderModified(new_parent, Time::Now());
234 BookmarkNodeData drag_data_(node);
235 std::vector<BookmarkNodeData::Element> elements(drag_data_.elements);
236 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
237 // don't need to send notifications here.
238 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index);
239
240 if (store_.get())
241 store_->ScheduleSave();
242 }
243
GetFavicon(const BookmarkNode * node)244 const SkBitmap& BookmarkModel::GetFavicon(const BookmarkNode* node) {
245 DCHECK(node);
246 if (!node->is_favicon_loaded()) {
247 BookmarkNode* mutable_node = AsMutable(node);
248 mutable_node->set_favicon_loaded(true);
249 LoadFavicon(mutable_node);
250 }
251 return node->favicon();
252 }
253
SetTitle(const BookmarkNode * node,const string16 & title)254 void BookmarkModel::SetTitle(const BookmarkNode* node, const string16& title) {
255 if (!node) {
256 NOTREACHED();
257 return;
258 }
259 if (node->GetTitle() == title)
260 return;
261
262 if (node == bookmark_bar_node_ || node == other_node_) {
263 NOTREACHED();
264 return;
265 }
266
267 // The title index doesn't support changing the title, instead we remove then
268 // add it back.
269 index_->Remove(node);
270 AsMutable(node)->set_title(title);
271 index_->Add(node);
272
273 if (store_.get())
274 store_->ScheduleSave();
275
276 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
277 BookmarkNodeChanged(this, node));
278 }
279
SetURL(const BookmarkNode * node,const GURL & url)280 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
281 if (!node) {
282 NOTREACHED();
283 return;
284 }
285
286 // We cannot change the URL of a folder.
287 if (node->is_folder()) {
288 NOTREACHED();
289 return;
290 }
291
292 if (url == node->GetURL())
293 return;
294
295 AsMutable(node)->InvalidateFavicon();
296 CancelPendingFaviconLoadRequests(AsMutable(node));
297
298 {
299 base::AutoLock url_lock(url_lock_);
300 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
301 AsMutable(node));
302 DCHECK(i != nodes_ordered_by_url_set_.end());
303 // i points to the first node with the URL, advance until we find the
304 // node we're removing.
305 while (*i != node)
306 ++i;
307 nodes_ordered_by_url_set_.erase(i);
308
309 AsMutable(node)->SetURL(url);
310 nodes_ordered_by_url_set_.insert(AsMutable(node));
311 }
312
313 if (store_.get())
314 store_->ScheduleSave();
315
316 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
317 BookmarkNodeChanged(this, node));
318 }
319
IsLoaded()320 bool BookmarkModel::IsLoaded() {
321 return loaded_;
322 }
323
GetNodesByURL(const GURL & url,std::vector<const BookmarkNode * > * nodes)324 void BookmarkModel::GetNodesByURL(const GURL& url,
325 std::vector<const BookmarkNode*>* nodes) {
326 base::AutoLock url_lock(url_lock_);
327 BookmarkNode tmp_node(url);
328 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
329 while (i != nodes_ordered_by_url_set_.end() && (*i)->GetURL() == url) {
330 nodes->push_back(*i);
331 ++i;
332 }
333 }
334
GetMostRecentlyAddedNodeForURL(const GURL & url)335 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
336 const GURL& url) {
337 std::vector<const BookmarkNode*> nodes;
338 GetNodesByURL(url, &nodes);
339 if (nodes.empty())
340 return NULL;
341
342 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
343 return nodes.front();
344 }
345
GetBookmarks(std::vector<GURL> * urls)346 void BookmarkModel::GetBookmarks(std::vector<GURL>* urls) {
347 base::AutoLock url_lock(url_lock_);
348 const GURL* last_url = NULL;
349 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
350 i != nodes_ordered_by_url_set_.end(); ++i) {
351 const GURL* url = &((*i)->GetURL());
352 // Only add unique URLs.
353 if (!last_url || *url != *last_url)
354 urls->push_back(*url);
355 last_url = url;
356 }
357 }
358
HasBookmarks()359 bool BookmarkModel::HasBookmarks() {
360 base::AutoLock url_lock(url_lock_);
361 return !nodes_ordered_by_url_set_.empty();
362 }
363
IsBookmarked(const GURL & url)364 bool BookmarkModel::IsBookmarked(const GURL& url) {
365 base::AutoLock url_lock(url_lock_);
366 return IsBookmarkedNoLock(url);
367 }
368
GetNodeByID(int64 id)369 const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) {
370 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
371 return GetNodeByID(&root_, id);
372 }
373
AddFolder(const BookmarkNode * parent,int index,const string16 & title)374 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
375 int index,
376 const string16& title) {
377 if (!loaded_ || parent == &root_ || !IsValidIndex(parent, index, true)) {
378 // Can't add to the root.
379 NOTREACHED();
380 return NULL;
381 }
382
383 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(),
384 GURL());
385 new_node->set_date_folder_modified(Time::Now());
386 new_node->set_title(title);
387 new_node->set_type(BookmarkNode::FOLDER);
388
389 return AddNode(AsMutable(parent), index, new_node, false);
390 }
391
AddURL(const BookmarkNode * parent,int index,const string16 & title,const GURL & url)392 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
393 int index,
394 const string16& title,
395 const GURL& url) {
396 return AddURLWithCreationTime(parent, index, title, url, Time::Now());
397 }
398
AddURLWithCreationTime(const BookmarkNode * parent,int index,const string16 & title,const GURL & url,const Time & creation_time)399 const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
400 const BookmarkNode* parent,
401 int index,
402 const string16& title,
403 const GURL& url,
404 const Time& creation_time) {
405 if (!loaded_ || !url.is_valid() || is_root(parent) ||
406 !IsValidIndex(parent, index, true)) {
407 NOTREACHED();
408 return NULL;
409 }
410
411 bool was_bookmarked = IsBookmarked(url);
412
413 SetDateFolderModified(parent, creation_time);
414
415 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
416 new_node->set_title(title);
417 new_node->set_date_added(creation_time);
418 new_node->set_type(BookmarkNode::URL);
419
420 {
421 // Only hold the lock for the duration of the insert.
422 base::AutoLock url_lock(url_lock_);
423 nodes_ordered_by_url_set_.insert(new_node);
424 }
425
426 return AddNode(AsMutable(parent), index, new_node, was_bookmarked);
427 }
428
SortChildren(const BookmarkNode * parent)429 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
430 if (!parent || !parent->is_folder() || is_root(parent) ||
431 parent->child_count() <= 1) {
432 return;
433 }
434
435 UErrorCode error = U_ZERO_ERROR;
436 scoped_ptr<icu::Collator> collator(
437 icu::Collator::createInstance(
438 icu::Locale(g_browser_process->GetApplicationLocale().c_str()),
439 error));
440 if (U_FAILURE(error))
441 collator.reset(NULL);
442 BookmarkNode* mutable_parent = AsMutable(parent);
443 std::sort(mutable_parent->children().begin(),
444 mutable_parent->children().end(),
445 SortComparator(collator.get()));
446
447 if (store_.get())
448 store_->ScheduleSave();
449
450 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
451 BookmarkNodeChildrenReordered(this, parent));
452 }
453
SetURLStarred(const GURL & url,const string16 & title,bool is_starred)454 void BookmarkModel::SetURLStarred(const GURL& url,
455 const string16& title,
456 bool is_starred) {
457 std::vector<const BookmarkNode*> bookmarks;
458 GetNodesByURL(url, &bookmarks);
459 bool bookmarks_exist = !bookmarks.empty();
460 if (is_starred == bookmarks_exist)
461 return; // Nothing to do, state already matches.
462
463 if (is_starred) {
464 // Create a bookmark.
465 const BookmarkNode* parent = GetParentForNewNodes();
466 AddURL(parent, parent->child_count(), title, url);
467 } else {
468 // Remove all the bookmarks.
469 for (size_t i = 0; i < bookmarks.size(); ++i) {
470 const BookmarkNode* node = bookmarks[i];
471 int index = node->parent()->GetIndexOf(node);
472 if (index > -1)
473 Remove(node->parent(), index);
474 }
475 }
476 }
477
SetDateFolderModified(const BookmarkNode * parent,const Time time)478 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
479 const Time time) {
480 DCHECK(parent);
481 AsMutable(parent)->set_date_folder_modified(time);
482
483 if (store_.get())
484 store_->ScheduleSave();
485 }
486
ResetDateFolderModified(const BookmarkNode * node)487 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
488 SetDateFolderModified(node, Time());
489 }
490
GetBookmarksWithTitlesMatching(const string16 & text,size_t max_count,std::vector<bookmark_utils::TitleMatch> * matches)491 void BookmarkModel::GetBookmarksWithTitlesMatching(
492 const string16& text,
493 size_t max_count,
494 std::vector<bookmark_utils::TitleMatch>* matches) {
495 if (!loaded_)
496 return;
497
498 index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
499 }
500
ClearStore()501 void BookmarkModel::ClearStore() {
502 registrar_.RemoveAll();
503 store_ = NULL;
504 }
505
IsBookmarkedNoLock(const GURL & url)506 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
507 BookmarkNode tmp_node(url);
508 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
509 nodes_ordered_by_url_set_.end());
510 }
511
FaviconLoaded(const BookmarkNode * node)512 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
513 // Send out notification to the observer.
514 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
515 BookmarkNodeFaviconLoaded(this, node));
516 }
517
RemoveNode(BookmarkNode * node,std::set<GURL> * removed_urls)518 void BookmarkModel::RemoveNode(BookmarkNode* node,
519 std::set<GURL>* removed_urls) {
520 if (!loaded_ || !node || is_permanent_node(node)) {
521 NOTREACHED();
522 return;
523 }
524
525 if (node->type() == BookmarkNode::URL) {
526 // NOTE: this is called in such a way that url_lock_ is already held. As
527 // such, this doesn't explicitly grab the lock.
528 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
529 DCHECK(i != nodes_ordered_by_url_set_.end());
530 // i points to the first node with the URL, advance until we find the
531 // node we're removing.
532 while (*i != node)
533 ++i;
534 nodes_ordered_by_url_set_.erase(i);
535 removed_urls->insert(node->GetURL());
536
537 index_->Remove(node);
538 }
539
540 CancelPendingFaviconLoadRequests(node);
541
542 // Recurse through children.
543 for (int i = node->child_count() - 1; i >= 0; --i)
544 RemoveNode(node->GetChild(i), removed_urls);
545 }
546
DoneLoading(BookmarkLoadDetails * details_delete_me)547 void BookmarkModel::DoneLoading(
548 BookmarkLoadDetails* details_delete_me) {
549 DCHECK(details_delete_me);
550 scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
551 if (loaded_) {
552 // We should only ever be loaded once.
553 NOTREACHED();
554 return;
555 }
556
557 next_node_id_ = details->max_id();
558 if (details->computed_checksum() != details->stored_checksum())
559 SetFileChanged();
560 if (details->computed_checksum() != details->stored_checksum() ||
561 details->ids_reassigned()) {
562 // If bookmarks file changed externally, the IDs may have changed
563 // externally. In that case, the decoder may have reassigned IDs to make
564 // them unique. So when the file has changed externally, we should save the
565 // bookmarks file to persist new IDs.
566 if (store_.get())
567 store_->ScheduleSave();
568 }
569 bookmark_bar_node_ = details->release_bb_node();
570 other_node_ = details->release_other_folder_node();
571 index_.reset(details->release_index());
572
573 // WARNING: order is important here, various places assume bookmark bar then
574 // other node.
575 root_.Add(bookmark_bar_node_, 0);
576 root_.Add(other_node_, 1);
577
578 {
579 base::AutoLock url_lock(url_lock_);
580 // Update nodes_ordered_by_url_set_ from the nodes.
581 PopulateNodesByURL(&root_);
582 }
583
584 loaded_ = true;
585
586 loaded_signal_.Signal();
587
588 // Notify our direct observers.
589 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, Loaded(this));
590
591 // And generic notification.
592 NotificationService::current()->Notify(
593 NotificationType::BOOKMARK_MODEL_LOADED,
594 Source<Profile>(profile_),
595 NotificationService::NoDetails());
596 }
597
RemoveAndDeleteNode(BookmarkNode * delete_me)598 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
599 scoped_ptr<BookmarkNode> node(delete_me);
600
601 BookmarkNode* parent = AsMutable(node->parent());
602 DCHECK(parent);
603 int index = parent->GetIndexOf(node.get());
604 parent->Remove(node.get());
605 history::URLsStarredDetails details(false);
606 {
607 base::AutoLock url_lock(url_lock_);
608 RemoveNode(node.get(), &details.changed_urls);
609
610 // RemoveNode adds an entry to changed_urls for each node of type URL. As we
611 // allow duplicates we need to remove any entries that are still bookmarked.
612 for (std::set<GURL>::iterator i = details.changed_urls.begin();
613 i != details.changed_urls.end(); ) {
614 if (IsBookmarkedNoLock(*i)) {
615 // When we erase the iterator pointing at the erasee is
616 // invalidated, so using i++ here within the "erase" call is
617 // important as it advances the iterator before passing the
618 // old value through to erase.
619 details.changed_urls.erase(i++);
620 } else {
621 ++i;
622 }
623 }
624 }
625
626 if (store_.get())
627 store_->ScheduleSave();
628
629 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
630 BookmarkNodeRemoved(this, parent, index, node.get()));
631
632 if (details.changed_urls.empty()) {
633 // No point in sending out notification if the starred state didn't change.
634 return;
635 }
636
637 if (profile_) {
638 HistoryService* history =
639 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
640 if (history)
641 history->URLsNoLongerBookmarked(details.changed_urls);
642 }
643
644 NotificationService::current()->Notify(
645 NotificationType::URLS_STARRED,
646 Source<Profile>(profile_),
647 Details<history::URLsStarredDetails>(&details));
648 }
649
BeginImportMode()650 void BookmarkModel::BeginImportMode() {
651 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
652 BookmarkImportBeginning(this));
653 }
654
EndImportMode()655 void BookmarkModel::EndImportMode() {
656 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
657 BookmarkImportEnding(this));
658 }
659
AddNode(BookmarkNode * parent,int index,BookmarkNode * node,bool was_bookmarked)660 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
661 int index,
662 BookmarkNode* node,
663 bool was_bookmarked) {
664 parent->Add(node, index);
665
666 if (store_.get())
667 store_->ScheduleSave();
668
669 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
670 BookmarkNodeAdded(this, parent, index));
671
672 index_->Add(node);
673
674 if (node->type() == BookmarkNode::URL && !was_bookmarked) {
675 history::URLsStarredDetails details(true);
676 details.changed_urls.insert(node->GetURL());
677 NotificationService::current()->Notify(
678 NotificationType::URLS_STARRED,
679 Source<Profile>(profile_),
680 Details<history::URLsStarredDetails>(&details));
681 }
682 return node;
683 }
684
BlockTillLoaded()685 void BookmarkModel::BlockTillLoaded() {
686 loaded_signal_.Wait();
687 }
688
GetNodeByID(const BookmarkNode * node,int64 id)689 const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
690 int64 id) {
691 if (node->id() == id)
692 return node;
693
694 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
695 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
696 if (result)
697 return result;
698 }
699 return NULL;
700 }
701
IsValidIndex(const BookmarkNode * parent,int index,bool allow_end)702 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
703 int index,
704 bool allow_end) {
705 return (parent && parent->is_folder() &&
706 (index >= 0 && (index < parent->child_count() ||
707 (allow_end && index == parent->child_count()))));
708 }
709
CreateBookmarkNode()710 BookmarkNode* BookmarkModel::CreateBookmarkNode() {
711 history::StarredEntry entry;
712 entry.type = history::StarredEntry::BOOKMARK_BAR;
713 return CreateRootNodeFromStarredEntry(entry);
714 }
715
CreateOtherBookmarksNode()716 BookmarkNode* BookmarkModel::CreateOtherBookmarksNode() {
717 history::StarredEntry entry;
718 entry.type = history::StarredEntry::OTHER;
719 return CreateRootNodeFromStarredEntry(entry);
720 }
721
CreateRootNodeFromStarredEntry(const history::StarredEntry & entry)722 BookmarkNode* BookmarkModel::CreateRootNodeFromStarredEntry(
723 const history::StarredEntry& entry) {
724 DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR ||
725 entry.type == history::StarredEntry::OTHER);
726 BookmarkNode* node = new BookmarkNode(generate_next_node_id(), GURL());
727 node->Reset(entry);
728 if (entry.type == history::StarredEntry::BOOKMARK_BAR) {
729 node->set_title(l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_FOLDER_NAME));
730 } else {
731 node->set_title(
732 l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
733 }
734 return node;
735 }
736
OnFaviconDataAvailable(FaviconService::Handle handle,history::FaviconData favicon)737 void BookmarkModel::OnFaviconDataAvailable(
738 FaviconService::Handle handle,
739 history::FaviconData favicon) {
740 SkBitmap favicon_bitmap;
741 BookmarkNode* node =
742 load_consumer_.GetClientData(
743 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), handle);
744 DCHECK(node);
745 node->set_favicon_load_handle(0);
746 if (favicon.is_valid() && gfx::PNGCodec::Decode(favicon.image_data->front(),
747 favicon.image_data->size(),
748 &favicon_bitmap)) {
749 node->set_favicon(favicon_bitmap);
750 FaviconLoaded(node);
751 }
752 }
753
LoadFavicon(BookmarkNode * node)754 void BookmarkModel::LoadFavicon(BookmarkNode* node) {
755 if (node->type() != BookmarkNode::URL)
756 return;
757
758 DCHECK(node->GetURL().is_valid());
759 FaviconService* favicon_service =
760 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
761 if (!favicon_service)
762 return;
763 FaviconService::Handle handle = favicon_service->GetFaviconForURL(
764 node->GetURL(), history::FAVICON, &load_consumer_,
765 NewCallback(this, &BookmarkModel::OnFaviconDataAvailable));
766 load_consumer_.SetClientData(favicon_service, handle, node);
767 node->set_favicon_load_handle(handle);
768 }
769
CancelPendingFaviconLoadRequests(BookmarkNode * node)770 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
771 if (node->favicon_load_handle()) {
772 FaviconService* favicon_service =
773 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
774 if (favicon_service)
775 favicon_service->CancelRequest(node->favicon_load_handle());
776 node->set_favicon_load_handle(0);
777 }
778 }
779
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)780 void BookmarkModel::Observe(NotificationType type,
781 const NotificationSource& source,
782 const NotificationDetails& details) {
783 switch (type.value) {
784 case NotificationType::FAVICON_CHANGED: {
785 // Prevent the observers from getting confused for multiple favicon loads.
786 Details<history::FaviconChangeDetails> favicon_details(details);
787 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
788 i != favicon_details->urls.end(); ++i) {
789 std::vector<const BookmarkNode*> nodes;
790 GetNodesByURL(*i, &nodes);
791 for (size_t i = 0; i < nodes.size(); ++i) {
792 // Got an updated favicon, for a URL, do a new request.
793 BookmarkNode* node = AsMutable(nodes[i]);
794 node->InvalidateFavicon();
795 CancelPendingFaviconLoadRequests(node);
796 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
797 BookmarkNodeChanged(this, node));
798 }
799 }
800 break;
801 }
802
803 default:
804 NOTREACHED();
805 break;
806 }
807 }
808
PopulateNodesByURL(BookmarkNode * node)809 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
810 // NOTE: this is called with url_lock_ already held. As such, this doesn't
811 // explicitly grab the lock.
812 if (node->is_url())
813 nodes_ordered_by_url_set_.insert(node);
814 for (int i = 0; i < node->child_count(); ++i)
815 PopulateNodesByURL(node->GetChild(i));
816 }
817
generate_next_node_id()818 int64 BookmarkModel::generate_next_node_id() {
819 return next_node_id_++;
820 }
821
SetFileChanged()822 void BookmarkModel::SetFileChanged() {
823 file_changed_ = true;
824 }
825
CreateLoadDetails()826 BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
827 BookmarkNode* bb_node = CreateBookmarkNode();
828 BookmarkNode* other_folder_node = CreateOtherBookmarksNode();
829 return new BookmarkLoadDetails(
830 bb_node, other_folder_node, new BookmarkIndex(profile()), next_node_id_);
831 }
832