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