| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/bookmarks/browser/bookmark_load_details.h" |
| |
| #include "base/uuid.h" |
| #include "components/bookmarks/browser/bookmark_uuids.h" |
| #include "components/bookmarks/browser/titled_url_index.h" |
| #include "components/bookmarks/browser/url_index.h" |
| #include "components/bookmarks/common/bookmark_constants.h" |
| #include "components/bookmarks/common/user_folder_load_stats.h" |
| |
| namespace bookmarks { |
| |
| namespace { |
| |
| // Number of top-level permanent folders excluding the managed node and account |
| // bookmarks. |
| constexpr size_t kNumDefaultTopLevelPermanentFolders = 3u; |
| |
| void UpdateUserFolderStatsRecursively(const BookmarkNode& node, |
| bool top_level, |
| UserFolderLoadStats& stats) { |
| DCHECK(!node.is_permanent_node()); |
| |
| if (!node.is_folder()) { |
| return; |
| } |
| |
| stats.total_folders++; |
| if (top_level) { |
| stats.total_top_level_folders++; |
| } |
| |
| for (auto& child : node.children()) { |
| UpdateUserFolderStatsRecursively(*child, /*top_level=*/false, stats); |
| } |
| } |
| |
| } // namespace |
| |
| BookmarkLoadDetails::BookmarkLoadDetails() |
| : titled_url_index_(std::make_unique<TitledUrlIndex>()), |
| load_start_(base::TimeTicks::Now()) { |
| // WARNING: do NOT add `client` as a member. Much of this code runs on another |
| // thread, and `client_` is not thread safe, and/or may be destroyed before |
| // this. |
| root_node_ = std::make_unique<BookmarkNode>( |
| kRootNodeId, base::Uuid::ParseLowercase(kRootNodeUuid), GURL()); |
| // WARNING: order is important here, various places assume the order is |
| // constant (but can vary between embedders with the initial visibility |
| // of permanent nodes). |
| // |
| // Zero is used as temporary ID for permanent nodes, until an actual value is |
| // loaded from disk or new/default values are allocated in |
| // `PopulateNodeIdsForLocalOrSyncablePermanentNodes()`. |
| bb_node_ = static_cast<BookmarkPermanentNode*>( |
| root_node_->Add(BookmarkPermanentNode::CreateBookmarkBar( |
| /*id=*/0, /*is_account_node=*/false))); |
| other_folder_node_ = static_cast<BookmarkPermanentNode*>( |
| root_node_->Add(BookmarkPermanentNode::CreateOtherBookmarks( |
| /*id=*/0, /*is_account_node=*/false))); |
| mobile_folder_node_ = static_cast<BookmarkPermanentNode*>( |
| root_node_->Add(BookmarkPermanentNode::CreateMobileBookmarks( |
| /*id=*/0, /*is_account_node=*/false))); |
| |
| // Set the nodes' `date_added` to the same time so that there is no inherent |
| // hierarchy in terms of their added time between them. This is relevant for |
| // deciding which folder should be the default parent for new nodes. |
| // Note that these timestamps will likely be overridden by `BookmarkCodec` |
| // with the values loaded from disk, if the JSON file exists. |
| const base::Time current_timestamp = base::Time::Now(); |
| bb_node_->set_date_added(current_timestamp); |
| other_folder_node_->set_date_added(current_timestamp); |
| mobile_folder_node_->set_date_added(current_timestamp); |
| |
| CHECK_EQ(kNumDefaultTopLevelPermanentFolders, root_node_->children().size()); |
| } |
| |
| BookmarkLoadDetails::~BookmarkLoadDetails() = default; |
| |
| void BookmarkLoadDetails::AddAccountPermanentNodes( |
| std::unique_ptr<BookmarkPermanentNode> account_bb_node, |
| std::unique_ptr<BookmarkPermanentNode> account_other_folder_node, |
| std::unique_ptr<BookmarkPermanentNode> account_mobile_folder_node) { |
| CHECK(account_bb_node); |
| CHECK(account_other_folder_node); |
| CHECK(account_mobile_folder_node); |
| CHECK(!account_bb_node_); |
| CHECK(!account_other_folder_node_); |
| CHECK(!account_mobile_folder_node_); |
| |
| // The order here is consistent with the one used for non-account permanent |
| // folders, created in the constructor. |
| account_bb_node_ = static_cast<BookmarkPermanentNode*>( |
| root_node_->Add(std::move(account_bb_node))); |
| account_other_folder_node_ = static_cast<BookmarkPermanentNode*>( |
| root_node_->Add(std::move(account_other_folder_node))); |
| account_mobile_folder_node_ = static_cast<BookmarkPermanentNode*>( |
| root_node_->Add(std::move(account_mobile_folder_node))); |
| } |
| |
| void BookmarkLoadDetails::PopulateNodeIdsForLocalOrSyncablePermanentNodes() { |
| CHECK(bb_node_); |
| CHECK(other_folder_node_); |
| CHECK(mobile_folder_node_); |
| |
| // AddManagedNode() may only be called after this function. |
| CHECK(!has_managed_node_); |
| |
| if (bb_node_->id() == 0) { |
| bb_node_->set_id(max_id_++); |
| } |
| |
| if (other_folder_node_->id() == 0) { |
| other_folder_node_->set_id(max_id_++); |
| } |
| |
| if (mobile_folder_node_->id() == 0) { |
| mobile_folder_node_->set_id(max_id_++); |
| } |
| } |
| |
| void BookmarkLoadDetails::AddManagedNode( |
| std::unique_ptr<BookmarkPermanentNode> managed_node) { |
| CHECK(managed_node); |
| CHECK(!has_managed_node_); |
| |
| // Ensure that `PopulateNodeIdsForLocalOrSyncablePermanentNodes` was invoked |
| // before this function. |
| CHECK_NE(bb_node_->id(), 0); |
| CHECK_NE(other_folder_node_->id(), 0); |
| CHECK_NE(mobile_folder_node_->id(), 0); |
| |
| has_managed_node_ = true; |
| root_node_->Add(std::move(managed_node)); |
| } |
| |
| void BookmarkLoadDetails::CreateIndices() { |
| local_or_syncable_uuid_index_.insert(root_node_.get()); |
| static_assert(kNumDefaultTopLevelPermanentFolders == 3u, |
| "The code below assumes three permanent nodes"); |
| for (const auto& child : root_node_->children()) { |
| if (child.get() == account_bb_node_ || |
| child.get() == account_other_folder_node_ || |
| child.get() == account_mobile_folder_node_) { |
| // Use a dedicated index for account folders and desdendants. |
| AddNodeToIndexRecursive(child.get(), account_uuid_index_); |
| } else { |
| AddNodeToIndexRecursive(child.get(), local_or_syncable_uuid_index_); |
| } |
| } |
| |
| url_index_ = base::MakeRefCounted<UrlIndex>(std::move(root_node_)); |
| } |
| |
| void BookmarkLoadDetails::ResetPermanentNodePointers() { |
| bb_node_ = nullptr; |
| other_folder_node_ = nullptr; |
| mobile_folder_node_ = nullptr; |
| account_bb_node_ = nullptr; |
| account_other_folder_node_ = nullptr; |
| account_mobile_folder_node_ = nullptr; |
| } |
| |
| const BookmarkNode* BookmarkLoadDetails::RootNodeForTest() const { |
| return GetRootNode(); |
| } |
| |
| UserFolderLoadStats BookmarkLoadDetails::ComputeUserFolderStats() const { |
| UserFolderLoadStats stats; |
| const BookmarkNode* root = GetRootNode(); |
| |
| for (const auto& root_child : root->children()) { |
| // Look for user-generated folders under permanent nodes. |
| if (!root_child->is_permanent_node()) { |
| continue; |
| } |
| |
| // We want to track the number of bookmarks at the top level of the |
| // Bookmark Bar. |
| if (root_child->is_folder() && |
| root_child->type() == BookmarkNode::BOOKMARK_BAR) { |
| stats.bookmark_bar_top_level_items = root_child->children().size(); |
| } |
| |
| for (const auto& child : root_child->children()) { |
| UpdateUserFolderStatsRecursively(*child, /*top_level=*/true, stats); |
| } |
| } |
| |
| return stats; |
| } |
| |
| void BookmarkLoadDetails::AddNodeToIndexRecursive(BookmarkNode* node, |
| UuidIndex& uuid_index) { |
| uuid_index.insert(node); |
| if (node->is_url()) { |
| if (node->url().is_valid()) { |
| titled_url_index_->Add(node); |
| } |
| } else { |
| titled_url_index_->AddPath(node); |
| for (const auto& child : node->children()) { |
| AddNodeToIndexRecursive(child.get(), uuid_index); |
| } |
| } |
| } |
| |
| const BookmarkNode* BookmarkLoadDetails::GetRootNode() const { |
| return url_index_ ? url_index_->root() : root_node_.get(); |
| } |
| |
| } // namespace bookmarks |