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