bookmark_model.cc 33 KB

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