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