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