blob: 01f73b6474f016a773a597585eb2c67243324419 [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_INTERFACE_H_
#define COMPONENTS_TABS_PUBLIC_TAB_INTERFACE_H_
#include <memory>
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/tabs/public/tab_handle_factory.h"
namespace ui {
class UnownedUserDataHost;
}
namespace base {
class CallbackListSubscription;
} // namespace base
namespace content {
class WebContents;
} // namespace content
#if !BUILDFLAG(IS_ANDROID)
class BrowserWindowInterface;
#endif // !BUILDFLAG(IS_ANDROID)
namespace split_tabs {
class SplitTabId;
} // namespace split_tabs
namespace tabs {
class TabCollection;
class TabFeatures;
// A feature which wants to show tab-modal UI should call
// TabInterface::ShowModalUI and keep alive the instance of ScopedTabModalUI for
// the duration of the tab-modal UI.
class ScopedTabModalUI {
public:
ScopedTabModalUI() = default;
virtual ~ScopedTabModalUI() = default;
};
// TODO(crbug.com/404889112): This interface will be reused for Android as part
// of the effort to share tab collections between desktop and Android. Some
// features of TabInterface are unsupported on Android. A buildflag is used to
// turn off this functionality.
// This is the public interface for tabs in a desktop browser. Most features in
// //chrome/browser depend on this interface, and thus to prevent circular
// dependencies this interface should not depend on anything else in //chrome.
// Ping erikchen for assistance if this class does not have the functionality
// your feature needs. This comment will be deleted after there are 10+ features
// in TabFeatures.
class TabInterface : public SupportsTabHandles {
public:
// This method exists to ease the transition from WebContents to TabInterface.
// This method should only be called on instances of WebContents that are
// known to be tabs. Calling this on a non-tab will crash.
static TabInterface* GetFromContents(content::WebContents* web_contents);
static const TabInterface* GetFromContents(
const content::WebContents* web_contents);
// Code that references a WebContents should already know whether the
// WebContents is a tab, and thus should use GetFromContents(). For historical
// reasons, there is code in the repository that typically lives in or below
// //content which does not know whether it's being invoked in the context of
// a tab. This is an anti-pattern that should be avoided. When it is
// unavoidable, this method may be used. Features that live entirely above the
// //content layer should not use this method.
static TabInterface* MaybeGetFromContents(content::WebContents* web_contents);
// Returns a weak pointer to `this`.
//
// WARNING: Many uses of base::WeakPtr are inappropriate and lead to bugs.
// An appropriate use case is as a variable passed to an asynchronously
// invoked PostTask.
// An inappropriate use case is to store as a member of an object that can
// outlive TabInterface. This leads to inconsistent state machines.
// For example (don't do this):
// class FooOutlivesTab{
// base::WeakPtr<TabInterface> tab_;
// // Conceptually, this member should only be set if tab_ is set.
// std::optional<SkColor> color_of_tab_;
// };
// For example (do this):
// class FooOutlivesTab {
// // Use RegisterWillDetach() to clear both tab_ and color_of_tab_ prior
// // to tab_ destruction.
// raw_ptr<TabInterface> tab_;
// std::optional<SkColor> color_of_tab_;
// };
virtual base::WeakPtr<TabInterface> GetWeakPtr() = 0;
// Returns the WebContents that is currently associated with this tab.
//
// The returned pointer is guaranteed to be non-null.
//
// However, the WebContents object *itself* can be replaced, most notably
// when a background tab's contents are discarded to save memory.
// Callers who need to observe the tab for its entire lifetime should not
// cache the WebContents pointer directly. Instead, they should hold a
// reference to the TabInterface and call GetContents() when needed, or use
// RegisterWillDiscardContents() to be notified of swaps.
virtual content::WebContents* GetContents() const = 0;
// Closes the tab.
virtual void Close() = 0;
// Register for this callback to detect changes to GetContents(). The first
// WebContents is the contents that will be discarded. The second WebContents
// is the new contents. The tab is guaranteed to be in the background.
using WillDiscardContentsCallback = base::RepeatingCallback<
void(TabInterface*, content::WebContents*, content::WebContents*)>;
virtual base::CallbackListSubscription RegisterWillDiscardContents(
WillDiscardContentsCallback callback) = 0;
// Whether the tab is the Active Tab in its browser window (see
// TabStripModel's SelectionModel's active index for more details about the
// active state). When a tab is in the foreground, this class guarantees that
// GetContents() will return a non-nullptr WebContents, and this WebContents
// will not change. If a tab is dragged out of a window (creating a new
// window), it will briefly be deactivated, and then reactivate. To see if
// this is happened, check the BrowserWindowInterface's session id.
virtual bool IsActivated() const = 0;
// Register for these two callbacks to detect changes to IsActivated().
using DidActivateCallback = base::RepeatingCallback<void(TabInterface*)>;
virtual base::CallbackListSubscription RegisterDidActivate(
DidActivateCallback callback) = 0;
using WillDeactivateCallback = base::RepeatingCallback<void(TabInterface*)>;
virtual base::CallbackListSubscription RegisterWillDeactivate(
WillDeactivateCallback callback) = 0;
// ONLY USE THIS IF YOUR FEATURE NEEDS MORE SPECIFICITY THAN IsActivated().
// Whether the tab is visible in the contents area of the browser window.
// This will directly match the attached state until there are features that
// provide multiple visible tabs per window. This state is not related to
// widget visibility or occlusion of the window.
virtual bool IsVisible() const = 0;
// Returns true if the tab is selected in its browser window. Note that
// "selected" is distinct from "activated" -- multiple tabs may be selected at
// a time, and a selected tab is not necessarily active.
virtual bool IsSelected() const = 0;
// Register for these two callbacks to detect changes to IsVisible().
using DidBecomeVisibleCallback = base::RepeatingCallback<void(TabInterface*)>;
virtual base::CallbackListSubscription RegisterDidBecomeVisible(
DidBecomeVisibleCallback callback) = 0;
using WillBecomeHiddenCallback = base::RepeatingCallback<void(TabInterface*)>;
virtual base::CallbackListSubscription RegisterWillBecomeHidden(
WillBecomeHiddenCallback callback) = 0;
// Register for this callback to detect when a tab will be detached from a
// window.
enum class DetachReason {
// The tab is about to be deleted.
kDelete,
// The tab is going to be removed, in order to be inserted into another
// window.
kInsertIntoOtherWindow
};
using WillDetach = base::RepeatingCallback<void(TabInterface*, DetachReason)>;
virtual base::CallbackListSubscription RegisterWillDetach(
WillDetach callback) = 0;
// Register for this callback to detect when a tab has been inserted into a
// window's tab strip. Registered callbacks will fire for all tab strip
// insertion events, including when the tab is first created and added to the
// tab strip if a callback has been registered early enough in the tab's
// lifecycle.
using DidInsertCallback = base::RepeatingCallback<void(TabInterface*)>;
virtual base::CallbackListSubscription RegisterDidInsert(
DidInsertCallback callback) = 0;
// Register for this callback to detect when the pinned state changes.
using PinnedStateChangedCallback =
base::RepeatingCallback<void(TabInterface*, bool new_pinned_state)>;
virtual base::CallbackListSubscription RegisterPinnedStateChanged(
PinnedStateChangedCallback callback) = 0;
// Register for this callback to detect when the group changes.
using GroupChangedCallback = base::RepeatingCallback<
void(TabInterface*, std::optional<tab_groups::TabGroupId> new_group)>;
virtual base::CallbackListSubscription RegisterGroupChanged(
GroupChangedCallback callback) = 0;
// Features that want to show tab-modal UI are mutually exclusive. Before
// showing a modal UI first check `CanShowModal`. Then call ShowModal() and
// keep `ScopedTabModal` alive to prevent other features from showing
// tab-modal UI.
virtual bool CanShowModalUI() const = 0;
virtual std::unique_ptr<ScopedTabModalUI> ShowModalUI() = 0;
// Register for this callback to detect when a modal is shown or hidden.
using TabInterfaceCallback = base::RepeatingCallback<void(TabInterface*)>;
virtual base::CallbackListSubscription RegisterModalUIChanged(
TabInterfaceCallback callback) = 0;
// A normal browser window has a tab strip and an omnibox. The returned value
// never changes.
virtual bool IsInNormalWindow() const = 0;
#if !BUILDFLAG(IS_ANDROID)
// Always valid in production code. Exceptions are:
// (1) Tabs briefly do not have a BrowserWindowInterface when they are
// detached from one window and moved to another. That is an implementation
// detail of tab dragging and should not affect code outside of the tab-strip
// implementation.
// (2) Some tab-related unit tests create a TabInterface but do not create
// TabFeatures, BrowserWindowInterface or BrowserWindowFeatures. Most code
// that accesses this method should typically be scoped to TabFeatures or
// BrowserWindowFeatures, but some code (e.g. tab_helpers) are currently
// created in these unit tests. The proper solution is to convert these
// tab_helpers to TabFeatures, which is tracked in
// https://crbug.com/369319589.
// This is a long winded way of saying: if you are using this code from
// TabFeatures or BrowserWindowFeatures, you can safely assume that this is
// always non-nullptr.
virtual BrowserWindowInterface* GetBrowserWindowInterface() = 0;
virtual const BrowserWindowInterface* GetBrowserWindowInterface() const = 0;
#endif // !BUILDFLAG(IS_ANDROID)
// Returns the feature controllers scoped to this tab.
// TabFeatures that depend on other TabFeatures should not use this method.
// Instead they should use use dependency injection to pass dependencies at
// construction or initialization. This method exists for three reasons:
// (1) BrowserWindowFeatures often depend on state of TabFeatures for the
// active tab, which can change. BrowserWindowFeatures need a way to
// dynamically fetch TabFeatures.
// (2) To expose TabFeatures for tests.
// (3) It is not possible to perform dependency injection for legacy code
// that is conceptually a TabFeature and needs access to other TabFeatures.
virtual tabs::TabFeatures* GetTabFeatures() = 0;
virtual const tabs::TabFeatures* GetTabFeatures() const = 0;
// Return true if the tab is pinned in its tabstrip, or false otherwise.
virtual bool IsPinned() const = 0;
// Return true if the tab is part of a split view, or false otherwise.
virtual bool IsSplit() const = 0;
// Returns the id of the tab group this tab belongs to, or nullopt if the tab
// is not grouped.
virtual std::optional<tab_groups::TabGroupId> GetGroup() const = 0;
// Returns the id of the split tab this tab belongs to, or nullopt if the tab
// is not part of a split tab.
virtual std::optional<split_tabs::SplitTabId> GetSplit() const = 0;
// Returns a pointer to the parent TabCollection.
virtual TabCollection* GetParentCollection(
base::PassKey<TabCollection>) const = 0;
virtual const TabCollection* GetParentCollection() const = 0;
// Updates the parent collection of the TabModel in response to structural
// changes such as pinning, grouping, or moving the tab between collections.
// This method ensures the TabModel remains correctly associated within the
// tab hierarchy, maintaining consistent organization.
virtual void OnReparented(TabCollection* parent,
base::PassKey<TabCollection>) = 0;
// Must be called whenever any of this tab's ancestor collections change.
virtual void OnAncestorChanged(base::PassKey<TabCollection>) = 0;
// Returns the UnownedUserDataHost associated with this tab. This is used to
// retrieve arbitrary features from the tab without requiring TabModel to have
// knowledge of them.
virtual ui::UnownedUserDataHost& GetUnownedUserDataHost() = 0;
virtual const ui::UnownedUserDataHost& GetUnownedUserDataHost() const = 0;
};
using TabHandle = TabInterface::Handle;
} // namespace tabs
#endif // COMPONENTS_TABS_PUBLIC_TAB_INTERFACE_H_