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