Home | History | Annotate | Download | only in bookmarks
      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.
     30 static BookmarkNode* AsMutable(const BookmarkNode* node) {
     31   return const_cast<BookmarkNode*>(node);
     32 }
     33 
     34 }  // anonymous namespace
     35 
     36 // BookmarkNode ---------------------------------------------------------------
     37 
     38 BookmarkNode::BookmarkNode(const GURL& url)
     39     : url_(url) {
     40   Initialize(0);
     41 }
     42 
     43 BookmarkNode::BookmarkNode(int64 id, const GURL& url)
     44     : url_(url) {
     45   Initialize(id);
     46 }
     47 
     48 BookmarkNode::~BookmarkNode() {
     49 }
     50 
     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 
     59 void BookmarkNode::InvalidateFavicon() {
     60   loaded_favicon_ = false;
     61   favicon_ = SkBitmap();
     62 }
     63 
     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:
     99   explicit SortComparator(icu::Collator* collator) : collator_(collator) { }
    100 
    101   // Returns true if lhs preceeds rhs.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    320 bool BookmarkModel::IsLoaded() {
    321   return loaded_;
    322 }
    323 
    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 
    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 
    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 
    359 bool BookmarkModel::HasBookmarks() {
    360   base::AutoLock url_lock(url_lock_);
    361   return !nodes_ordered_by_url_set_.empty();
    362 }
    363 
    364 bool BookmarkModel::IsBookmarked(const GURL& url) {
    365   base::AutoLock url_lock(url_lock_);
    366   return IsBookmarkedNoLock(url);
    367 }
    368 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    487 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
    488   SetDateFolderModified(node, Time());
    489 }
    490 
    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 
    501 void BookmarkModel::ClearStore() {
    502   registrar_.RemoveAll();
    503   store_ = NULL;
    504 }
    505 
    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 
    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 
    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 
    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 
    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 
    650 void BookmarkModel::BeginImportMode() {
    651   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    652                     BookmarkImportBeginning(this));
    653 }
    654 
    655 void BookmarkModel::EndImportMode() {
    656   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    657                     BookmarkImportEnding(this));
    658 }
    659 
    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 
    685 void BookmarkModel::BlockTillLoaded() {
    686   loaded_signal_.Wait();
    687 }
    688 
    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 
    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 
    710 BookmarkNode* BookmarkModel::CreateBookmarkNode() {
    711   history::StarredEntry entry;
    712   entry.type = history::StarredEntry::BOOKMARK_BAR;
    713   return CreateRootNodeFromStarredEntry(entry);
    714 }
    715 
    716 BookmarkNode* BookmarkModel::CreateOtherBookmarksNode() {
    717   history::StarredEntry entry;
    718   entry.type = history::StarredEntry::OTHER;
    719   return CreateRootNodeFromStarredEntry(entry);
    720 }
    721 
    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 
    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 
    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 
    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 
    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 
    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 
    818 int64 BookmarkModel::generate_next_node_id() {
    819   return next_node_id_++;
    820 }
    821 
    822 void BookmarkModel::SetFileChanged() {
    823   file_changed_ = true;
    824 }
    825 
    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