custom_links_manager_impl.cc 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. // Copyright 2018 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/ntp_tiles/custom_links_manager_impl.h"
  5. #include <memory>
  6. #include <string>
  7. #include <utility>
  8. #include "base/auto_reset.h"
  9. #include "base/bind.h"
  10. #include "base/containers/cxx20_erase.h"
  11. #include "components/ntp_tiles/constants.h"
  12. #include "components/ntp_tiles/deleted_tile_type.h"
  13. #include "components/ntp_tiles/metrics.h"
  14. #include "components/ntp_tiles/most_visited_sites.h"
  15. #include "components/ntp_tiles/pref_names.h"
  16. #include "components/pref_registry/pref_registry_syncable.h"
  17. #include "components/prefs/pref_service.h"
  18. namespace ntp_tiles {
  19. CustomLinksManagerImpl::CustomLinksManagerImpl(
  20. PrefService* prefs,
  21. history::HistoryService* history_service)
  22. : prefs_(prefs), store_(prefs) {
  23. DCHECK(prefs);
  24. if (history_service)
  25. history_service_observation_.Observe(history_service);
  26. if (IsInitialized()) {
  27. current_links_ = store_.RetrieveLinks();
  28. RemoveCustomLinksForPreinstalledApps();
  29. }
  30. base::RepeatingClosure callback =
  31. base::BindRepeating(&CustomLinksManagerImpl::OnPreferenceChanged,
  32. weak_ptr_factory_.GetWeakPtr());
  33. pref_change_registrar_.Init(prefs_);
  34. pref_change_registrar_.Add(prefs::kCustomLinksInitialized, callback);
  35. pref_change_registrar_.Add(prefs::kCustomLinksList, callback);
  36. }
  37. CustomLinksManagerImpl::~CustomLinksManagerImpl() = default;
  38. bool CustomLinksManagerImpl::Initialize(const NTPTilesVector& tiles) {
  39. if (IsInitialized())
  40. return false;
  41. for (const NTPTile& tile : tiles)
  42. current_links_.emplace_back(Link{tile.url, tile.title, true});
  43. {
  44. base::AutoReset<bool> auto_reset(&updating_preferences_, true);
  45. prefs_->SetBoolean(prefs::kCustomLinksInitialized, true);
  46. }
  47. StoreLinks();
  48. return true;
  49. }
  50. void CustomLinksManagerImpl::Uninitialize() {
  51. {
  52. base::AutoReset<bool> auto_reset(&updating_preferences_, true);
  53. prefs_->SetBoolean(prefs::kCustomLinksInitialized, false);
  54. }
  55. ClearLinks();
  56. }
  57. bool CustomLinksManagerImpl::IsInitialized() const {
  58. return prefs_->GetBoolean(prefs::kCustomLinksInitialized);
  59. }
  60. const std::vector<CustomLinksManager::Link>& CustomLinksManagerImpl::GetLinks()
  61. const {
  62. return current_links_;
  63. }
  64. bool CustomLinksManagerImpl::AddLink(const GURL& url,
  65. const std::u16string& title) {
  66. if (!IsInitialized() || !url.is_valid() ||
  67. current_links_.size() == ntp_tiles::kMaxNumCustomLinks) {
  68. return false;
  69. }
  70. if (FindLinkWithUrl(url) != current_links_.end())
  71. return false;
  72. previous_links_ = current_links_;
  73. current_links_.emplace_back(Link{url, title, false});
  74. StoreLinks();
  75. return true;
  76. }
  77. bool CustomLinksManagerImpl::UpdateLink(const GURL& url,
  78. const GURL& new_url,
  79. const std::u16string& new_title) {
  80. if (!IsInitialized() || !url.is_valid() ||
  81. (new_url.is_empty() && new_title.empty())) {
  82. return false;
  83. }
  84. // Do not update if |new_url| is invalid or already exists in the list.
  85. if (!new_url.is_empty() &&
  86. (!new_url.is_valid() ||
  87. FindLinkWithUrl(new_url) != current_links_.end())) {
  88. return false;
  89. }
  90. auto it = FindLinkWithUrl(url);
  91. if (it == current_links_.end())
  92. return false;
  93. // At this point, we will be modifying at least one of the values.
  94. previous_links_ = current_links_;
  95. if (!new_url.is_empty())
  96. it->url = new_url;
  97. if (!new_title.empty())
  98. it->title = new_title;
  99. it->is_most_visited = false;
  100. StoreLinks();
  101. return true;
  102. }
  103. bool CustomLinksManagerImpl::ReorderLink(const GURL& url, size_t new_pos) {
  104. if (!IsInitialized() || !url.is_valid() || new_pos < 0 ||
  105. new_pos >= current_links_.size()) {
  106. return false;
  107. }
  108. auto curr_it = FindLinkWithUrl(url);
  109. if (curr_it == current_links_.end())
  110. return false;
  111. auto new_it = current_links_.begin() + new_pos;
  112. if (new_it == curr_it)
  113. return false;
  114. previous_links_ = current_links_;
  115. // If the new position is to the left of the current position, left rotate the
  116. // range [new_pos, curr_pos] until the link is first.
  117. if (new_it < curr_it)
  118. std::rotate(new_it, curr_it, curr_it + 1);
  119. // If the new position is to the right, we only need to left rotate the range
  120. // [curr_pos, new_pos] once so that the link is last.
  121. else
  122. std::rotate(curr_it, curr_it + 1, new_it + 1);
  123. StoreLinks();
  124. return true;
  125. }
  126. bool CustomLinksManagerImpl::DeleteLink(const GURL& url) {
  127. if (!IsInitialized() || !url.is_valid())
  128. return false;
  129. auto it = FindLinkWithUrl(url);
  130. if (it == current_links_.end())
  131. return false;
  132. previous_links_ = current_links_;
  133. current_links_.erase(it);
  134. StoreLinks();
  135. return true;
  136. }
  137. bool CustomLinksManagerImpl::UndoAction() {
  138. if (!IsInitialized() || !previous_links_.has_value())
  139. return false;
  140. // Replace the current links with the previous state.
  141. current_links_ = *previous_links_;
  142. previous_links_ = absl::nullopt;
  143. StoreLinks();
  144. return true;
  145. }
  146. void CustomLinksManagerImpl::ClearLinks() {
  147. {
  148. base::AutoReset<bool> auto_reset(&updating_preferences_, true);
  149. store_.ClearLinks();
  150. }
  151. current_links_.clear();
  152. previous_links_ = absl::nullopt;
  153. }
  154. void CustomLinksManagerImpl::StoreLinks() {
  155. base::AutoReset<bool> auto_reset(&updating_preferences_, true);
  156. store_.StoreLinks(current_links_);
  157. }
  158. void CustomLinksManagerImpl::RemoveCustomLinksForPreinstalledApps() {
  159. if (!prefs_->GetBoolean(prefs::kCustomLinksForPreinstalledAppsRemoved)) {
  160. bool default_app_links_deleted = false;
  161. for (const Link& link : current_links_) {
  162. if (MostVisitedSites::IsNtpTileFromPreinstalledApp(link.url) &&
  163. MostVisitedSites::WasNtpAppMigratedToWebApp(prefs_, link.url)) {
  164. DeleteLink(link.url);
  165. default_app_links_deleted = true;
  166. }
  167. }
  168. if (default_app_links_deleted) {
  169. metrics::RecordsMigratedDefaultAppDeleted(DeletedTileType::kCustomLink);
  170. prefs_->SetBoolean(prefs::kCustomLinksForPreinstalledAppsRemoved, true);
  171. }
  172. }
  173. }
  174. std::vector<CustomLinksManager::Link>::iterator
  175. CustomLinksManagerImpl::FindLinkWithUrl(const GURL& url) {
  176. return std::find_if(current_links_.begin(), current_links_.end(),
  177. [&url](const Link& link) { return link.url == url; });
  178. }
  179. base::CallbackListSubscription
  180. CustomLinksManagerImpl::RegisterCallbackForOnChanged(
  181. base::RepeatingClosure callback) {
  182. return closure_list_.Add(callback);
  183. }
  184. // history::HistoryServiceObserver implementation.
  185. void CustomLinksManagerImpl::OnURLsDeleted(
  186. history::HistoryService* history_service,
  187. const history::DeletionInfo& deletion_info) {
  188. // We don't care about expired entries.
  189. if (!IsInitialized() || deletion_info.is_from_expiration())
  190. return;
  191. size_t initial_size = current_links_.size();
  192. if (deletion_info.IsAllHistory()) {
  193. base::EraseIf(current_links_,
  194. [](auto& link) { return link.is_most_visited; });
  195. } else {
  196. for (const history::URLRow& row : deletion_info.deleted_rows()) {
  197. auto it = FindLinkWithUrl(row.url());
  198. if (it != current_links_.end() && it->is_most_visited)
  199. current_links_.erase(it);
  200. }
  201. }
  202. StoreLinks();
  203. previous_links_ = absl::nullopt;
  204. // Alert MostVisitedSites that some links have been deleted.
  205. if (initial_size != current_links_.size())
  206. closure_list_.Notify();
  207. }
  208. void CustomLinksManagerImpl::HistoryServiceBeingDeleted(
  209. history::HistoryService* history_service) {
  210. DCHECK(history_service_observation_.IsObserving());
  211. history_service_observation_.Reset();
  212. }
  213. void CustomLinksManagerImpl::OnPreferenceChanged() {
  214. if (updating_preferences_)
  215. return;
  216. if (IsInitialized())
  217. current_links_ = store_.RetrieveLinks();
  218. else
  219. current_links_.clear();
  220. previous_links_ = absl::nullopt;
  221. closure_list_.Notify();
  222. }
  223. // static
  224. void CustomLinksManagerImpl::RegisterProfilePrefs(
  225. user_prefs::PrefRegistrySyncable* user_prefs) {
  226. user_prefs->RegisterBooleanPref(
  227. prefs::kCustomLinksInitialized, false,
  228. user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  229. user_prefs->RegisterBooleanPref(prefs::kCustomLinksForPreinstalledAppsRemoved,
  230. false);
  231. CustomLinksStore::RegisterProfilePrefs(user_prefs);
  232. }
  233. } // namespace ntp_tiles