bookmark_model_type_processor.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. // Copyright 2017 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/sync_bookmarks/bookmark_model_type_processor.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback.h"
  8. #include "base/feature_list.h"
  9. #include "base/logging.h"
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/strings/utf_string_conversions.h"
  12. #include "base/threading/sequenced_task_runner_handle.h"
  13. #include "base/trace_event/memory_usage_estimator.h"
  14. #include "base/trace_event/trace_event.h"
  15. #include "components/bookmarks/browser/bookmark_model.h"
  16. #include "components/bookmarks/browser/bookmark_node.h"
  17. #include "components/bookmarks/browser/bookmark_utils.h"
  18. #include "components/sync/base/client_tag_hash.h"
  19. #include "components/sync/base/data_type_histogram.h"
  20. #include "components/sync/base/model_type.h"
  21. #include "components/sync/base/time.h"
  22. #include "components/sync/engine/commit_queue.h"
  23. #include "components/sync/engine/data_type_activation_response.h"
  24. #include "components/sync/engine/model_type_processor_metrics.h"
  25. #include "components/sync/engine/model_type_processor_proxy.h"
  26. #include "components/sync/model/data_type_activation_request.h"
  27. #include "components/sync/model/type_entities_count.h"
  28. #include "components/sync/protocol/bookmark_model_metadata.pb.h"
  29. #include "components/sync/protocol/proto_value_conversions.h"
  30. #include "components/sync_bookmarks/bookmark_local_changes_builder.h"
  31. #include "components/sync_bookmarks/bookmark_model_merger.h"
  32. #include "components/sync_bookmarks/bookmark_model_observer_impl.h"
  33. #include "components/sync_bookmarks/bookmark_remote_updates_handler.h"
  34. #include "components/sync_bookmarks/bookmark_specifics_conversions.h"
  35. #include "components/sync_bookmarks/parent_guid_preprocessing.h"
  36. #include "components/sync_bookmarks/switches.h"
  37. #include "components/sync_bookmarks/synced_bookmark_tracker_entity.h"
  38. #include "components/undo/bookmark_undo_utils.h"
  39. namespace sync_bookmarks {
  40. namespace {
  41. class ScopedRemoteUpdateBookmarks {
  42. public:
  43. // |bookmark_model|, |bookmark_undo_service| and |observer| must not be null
  44. // and must outlive this object.
  45. ScopedRemoteUpdateBookmarks(bookmarks::BookmarkModel* bookmark_model,
  46. BookmarkUndoService* bookmark_undo_service,
  47. bookmarks::BookmarkModelObserver* observer)
  48. : bookmark_model_(bookmark_model),
  49. suspend_undo_(bookmark_undo_service),
  50. observer_(observer) {
  51. // Notify UI intensive observers of BookmarkModel that we are about to make
  52. // potentially significant changes to it, so the updates may be batched. For
  53. // example, on Mac, the bookmarks bar displays animations when bookmark
  54. // items are added or deleted.
  55. DCHECK(bookmark_model_);
  56. bookmark_model_->BeginExtensiveChanges();
  57. // Shouldn't be notified upon changes due to sync.
  58. bookmark_model_->RemoveObserver(observer_);
  59. }
  60. ScopedRemoteUpdateBookmarks(const ScopedRemoteUpdateBookmarks&) = delete;
  61. ScopedRemoteUpdateBookmarks& operator=(const ScopedRemoteUpdateBookmarks&) =
  62. delete;
  63. ~ScopedRemoteUpdateBookmarks() {
  64. // Notify UI intensive observers of BookmarkModel that all updates have been
  65. // applied, and that they may now be consumed. This prevents issues like the
  66. // one described in https://crbug.com/281562, where old and new items on the
  67. // bookmarks bar would overlap.
  68. bookmark_model_->EndExtensiveChanges();
  69. bookmark_model_->AddObserver(observer_);
  70. }
  71. private:
  72. const raw_ptr<bookmarks::BookmarkModel> bookmark_model_;
  73. // Changes made to the bookmark model due to sync should not be undoable.
  74. ScopedSuspendBookmarkUndo suspend_undo_;
  75. const raw_ptr<bookmarks::BookmarkModelObserver> observer_;
  76. };
  77. std::string ComputeServerDefinedUniqueTagForDebugging(
  78. const bookmarks::BookmarkNode* node,
  79. bookmarks::BookmarkModel* model) {
  80. if (node == model->bookmark_bar_node()) {
  81. return "bookmark_bar";
  82. }
  83. if (node == model->other_node()) {
  84. return "other_bookmarks";
  85. }
  86. if (node == model->mobile_node()) {
  87. return "synced_bookmarks";
  88. }
  89. return "";
  90. }
  91. } // namespace
  92. BookmarkModelTypeProcessor::BookmarkModelTypeProcessor(
  93. BookmarkUndoService* bookmark_undo_service)
  94. : bookmark_undo_service_(bookmark_undo_service) {}
  95. BookmarkModelTypeProcessor::~BookmarkModelTypeProcessor() {
  96. if (bookmark_model_ && bookmark_model_observer_) {
  97. bookmark_model_->RemoveObserver(bookmark_model_observer_.get());
  98. }
  99. }
  100. void BookmarkModelTypeProcessor::ConnectSync(
  101. std::unique_ptr<syncer::CommitQueue> worker) {
  102. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  103. DCHECK(!worker_);
  104. DCHECK(bookmark_model_);
  105. worker_ = std::move(worker);
  106. // |bookmark_tracker_| is instantiated only after initial sync is done.
  107. if (bookmark_tracker_) {
  108. NudgeForCommitIfNeeded();
  109. }
  110. }
  111. void BookmarkModelTypeProcessor::DisconnectSync() {
  112. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  113. weak_ptr_factory_for_worker_.InvalidateWeakPtrs();
  114. if (!worker_) {
  115. return;
  116. }
  117. DVLOG(1) << "Disconnecting sync for Bookmarks";
  118. worker_.reset();
  119. }
  120. void BookmarkModelTypeProcessor::GetLocalChanges(
  121. size_t max_entries,
  122. GetLocalChangesCallback callback) {
  123. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  124. BookmarkLocalChangesBuilder builder(bookmark_tracker_.get(), bookmark_model_);
  125. std::move(callback).Run(builder.BuildCommitRequests(max_entries));
  126. }
  127. void BookmarkModelTypeProcessor::OnCommitCompleted(
  128. const sync_pb::ModelTypeState& type_state,
  129. const syncer::CommitResponseDataList& committed_response_list,
  130. const syncer::FailedCommitResponseDataList& error_response_list) {
  131. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  132. // |error_response_list| is ignored, because all errors are treated as
  133. // transient and the processor with eventually retry.
  134. for (const syncer::CommitResponseData& response : committed_response_list) {
  135. const SyncedBookmarkTrackerEntity* entity =
  136. bookmark_tracker_->GetEntityForClientTagHash(response.client_tag_hash);
  137. if (!entity) {
  138. DLOG(WARNING) << "Received a commit response for an unknown entity.";
  139. continue;
  140. }
  141. bookmark_tracker_->UpdateUponCommitResponse(entity, response.id,
  142. response.response_version,
  143. response.sequence_number);
  144. }
  145. bookmark_tracker_->set_model_type_state(type_state);
  146. schedule_save_closure_.Run();
  147. }
  148. void BookmarkModelTypeProcessor::OnUpdateReceived(
  149. const sync_pb::ModelTypeState& model_type_state,
  150. syncer::UpdateResponseDataList updates) {
  151. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  152. DCHECK(!model_type_state.cache_guid().empty());
  153. DCHECK_EQ(model_type_state.cache_guid(), cache_guid_);
  154. DCHECK(model_type_state.initial_sync_done());
  155. syncer::LogUpdatesReceivedByProcessorHistogram(
  156. syncer::BOOKMARKS,
  157. /*is_initial_sync=*/!bookmark_tracker_, updates.size());
  158. // Clients before M94 did not populate the parent GUID in specifics.
  159. PopulateParentGuidInSpecifics(bookmark_tracker_.get(), &updates);
  160. if (!bookmark_tracker_) {
  161. OnInitialUpdateReceived(model_type_state, std::move(updates));
  162. return;
  163. }
  164. // Incremental updates.
  165. ScopedRemoteUpdateBookmarks update_bookmarks(
  166. bookmark_model_, bookmark_undo_service_, bookmark_model_observer_.get());
  167. BookmarkRemoteUpdatesHandler updates_handler(
  168. bookmark_model_, favicon_service_, bookmark_tracker_.get());
  169. const bool got_new_encryption_requirements =
  170. bookmark_tracker_->model_type_state().encryption_key_name() !=
  171. model_type_state.encryption_key_name();
  172. bookmark_tracker_->set_model_type_state(model_type_state);
  173. updates_handler.Process(updates, got_new_encryption_requirements);
  174. if (bookmark_tracker_->ReuploadBookmarksOnLoadIfNeeded()) {
  175. NudgeForCommitIfNeeded();
  176. }
  177. // There are cases when we receive non-empty updates that don't result in
  178. // model changes (e.g. reflections). In that case, issue a write to persit the
  179. // progress marker in order to avoid downloading those updates again.
  180. if (!updates.empty()) {
  181. // Schedule save just in case one is needed.
  182. schedule_save_closure_.Run();
  183. }
  184. }
  185. const SyncedBookmarkTracker* BookmarkModelTypeProcessor::GetTrackerForTest()
  186. const {
  187. return bookmark_tracker_.get();
  188. }
  189. bool BookmarkModelTypeProcessor::IsConnectedForTest() const {
  190. return worker_ != nullptr;
  191. }
  192. std::string BookmarkModelTypeProcessor::EncodeSyncMetadata() const {
  193. std::string metadata_str;
  194. if (bookmark_tracker_) {
  195. sync_pb::BookmarkModelMetadata model_metadata =
  196. bookmark_tracker_->BuildBookmarkModelMetadata();
  197. model_metadata.SerializeToString(&metadata_str);
  198. }
  199. return metadata_str;
  200. }
  201. void BookmarkModelTypeProcessor::ModelReadyToSync(
  202. const std::string& metadata_str,
  203. const base::RepeatingClosure& schedule_save_closure,
  204. bookmarks::BookmarkModel* model) {
  205. DCHECK(model);
  206. DCHECK(model->loaded());
  207. DCHECK(!bookmark_model_);
  208. DCHECK(!bookmark_tracker_);
  209. DCHECK(!bookmark_model_observer_);
  210. // TODO(crbug.com/950869): Remove after investigations are completed.
  211. TRACE_EVENT0("browser", "BookmarkModelTypeProcessor::ModelReadyToSync");
  212. bookmark_model_ = model;
  213. schedule_save_closure_ = schedule_save_closure;
  214. sync_pb::BookmarkModelMetadata model_metadata;
  215. model_metadata.ParseFromString(metadata_str);
  216. bookmark_tracker_ = SyncedBookmarkTracker::CreateFromBookmarkModelAndMetadata(
  217. model, std::move(model_metadata));
  218. if (bookmark_tracker_) {
  219. StartTrackingMetadata();
  220. } else if (!metadata_str.empty()) {
  221. DLOG(WARNING)
  222. << "Persisted bookmark sync metadata invalidated when loading.";
  223. // Schedule a save to make sure the corrupt metadata is deleted from disk as
  224. // soon as possible, to avoid reporting again after restart if nothing else
  225. // schedules a save meanwhile (which is common if sync is not running
  226. // properly, e.g. auth error).
  227. schedule_save_closure_.Run();
  228. }
  229. ConnectIfReady();
  230. }
  231. void BookmarkModelTypeProcessor::SetFaviconService(
  232. favicon::FaviconService* favicon_service) {
  233. DCHECK(favicon_service);
  234. favicon_service_ = favicon_service;
  235. }
  236. size_t BookmarkModelTypeProcessor::EstimateMemoryUsage() const {
  237. using base::trace_event::EstimateMemoryUsage;
  238. size_t memory_usage = 0;
  239. if (bookmark_tracker_) {
  240. memory_usage += bookmark_tracker_->EstimateMemoryUsage();
  241. }
  242. memory_usage += EstimateMemoryUsage(cache_guid_);
  243. return memory_usage;
  244. }
  245. base::WeakPtr<syncer::ModelTypeControllerDelegate>
  246. BookmarkModelTypeProcessor::GetWeakPtr() {
  247. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  248. return weak_ptr_factory_for_controller_.GetWeakPtr();
  249. }
  250. void BookmarkModelTypeProcessor::OnSyncStarting(
  251. const syncer::DataTypeActivationRequest& request,
  252. StartCallback start_callback) {
  253. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  254. DCHECK(start_callback);
  255. // |favicon_service_| should have been set by now.
  256. DCHECK(favicon_service_);
  257. DVLOG(1) << "Sync is starting for Bookmarks";
  258. cache_guid_ = request.cache_guid;
  259. start_callback_ = std::move(start_callback);
  260. error_handler_ = request.error_handler;
  261. DCHECK(!cache_guid_.empty());
  262. ConnectIfReady();
  263. }
  264. void BookmarkModelTypeProcessor::ConnectIfReady() {
  265. // Return if the model isn't ready.
  266. if (!bookmark_model_) {
  267. return;
  268. }
  269. // Return if Sync didn't start yet.
  270. if (!start_callback_) {
  271. return;
  272. }
  273. DCHECK(!cache_guid_.empty());
  274. if (bookmark_tracker_ &&
  275. bookmark_tracker_->model_type_state().cache_guid() != cache_guid_) {
  276. // TODO(crbug.com/820049): Add basic unit testing consider using
  277. // StopTrackingMetadata().
  278. // In case of a cache guid mismatch, treat it as a corrupted metadata and
  279. // start clean.
  280. bookmark_model_->RemoveObserver(bookmark_model_observer_.get());
  281. bookmark_model_observer_.reset();
  282. bookmark_tracker_.reset();
  283. }
  284. auto activation_context =
  285. std::make_unique<syncer::DataTypeActivationResponse>();
  286. if (bookmark_tracker_) {
  287. activation_context->model_type_state =
  288. bookmark_tracker_->model_type_state();
  289. } else {
  290. sync_pb::ModelTypeState model_type_state;
  291. model_type_state.mutable_progress_marker()->set_data_type_id(
  292. GetSpecificsFieldNumberFromModelType(syncer::BOOKMARKS));
  293. model_type_state.set_cache_guid(cache_guid_);
  294. activation_context->model_type_state = model_type_state;
  295. }
  296. activation_context->type_processor =
  297. std::make_unique<syncer::ModelTypeProcessorProxy>(
  298. weak_ptr_factory_for_worker_.GetWeakPtr(),
  299. base::SequencedTaskRunnerHandle::Get());
  300. std::move(start_callback_).Run(std::move(activation_context));
  301. }
  302. void BookmarkModelTypeProcessor::OnSyncStopping(
  303. syncer::SyncStopMetadataFate metadata_fate) {
  304. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  305. // Disabling sync for a type shouldn't happen before the model is loaded
  306. // because OnSyncStopping() is not allowed to be called before
  307. // OnSyncStarting() has completed..
  308. DCHECK(bookmark_model_);
  309. DCHECK(!start_callback_);
  310. cache_guid_.clear();
  311. worker_.reset();
  312. switch (metadata_fate) {
  313. case syncer::KEEP_METADATA: {
  314. break;
  315. }
  316. case syncer::CLEAR_METADATA: {
  317. // Stop observing local changes. We'll start observing local changes again
  318. // when Sync is (re)started in StartTrackingMetadata().
  319. if (bookmark_tracker_) {
  320. DCHECK(bookmark_model_observer_);
  321. bookmark_model_->RemoveObserver(bookmark_model_observer_.get());
  322. bookmark_model_observer_.reset();
  323. bookmark_tracker_.reset();
  324. }
  325. schedule_save_closure_.Run();
  326. break;
  327. }
  328. }
  329. // Do not let any delayed callbacks to be called.
  330. weak_ptr_factory_for_controller_.InvalidateWeakPtrs();
  331. weak_ptr_factory_for_worker_.InvalidateWeakPtrs();
  332. }
  333. void BookmarkModelTypeProcessor::NudgeForCommitIfNeeded() {
  334. DCHECK(bookmark_tracker_);
  335. // Don't bother sending anything if there's no one to send to.
  336. if (!worker_) {
  337. return;
  338. }
  339. // Nudge worker if there are any entities with local changes.
  340. if (bookmark_tracker_->HasLocalChanges()) {
  341. worker_->NudgeForCommit();
  342. }
  343. }
  344. void BookmarkModelTypeProcessor::OnBookmarkModelBeingDeleted() {
  345. DCHECK(bookmark_model_);
  346. DCHECK(bookmark_model_observer_);
  347. StopTrackingMetadata();
  348. }
  349. void BookmarkModelTypeProcessor::OnInitialUpdateReceived(
  350. const sync_pb::ModelTypeState& model_type_state,
  351. syncer::UpdateResponseDataList updates) {
  352. DCHECK(!bookmark_tracker_);
  353. TRACE_EVENT0("sync", "BookmarkModelTypeProcessor::OnInitialUpdateReceived");
  354. bookmark_tracker_ = SyncedBookmarkTracker::CreateEmpty(model_type_state);
  355. StartTrackingMetadata();
  356. {
  357. ScopedRemoteUpdateBookmarks update_bookmarks(
  358. bookmark_model_, bookmark_undo_service_,
  359. bookmark_model_observer_.get());
  360. BookmarkModelMerger model_merger(std::move(updates), bookmark_model_,
  361. favicon_service_, bookmark_tracker_.get());
  362. model_merger.Merge();
  363. }
  364. // If any of the permanent nodes is missing, we treat it as failure.
  365. if (!bookmark_tracker_->GetEntityForBookmarkNode(
  366. bookmark_model_->bookmark_bar_node()) ||
  367. !bookmark_tracker_->GetEntityForBookmarkNode(
  368. bookmark_model_->other_node()) ||
  369. !bookmark_tracker_->GetEntityForBookmarkNode(
  370. bookmark_model_->mobile_node())) {
  371. StopTrackingMetadata();
  372. bookmark_tracker_.reset();
  373. error_handler_.Run(
  374. syncer::ModelError(FROM_HERE, "Permanent bookmark entities missing"));
  375. return;
  376. }
  377. bookmark_tracker_->CheckAllNodesTracked(bookmark_model_);
  378. schedule_save_closure_.Run();
  379. NudgeForCommitIfNeeded();
  380. }
  381. void BookmarkModelTypeProcessor::StartTrackingMetadata() {
  382. DCHECK(bookmark_tracker_);
  383. DCHECK(!bookmark_model_observer_);
  384. bookmark_model_observer_ = std::make_unique<BookmarkModelObserverImpl>(
  385. base::BindRepeating(&BookmarkModelTypeProcessor::NudgeForCommitIfNeeded,
  386. base::Unretained(this)),
  387. base::BindOnce(&BookmarkModelTypeProcessor::OnBookmarkModelBeingDeleted,
  388. base::Unretained(this)),
  389. bookmark_tracker_.get());
  390. bookmark_model_->AddObserver(bookmark_model_observer_.get());
  391. }
  392. void BookmarkModelTypeProcessor::StopTrackingMetadata() {
  393. DCHECK(bookmark_model_observer_);
  394. bookmark_model_->RemoveObserver(bookmark_model_observer_.get());
  395. bookmark_model_ = nullptr;
  396. bookmark_model_observer_.reset();
  397. DisconnectSync();
  398. }
  399. void BookmarkModelTypeProcessor::GetAllNodesForDebugging(
  400. AllNodesCallback callback) {
  401. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  402. base::Value::List all_nodes;
  403. // Create a permanent folder since sync server no longer create root folders,
  404. // and USS won't migrate root folders from directory, we create root folders.
  405. base::Value::Dict root_node;
  406. // Function isTypeRootNode in sync_node_browser.js use PARENT_ID and
  407. // UNIQUE_SERVER_TAG to check if the node is root node. isChildOf in
  408. // sync_node_browser.js uses modelType to check if root node is parent of real
  409. // data node. NON_UNIQUE_NAME will be the name of node to display.
  410. root_node.Set("ID", "BOOKMARKS_ROOT");
  411. root_node.Set("PARENT_ID", "r");
  412. root_node.Set("UNIQUE_SERVER_TAG", "Bookmarks");
  413. root_node.Set("IS_DIR", true);
  414. root_node.Set("modelType", "Bookmarks");
  415. root_node.Set("NON_UNIQUE_NAME", "Bookmarks");
  416. all_nodes.Append(std::move(root_node));
  417. const bookmarks::BookmarkNode* model_root_node = bookmark_model_->root_node();
  418. int i = 0;
  419. for (const auto& child : model_root_node->children()) {
  420. AppendNodeAndChildrenForDebugging(child.get(), i++, &all_nodes);
  421. }
  422. std::move(callback).Run(syncer::BOOKMARKS, std::move(all_nodes));
  423. }
  424. void BookmarkModelTypeProcessor::AppendNodeAndChildrenForDebugging(
  425. const bookmarks::BookmarkNode* node,
  426. int index,
  427. base::Value::List* all_nodes) const {
  428. const SyncedBookmarkTrackerEntity* entity =
  429. bookmark_tracker_->GetEntityForBookmarkNode(node);
  430. // Include only tracked nodes. Newly added nodes are tracked even before being
  431. // sent to the server. Managed bookmarks (that are installed by a policy)
  432. // aren't syncable and hence not tracked.
  433. if (!entity) {
  434. return;
  435. }
  436. const sync_pb::EntityMetadata& metadata = entity->metadata();
  437. // Copy data to an EntityData object to reuse its conversion
  438. // ToDictionaryValue() methods.
  439. syncer::EntityData data;
  440. data.id = metadata.server_id();
  441. data.creation_time = node->date_added();
  442. data.modification_time =
  443. syncer::ProtoTimeToTime(metadata.modification_time());
  444. data.name = base::UTF16ToUTF8(node->GetTitle());
  445. data.specifics = CreateSpecificsFromBookmarkNode(
  446. node, bookmark_model_, metadata.unique_position(),
  447. /*force_favicon_load=*/false);
  448. if (node->is_permanent_node()) {
  449. data.server_defined_unique_tag =
  450. ComputeServerDefinedUniqueTagForDebugging(node, bookmark_model_);
  451. // Set the parent to empty string to indicate it's parent of the root node
  452. // for bookmarks. The code in sync_node_browser.js links nodes with the
  453. // "modelType" when they are lacking a parent id.
  454. data.legacy_parent_id = "";
  455. } else {
  456. const bookmarks::BookmarkNode* parent = node->parent();
  457. const SyncedBookmarkTrackerEntity* parent_entity =
  458. bookmark_tracker_->GetEntityForBookmarkNode(parent);
  459. DCHECK(parent_entity);
  460. data.legacy_parent_id = parent_entity->metadata().server_id();
  461. }
  462. base::Value::Dict data_dictionary = data.ToDictionaryValue();
  463. // Set ID value as in legacy directory-based implementation, "s" means server.
  464. data_dictionary.Set("ID", "s" + metadata.server_id());
  465. if (node->is_permanent_node()) {
  466. // Hardcode the parent of permanent nodes.
  467. data_dictionary.Set("PARENT_ID", "BOOKMARKS_ROOT");
  468. data_dictionary.Set("UNIQUE_SERVER_TAG", data.server_defined_unique_tag);
  469. } else {
  470. data_dictionary.Set("PARENT_ID", "s" + data.legacy_parent_id);
  471. }
  472. data_dictionary.Set("LOCAL_EXTERNAL_ID", static_cast<int>(node->id()));
  473. data_dictionary.Set("positionIndex", index);
  474. data_dictionary.Set("metadata", base::Value::FromUniquePtrValue(
  475. syncer::EntityMetadataToValue(metadata)));
  476. data_dictionary.Set("modelType", "Bookmarks");
  477. data_dictionary.Set("IS_DIR", node->is_folder());
  478. all_nodes->Append(std::move(data_dictionary));
  479. int i = 0;
  480. for (const auto& child : node->children()) {
  481. AppendNodeAndChildrenForDebugging(child.get(), i++, all_nodes);
  482. }
  483. }
  484. void BookmarkModelTypeProcessor::GetTypeEntitiesCountForDebugging(
  485. base::OnceCallback<void(const syncer::TypeEntitiesCount&)> callback) const {
  486. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  487. syncer::TypeEntitiesCount count(syncer::BOOKMARKS);
  488. if (bookmark_tracker_) {
  489. count.non_tombstone_entities = bookmark_tracker_->TrackedBookmarksCount();
  490. count.entities = count.non_tombstone_entities +
  491. bookmark_tracker_->TrackedUncommittedTombstonesCount();
  492. }
  493. std::move(callback).Run(count);
  494. }
  495. void BookmarkModelTypeProcessor::RecordMemoryUsageAndCountsHistograms() {
  496. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  497. SyncRecordModelTypeMemoryHistogram(syncer::BOOKMARKS, EstimateMemoryUsage());
  498. if (bookmark_tracker_) {
  499. SyncRecordModelTypeCountHistogram(
  500. syncer::BOOKMARKS, bookmark_tracker_->TrackedBookmarksCount());
  501. } else {
  502. SyncRecordModelTypeCountHistogram(syncer::BOOKMARKS, 0);
  503. }
  504. }
  505. } // namespace sync_bookmarks