910e62b5创建于 1月15日历史提交
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MODEL_H_
#define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MODEL_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>

#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/sequence_checker.h"
#include "base/supports_user_data.h"
#include "base/uuid.h"
#include "components/bookmarks/browser/bookmark_client.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/browser/bookmark_undo_provider.h"
#include "components/bookmarks/browser/uuid_index.h"
#include "components/bookmarks/common/bookmark_metrics.h"
#include "components/keyed_service/core/keyed_service.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"

namespace base {
class FilePath;
class Location;
}  // namespace base

namespace favicon_base {
struct FaviconImageResult;
}

namespace query_parser {
enum class MatchingAlgorithm;
}

namespace bookmarks {

class BookmarkCodecTest;
class BookmarkLoadDetails;
class BookmarkModelObserver;
class BookmarkStorage;
class ModelLoader;
class ScopedGroupBookmarkActions;
class TitledUrlIndex;
class UrlIndex;

struct UrlAndTitle;
struct TitledUrlMatch;

// BookmarkModel --------------------------------------------------------------

// BookmarkModel provides a directed acyclic graph of URLs and folders.
// Three graphs are provided for the three entry points: those on the 'bookmarks
// bar', those in the 'other bookmarks' folder and those in the 'mobile' folder.
//
// An observer may be attached to observe relevant events.
//
// You should NOT directly create a BookmarkModel, instead go through the
// BookmarkModelFactory.
class BookmarkModel : public BookmarkUndoProvider,
                      public KeyedService,
                      public base::SupportsUserData {
 public:
  // `client` must not be null.
  explicit BookmarkModel(std::unique_ptr<BookmarkClient> client);

  BookmarkModel(const BookmarkModel&) = delete;
  BookmarkModel& operator=(const BookmarkModel&) = delete;

  ~BookmarkModel() override;

  // Triggers the loading of bookmarks, which is an asynchronous operation with
  // most heavy-lifting taking place in a background sequence. Upon completion,
  // loaded() will return true and observers will be notified via
  // BookmarkModelLoaded().
  void Load(const base::FilePath& profile_path);

  // Returns true if the model finished loading.
  bool loaded() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return loaded_;
  }

  // Returns the object responsible for tracking loading.
  scoped_refptr<ModelLoader> model_loader();

  // Returns the root node. The 'bookmark bar' node and 'other' node are
  // children of the root node.
  const BookmarkNode* root_node() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return root_;
  }

  // Returns the 'bookmark bar' node for the local-or-syncable storage.
  // Local-or-syncable storage is used for syncing bookmarks *only* if
  // Sync-the-feature is enabled. After Sync-to-Signin migration is finished -
  // local-or-syncable storage (and this folder) will become purely local.
  // This is null until loaded.
  const BookmarkPermanentNode* bookmark_bar_node() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return bookmark_bar_node_;
  }

  // Returns the 'other' node for the local-or-syncable storage.
  // Local-or-syncable storage is used for syncing bookmarks *only* if
  // Sync-the-feature is enabled. After Sync-to-Signin migration is finished -
  // local-or-syncable storage (and this folder) will become purely local.
  // This is null until loaded.
  const BookmarkPermanentNode* other_node() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return other_node_;
  }

  // Returns the 'mobile' node for the local-or-syncable storage.
  // Local-or-syncable storage is used for syncing bookmarks *only* if
  // Sync-the-feature is enabled. After Sync-to-Signin migration is finished -
  // local-or-syncable storage (and this folder) will become purely local.
  // This is null until loaded.
  const BookmarkPermanentNode* mobile_node() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return mobile_node_;
  }

  // Returns the 'bookmark bar' node for the account storage. This is null until
  // loaded or if the user is not signed in (or isn't opted into syncing
  // bookmarks in the account storage).
  const BookmarkPermanentNode* account_bookmark_bar_node() const;

  // Returns the 'other' node for the account storage. This is null until loaded
  // or if the user is not signed in (or isn't opted into syncing bookmarks in
  // the account storage).
  const BookmarkPermanentNode* account_other_node() const;

  // Returns the 'mobile' node for the account storage. This is null until
  // loaded or if the user is not signed in (or isn't opted into syncing
  // bookmarks in the account storage).
  const BookmarkPermanentNode* account_mobile_node() const;

  bool is_root_node(const BookmarkNode* node) const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return node == root_;
  }

  // Returns whether the given `node` is one of the permanent nodes - root node,
  // 'bookmark bar' node, 'other' node or 'mobile' node, or one of the root
  // nodes supplied by the `client_`.
  bool is_permanent_node(const BookmarkNode* node) const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return node && (node == root_ || node->parent() == root_);
  }

  // Returns true if `node` represents a bookmark that is stored on the local
  // profile but not saved to the user's server-side account. The opposite case,
  // returning null, can happen because the user turned sync-the-feature on,
  // which syncs all bookmarks, or because `node` is a descendant of an account
  // permanent folder, e.g. `account_bookmark_bar_node()`.
  bool IsLocalOnlyNode(const BookmarkNode& node) const;

  void AddObserver(BookmarkModelObserver* observer);
  void RemoveObserver(BookmarkModelObserver* observer);

  // Notifies the observers that an extensive set of changes is about to happen,
  // such as during import or sync, so they can delay any expensive UI updates
  // until it's finished.
  //
  // Undo tracking is suspended during extensive changes.
  void BeginExtensiveChanges();
  void EndExtensiveChanges();

  // Returns true if this bookmark model is currently in a mode where extensive
  // changes might happen, such as for import and sync. This is helpful for
  // observers that are created after the mode has started, and want to check
  // state during their own initializer, such as the NTP.
  bool IsDoingExtensiveChanges() const { return extensive_changes_ > 0; }

  // Removes `node` from the model and deletes it. Removing a folder node
  // recursively removes all nodes. Observers are notified immediately. `node`
  // must not be a permanent node. The source of the removal is passed through
  // `source`. `location` is used for logging purposes and investigations.
  void Remove(const BookmarkNode* node,
              metrics::BookmarkEditSource source,
              const base::Location& location);

  // Removes the last child under `parent`. This is identical to invoking
  // `Remove()` for the actual child, i.e.
  // `Remove(parent->children()[parent->children().size() - 1].get())`. The
  // only difference is that `RemoveLastChild()` is guaranteed to require
  // constant time, for advanced cases where performance is a concern (to be
  // more accurate, it exhibits logarithmic runtime complexity with respect to
  // the tree depth, excluding the cost incurred in observers, which may
  // implement arbitrary logic outside BookmarkModel's control).
  void RemoveLastChild(const BookmarkNode* parent,
                       metrics::BookmarkEditSource source,
                       const base::Location& location);

  // Removes all the non-permanent bookmark nodes that are editable by the user.
  // Observers are only notified when all nodes have been removed. There is no
  // notification for individual node removals. `location` is used for logging
  // purposes and investigations.
  void RemoveAllUserBookmarks(const base::Location& location);

  // Moves `node` to `new_parent` and inserts it at the given `index`.
  //
  // Note: this might cause UUIDs to get reassigned for `node` or its
  // descendants, when the node is moved between local and account storages.
  //
  // `new_parent` may be the same as `node`'s current parent, in which case the
  // semantics are "insert before the element currently at `index`". Suppose the
  // initial current children of new_parent are ordered [A, B, C]:
  // * Move(B, new_parent, 0) -> [B, A, C]
  // * Move(B, new_parent, 1) -> [A, B, C]
  // * Move(B, new_parent, 2) -> [A, B, C]
  // * Move(B, new_parent, 3) -> [A, C, B]
  void Move(const BookmarkNode* node,
            const BookmarkNode* new_parent,
            size_t index);

  // Returns the favicon for `node`. If the favicon has not yet been loaded,
  // a load will be triggered and the observer of the model notified when done.
  // This also means that, on return, the node's state is guaranteed to be
  // either LOADED_FAVICON (if it was already loaded prior to the call) or
  // LOADING_FAVICON (with the exception of folders, where the call is a no-op).
  const gfx::Image& GetFavicon(const BookmarkNode* node);

  // Sets the title of `node`.
  void SetTitle(const BookmarkNode* node,
                const std::u16string& title,
                metrics::BookmarkEditSource source);

  // Sets the URL of `node`.
  void SetURL(const BookmarkNode* node,
              const GURL& url,
              metrics::BookmarkEditSource source);

  // Sets the date added time of `node`.
  void SetDateAdded(const BookmarkNode* node, base::Time date_added);

  // Returns the set of nodes with the `url`.
  [[nodiscard]] std::vector<raw_ptr<const BookmarkNode, VectorExperimental>>
  GetNodesByURL(const GURL& url) const;

  // Enum determining a subset of bookmark nodes within a BookmarkModel for the
  // purpose of issuing UUID-based lookups. It is needed because, in some
  // advanced scenarios, the same UUID may be used by two BookmarkNode-s, in
  // particular if a bookmark duplicate exists as a result of Sync having left
  // behind a copy of the data.
  //
  // Below some guidance about the use of this enum:
  // 1. For callers that don't particularly mind the nature of the bookmark
  //    node, a sensible default is to issue two lookups, starting with
  //    `kAccountNodes` and (if the first returns null) following up with
  //    `kLocalOrSyncableNodes`.
  //
  // 2. For callers that specifically need to identify bookmarks that are saved
  //    (sync-ed) to the user's server-side account, it might be required to
  //    only conditionally issue the second lookup (`kLocalOrSyncableNodes`),
  //    based on whether user-facing Sync is on (exposed outside
  //    components/bookmarks, namely in SyncService).
  //
  // 3. For callers that specifically need the opposite, that is, identify
  //    bookmarks that are local-only, a lookup using `kLocalOrSyncableNodes`
  //    is sufficient, but it should also be done conditionally to the
  //    user-facing Sync being off (exposed outside components/bookmarks, namely
  //    in SyncService).
  //
  // In doubt, please reach out to components/bookmarks owners for guidance.
  enum class NodeTypeForUuidLookup {
    // Local or syncable nodes include all bookmark nodes that are not
    // descendants of account permanent folders (e.g. as returned by
    // account_bookmark_bar_node()).
    kLocalOrSyncableNodes,
    // Account nodes include all bookmarks that are descendants of account
    // permanent folders (e.g. as returned by account_bookmark_bar_node()).
    kAccountNodes,
  };

  // Returns the node with the given `uuid` among the subset of nodes determined
  // by `type`. Returns null if no node exists matching `uuid`.
  //
  // WARNING: UUID-based lookups are subtle and should be done with care, please
  // see details above in `NodeTypeForUuidLookup` and in doubt please reach out
  // to components/bookmarks owners for guidance.
  const BookmarkNode* GetNodeByUuid(const base::Uuid& uuid,
                                    NodeTypeForUuidLookup type) const;

  // Returns the most recently added user node for the `url`; urls from any
  // nodes that are not editable by the user are never returned by this call.
  // Returns NULL if `url` is not bookmarked.
  const BookmarkNode* GetMostRecentlyAddedUserNodeForURL(const GURL& url) const;

  // Returns true if there are bookmarks, otherwise returns false.
  bool HasBookmarks() const;

  // Returns true is there is at least one user-created bookmark or folder. This
  // includes bookmarks downloaded via Sync but excludes managed nodes
  // (enterprise) as well as, on Android, partner bookmarks (which are not
  // included in BookmarkModel).
  bool HasUserCreatedBookmarksOrFolders() const;

  // Returns true if the specified URL is bookmarked.
  bool IsBookmarked(const GURL& url) const;

  // Return the set of bookmarked urls and their titles. This returns the unique
  // set of URLs. For example, if two bookmarks reference the same URL only one
  // entry is added not matter the titles are same or not.
  [[nodiscard]] std::vector<UrlAndTitle> GetUniqueUrls() const;

  // Returns the type of `folder` as represented in metrics.
  metrics::BookmarkFolderTypeForUMA GetFolderType(
      const BookmarkNode* folder) const;

  // Adds a new folder node at the specified position with the given
  // `creation_time`, `uuid` and `meta_info`. If no UUID is provided (i.e.
  // nullopt), then a random one will be generated. If a UUID is provided, it
  // must be valid.
  const BookmarkNode* AddFolder(
      const BookmarkNode* parent,
      size_t index,
      const std::u16string& title,
      const BookmarkNode::MetaInfoMap* meta_info = nullptr,
      std::optional<base::Time> creation_time = std::nullopt,
      std::optional<base::Uuid> uuid = std::nullopt);

  // Adds a new bookmark for the given `url` at the specified position with the
  // given `meta_info`. Used for bookmarks being added through some direct user
  // action (e.g. the bookmark star).
  const BookmarkNode* AddNewURL(
      const BookmarkNode* parent,
      size_t index,
      const std::u16string& title,
      const GURL& url,
      const BookmarkNode::MetaInfoMap* meta_info = nullptr);

  // Adds a url at the specified position with the given `creation_time`,
  // `meta_info`, `uuid`, and `last_used_time`. If no UUID is provided
  // (i.e. nullopt), then a random one will be generated. If a UUID is
  // provided, it must be valid. Used for bookmarks not being added from
  // direct user actions (e.g. created via sync, locally modified bookmark
  // or pre-existing bookmark). `added_by_user` is true when a new bookmark was
  // added by the user and false when a node is added by sync or duplicated.
  const BookmarkNode* AddURL(
      const BookmarkNode* parent,
      size_t index,
      const std::u16string& title,
      const GURL& url,
      const BookmarkNode::MetaInfoMap* meta_info = nullptr,
      std::optional<base::Time> creation_time = std::nullopt,
      std::optional<base::Uuid> uuid = std::nullopt,
      bool added_by_user = false);

  // Sorts the children of `parent`, notifying observers by way of the
  // BookmarkNodeChildrenReordered method.
  void SortChildren(const BookmarkNode* parent);

  // Order the children of `parent` as specified in `ordered_nodes`.  This
  // function should only be used to reorder the child nodes of `parent` and
  // is not meant to move nodes between different parent. Notifies observers
  // using the BookmarkNodeChildrenReordered method.
  void ReorderChildren(const BookmarkNode* parent,
                       const std::vector<const BookmarkNode*>& ordered_nodes);

  // Sets the date when the folder was modified.
  void SetDateFolderModified(const BookmarkNode* node, const base::Time time);

  // Resets the 'date modified' time of the node to 0. This is used during
  // importing to exclude the newly created folders from showing up in the
  // combobox of most recently modified folders.
  void ResetDateFolderModified(const BookmarkNode* node);

  // Updates the last used `time` for the given `node`. `just_opened`
  // indicates whether this is being called as a result of the bookmark being
  // opened. `just_opened` being false means that this update didn't come from
  // a user, such as sync updating a node automatically.
  void UpdateLastUsedTime(const BookmarkNode* node,
                          const base::Time time,
                          bool just_opened);

  // Clears the last used time for the given time range. Called when the user
  // clears their history. Time() and Time::Max() are used for min/max values.
  void ClearLastUsedTimeInRange(const base::Time delete_begin,
                                const base::Time delete_end);

  // Returns up to `max_count` bookmarks containing each term from `query` in
  // either the title, URL, or the titles of ancestors. `matching_algorithm`
  // determines the algorithm used by QueryParser internally to parse `query`.
  [[nodiscard]] std::vector<TitledUrlMatch> GetBookmarksMatching(
      const std::u16string& query,
      size_t max_count,
      query_parser::MatchingAlgorithm matching_algorithm) const;

  // Disables the persistence to disk, useful during testing to speed up
  // testing.
  void DisableWritesToDiskForTest();

  // Returns the next node ID.
  int64_t next_node_id() const { return next_node_id_; }

  // Sets/deletes meta info of `node`.
  void SetNodeMetaInfo(const BookmarkNode* node,
                       const std::string& key,
                       const std::string& value);
  void SetNodeMetaInfoMap(const BookmarkNode* node,
                          const BookmarkNode::MetaInfoMap& meta_info_map);
  void DeleteNodeMetaInfo(const BookmarkNode* node, const std::string& key);

  // Notify BookmarkModel that the favicons for the given page URLs (e.g.
  // http://www.google.com) and the given icon URL (e.g.
  // http://www.google.com/favicon.ico) have changed. It is valid to call
  // OnFaviconsChanged() with non-empty `page_urls` and an empty `icon_url` and
  // vice versa.
  void OnFaviconsChanged(const std::set<GURL>& page_urls, const GURL& icon_url);

  // Returns the client used by this BookmarkModel.
  BookmarkClient* client() const { return client_.get(); }

  // Creates folders for account storage. Must be invoked by sync code only.
  // Must only be invoked after BookmarkModel is loaded.
  void CreateAccountPermanentFolders();

  // Removes folders for account storage. Calling this method will destroy ALL
  // bookmarks in account storage, including permanent folders. Must be invoked
  // by sync code only. Must only be invoked after BookmarkModel is loaded.
  void RemoveAccountPermanentFolders();

  // Returns the total number of bookmark nodes (URLs and folders) in the model.
  // This is equivalent to iterating the entire tree starting with root_node(),
  // root node included.
  // On Android this does *not* include partner bookmarks.
  size_t GetTotalNumberOfUrlsAndFoldersIncludingManagedNodes() const;

  base::WeakPtr<BookmarkModel> AsWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

  // Similar to Load() but allows unit-tests to mimic an empty JSON file being
  // loaded from disk, without dealing with actual files, and complete loading
  // synchronously.
  void LoadEmptyForTest();

  // If a write to disk is scheduled, performs it immediately. This is useful
  // for tests that restart the browser without necessarily shutting it down
  // cleanly first.
  void CommitPendingWriteForTest();

  // Returns whether pending writes are pending/scheduled.
  bool LocalOrSyncableStorageHasPendingWriteForTest() const;
  bool AccountStorageHasPendingWriteForTest() const;

 private:
  friend class BookmarkCodecTest;
  friend class BookmarkModelFaviconTest;
  friend class BookmarkStorage;
  friend class ScopedGroupBookmarkActions;

  // BookmarkUndoProvider:
  void RestoreRemovedNode(const BookmarkNode* parent,
                          size_t index,
                          std::unique_ptr<BookmarkNode> node) override;

  // Given a node that is already part of the model, it determines the
  // corresponding type for the purpose of understanding uniqueness properties
  // of its UUID. That is, which subset of nodes this UUID is guaranteed to be
  // unique among.
  NodeTypeForUuidLookup DetermineTypeForUuidLookupForExistingNode(
      const BookmarkNode* node) const;

  // Notifies the observers for adding every descendant of `node`.
  void NotifyNodeAddedForAllDescendants(const BookmarkNode* node,
                                        bool added_by_user);

  // Called when done loading. Updates internal state and notifies observers.
  void DoneLoading(std::unique_ptr<BookmarkLoadDetails> details);

  // Adds the `node` at `parent` in the specified `index` and notifies its
  // observers. `added_by_user` is true when a new bookmark was added by the
  // user and false when a node is added by sync or duplicated.
  BookmarkNode* AddNode(BookmarkNode* parent,
                        size_t index,
                        std::unique_ptr<BookmarkNode> node,
                        bool added_by_user,
                        NodeTypeForUuidLookup type_for_uuid_lookup);

  // Adds `node` to all lookups indices and recursively invokes this for all
  // children.
  void AddNodeToIndicesRecursive(const BookmarkNode* node,
                                 NodeTypeForUuidLookup type_for_uuid_lookup);

  // Removes a child under `parent` at position `index` and notifies its
  // observers. `is_undoable` determines whether the deletion should be
  // propagated via BookmarkClient to the undo stack. `notify_observers`
  // determines whether observers are notified about the removal.
  void RemoveChildAt(const BookmarkNode* parent,
                     size_t index,
                     const base::Location& location,
                     std::optional<metrics::BookmarkEditSource> source,
                     bool is_undoable,
                     bool notify_observers);

  // Private counterpart of `RemoveAccountPermanentFolders()` that allows
  // controlling whether observers are notified.
  void RemoveAccountPermanentFoldersImpl(bool notify_observers);

  // Removes the node from internal maps and recurses through all children. If
  // the node is a url, its url is added to removed_urls.
  //
  // This does NOT delete the node.
  void RemoveNodeFromIndicesRecursive(
      BookmarkNode* node,
      NodeTypeForUuidLookup type_for_uuid_lookup);

  // Updates the UUID index to ensure that `node`, whose former type was
  // `old_type_for_uuid_lookup`, is instead indexed under type
  // `new_type_for_uuid_lookup`. This is exercised when a node is moved across
  // type boundaries, which requires updating the UUID index.
  void UpdateUuidIndexUponNodeMoveRecursive(
      const BookmarkNode* node,
      NodeTypeForUuidLookup old_type_for_uuid_lookup,
      NodeTypeForUuidLookup new_type_for_uuid_lookup);

  // Returns true if the parent and index are valid.
  bool IsValidIndex(const BookmarkNode* parent, size_t index, bool allow_end);

  // Notification that a favicon has finished loading. If we can decode the
  // favicon, FaviconLoaded is invoked.
  void OnFaviconDataAvailable(
      BookmarkNode* node,
      const favicon_base::FaviconImageResult& image_result);

  // Invoked from the node to load the favicon. Requests the favicon from the
  // favicon service.
  void LoadFavicon(BookmarkNode* node);

  // Called to notify the observers that the favicon has been loaded.
  void FaviconLoaded(const BookmarkNode* node);

  // If we're waiting on a favicon for node, the load request is canceled.
  void CancelPendingFaviconLoadRequests(BookmarkNode* node);

  // Notifies the observers that a set of changes initiated by a single user
  // action is about to happen and has completed.
  void BeginGroupedChanges();
  void EndGroupedChanges();

  // Generates and returns the next node ID.
  int64_t generate_next_node_id();

  // Sets the maximum node ID to the given value.
  // This is used by BookmarkCodec to report the maximum ID after it's done
  // decoding since during decoding codec assigns node IDs.
  void set_next_node_id(int64_t id) { next_node_id_ = id; }

  // Implementation of `UpdateLastUsedTime` which gives the option to skip
  // saving the change to `BookmarkStorage. Used to efficiently make changes
  // to multiple bookmarks.
  void UpdateLastUsedTimeImpl(const BookmarkNode* node, base::Time time);

  void ClearLastUsedTimeInRangeRecursive(BookmarkNode* node,
                                         const base::Time delete_begin,
                                         const base::Time delete_end);

  // Schedules saving the bookmark model to disk as a result of `node` having
  // changed. When multiple BookmarkStorage instances are involved, `node`
  // allows determining which of the two needs to be persisted.
  void ScheduleSaveForNode(const BookmarkNode* node);

  // Returns which BookmakStorage instance is used to persist `node` to disk.
  // The returned value will be either `local_or_syncable_store_` or
  // `account_store_`. It may return null in tests.
  BookmarkStorage* GetStorageForNode(const BookmarkNode* node);

  // Returns an enum representing how metrics associated to `node` should be
  // suffixed with for the purpose of metric breakdowns.
  metrics::StorageStateForUma GetStorageStateForUma(
      const BookmarkNode* node) const;

  // Returns true if the given `node` should be visible in UI surfaces.
  // This method is used during transient states (eg. just before or just after
  // making model updates), therefore explicit parameters are passed in for
  // properties of the node/model that shouldn't be read directly.
  bool DetermineIfNodeShouldBeVisible(const BookmarkNode& node,
                                      bool account_folders_exist,
                                      bool local_bookmarks_exist) const;

  // Updates the visibility of all local permanent folders.
  void RefreshPermanentFolderVisibility(bool notify_observers);

  // Updates the visibility of `node` and notifies observers if the visibility
  // changed and `notify_observers` is true.
  void UpdateNodeVisibilityIfNeeded(BookmarkNode& node,
                                    bool new_visibility,
                                    bool notify_observers);

  // Whether the initial set of data has been loaded.
  bool loaded_ = false;

  // See `root_` for details.
  std::unique_ptr<BookmarkNode> owned_root_;

  // The root node. This contains the bookmark bar node, the 'other' node and
  // the mobile node as children. The value of `root_` is initially that of
  // `owned_root_`. Once loading has completed, `owned_root_` is destroyed and
  // this is set to url_index_->root(). `owned_root_` is done as lots of
  // existing code assumes the root is non-null while loading.
  raw_ptr<BookmarkNode, AcrossTasksDanglingUntriaged> root_ = nullptr;

  raw_ptr<BookmarkPermanentNode, AcrossTasksDanglingUntriaged>
      bookmark_bar_node_ = nullptr;
  raw_ptr<BookmarkPermanentNode, AcrossTasksDanglingUntriaged> other_node_ =
      nullptr;
  raw_ptr<BookmarkPermanentNode, AcrossTasksDanglingUntriaged> mobile_node_ =
      nullptr;

  // Permanent nodes for account storage.
  raw_ptr<BookmarkPermanentNode> account_bookmark_bar_node_ = nullptr;
  raw_ptr<BookmarkPermanentNode> account_other_node_ = nullptr;
  raw_ptr<BookmarkPermanentNode> account_mobile_node_ = nullptr;

  // The maximum ID assigned to the bookmark nodes in the model.
  int64_t next_node_id_ = 1;

  // The observers.
  base::ObserverList<BookmarkModelObserver, true> observers_;

  std::unique_ptr<BookmarkClient> client_;

  // Used for loading favicons.
  base::CancelableTaskTracker cancelable_task_tracker_;

  // Write bookmarks to disk.
  std::unique_ptr<BookmarkStorage> local_or_syncable_store_;
  std::unique_ptr<BookmarkStorage> account_store_;

  std::unique_ptr<TitledUrlIndex> titled_url_index_;

  // All nodes indexed by UUID. An independent index exists for each value in
  // NodeTypeForUuidLookup, because UUID uniqueness is guaranteed only within
  // the scope of each NodeTypeForUuidLookup value.
  base::flat_map<NodeTypeForUuidLookup, UuidIndex> uuid_index_;

  scoped_refptr<UrlIndex> url_index_;

  // See description of IsDoingExtensiveChanges above.
  int extensive_changes_ = 0;

  scoped_refptr<ModelLoader> model_loader_;

  SEQUENCE_CHECKER(sequence_checker_);

  base::WeakPtrFactory<BookmarkModel> weak_factory_{this};
};

}  // namespace bookmarks

#endif  // COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MODEL_H_