blob: 0f7cfe1edf980333795ee0cc68a222641a92239e [file] [log] [blame]
// Copyright 2024 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_TABS_PUBLIC_TAB_COLLECTION_H_
#define COMPONENTS_TABS_PUBLIC_TAB_COLLECTION_H_
#include <cstddef>
#include <list>
#include <memory>
#include <optional>
#include <unordered_set>
#include <variant>
#include <vector>
#include "base/check.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/stack_allocated.h"
#include "base/types/pass_key.h"
#include "components/tabs/public/supports_handles.h"
#include "components/tabs/public/tab_collection_storage.h"
namespace tabs_api {
class MojoTreeBuilder;
} // namespace tabs_api
namespace tabs {
class TabInterface;
DECLARE_HANDLE_FACTORY(TabCollection);
// This is an interface that representing the hierarchical storage of tabs.
// This can be used to access and manipulate tabs and the state of the tabstrip.
// Different types of collections should implement this base class based on how
// their feature works. For example, a pinned collection can implement tab
// collection that does not store any collection.
class TabCollection : public SupportsHandles<TabCollectionHandleFactory> {
public:
// TabIterator provides a way to traverse all tab objects within this
// TabCollection and its sub-collections in a depth first inorder traversal
// manner. This should not be mutated while holding reference to an iterator
// as otherwise it will break as it is holding access to the index and tab
// pointer.
class TabIterator {
STACK_ALLOCATED();
public:
using iterator_category = std::forward_iterator_tag;
using value_type = tabs::TabInterface*;
using difference_type = ptrdiff_t;
using pointer = value_type;
using reference = value_type;
TabIterator();
TabIterator(base::PassKey<TabCollection>,
const tabs::TabCollection* root,
bool is_end = false);
TabIterator(const TabIterator& iterator);
~TabIterator();
pointer operator->() const { return cur_; }
reference operator*() const { return cur_; }
TabIterator& operator++() {
Next();
return *this;
}
TabIterator operator++(int) {
TabIterator it(*this);
Next();
return it;
}
bool operator==(const TabIterator& other) const {
return cur_ == other.cur_;
}
private:
TabIterator(const tabs::TabCollection* root, bool is_end);
void Next();
// Contains information of the index within a collection to access during
// the tree traversal. Multiple frames can be stored in the stack which
// corresponds to children for multiple subcollection.
struct Frame {
raw_ptr<const TabCollection> collection;
size_t index;
};
// Points to the currently accessed tab during iteration.
raw_ptr<tabs::TabInterface> cur_;
// Points to the root tab collection that the iterator is traversing.
raw_ptr<const tabs::TabCollection> root_;
// A stack used to maintain the traversal state for inorder traversal
// of tabs within the TabCollection hierarchy. Each Frame on the stack
// is a TabCollection in the current path of traversal, along
// with the index of the next child to be visited. This is implemented as a
// vector to take advantage of reserving memory for performance reasons.
std::vector<Frame> stack_;
};
using iterator = TabIterator;
using const_iterator = TabIterator;
const_iterator begin() const {
return TabIterator(GetPassKey(), this, false);
}
const_iterator end() const { return TabIterator(GetPassKey(), this, true); }
// Type describes the various kinds of tab collections:
// - TABSTRIP: The main container for tabs in a browser window.
// - PINNED: A container for pinned tabs.
// - UNPINNED: A container for unpinned tabs.
// - GROUP: A container to grouped tabs.
// - SPLIT: A container for split tabs.
enum class Type { TABSTRIP, PINNED, UNPINNED, GROUP, SPLIT };
~TabCollection() override;
TabCollection(const TabCollection&) = delete;
TabCollection& operator=(const TabCollection&) = delete;
// Returns true is the tab collection contains the collection. This is a
// non-recursive check.
bool ContainsCollection(TabCollection* collection) const;
// Non-recursively get the index of a tab.
std::optional<size_t> GetIndexOfTab(const TabInterface* tab) const;
// Recursively get the index of the tab among all the leaf tabs.
std::optional<size_t> GetIndexOfTabRecursive(const TabInterface* tab) const;
// Recursively get the tab at the index.
TabInterface* GetTabAtIndexRecursive(size_t index) const;
// Returns a flattened list of all tabs in this collection and its
// subcollections. Prefer using the result of this function instead of looping
// over an index and using GetTabAtIndexRecursive, as this function will only
// do one pass over the subcollections.
std::vector<TabInterface*> GetTabsRecursive() const;
// Non-recursively get the index of a collection.
std::optional<size_t> GetIndexOfCollection(TabCollection* collection) const;
// Returns the direct child index of the collection containing the tab or the
// direct child index of the tab if it is a direct child of this collection.
std::optional<size_t> GetDirectChildIndexOfCollectionContainingTab(
const TabInterface* tab) const;
// Convert a recursive index to a direct index. Fails a CHECK if the tab at
// the recursive index lies within a subcollection.
size_t ToDirectIndex(size_t index);
// Total number of children that directly have this collection as their
// parent.
size_t ChildCount() const;
TabCollectionStorage* GetTabCollectionStorageForTesting() {
return impl_.get();
}
Type type() const { return type_; }
// Total number of tabs the collection contains.
size_t TabCountRecursive() const { return recursive_tab_count_; }
// Callbacks that are triggered by the impl_ when tabs/collections are added
// or removed from the tree.
void OnCollectionAddedToTree(TabCollection* collection);
void OnCollectionRemovedFromTree(TabCollection* collection);
void OnTabAddedToTree();
void OnTabRemovedFromTree();
// Manipulate direct child tabs.
TabInterface* AddTab(std::unique_ptr<TabInterface> tab, size_t index);
void MoveTab(TabInterface* tab, size_t index);
// Removes the tab if it is a direct child of this collection. This is then
// returned to the caller as an unique_ptr. If the tab is not present it will
// crash. This may overridden to return nullptr if the collection does not
// support removing tabs.
[[nodiscard]] virtual std::unique_ptr<TabInterface> MaybeRemoveTab(
TabInterface* tab);
// Manipulate direct child collections.
// Adds a collection as a direct child of this collection. If this succeeds it
// will return a pointer to the collection, otherwise it will return nullptr.
template <std::derived_from<TabCollection> T>
T* AddCollection(std::unique_ptr<T> collection, size_t index) {
CHECK(collection);
CHECK(supported_child_collections_.contains(collection->type()));
CHECK(index <= ChildCount());
TabCollection* added_collection =
impl_->AddCollection(std::move(collection), index);
added_collection->OnReparented(this);
return static_cast<T*>(added_collection);
}
// Removes the collection if it is a direct child of this collection. This is
// then returned to the caller as an unique_ptr. If the collection is not
// present it will crash. This may be overridden to return nullptr if the
// collection does not support removing collections.
[[nodiscard]] virtual std::unique_ptr<TabCollection> MaybeRemoveCollection(
TabCollection* collection);
TabCollection* GetParentCollection() const { return parent_; }
// This should be called either when this collection is added to another
// collection or it is removed from another collection. The child collection
// should not try to call this internally and set its parent.
void OnReparented(TabCollection* new_parent);
const ChildrenVector& GetChildren(
base::PassKey<tabs_api::MojoTreeBuilder> pass_key) const {
return GetChildren();
}
protected:
explicit TabCollection(Type type,
std::unordered_set<Type> supported_child_collections,
bool supports_tabs);
// Returns the pass key to be used by derived classes as operations such as
// setting the parent of a tab can only be performed by a `TabCollection`.
base::PassKey<TabCollection> GetPassKey() const {
return base::PassKey<TabCollection>();
}
const ChildrenVector& GetChildren() const { return impl_->GetChildren(); }
// Total number of tabs in the collection.
size_t recursive_tab_count_ = 0;
private:
raw_ptr<TabCollection> parent_ = nullptr;
Type type_;
std::unordered_set<Type> supported_child_collections_;
bool supports_tabs_;
// Underlying implementation for the storage of children.
std::unique_ptr<TabCollectionStorage> impl_;
};
using TabCollectionHandle = TabCollection::Handle;
} // namespace tabs
#endif // COMPONENTS_TABS_PUBLIC_TAB_COLLECTION_H_