Home | History | Annotate | Download | only in bookmarks
      1 // Copyright (c) 2012 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/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/i18n/string_compare.h"
     13 #include "base/sequenced_task_runner.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "chrome/browser/bookmarks/bookmark_expanded_state_tracker.h"
     17 #include "chrome/browser/bookmarks/bookmark_index.h"
     18 #include "chrome/browser/bookmarks/bookmark_model_observer.h"
     19 #include "chrome/browser/bookmarks/bookmark_storage.h"
     20 #include "chrome/browser/bookmarks/bookmark_title_match.h"
     21 #include "chrome/browser/bookmarks/bookmark_utils.h"
     22 #include "chrome/browser/chrome_notification_types.h"
     23 #include "chrome/browser/favicon/favicon_changed_details.h"
     24 #include "chrome/browser/favicon/favicon_service.h"
     25 #include "chrome/browser/favicon/favicon_service_factory.h"
     26 #include "chrome/browser/history/history_service.h"
     27 #include "chrome/browser/history/history_service_factory.h"
     28 #include "chrome/browser/profiles/profile.h"
     29 #include "chrome/common/favicon/favicon_types.h"
     30 #include "content/public/browser/notification_details.h"
     31 #include "content/public/browser/notification_source.h"
     32 #include "grit/generated_resources.h"
     33 #include "ui/base/l10n/l10n_util.h"
     34 #include "ui/gfx/favicon_size.h"
     35 #include "ui/gfx/image/image_util.h"
     36 
     37 using base::Time;
     38 
     39 namespace {
     40 
     41 // Helper to get a mutable bookmark node.
     42 BookmarkNode* AsMutable(const BookmarkNode* node) {
     43   return const_cast<BookmarkNode*>(node);
     44 }
     45 
     46 // Whitespace characters to strip from bookmark titles.
     47 const char16 kInvalidChars[] = {
     48   '\n', '\r', '\t',
     49   0x2028,  // Line separator
     50   0x2029,  // Paragraph separator
     51   0
     52 };
     53 
     54 }  // namespace
     55 
     56 // BookmarkNode ---------------------------------------------------------------
     57 
     58 const int64 BookmarkNode::kInvalidSyncTransactionVersion = -1;
     59 
     60 BookmarkNode::BookmarkNode(const GURL& url)
     61     : url_(url) {
     62   Initialize(0);
     63 }
     64 
     65 BookmarkNode::BookmarkNode(int64 id, const GURL& url)
     66     : url_(url) {
     67   Initialize(id);
     68 }
     69 
     70 BookmarkNode::~BookmarkNode() {
     71 }
     72 
     73 void BookmarkNode::SetTitle(const base::string16& title) {
     74   // Replace newlines and other problematic whitespace characters in
     75   // folder/bookmark names with spaces.
     76   base::string16 trimmed_title;
     77   base::ReplaceChars(title, kInvalidChars, ASCIIToUTF16(" "), &trimmed_title);
     78   ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
     79 }
     80 
     81 bool BookmarkNode::IsVisible() const {
     82   return true;
     83 }
     84 
     85 bool BookmarkNode::GetMetaInfo(const std::string& key,
     86                                std::string* value) const {
     87   if (!meta_info_map_)
     88     return false;
     89 
     90   MetaInfoMap::const_iterator it = meta_info_map_->find(key);
     91   if (it == meta_info_map_->end())
     92     return false;
     93 
     94   *value = it->second;
     95   return true;
     96 }
     97 
     98 bool BookmarkNode::SetMetaInfo(const std::string& key,
     99                                const std::string& value) {
    100   if (!meta_info_map_)
    101     meta_info_map_.reset(new MetaInfoMap);
    102 
    103   MetaInfoMap::iterator it = meta_info_map_->find(key);
    104   if (it == meta_info_map_->end()) {
    105     (*meta_info_map_)[key] = value;
    106     return true;
    107   }
    108   // Key already in map, check if the value has changed.
    109   if (it->second == value)
    110     return false;
    111   it->second = value;
    112   return true;
    113 }
    114 
    115 bool BookmarkNode::DeleteMetaInfo(const std::string& key) {
    116   if (!meta_info_map_)
    117     return false;
    118   bool erased = meta_info_map_->erase(key) != 0;
    119   if (meta_info_map_->empty())
    120     meta_info_map_.reset();
    121   return erased;
    122 }
    123 
    124 void BookmarkNode::SetMetaInfoMap(const MetaInfoMap& meta_info_map) {
    125   if (meta_info_map.empty())
    126     meta_info_map_.reset();
    127   else
    128     meta_info_map_.reset(new MetaInfoMap(meta_info_map));
    129 }
    130 
    131 const BookmarkNode::MetaInfoMap* BookmarkNode::GetMetaInfoMap() const {
    132   return meta_info_map_.get();
    133 }
    134 
    135 void BookmarkNode::Initialize(int64 id) {
    136   id_ = id;
    137   type_ = url_.is_empty() ? FOLDER : URL;
    138   date_added_ = Time::Now();
    139   favicon_state_ = INVALID_FAVICON;
    140   favicon_load_task_id_ = CancelableTaskTracker::kBadTaskId;
    141   meta_info_map_.reset();
    142   sync_transaction_version_ = kInvalidSyncTransactionVersion;
    143 }
    144 
    145 void BookmarkNode::InvalidateFavicon() {
    146   icon_url_ = GURL();
    147   favicon_ = gfx::Image();
    148   favicon_state_ = INVALID_FAVICON;
    149 }
    150 
    151 namespace {
    152 
    153 // Comparator used when sorting bookmarks. Folders are sorted first, then
    154 // bookmarks.
    155 class SortComparator : public std::binary_function<const BookmarkNode*,
    156                                                    const BookmarkNode*,
    157                                                    bool> {
    158  public:
    159   explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
    160 
    161   // Returns true if |n1| preceeds |n2|.
    162   bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
    163     if (n1->type() == n2->type()) {
    164       // Types are the same, compare the names.
    165       if (!collator_)
    166         return n1->GetTitle() < n2->GetTitle();
    167       return base::i18n::CompareString16WithCollator(
    168           collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
    169     }
    170     // Types differ, sort such that folders come first.
    171     return n1->is_folder();
    172   }
    173 
    174  private:
    175   icu::Collator* collator_;
    176 };
    177 
    178 }  // namespace
    179 
    180 // BookmarkPermanentNode -------------------------------------------------------
    181 
    182 BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
    183     : BookmarkNode(id, GURL()),
    184       visible_(true) {
    185 }
    186 
    187 BookmarkPermanentNode::~BookmarkPermanentNode() {
    188 }
    189 
    190 bool BookmarkPermanentNode::IsVisible() const {
    191   return visible_ || !empty();
    192 }
    193 
    194 // BookmarkModel --------------------------------------------------------------
    195 
    196 BookmarkModel::BookmarkModel(Profile* profile)
    197     : profile_(profile),
    198       loaded_(false),
    199       root_(GURL()),
    200       bookmark_bar_node_(NULL),
    201       other_node_(NULL),
    202       mobile_node_(NULL),
    203       next_node_id_(1),
    204       observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
    205       loaded_signal_(true, false),
    206       extensive_changes_(0) {
    207   if (!profile_) {
    208     // Profile is null during testing.
    209     DoneLoading(CreateLoadDetails());
    210   }
    211 }
    212 
    213 BookmarkModel::~BookmarkModel() {
    214   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    215                     BookmarkModelBeingDeleted(this));
    216 
    217   if (store_.get()) {
    218     // The store maintains a reference back to us. We need to tell it we're gone
    219     // so that it doesn't try and invoke a method back on us again.
    220     store_->BookmarkModelDeleted();
    221   }
    222 }
    223 
    224 void BookmarkModel::Shutdown() {
    225   if (loaded_)
    226     return;
    227 
    228   // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
    229   // details. It is also called when the BookmarkModel is deleted.
    230   loaded_signal_.Signal();
    231 }
    232 
    233 void BookmarkModel::Load(
    234     const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
    235   if (store_.get()) {
    236     // If the store is non-null, it means Load was already invoked. Load should
    237     // only be invoked once.
    238     NOTREACHED();
    239     return;
    240   }
    241 
    242   expanded_state_tracker_.reset(
    243       new BookmarkExpandedStateTracker(this, profile_->GetPrefs()));
    244 
    245   // Listen for changes to favicons so that we can update the favicon of the
    246   // node appropriately.
    247   registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
    248                  content::Source<Profile>(profile_));
    249 
    250   // Load the bookmarks. BookmarkStorage notifies us when done.
    251   store_ = new BookmarkStorage(profile_, this, task_runner.get());
    252   store_->LoadBookmarks(CreateLoadDetails());
    253 }
    254 
    255 const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
    256   std::vector<const BookmarkNode*> nodes =
    257       bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
    258   DCHECK(!nodes.empty());  // This list is always padded with default folders.
    259   return nodes[0];
    260 }
    261 
    262 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
    263   observers_.AddObserver(observer);
    264 }
    265 
    266 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
    267   observers_.RemoveObserver(observer);
    268 }
    269 
    270 void BookmarkModel::BeginExtensiveChanges() {
    271   if (++extensive_changes_ == 1) {
    272     FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    273                       ExtensiveBookmarkChangesBeginning(this));
    274   }
    275 }
    276 
    277 void BookmarkModel::EndExtensiveChanges() {
    278   --extensive_changes_;
    279   DCHECK_GE(extensive_changes_, 0);
    280   if (extensive_changes_ == 0) {
    281     FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    282                       ExtensiveBookmarkChangesEnded(this));
    283   }
    284 }
    285 
    286 void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
    287   if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
    288     NOTREACHED();
    289     return;
    290   }
    291   RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
    292 }
    293 
    294 void BookmarkModel::RemoveAll() {
    295   std::set<GURL> removed_urls;
    296   ScopedVector<BookmarkNode> removed_nodes;
    297 
    298   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    299                     OnWillRemoveAllBookmarks(this));
    300 
    301   BeginExtensiveChanges();
    302   // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
    303   // its immediate children. For removing all non permanent nodes just remove
    304   // all children of non-root permanent nodes.
    305   {
    306     base::AutoLock url_lock(url_lock_);
    307     for (int i = 0; i < root_.child_count(); ++i) {
    308       const BookmarkNode* permanent_node = root_.GetChild(i);
    309       for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
    310         BookmarkNode* child_node = AsMutable(permanent_node->GetChild(j));
    311         removed_nodes.push_back(child_node);
    312         RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
    313       }
    314     }
    315   }
    316   EndExtensiveChanges();
    317   if (store_.get())
    318     store_->ScheduleSave();
    319 
    320   NotifyHistoryAboutRemovedBookmarks(removed_urls);
    321 
    322   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    323                     BookmarkAllNodesRemoved(this));
    324 }
    325 
    326 void BookmarkModel::Move(const BookmarkNode* node,
    327                          const BookmarkNode* new_parent,
    328                          int index) {
    329   if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
    330       is_root_node(new_parent) || is_permanent_node(node)) {
    331     NOTREACHED();
    332     return;
    333   }
    334 
    335   if (new_parent->HasAncestor(node)) {
    336     // Can't make an ancestor of the node be a child of the node.
    337     NOTREACHED();
    338     return;
    339   }
    340 
    341   const BookmarkNode* old_parent = node->parent();
    342   int old_index = old_parent->GetIndexOf(node);
    343 
    344   if (old_parent == new_parent &&
    345       (index == old_index || index == old_index + 1)) {
    346     // Node is already in this position, nothing to do.
    347     return;
    348   }
    349 
    350   SetDateFolderModified(new_parent, Time::Now());
    351 
    352   if (old_parent == new_parent && index > old_index)
    353     index--;
    354   BookmarkNode* mutable_new_parent = AsMutable(new_parent);
    355   mutable_new_parent->Add(AsMutable(node), index);
    356 
    357   if (store_.get())
    358     store_->ScheduleSave();
    359 
    360   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    361                     BookmarkNodeMoved(this, old_parent, old_index,
    362                                       new_parent, index));
    363 }
    364 
    365 void BookmarkModel::Copy(const BookmarkNode* node,
    366                          const BookmarkNode* new_parent,
    367                          int index) {
    368   if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
    369       is_root_node(new_parent) || is_permanent_node(node)) {
    370     NOTREACHED();
    371     return;
    372   }
    373 
    374   if (new_parent->HasAncestor(node)) {
    375     // Can't make an ancestor of the node be a child of the node.
    376     NOTREACHED();
    377     return;
    378   }
    379 
    380   SetDateFolderModified(new_parent, Time::Now());
    381   BookmarkNodeData drag_data(node);
    382   std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
    383   // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
    384   // don't need to send notifications here.
    385   bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index, true);
    386 
    387   if (store_.get())
    388     store_->ScheduleSave();
    389 }
    390 
    391 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
    392   DCHECK(node);
    393   if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
    394     BookmarkNode* mutable_node = AsMutable(node);
    395     mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
    396     LoadFavicon(mutable_node);
    397   }
    398   return node->favicon();
    399 }
    400 
    401 void BookmarkModel::SetTitle(const BookmarkNode* node, const base::string16& title) {
    402   if (!node) {
    403     NOTREACHED();
    404     return;
    405   }
    406   if (node->GetTitle() == title)
    407     return;
    408 
    409   if (is_permanent_node(node)) {
    410     NOTREACHED();
    411     return;
    412   }
    413 
    414   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    415                     OnWillChangeBookmarkNode(this, node));
    416 
    417   // The title index doesn't support changing the title, instead we remove then
    418   // add it back.
    419   index_->Remove(node);
    420   AsMutable(node)->SetTitle(title);
    421   index_->Add(node);
    422 
    423   if (store_.get())
    424     store_->ScheduleSave();
    425 
    426   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    427                     BookmarkNodeChanged(this, node));
    428 }
    429 
    430 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
    431   if (!node) {
    432     NOTREACHED();
    433     return;
    434   }
    435 
    436   // We cannot change the URL of a folder.
    437   if (node->is_folder()) {
    438     NOTREACHED();
    439     return;
    440   }
    441 
    442   if (node->url() == url)
    443     return;
    444 
    445   BookmarkNode* mutable_node = AsMutable(node);
    446   mutable_node->InvalidateFavicon();
    447   CancelPendingFaviconLoadRequests(mutable_node);
    448 
    449   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    450                     OnWillChangeBookmarkNode(this, node));
    451 
    452   {
    453     base::AutoLock url_lock(url_lock_);
    454     NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
    455         mutable_node);
    456     DCHECK(i != nodes_ordered_by_url_set_.end());
    457     // i points to the first node with the URL, advance until we find the
    458     // node we're removing.
    459     while (*i != node)
    460       ++i;
    461     nodes_ordered_by_url_set_.erase(i);
    462 
    463     mutable_node->set_url(url);
    464     nodes_ordered_by_url_set_.insert(mutable_node);
    465   }
    466 
    467   if (store_.get())
    468     store_->ScheduleSave();
    469 
    470   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    471                     BookmarkNodeChanged(this, node));
    472 }
    473 
    474 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
    475                                     const std::string& key,
    476                                     const std::string& value) {
    477   std::string old_value;
    478   if (node->GetMetaInfo(key, &old_value) && old_value == value)
    479     return;
    480 
    481   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    482                     OnWillChangeBookmarkMetaInfo(this, node));
    483 
    484   if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
    485     store_->ScheduleSave();
    486 
    487   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    488                     BookmarkMetaInfoChanged(this, node));
    489 }
    490 
    491 void BookmarkModel::SetNodeMetaInfoMap(
    492     const BookmarkNode* node,
    493     const BookmarkNode::MetaInfoMap& meta_info_map) {
    494   const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
    495   if ((!old_meta_info_map && meta_info_map.empty()) ||
    496       (old_meta_info_map && meta_info_map == *old_meta_info_map))
    497     return;
    498 
    499   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    500                     OnWillChangeBookmarkMetaInfo(this, node));
    501 
    502   AsMutable(node)->SetMetaInfoMap(meta_info_map);
    503   if (store_.get())
    504     store_->ScheduleSave();
    505 
    506   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    507                     BookmarkMetaInfoChanged(this, node));
    508 }
    509 
    510 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
    511                                        const std::string& key) {
    512   const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
    513   if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
    514     return;
    515 
    516   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    517                     OnWillChangeBookmarkMetaInfo(this, node));
    518 
    519   if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
    520     store_->ScheduleSave();
    521 
    522   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    523                     BookmarkMetaInfoChanged(this, node));
    524 }
    525 
    526 void BookmarkModel::SetNodeSyncTransactionVersion(
    527     const BookmarkNode* node,
    528     int64 sync_transaction_version) {
    529   if (sync_transaction_version == node->sync_transaction_version())
    530     return;
    531 
    532   AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
    533   if (store_.get())
    534     store_->ScheduleSave();
    535 }
    536 
    537 void BookmarkModel::SetDateAdded(const BookmarkNode* node,
    538                                  base::Time date_added) {
    539   if (!node) {
    540     NOTREACHED();
    541     return;
    542   }
    543 
    544   if (node->date_added() == date_added)
    545     return;
    546 
    547   if (is_permanent_node(node)) {
    548     NOTREACHED();
    549     return;
    550   }
    551 
    552   AsMutable(node)->set_date_added(date_added);
    553 
    554   // Syncing might result in dates newer than the folder's last modified date.
    555   if (date_added > node->parent()->date_folder_modified()) {
    556     // Will trigger store_->ScheduleSave().
    557     SetDateFolderModified(node->parent(), date_added);
    558   } else if (store_.get()) {
    559     store_->ScheduleSave();
    560   }
    561 }
    562 
    563 void BookmarkModel::GetNodesByURL(const GURL& url,
    564                                   std::vector<const BookmarkNode*>* nodes) {
    565   base::AutoLock url_lock(url_lock_);
    566   BookmarkNode tmp_node(url);
    567   NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
    568   while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
    569     nodes->push_back(*i);
    570     ++i;
    571   }
    572 }
    573 
    574 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
    575     const GURL& url) {
    576   std::vector<const BookmarkNode*> nodes;
    577   GetNodesByURL(url, &nodes);
    578   if (nodes.empty())
    579     return NULL;
    580 
    581   std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
    582   return nodes.front();
    583 }
    584 
    585 bool BookmarkModel::HasBookmarks() {
    586   base::AutoLock url_lock(url_lock_);
    587   return !nodes_ordered_by_url_set_.empty();
    588 }
    589 
    590 bool BookmarkModel::IsBookmarked(const GURL& url) {
    591   base::AutoLock url_lock(url_lock_);
    592   return IsBookmarkedNoLock(url);
    593 }
    594 
    595 void BookmarkModel::GetBookmarks(
    596     std::vector<BookmarkService::URLAndTitle>* bookmarks) {
    597   base::AutoLock url_lock(url_lock_);
    598   const GURL* last_url = NULL;
    599   for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
    600        i != nodes_ordered_by_url_set_.end(); ++i) {
    601     const GURL* url = &((*i)->url());
    602     // Only add unique URLs.
    603     if (!last_url || *url != *last_url) {
    604       BookmarkService::URLAndTitle bookmark;
    605       bookmark.url = *url;
    606       bookmark.title = (*i)->GetTitle();
    607       bookmarks->push_back(bookmark);
    608     }
    609     last_url = url;
    610   }
    611 }
    612 
    613 void BookmarkModel::BlockTillLoaded() {
    614   loaded_signal_.Wait();
    615 }
    616 
    617 const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
    618   // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
    619   return GetNodeByID(&root_, id);
    620 }
    621 
    622 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
    623                                              int index,
    624                                              const base::string16& title) {
    625   if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
    626     // Can't add to the root.
    627     NOTREACHED();
    628     return NULL;
    629   }
    630 
    631   BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
    632   new_node->set_date_folder_modified(Time::Now());
    633   // Folders shouldn't have line breaks in their titles.
    634   new_node->SetTitle(title);
    635   new_node->set_type(BookmarkNode::FOLDER);
    636 
    637   return AddNode(AsMutable(parent), index, new_node);
    638 }
    639 
    640 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
    641                                           int index,
    642                                           const base::string16& title,
    643                                           const GURL& url) {
    644   return AddURLWithCreationTime(parent, index,
    645                                 CollapseWhitespace(title, false),
    646                                 url, Time::Now());
    647 }
    648 
    649 const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
    650     const BookmarkNode* parent,
    651     int index,
    652     const base::string16& title,
    653     const GURL& url,
    654     const Time& creation_time) {
    655   if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
    656       !IsValidIndex(parent, index, true)) {
    657     NOTREACHED();
    658     return NULL;
    659   }
    660 
    661   // Syncing may result in dates newer than the last modified date.
    662   if (creation_time > parent->date_folder_modified())
    663     SetDateFolderModified(parent, creation_time);
    664 
    665   BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
    666   new_node->SetTitle(title);
    667   new_node->set_date_added(creation_time);
    668   new_node->set_type(BookmarkNode::URL);
    669 
    670   {
    671     // Only hold the lock for the duration of the insert.
    672     base::AutoLock url_lock(url_lock_);
    673     nodes_ordered_by_url_set_.insert(new_node);
    674   }
    675 
    676   return AddNode(AsMutable(parent), index, new_node);
    677 }
    678 
    679 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
    680   if (!parent || !parent->is_folder() || is_root_node(parent) ||
    681       parent->child_count() <= 1) {
    682     return;
    683   }
    684 
    685   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    686                     OnWillReorderBookmarkNode(this, parent));
    687 
    688   UErrorCode error = U_ZERO_ERROR;
    689   scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
    690   if (U_FAILURE(error))
    691     collator.reset(NULL);
    692   BookmarkNode* mutable_parent = AsMutable(parent);
    693   std::sort(mutable_parent->children().begin(),
    694             mutable_parent->children().end(),
    695             SortComparator(collator.get()));
    696 
    697   if (store_.get())
    698     store_->ScheduleSave();
    699 
    700   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    701                     BookmarkNodeChildrenReordered(this, parent));
    702 }
    703 
    704 void BookmarkModel::ReorderChildren(
    705     const BookmarkNode* parent,
    706     const std::vector<const BookmarkNode*>& ordered_nodes) {
    707   // Ensure that all children in |parent| are in |ordered_nodes|.
    708   DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
    709   for (size_t i = 0; i < ordered_nodes.size(); ++i)
    710     DCHECK_EQ(parent, ordered_nodes[i]->parent());
    711 
    712   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    713                     OnWillReorderBookmarkNode(this, parent));
    714 
    715   AsMutable(parent)->SetChildren(
    716       *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
    717 
    718   if (store_.get())
    719     store_->ScheduleSave();
    720 
    721   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    722                     BookmarkNodeChildrenReordered(this, parent));
    723 }
    724 
    725 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
    726                                           const Time time) {
    727   DCHECK(parent);
    728   AsMutable(parent)->set_date_folder_modified(time);
    729 
    730   if (store_.get())
    731     store_->ScheduleSave();
    732 }
    733 
    734 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
    735   SetDateFolderModified(node, Time());
    736 }
    737 
    738 void BookmarkModel::GetBookmarksWithTitlesMatching(
    739     const base::string16& text,
    740     size_t max_count,
    741     std::vector<BookmarkTitleMatch>* matches) {
    742   if (!loaded_)
    743     return;
    744 
    745   index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
    746 }
    747 
    748 void BookmarkModel::ClearStore() {
    749   registrar_.RemoveAll();
    750   store_ = NULL;
    751 }
    752 
    753 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
    754                                             bool value) {
    755   DCHECK(loaded_);
    756   switch (type) {
    757     case BookmarkNode::BOOKMARK_BAR:
    758       bookmark_bar_node_->set_visible(value);
    759       break;
    760     case BookmarkNode::OTHER_NODE:
    761       other_node_->set_visible(value);
    762       break;
    763     case BookmarkNode::MOBILE:
    764       mobile_node_->set_visible(value);
    765       break;
    766     default:
    767       NOTREACHED();
    768   }
    769 }
    770 
    771 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
    772   BookmarkNode tmp_node(url);
    773   return (nodes_ordered_by_url_set_.find(&tmp_node) !=
    774           nodes_ordered_by_url_set_.end());
    775 }
    776 
    777 void BookmarkModel::RemoveNode(BookmarkNode* node,
    778                                std::set<GURL>* removed_urls) {
    779   if (!loaded_ || !node || is_permanent_node(node)) {
    780     NOTREACHED();
    781     return;
    782   }
    783 
    784   url_lock_.AssertAcquired();
    785   if (node->is_url()) {
    786     // NOTE: this is called in such a way that url_lock_ is already held. As
    787     // such, this doesn't explicitly grab the lock.
    788     NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
    789     DCHECK(i != nodes_ordered_by_url_set_.end());
    790     // i points to the first node with the URL, advance until we find the
    791     // node we're removing.
    792     while (*i != node)
    793       ++i;
    794     nodes_ordered_by_url_set_.erase(i);
    795     removed_urls->insert(node->url());
    796 
    797     index_->Remove(node);
    798   }
    799 
    800   CancelPendingFaviconLoadRequests(node);
    801 
    802   // Recurse through children.
    803   for (int i = node->child_count() - 1; i >= 0; --i)
    804     RemoveNode(node->GetChild(i), removed_urls);
    805 }
    806 
    807 void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
    808   DCHECK(details_delete_me);
    809   scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
    810   if (loaded_) {
    811     // We should only ever be loaded once.
    812     NOTREACHED();
    813     return;
    814   }
    815 
    816   next_node_id_ = details->max_id();
    817   if (details->computed_checksum() != details->stored_checksum() ||
    818       details->ids_reassigned()) {
    819     // If bookmarks file changed externally, the IDs may have changed
    820     // externally. In that case, the decoder may have reassigned IDs to make
    821     // them unique. So when the file has changed externally, we should save the
    822     // bookmarks file to persist new IDs.
    823     if (store_.get())
    824       store_->ScheduleSave();
    825   }
    826   bookmark_bar_node_ = details->release_bb_node();
    827   other_node_ = details->release_other_folder_node();
    828   mobile_node_ = details->release_mobile_folder_node();
    829   index_.reset(details->release_index());
    830 
    831   // WARNING: order is important here, various places assume the order is
    832   // constant.
    833   root_.Add(bookmark_bar_node_, 0);
    834   root_.Add(other_node_, 1);
    835   root_.Add(mobile_node_, 2);
    836 
    837   root_.SetMetaInfoMap(details->model_meta_info_map());
    838   root_.set_sync_transaction_version(details->model_sync_transaction_version());
    839 
    840   {
    841     base::AutoLock url_lock(url_lock_);
    842     // Update nodes_ordered_by_url_set_ from the nodes.
    843     PopulateNodesByURL(&root_);
    844   }
    845 
    846   loaded_ = true;
    847 
    848   loaded_signal_.Signal();
    849 
    850   // Notify our direct observers.
    851   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    852                     Loaded(this, details->ids_reassigned()));
    853 }
    854 
    855 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
    856   scoped_ptr<BookmarkNode> node(delete_me);
    857 
    858   const BookmarkNode* parent = node->parent();
    859   DCHECK(parent);
    860   int index = parent->GetIndexOf(node.get());
    861 
    862   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    863                     OnWillRemoveBookmarks(this, parent, index, node.get()));
    864 
    865   std::set<GURL> removed_urls;
    866   {
    867     base::AutoLock url_lock(url_lock_);
    868     RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
    869   }
    870 
    871   if (store_.get())
    872     store_->ScheduleSave();
    873 
    874   NotifyHistoryAboutRemovedBookmarks(removed_urls);
    875 
    876   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    877                     BookmarkNodeRemoved(this, parent, index, node.get()));
    878 }
    879 
    880 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
    881                                                 std::set<GURL>* removed_urls) {
    882   // NOTE: this method should be always called with |url_lock_| held.
    883   // This method does not explicitly acquires a lock.
    884   url_lock_.AssertAcquired();
    885   DCHECK(removed_urls);
    886   BookmarkNode* parent = AsMutable(node->parent());
    887   DCHECK(parent);
    888   parent->Remove(node);
    889   RemoveNode(node, removed_urls);
    890   // RemoveNode adds an entry to removed_urls for each node of type URL. As we
    891   // allow duplicates we need to remove any entries that are still bookmarked.
    892   for (std::set<GURL>::iterator i = removed_urls->begin();
    893        i != removed_urls->end();) {
    894     if (IsBookmarkedNoLock(*i)) {
    895       // When we erase the iterator pointing at the erasee is
    896       // invalidated, so using i++ here within the "erase" call is
    897       // important as it advances the iterator before passing the
    898       // old value through to erase.
    899       removed_urls->erase(i++);
    900     } else {
    901       ++i;
    902     }
    903   }
    904 }
    905 
    906 void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
    907     const std::set<GURL>& removed_bookmark_urls) const {
    908   if (removed_bookmark_urls.empty()) {
    909     // No point in sending out notification if the starred state didn't change.
    910     return;
    911   }
    912 
    913   if (profile_) {
    914     HistoryService* history =
    915         HistoryServiceFactory::GetForProfile(profile_,
    916                                              Profile::EXPLICIT_ACCESS);
    917     if (history)
    918       history->URLsNoLongerBookmarked(removed_bookmark_urls);
    919   }
    920 }
    921 
    922 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
    923                                      int index,
    924                                      BookmarkNode* node) {
    925   parent->Add(node, index);
    926 
    927   if (store_.get())
    928     store_->ScheduleSave();
    929 
    930   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
    931                     BookmarkNodeAdded(this, parent, index));
    932 
    933   index_->Add(node);
    934 
    935   return node;
    936 }
    937 
    938 const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
    939                                                int64 id) const {
    940   if (node->id() == id)
    941     return node;
    942 
    943   for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
    944     const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
    945     if (result)
    946       return result;
    947   }
    948   return NULL;
    949 }
    950 
    951 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
    952                                  int index,
    953                                  bool allow_end) {
    954   return (parent && parent->is_folder() &&
    955           (index >= 0 && (index < parent->child_count() ||
    956                           (allow_end && index == parent->child_count()))));
    957 }
    958 
    959 BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
    960     BookmarkNode::Type type) {
    961   DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
    962          type == BookmarkNode::OTHER_NODE ||
    963          type == BookmarkNode::MOBILE);
    964   BookmarkPermanentNode* node =
    965       new BookmarkPermanentNode(generate_next_node_id());
    966   if (type == BookmarkNode::MOBILE)
    967     node->set_visible(false);  // Mobile node is initially hidden.
    968 
    969   int title_id;
    970   switch (type) {
    971     case BookmarkNode::BOOKMARK_BAR:
    972       title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
    973       break;
    974     case BookmarkNode::OTHER_NODE:
    975       title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
    976       break;
    977     case BookmarkNode::MOBILE:
    978       title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
    979       break;
    980     default:
    981       NOTREACHED();
    982       title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
    983       break;
    984   }
    985   node->SetTitle(l10n_util::GetStringUTF16(title_id));
    986   node->set_type(type);
    987   return node;
    988 }
    989 
    990 void BookmarkModel::OnFaviconDataAvailable(
    991     BookmarkNode* node,
    992     const chrome::FaviconImageResult& image_result) {
    993   DCHECK(node);
    994   node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
    995   node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
    996   if (!image_result.image.IsEmpty()) {
    997     node->set_favicon(image_result.image);
    998     node->set_icon_url(image_result.icon_url);
    999     FaviconLoaded(node);
   1000   }
   1001 }
   1002 
   1003 void BookmarkModel::LoadFavicon(BookmarkNode* node) {
   1004   if (node->is_folder())
   1005     return;
   1006 
   1007   DCHECK(node->url().is_valid());
   1008   FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
   1009       profile_, Profile::EXPLICIT_ACCESS);
   1010   if (!favicon_service)
   1011     return;
   1012   FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
   1013       FaviconService::FaviconForURLParams(node->url(),
   1014                                           chrome::FAVICON,
   1015                                           gfx::kFaviconSize),
   1016       base::Bind(&BookmarkModel::OnFaviconDataAvailable,
   1017                  base::Unretained(this), node),
   1018       &cancelable_task_tracker_);
   1019   node->set_favicon_load_task_id(handle);
   1020 }
   1021 
   1022 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
   1023   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
   1024                     BookmarkNodeFaviconChanged(this, node));
   1025 }
   1026 
   1027 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
   1028   if (node->favicon_load_task_id() != CancelableTaskTracker::kBadTaskId) {
   1029     cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
   1030     node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
   1031   }
   1032 }
   1033 
   1034 void BookmarkModel::Observe(int type,
   1035                             const content::NotificationSource& source,
   1036                             const content::NotificationDetails& details) {
   1037   switch (type) {
   1038     case chrome::NOTIFICATION_FAVICON_CHANGED: {
   1039       // Prevent the observers from getting confused for multiple favicon loads.
   1040       content::Details<FaviconChangedDetails> favicon_details(details);
   1041       for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
   1042            i != favicon_details->urls.end(); ++i) {
   1043         std::vector<const BookmarkNode*> nodes;
   1044         GetNodesByURL(*i, &nodes);
   1045         for (size_t i = 0; i < nodes.size(); ++i) {
   1046           // Got an updated favicon, for a URL, do a new request.
   1047           BookmarkNode* node = AsMutable(nodes[i]);
   1048           node->InvalidateFavicon();
   1049           CancelPendingFaviconLoadRequests(node);
   1050           FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
   1051                             BookmarkNodeFaviconChanged(this, node));
   1052         }
   1053       }
   1054       break;
   1055     }
   1056 
   1057     default:
   1058       NOTREACHED();
   1059       break;
   1060   }
   1061 }
   1062 
   1063 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
   1064   // NOTE: this is called with url_lock_ already held. As such, this doesn't
   1065   // explicitly grab the lock.
   1066   if (node->is_url())
   1067     nodes_ordered_by_url_set_.insert(node);
   1068   for (int i = 0; i < node->child_count(); ++i)
   1069     PopulateNodesByURL(node->GetChild(i));
   1070 }
   1071 
   1072 int64 BookmarkModel::generate_next_node_id() {
   1073   return next_node_id_++;
   1074 }
   1075 
   1076 BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
   1077   BookmarkPermanentNode* bb_node =
   1078       CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
   1079   BookmarkPermanentNode* other_node =
   1080       CreatePermanentNode(BookmarkNode::OTHER_NODE);
   1081   BookmarkPermanentNode* mobile_node =
   1082       CreatePermanentNode(BookmarkNode::MOBILE);
   1083   return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
   1084                                  new BookmarkIndex(profile_),
   1085                                  next_node_id_);
   1086 }
   1087