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