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