• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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