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 #include <memory>
10 #include <string>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/guid.h"
16 #include "base/i18n/string_compare.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/strings/string_util.h"
21 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
22 #include "components/bookmarks/browser/bookmark_model_observer.h"
23 #include "components/bookmarks/browser/bookmark_node_data.h"
24 #include "components/bookmarks/browser/bookmark_storage.h"
25 #include "components/bookmarks/browser/bookmark_undo_delegate.h"
26 #include "components/bookmarks/browser/bookmark_utils.h"
27 #include "components/bookmarks/browser/model_loader.h"
28 #include "components/bookmarks/browser/titled_url_index.h"
29 #include "components/bookmarks/browser/titled_url_match.h"
30 #include "components/bookmarks/browser/typed_count_sorter.h"
31 #include "components/bookmarks/browser/url_and_title.h"
32 #include "components/bookmarks/browser/url_index.h"
33 #include "components/bookmarks/common/bookmark_constants.h"
34 #include "components/favicon_base/favicon_types.h"
35 #include "components/strings/grit/components_strings.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/gfx/favicon_size.h"
38 
39 using base::Time;
40 
41 namespace bookmarks {
42 
43 namespace {
44 
45 // Helper to get a mutable bookmark node.
AsMutable(const BookmarkNode * node)46 BookmarkNode* AsMutable(const BookmarkNode* node) {
47   return const_cast<BookmarkNode*>(node);
48 }
49 
50 // Comparator used when sorting permanent nodes. Nodes that are initially
51 // visible are sorted before nodes that are initially hidden.
52 class VisibilityComparator {
53  public:
VisibilityComparator(BookmarkClient * client)54   explicit VisibilityComparator(BookmarkClient* client) : client_(client) {}
55 
56   // Returns true if |n1| precedes |n2|.
operator ()(const std::unique_ptr<BookmarkNode> & n1,const std::unique_ptr<BookmarkNode> & n2)57   bool operator()(const std::unique_ptr<BookmarkNode>& n1,
58                   const std::unique_ptr<BookmarkNode>& n2) {
59     DCHECK(n1->is_permanent_node());
60     DCHECK(n2->is_permanent_node());
61     bool n1_visible = client_->IsPermanentNodeVisibleWhenEmpty(n1->type());
62     bool n2_visible = client_->IsPermanentNodeVisibleWhenEmpty(n2->type());
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 {
73  public:
SortComparator(icu::Collator * collator)74   explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
75 
76   // Returns true if |n1| precedes |n2|.
operator ()(const std::unique_ptr<BookmarkNode> & n1,const std::unique_ptr<BookmarkNode> & n2)77   bool operator()(const std::unique_ptr<BookmarkNode>& n1,
78                   const std::unique_ptr<BookmarkNode>& n2) {
79     if (n1->type() == n2->type()) {
80       // Types are the same, compare the names.
81       if (!collator_)
82         return n1->GetTitle() < n2->GetTitle();
83       return base::i18n::CompareString16WithCollator(
84                  *collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
85     }
86     // Types differ, sort such that folders come first.
87     return n1->is_folder();
88   }
89 
90  private:
91   icu::Collator* collator_;
92 };
93 
94 // Delegate that does nothing.
95 class EmptyUndoDelegate : public BookmarkUndoDelegate {
96  public:
EmptyUndoDelegate()97   EmptyUndoDelegate() {}
~EmptyUndoDelegate()98   ~EmptyUndoDelegate() override {}
99 
100  private:
101   // BookmarkUndoDelegate:
SetUndoProvider(BookmarkUndoProvider * provider)102   void SetUndoProvider(BookmarkUndoProvider* provider) override {}
OnBookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,size_t index,std::unique_ptr<BookmarkNode> node)103   void OnBookmarkNodeRemoved(BookmarkModel* model,
104                              const BookmarkNode* parent,
105                              size_t index,
106                              std::unique_ptr<BookmarkNode> node) override {}
107 
108   DISALLOW_COPY_AND_ASSIGN(EmptyUndoDelegate);
109 };
110 
111 #if DCHECK_IS_ON()
AddGuidsToIndexRecursive(const BookmarkNode * node,std::set<std::string> * guid_index)112 void AddGuidsToIndexRecursive(const BookmarkNode* node,
113                               std::set<std::string>* guid_index) {
114   bool success = guid_index->insert(node->guid()).second;
115   DCHECK(success);
116 
117   // Recurse through children.
118   for (size_t i = node->children().size(); i > 0; --i)
119     AddGuidsToIndexRecursive(node->children()[i - 1].get(), guid_index);
120 }
121 #endif  // DCHECK_IS_ON()
122 
123 }  // namespace
124 
125 // BookmarkModel --------------------------------------------------------------
126 
BookmarkModel(std::unique_ptr<BookmarkClient> client)127 BookmarkModel::BookmarkModel(std::unique_ptr<BookmarkClient> client)
128     : client_(std::move(client)),
129       owned_root_(std::make_unique<BookmarkNode>(/*id=*/0,
130                                                  BookmarkNode::RootNodeGuid(),
131                                                  GURL())),
132       root_(owned_root_.get()),
133       observers_(base::ObserverListPolicy::EXISTING_ONLY),
134       empty_undo_delegate_(std::make_unique<EmptyUndoDelegate>()) {
135   DCHECK(client_);
136   client_->Init(this);
137 }
138 
~BookmarkModel()139 BookmarkModel::~BookmarkModel() {
140   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
141 
142   for (BookmarkModelObserver& observer : observers_)
143     observer.BookmarkModelBeingDeleted(this);
144 
145   if (store_) {
146     // The store maintains a reference back to us. We need to tell it we're gone
147     // so that it doesn't try and invoke a method back on us again.
148     store_->BookmarkModelDeleted();
149   }
150 }
151 
Load(PrefService * pref_service,const base::FilePath & profile_path,const scoped_refptr<base::SequencedTaskRunner> & io_task_runner,const scoped_refptr<base::SequencedTaskRunner> & ui_task_runner)152 void BookmarkModel::Load(
153     PrefService* pref_service,
154     const base::FilePath& profile_path,
155     const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
156     const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner) {
157   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
158   // If the store is non-null, it means Load was already invoked. Load should
159   // only be invoked once.
160   DCHECK(!store_);
161 
162   expanded_state_tracker_ =
163       std::make_unique<BookmarkExpandedStateTracker>(this, pref_service);
164 
165   store_ = std::make_unique<BookmarkStorage>(this, profile_path,
166                                              io_task_runner.get());
167   // Creating ModelLoader schedules the load on |io_task_runner|.
168   model_loader_ = ModelLoader::Create(
169       profile_path.Append(kBookmarksFileName), io_task_runner.get(),
170       std::make_unique<BookmarkLoadDetails>(client_.get()),
171       base::BindOnce(&BookmarkModel::DoneLoading, AsWeakPtr()));
172 }
173 
model_loader()174 scoped_refptr<ModelLoader> BookmarkModel::model_loader() {
175   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
176   return model_loader_;
177 }
178 
AddObserver(BookmarkModelObserver * observer)179 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
180   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
181   observers_.AddObserver(observer);
182 }
183 
RemoveObserver(BookmarkModelObserver * observer)184 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
185   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
186   observers_.RemoveObserver(observer);
187 }
188 
BeginExtensiveChanges()189 void BookmarkModel::BeginExtensiveChanges() {
190   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
191   if (++extensive_changes_ == 1) {
192     for (BookmarkModelObserver& observer : observers_)
193       observer.ExtensiveBookmarkChangesBeginning(this);
194   }
195 }
196 
EndExtensiveChanges()197 void BookmarkModel::EndExtensiveChanges() {
198   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
199   --extensive_changes_;
200   DCHECK_GE(extensive_changes_, 0);
201   if (extensive_changes_ == 0) {
202     for (BookmarkModelObserver& observer : observers_)
203       observer.ExtensiveBookmarkChangesEnded(this);
204   }
205 }
206 
BeginGroupedChanges()207 void BookmarkModel::BeginGroupedChanges() {
208   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
209   for (BookmarkModelObserver& observer : observers_)
210     observer.GroupedBookmarkChangesBeginning(this);
211 }
212 
EndGroupedChanges()213 void BookmarkModel::EndGroupedChanges() {
214   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
215   for (BookmarkModelObserver& observer : observers_)
216     observer.GroupedBookmarkChangesEnded(this);
217 }
218 
Remove(const BookmarkNode * node)219 void BookmarkModel::Remove(const BookmarkNode* node) {
220   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
221   DCHECK(loaded_);
222   DCHECK(node);
223   DCHECK(!is_root_node(node));
224   const BookmarkNode* parent = node->parent();
225   DCHECK(parent);
226   size_t index = size_t{parent->GetIndexOf(node)};
227   DCHECK_NE(size_t{-1}, index);
228 
229   // Removing a permanent node is problematic and can cause crashes elsewhere
230   // that are difficult to trace back.
231   CHECK(!is_permanent_node(node)) << "for type " << node->type();
232 
233   for (BookmarkModelObserver& observer : observers_)
234     observer.OnWillRemoveBookmarks(this, parent, index, node);
235 
236   std::set<GURL> removed_urls;
237   std::unique_ptr<BookmarkNode> owned_node =
238       url_index_->Remove(AsMutable(node), &removed_urls);
239   RemoveNodeFromIndexRecursive(owned_node.get());
240 
241   if (store_)
242     store_->ScheduleSave();
243 
244   for (BookmarkModelObserver& observer : observers_)
245     observer.BookmarkNodeRemoved(this, parent, index, node, removed_urls);
246 
247   undo_delegate()->OnBookmarkNodeRemoved(this, parent, index,
248                                          std::move(owned_node));
249 }
250 
RemoveAllUserBookmarks()251 void BookmarkModel::RemoveAllUserBookmarks() {
252   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
253   std::set<GURL> removed_urls;
254   struct RemoveNodeData {
255     const BookmarkNode* parent;
256     int index;
257     std::unique_ptr<BookmarkNode> node;
258   };
259   std::vector<RemoveNodeData> removed_node_data_list;
260 
261   for (BookmarkModelObserver& observer : observers_)
262     observer.OnWillRemoveAllUserBookmarks(this);
263 
264   BeginExtensiveChanges();
265   // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
266   // its immediate children. For removing all non permanent nodes just remove
267   // all children of non-root permanent nodes.
268   {
269     for (const auto& permanent_node : root_->children()) {
270       if (!client_->CanBeEditedByUser(permanent_node.get()))
271         continue;
272 
273       for (size_t j = permanent_node->children().size(); j > 0; --j) {
274         std::unique_ptr<BookmarkNode> node = url_index_->Remove(
275             permanent_node->children()[j - 1].get(), &removed_urls);
276         RemoveNodeFromIndexRecursive(node.get());
277         removed_node_data_list.push_back(
278             {permanent_node.get(), j - 1, std::move(node)});
279       }
280     }
281   }
282   EndExtensiveChanges();
283   if (store_)
284     store_->ScheduleSave();
285 
286   for (BookmarkModelObserver& observer : observers_)
287     observer.BookmarkAllUserNodesRemoved(this, removed_urls);
288 
289   BeginGroupedChanges();
290   for (auto& removed_node_data : removed_node_data_list) {
291     undo_delegate()->OnBookmarkNodeRemoved(this, removed_node_data.parent,
292                                            removed_node_data.index,
293                                            std::move(removed_node_data.node));
294   }
295   EndGroupedChanges();
296 }
297 
Move(const BookmarkNode * node,const BookmarkNode * new_parent,size_t index)298 void BookmarkModel::Move(const BookmarkNode* node,
299                          const BookmarkNode* new_parent,
300                          size_t index) {
301   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
302   DCHECK(loaded_);
303   DCHECK(node);
304   DCHECK(IsValidIndex(new_parent, index, true));
305   DCHECK(!is_root_node(new_parent));
306   DCHECK(!is_permanent_node(node));
307   DCHECK(!new_parent->HasAncestor(node));
308 
309   const BookmarkNode* old_parent = node->parent();
310   size_t old_index = old_parent->GetIndexOf(node);
311 
312   if (old_parent == new_parent &&
313       (index == old_index || index == old_index + 1)) {
314     // Node is already in this position, nothing to do.
315     return;
316   }
317 
318   SetDateFolderModified(new_parent, Time::Now());
319 
320   if (old_parent == new_parent && index > old_index)
321     index--;
322 
323   BookmarkNode* mutable_old_parent = AsMutable(old_parent);
324   std::unique_ptr<BookmarkNode> owned_node =
325       mutable_old_parent->Remove(old_index);
326   BookmarkNode* mutable_new_parent = AsMutable(new_parent);
327   mutable_new_parent->Add(std::move(owned_node), index);
328 
329   if (store_)
330     store_->ScheduleSave();
331 
332   for (BookmarkModelObserver& observer : observers_)
333     observer.BookmarkNodeMoved(this, old_parent, old_index, new_parent, index);
334 }
335 
Copy(const BookmarkNode * node,const BookmarkNode * new_parent,size_t index)336 void BookmarkModel::Copy(const BookmarkNode* node,
337                          const BookmarkNode* new_parent,
338                          size_t index) {
339   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
340   DCHECK(loaded_);
341   DCHECK(node);
342   DCHECK(IsValidIndex(new_parent, index, true));
343   DCHECK(!is_root_node(new_parent));
344   DCHECK(!is_permanent_node(node));
345   DCHECK(!new_parent->HasAncestor(node));
346 
347   SetDateFolderModified(new_parent, Time::Now());
348   BookmarkNodeData drag_data(node);
349   // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
350   // don't need to send notifications here.
351   CloneBookmarkNode(this, drag_data.elements, new_parent, index, true);
352 
353   if (store_)
354     store_->ScheduleSave();
355 }
356 
GetFavicon(const BookmarkNode * node)357 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
358   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
359   DCHECK(node);
360   if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
361     BookmarkNode* mutable_node = AsMutable(node);
362     LoadFavicon(mutable_node, client_->PreferTouchIcon()
363                                   ? favicon_base::IconType::kTouchIcon
364                                   : favicon_base::IconType::kFavicon);
365   }
366   return node->favicon();
367 }
368 
GetFaviconType(const BookmarkNode * node)369 favicon_base::IconType BookmarkModel::GetFaviconType(const BookmarkNode* node) {
370   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
371   DCHECK(node);
372   return node->favicon_type();
373 }
374 
SetTitle(const BookmarkNode * node,const base::string16 & title)375 void BookmarkModel::SetTitle(const BookmarkNode* node,
376                              const base::string16& title) {
377   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
378   DCHECK(node);
379 
380   if (node->GetTitle() == title)
381     return;
382 
383   if (is_permanent_node(node) && !client_->CanSetPermanentNodeTitle(node)) {
384     NOTREACHED();
385     return;
386   }
387 
388   for (BookmarkModelObserver& observer : observers_)
389     observer.OnWillChangeBookmarkNode(this, node);
390 
391   // The title index doesn't support changing the title, instead we remove then
392   // add it back. Only do this for URL nodes. A directory node can have its
393   // title changed but should be excluded from the index.
394   if (node->is_url())
395     titled_url_index_->Remove(node);
396   url_index_->SetTitle(AsMutable(node), title);
397   if (node->is_url())
398     titled_url_index_->Add(node);
399 
400   if (store_)
401     store_->ScheduleSave();
402 
403   for (BookmarkModelObserver& observer : observers_)
404     observer.BookmarkNodeChanged(this, node);
405 }
406 
SetURL(const BookmarkNode * node,const GURL & url)407 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
408   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
409   DCHECK(node);
410   DCHECK(!node->is_folder());
411 
412   if (node->url() == url)
413     return;
414 
415   BookmarkNode* mutable_node = AsMutable(node);
416   mutable_node->InvalidateFavicon();
417   CancelPendingFaviconLoadRequests(mutable_node);
418 
419   for (BookmarkModelObserver& observer : observers_)
420     observer.OnWillChangeBookmarkNode(this, node);
421 
422   // The title index doesn't support changing the URL, instead we remove then
423   // add it back.
424   titled_url_index_->Remove(mutable_node);
425   url_index_->SetUrl(mutable_node, url);
426   titled_url_index_->Add(mutable_node);
427 
428   if (store_)
429     store_->ScheduleSave();
430 
431   for (BookmarkModelObserver& observer : observers_)
432     observer.BookmarkNodeChanged(this, node);
433 }
434 
SetNodeMetaInfo(const BookmarkNode * node,const std::string & key,const std::string & value)435 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
436                                     const std::string& key,
437                                     const std::string& value) {
438   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
439 
440   std::string old_value;
441   if (node->GetMetaInfo(key, &old_value) && old_value == value)
442     return;
443 
444   for (BookmarkModelObserver& observer : observers_)
445     observer.OnWillChangeBookmarkMetaInfo(this, node);
446 
447   if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
448     store_->ScheduleSave();
449 
450   for (BookmarkModelObserver& observer : observers_)
451     observer.BookmarkMetaInfoChanged(this, node);
452 }
453 
SetNodeMetaInfoMap(const BookmarkNode * node,const BookmarkNode::MetaInfoMap & meta_info_map)454 void BookmarkModel::SetNodeMetaInfoMap(
455     const BookmarkNode* node,
456     const BookmarkNode::MetaInfoMap& meta_info_map) {
457   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
458 
459   const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
460   if ((!old_meta_info_map && meta_info_map.empty()) ||
461       (old_meta_info_map && meta_info_map == *old_meta_info_map))
462     return;
463 
464   for (BookmarkModelObserver& observer : observers_)
465     observer.OnWillChangeBookmarkMetaInfo(this, node);
466 
467   AsMutable(node)->SetMetaInfoMap(meta_info_map);
468   if (store_)
469     store_->ScheduleSave();
470 
471   for (BookmarkModelObserver& observer : observers_)
472     observer.BookmarkMetaInfoChanged(this, node);
473 }
474 
DeleteNodeMetaInfo(const BookmarkNode * node,const std::string & key)475 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
476                                        const std::string& key) {
477   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
478 
479   const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
480   if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
481     return;
482 
483   for (BookmarkModelObserver& observer : observers_)
484     observer.OnWillChangeBookmarkMetaInfo(this, node);
485 
486   if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
487     store_->ScheduleSave();
488 
489   for (BookmarkModelObserver& observer : observers_)
490     observer.BookmarkMetaInfoChanged(this, node);
491 }
492 
AddNonClonedKey(const std::string & key)493 void BookmarkModel::AddNonClonedKey(const std::string& key) {
494   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
495   non_cloned_keys_.insert(key);
496 }
497 
OnFaviconsChanged(const std::set<GURL> & page_urls,const GURL & icon_url)498 void BookmarkModel::OnFaviconsChanged(const std::set<GURL>& page_urls,
499                                       const GURL& icon_url) {
500   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
501 
502   if (!loaded_)
503     return;
504 
505   std::set<const BookmarkNode*> to_update;
506   for (const GURL& page_url : page_urls) {
507     std::vector<const BookmarkNode*> nodes;
508     GetNodesByURL(page_url, &nodes);
509     to_update.insert(nodes.begin(), nodes.end());
510   }
511 
512   if (!icon_url.is_empty()) {
513     // Log Histogram to determine how often |icon_url| is non empty in
514     // practice.
515     // TODO(pkotwicz): Do something more efficient if |icon_url| is non-empty
516     // many times a day for each user.
517     UMA_HISTOGRAM_BOOLEAN("Bookmarks.OnFaviconsChangedIconURL", true);
518 
519     url_index_->GetNodesWithIconUrl(icon_url, &to_update);
520   }
521 
522   for (const BookmarkNode* node : to_update) {
523     // Rerequest the favicon.
524     BookmarkNode* mutable_node = AsMutable(node);
525     mutable_node->InvalidateFavicon();
526     CancelPendingFaviconLoadRequests(mutable_node);
527     for (BookmarkModelObserver& observer : observers_)
528       observer.BookmarkNodeFaviconChanged(this, node);
529   }
530 }
531 
SetDateAdded(const BookmarkNode * node,Time date_added)532 void BookmarkModel::SetDateAdded(const BookmarkNode* node, Time date_added) {
533   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
534   DCHECK(node);
535   DCHECK(!is_permanent_node(node));
536 
537   if (node->date_added() == date_added)
538     return;
539 
540   AsMutable(node)->set_date_added(date_added);
541 
542   // Syncing might result in dates newer than the folder's last modified date.
543   if (date_added > node->parent()->date_folder_modified()) {
544     // Will trigger store_->ScheduleSave().
545     SetDateFolderModified(node->parent(), date_added);
546   } else if (store_) {
547     store_->ScheduleSave();
548   }
549 }
550 
GetNodesByURL(const GURL & url,std::vector<const BookmarkNode * > * nodes)551 void BookmarkModel::GetNodesByURL(const GURL& url,
552                                   std::vector<const BookmarkNode*>* nodes) {
553   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
554 
555   if (url_index_)
556     url_index_->GetNodesByUrl(url, nodes);
557 }
558 
GetMostRecentlyAddedUserNodeForURL(const GURL & url)559 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
560     const GURL& url) {
561   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
562 
563   std::vector<const BookmarkNode*> nodes;
564   GetNodesByURL(url, &nodes);
565   std::sort(nodes.begin(), nodes.end(), &MoreRecentlyAdded);
566 
567   // Look for the first node that the user can edit.
568   for (size_t i = 0; i < nodes.size(); ++i) {
569     if (client_->CanBeEditedByUser(nodes[i]))
570       return nodes[i];
571   }
572 
573   return nullptr;
574 }
575 
HasBookmarks()576 bool BookmarkModel::HasBookmarks() {
577   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
578   return url_index_ && url_index_->HasBookmarks();
579 }
580 
HasNoUserCreatedBookmarksOrFolders()581 bool BookmarkModel::HasNoUserCreatedBookmarksOrFolders() {
582   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
583   return bookmark_bar_node_->children().empty() &&
584          other_node_->children().empty() && mobile_node_->children().empty();
585 }
586 
IsBookmarked(const GURL & url)587 bool BookmarkModel::IsBookmarked(const GURL& url) {
588   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
589   return url_index_ && url_index_->IsBookmarked(url);
590 }
591 
GetBookmarks(std::vector<UrlAndTitle> * bookmarks)592 void BookmarkModel::GetBookmarks(std::vector<UrlAndTitle>* bookmarks) {
593   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
594   if (url_index_)
595     url_index_->GetBookmarks(bookmarks);
596 }
597 
AddFolder(const BookmarkNode * parent,size_t index,const base::string16 & title,const BookmarkNode::MetaInfoMap * meta_info,base::Optional<std::string> guid)598 const BookmarkNode* BookmarkModel::AddFolder(
599     const BookmarkNode* parent,
600     size_t index,
601     const base::string16& title,
602     const BookmarkNode::MetaInfoMap* meta_info,
603     base::Optional<std::string> guid) {
604   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
605   DCHECK(loaded_);
606   DCHECK(parent);
607   DCHECK(parent->is_folder());
608   DCHECK(!is_root_node(parent));
609   DCHECK(IsValidIndex(parent, index, true));
610 
611   if (guid)
612     DCHECK(base::IsValidGUIDOutputString(*guid));
613   else
614     guid = base::GenerateGUID();
615 
616   auto new_node =
617       std::make_unique<BookmarkNode>(generate_next_node_id(), *guid, GURL());
618   new_node->set_date_folder_modified(Time::Now());
619   // Folders shouldn't have line breaks in their titles.
620   new_node->SetTitle(title);
621   if (meta_info)
622     new_node->SetMetaInfoMap(*meta_info);
623 
624   return AddNode(AsMutable(parent), index, std::move(new_node));
625 }
626 
AddURL(const BookmarkNode * parent,size_t index,const base::string16 & title,const GURL & url,const BookmarkNode::MetaInfoMap * meta_info,base::Optional<base::Time> creation_time,base::Optional<std::string> guid)627 const BookmarkNode* BookmarkModel::AddURL(
628     const BookmarkNode* parent,
629     size_t index,
630     const base::string16& title,
631     const GURL& url,
632     const BookmarkNode::MetaInfoMap* meta_info,
633     base::Optional<base::Time> creation_time,
634     base::Optional<std::string> guid) {
635   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
636   DCHECK(loaded_);
637   DCHECK(url.is_valid());
638   DCHECK(parent);
639   DCHECK(parent->is_folder());
640   DCHECK(!is_root_node(parent));
641   DCHECK(IsValidIndex(parent, index, true));
642 
643   if (guid)
644     DCHECK(base::IsValidGUIDOutputString(*guid));
645   else
646     guid = base::GenerateGUID();
647 
648   if (!creation_time)
649     creation_time = Time::Now();
650 
651   // Syncing may result in dates newer than the last modified date.
652   if (*creation_time > parent->date_folder_modified())
653     SetDateFolderModified(parent, *creation_time);
654 
655   auto new_node =
656       std::make_unique<BookmarkNode>(generate_next_node_id(), *guid, url);
657   new_node->SetTitle(title);
658   new_node->set_date_added(*creation_time);
659   if (meta_info)
660     new_node->SetMetaInfoMap(*meta_info);
661 
662   return AddNode(AsMutable(parent), index, std::move(new_node));
663 }
664 
SortChildren(const BookmarkNode * parent)665 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
666   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
667   DCHECK(client_->CanBeEditedByUser(parent));
668 
669   if (!parent || !parent->is_folder() || is_root_node(parent) ||
670       parent->children().size() <= 1) {
671     return;
672   }
673 
674   for (BookmarkModelObserver& observer : observers_)
675     observer.OnWillReorderBookmarkNode(this, parent);
676 
677   UErrorCode error = U_ZERO_ERROR;
678   std::unique_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
679   if (U_FAILURE(error))
680     collator.reset(nullptr);
681   BookmarkNode* mutable_parent = AsMutable(parent);
682   std::sort(mutable_parent->children_.begin(), mutable_parent->children_.end(),
683             SortComparator(collator.get()));
684 
685   if (store_)
686     store_->ScheduleSave();
687 
688   for (BookmarkModelObserver& observer : observers_)
689     observer.BookmarkNodeChildrenReordered(this, parent);
690 }
691 
ReorderChildren(const BookmarkNode * parent,const std::vector<const BookmarkNode * > & ordered_nodes)692 void BookmarkModel::ReorderChildren(
693     const BookmarkNode* parent,
694     const std::vector<const BookmarkNode*>& ordered_nodes) {
695   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
696   DCHECK(client_->CanBeEditedByUser(parent));
697 
698   // Ensure that all children in |parent| are in |ordered_nodes|.
699   DCHECK_EQ(parent->children().size(), ordered_nodes.size());
700   for (const BookmarkNode* node : ordered_nodes)
701     DCHECK_EQ(parent, node->parent());
702 
703   for (BookmarkModelObserver& observer : observers_)
704     observer.OnWillReorderBookmarkNode(this, parent);
705 
706   if (ordered_nodes.size() > 1) {
707     std::map<const BookmarkNode*, int> order;
708     for (size_t i = 0; i < ordered_nodes.size(); ++i)
709       order[ordered_nodes[i]] = i;
710 
711     std::vector<std::unique_ptr<BookmarkNode>> new_children(
712         ordered_nodes.size());
713     BookmarkNode* mutable_parent = AsMutable(parent);
714     for (auto& child : mutable_parent->children_) {
715       size_t new_location = order[child.get()];
716       new_children[new_location] = std::move(child);
717     }
718     mutable_parent->children_.swap(new_children);
719 
720     if (store_)
721       store_->ScheduleSave();
722   }
723 
724   for (BookmarkModelObserver& observer : observers_)
725     observer.BookmarkNodeChildrenReordered(this, parent);
726 }
727 
SetDateFolderModified(const BookmarkNode * parent,const Time time)728 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
729                                           const Time time) {
730   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
731   DCHECK(parent);
732   AsMutable(parent)->set_date_folder_modified(time);
733 
734   if (store_)
735     store_->ScheduleSave();
736 }
737 
ResetDateFolderModified(const BookmarkNode * node)738 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
739   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
740   SetDateFolderModified(node, Time());
741 }
742 
GetBookmarksMatching(const base::string16 & text,size_t max_count,std::vector<TitledUrlMatch> * matches)743 void BookmarkModel::GetBookmarksMatching(const base::string16& text,
744                                          size_t max_count,
745                                          std::vector<TitledUrlMatch>* matches) {
746   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
747   GetBookmarksMatching(text, max_count,
748                        query_parser::MatchingAlgorithm::DEFAULT, matches);
749 }
750 
GetBookmarksMatching(const base::string16 & text,size_t max_count,query_parser::MatchingAlgorithm matching_algorithm,std::vector<TitledUrlMatch> * matches)751 void BookmarkModel::GetBookmarksMatching(
752     const base::string16& text,
753     size_t max_count,
754     query_parser::MatchingAlgorithm matching_algorithm,
755     std::vector<TitledUrlMatch>* matches) {
756   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
757 
758   if (!loaded_)
759     return;
760 
761   titled_url_index_->GetResultsMatching(text, max_count, matching_algorithm,
762                                         matches);
763 }
764 
ClearStore()765 void BookmarkModel::ClearStore() {
766   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
767   store_.reset();
768 }
769 
RestoreRemovedNode(const BookmarkNode * parent,size_t index,std::unique_ptr<BookmarkNode> node)770 void BookmarkModel::RestoreRemovedNode(const BookmarkNode* parent,
771                                        size_t index,
772                                        std::unique_ptr<BookmarkNode> node) {
773   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
774 
775   BookmarkNode* node_ptr = node.get();
776   AddNode(AsMutable(parent), index, std::move(node));
777 
778   // We might be restoring a folder node that have already contained a set of
779   // child nodes. We need to notify all of them.
780   NotifyNodeAddedForAllDescendents(node_ptr);
781 }
782 
NotifyNodeAddedForAllDescendents(const BookmarkNode * node)783 void BookmarkModel::NotifyNodeAddedForAllDescendents(const BookmarkNode* node) {
784   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
785 
786   for (size_t i = 0; i < node->children().size(); ++i) {
787     for (BookmarkModelObserver& observer : observers_)
788       observer.BookmarkNodeAdded(this, node, i);
789     NotifyNodeAddedForAllDescendents(node->children()[i].get());
790   }
791 }
792 
RemoveNodeFromIndexRecursive(BookmarkNode * node)793 void BookmarkModel::RemoveNodeFromIndexRecursive(BookmarkNode* node) {
794   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
795   DCHECK(loaded_);
796   DCHECK(!is_permanent_node(node));
797 
798   if (node->is_url())
799     titled_url_index_->Remove(node);
800 
801   // Note that |guid_index_| is used for DCHECK-enabled builds only.
802 #if DCHECK_IS_ON()
803   DCHECK(guid_index_.erase(node->guid()))
804       << "Bookmark GUID missing in index: " << node->guid();
805 #endif  // DCHECK_IS_ON()
806 
807   CancelPendingFaviconLoadRequests(node);
808 
809   // Recurse through children.
810   for (size_t i = node->children().size(); i > 0; --i)
811     RemoveNodeFromIndexRecursive(node->children()[i - 1].get());
812 }
813 
DoneLoading(std::unique_ptr<BookmarkLoadDetails> details)814 void BookmarkModel::DoneLoading(std::unique_ptr<BookmarkLoadDetails> details) {
815   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
816   DCHECK(details);
817   DCHECK(!loaded_);
818 
819   next_node_id_ = details->max_id();
820   if (details->computed_checksum() != details->stored_checksum() ||
821       details->ids_reassigned() || details->guids_reassigned()) {
822     // If bookmarks file changed externally, the IDs may have changed
823     // externally. In that case, the decoder may have reassigned IDs to make
824     // them unique. So when the file has changed externally, we should save the
825     // bookmarks file to persist such changes. The same applies if new GUIDs
826     // have been assigned to bookmarks.
827     if (store_)
828       store_->ScheduleSave();
829   }
830 
831   titled_url_index_ = details->owned_index();
832   url_index_ = details->url_index();
833   root_ = details->root_node();
834   // See declaration for details on why |owned_root_| is reset.
835   owned_root_.reset();
836   bookmark_bar_node_ = details->bb_node();
837   other_node_ = details->other_folder_node();
838   mobile_node_ = details->mobile_folder_node();
839 
840 #if DCHECK_IS_ON()
841   AddGuidsToIndexRecursive(root_, &guid_index_);
842 #endif  // DCHECK_IS_ON()
843 
844   titled_url_index_->SetNodeSorter(
845       std::make_unique<TypedCountSorter>(client_.get()));
846   // Sorting the permanent nodes has to happen on the main thread, so we do it
847   // here, after loading completes.
848   std::stable_sort(root_->children_.begin(), root_->children_.end(),
849                    VisibilityComparator(client_.get()));
850 
851   root_->SetMetaInfoMap(details->model_meta_info_map());
852 
853   loaded_ = true;
854   client_->DecodeBookmarkSyncMetadata(
855       details->sync_metadata_str(),
856       store_ ? base::BindRepeating(&BookmarkStorage::ScheduleSave,
857                                    base::Unretained(store_.get()))
858              : base::DoNothing());
859 
860   // Notify our direct observers.
861   for (BookmarkModelObserver& observer : observers_)
862     observer.BookmarkModelLoaded(this, details->ids_reassigned());
863 }
864 
AddNode(BookmarkNode * parent,size_t index,std::unique_ptr<BookmarkNode> node)865 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
866                                      size_t index,
867                                      std::unique_ptr<BookmarkNode> node) {
868   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
869 
870   BookmarkNode* node_ptr = node.get();
871   url_index_->Add(parent, index, std::move(node));
872 
873   if (store_)
874     store_->ScheduleSave();
875 
876   AddNodeToIndexRecursive(node_ptr);
877 
878   for (BookmarkModelObserver& observer : observers_)
879     observer.BookmarkNodeAdded(this, parent, index);
880 
881   return node_ptr;
882 }
883 
AddNodeToIndexRecursive(BookmarkNode * node)884 void BookmarkModel::AddNodeToIndexRecursive(BookmarkNode* node) {
885   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
886 
887   if (node->is_url())
888     titled_url_index_->Add(node);
889 
890   // The node's GUID must be unique. Note that |guid_index_| is used for
891   // DCHECK-enabled builds only.
892 #if DCHECK_IS_ON()
893   DCHECK(guid_index_.insert(node->guid()).second)
894       << "Duplicate bookmark GUID: " << node->guid();
895 #endif  // DCHECK_IS_ON()
896 
897   for (const auto& child : node->children())
898     AddNodeToIndexRecursive(child.get());
899 }
900 
IsValidIndex(const BookmarkNode * parent,size_t index,bool allow_end)901 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
902                                  size_t index,
903                                  bool allow_end) {
904   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
905   return parent && parent->is_folder() &&
906          (index < parent->children().size() ||
907           (allow_end && index == parent->children().size()));
908 }
909 
OnFaviconDataAvailable(BookmarkNode * node,favicon_base::IconType icon_type,const favicon_base::FaviconImageResult & image_result)910 void BookmarkModel::OnFaviconDataAvailable(
911     BookmarkNode* node,
912     favicon_base::IconType icon_type,
913     const favicon_base::FaviconImageResult& image_result) {
914   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
915   DCHECK(node);
916 
917   node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
918   node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
919   if (!image_result.image.IsEmpty()) {
920     node->set_favicon_type(icon_type);
921     node->set_favicon(image_result.image);
922     node->set_icon_url(image_result.icon_url);
923     FaviconLoaded(node);
924   } else if (icon_type == favicon_base::IconType::kTouchIcon) {
925     // Couldn't load the touch icon, fallback to the regular favicon.
926     DCHECK(client_->PreferTouchIcon());
927     LoadFavicon(node, favicon_base::IconType::kFavicon);
928   } else {
929     // No favicon available, but we still notify observers.
930     FaviconLoaded(node);
931   }
932 }
933 
LoadFavicon(BookmarkNode * node,favicon_base::IconType icon_type)934 void BookmarkModel::LoadFavicon(BookmarkNode* node,
935                                 favicon_base::IconType icon_type) {
936   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
937 
938   if (node->is_folder())
939     return;
940 
941   DCHECK(node->url().is_valid());
942   node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
943   base::CancelableTaskTracker::TaskId taskId =
944       client_->GetFaviconImageForPageURL(
945           node->url(), icon_type,
946           base::BindOnce(&BookmarkModel::OnFaviconDataAvailable,
947                          base::Unretained(this), node, icon_type),
948           &cancelable_task_tracker_);
949   if (taskId != base::CancelableTaskTracker::kBadTaskId)
950     node->set_favicon_load_task_id(taskId);
951 }
952 
FaviconLoaded(const BookmarkNode * node)953 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
954   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
955   for (BookmarkModelObserver& observer : observers_)
956     observer.BookmarkNodeFaviconChanged(this, node);
957 }
958 
CancelPendingFaviconLoadRequests(BookmarkNode * node)959 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
960   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
961   if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) {
962     cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
963     node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
964   }
965 }
966 
generate_next_node_id()967 int64_t BookmarkModel::generate_next_node_id() {
968   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
969   DCHECK(loaded_);
970   return next_node_id_++;
971 }
972 
SetUndoDelegate(BookmarkUndoDelegate * undo_delegate)973 void BookmarkModel::SetUndoDelegate(BookmarkUndoDelegate* undo_delegate) {
974   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
975   undo_delegate_ = undo_delegate;
976   if (undo_delegate_)
977     undo_delegate_->SetUndoProvider(this);
978 }
979 
undo_delegate() const980 BookmarkUndoDelegate* BookmarkModel::undo_delegate() const {
981   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
982   return undo_delegate_ ? undo_delegate_ : empty_undo_delegate_.get();
983 }
984 
985 }  // namespace bookmarks
986