blob: 1e06aeda45e56f633f1142ee1baa5a31460a7822 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_TABS_TAB_MODEL_H_
#define CHROME_BROWSER_UI_TABS_TAB_MODEL_H_
#include <memory>
#include <optional>
#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/tabs/public/tab_interface.h"
#include "chrome/browser/ui/tabs/supports_handles.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "components/tab_groups/tab_group_id.h"
#include "content/public/browser/web_contents.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
class TabStripModel;
namespace tabs {
class TabCollection;
class TabFeatures;
class TabModel final : public SupportsHandles<TabModel>,
public TabInterface,
public TabStripModelObserver {
public:
// Conceptually, tabs should always be a part of a normal window. There are
// currently 2 cases where they are not:
// (1) Tabbed PWAs is a ChromeOS_only feature that exposes Tabs to PWAs.
// (2) Non-browser windows currently have a tab-strip and may use tabs. See
// TODO(https://crbug.com/331031753) which tracks their eventual removal.
// TODO(https://crbug.com/346692548): Tabs should never be constructed in
// isolation of a model.
TabModel(std::unique_ptr<content::WebContents> contents,
TabStripModel* soon_to_be_owning_model);
~TabModel() override;
TabModel(const TabModel&) = delete;
TabModel& operator=(const TabModel&) = delete;
void OnAddedToModel(TabStripModel* owning_model);
void OnRemovedFromModel();
content::WebContents* contents() const { return contents_.get(); }
TabStripModel* owning_model() const { return owning_model_.get(); }
tabs::TabModel* opener() const { return opener_; }
bool reset_opener_on_active_tab_change() const {
return reset_opener_on_active_tab_change_;
}
bool pinned() const { return pinned_; }
bool blocked() const { return blocked_; }
std::optional<tab_groups::TabGroupId> group() const { return group_; }
void set_opener(tabs::TabModel* opener) { opener_ = opener; }
void set_reset_opener_on_active_tab_change(
bool reset_opener_on_active_tab_change) {
reset_opener_on_active_tab_change_ = reset_opener_on_active_tab_change;
}
void set_pinned(bool pinned) { pinned_ = pinned; }
void set_blocked(bool blocked) { blocked_ = blocked; }
void set_group(std::optional<tab_groups::TabGroupId> group) {
group_ = group;
}
void WriteIntoTrace(perfetto::TracedValue context) const;
// https://crbug.com/331022416: Do not use this method. This is only used by
// tab discard, which is being refactored to not need this.
std::unique_ptr<content::WebContents> DiscardContents(
std::unique_ptr<content::WebContents> contents);
// This destroys the TabModel and takes ownership of the underlying
// WebContents.
static std::unique_ptr<content::WebContents> DestroyAndTakeWebContents(
std::unique_ptr<TabModel> tab_model);
// When a tab is going to be removed from the tabstrip in preparation for
// destruction, `TabFeatures` should be destroyed first to ensure individual
// features do not need to handle the situation of existing outside the
// context of a tab strip.
void DestroyTabFeatures();
TabFeatures* tab_features() { return tab_features_.get(); }
// Returns a pointer to the parent TabCollection. This method is specifically
// designed to be accessible only within the collection tree that has the
// kTabStripCollectionStorage flag enabled.
TabCollection* GetParentCollection(base::PassKey<TabCollection>) const;
// Provides access to the parent_collection_ for testing purposes. This method
// bypasses the PassKey mechanism, allowing tests to simulate scenarios and
// inspect the state without needing to replicate complex authorization
// mechanisms.
TabCollection* GetParentCollectionForTesting() { return parent_collection_; }
// 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.
void OnReparented(TabCollection* parent, base::PassKey<TabCollection>);
// Called by TabStripModel when a tab is going to be backgrounded (any
// operation that makes the tab no longer visible, including removal from the
// TabStripModel). Not called if TabStripModel is being destroyed.
void WillEnterBackground(base::PassKey<TabStripModel>);
// Called by TabStripModel when a tab is going to be detached for reinsertion
// into a different tab strip.
void WillDetach(base::PassKey<TabStripModel>,
tabs::TabInterface::DetachReason reason);
// TabInterface overrides:
content::WebContents* GetContents() const override;
base::CallbackListSubscription RegisterWillDiscardContents(
TabInterface::WillDiscardContentsCallback callback) override;
bool IsInForeground() const override;
base::CallbackListSubscription RegisterDidEnterForeground(
TabInterface::DidEnterForegroundCallback callback) override;
base::CallbackListSubscription RegisterWillEnterBackground(
TabInterface::WillEnterBackgroundCallback callback) override;
base::CallbackListSubscription RegisterWillDetach(
TabInterface::WillDetach callback) override;
bool CanShowModalUI() const override;
std::unique_ptr<ScopedTabModalUI> ShowModalUI() override;
bool IsInNormalWindow() const override;
BrowserWindowInterface* GetBrowserWindowInterface() override;
tabs::TabFeatures* GetTabFeatures() override;
uint32_t GetTabHandle() override;
void Close() override;
private:
// Overridden from TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
// TODO(https://crbug.com/346692548): This will not be necessary once
// soon_to_be_owning_model_ is removed. TabInterface logic can only be invoked
// in contexts where a model exists.
TabStripModel* GetModelForTabInterface() const;
// Tracks whether a tab-modal UI is showing.
class ScopedTabModalUIImpl : public ScopedTabModalUI {
public:
explicit ScopedTabModalUIImpl(TabModel* tab);
~ScopedTabModalUIImpl() override;
private:
// Owns this.
raw_ptr<TabModel> tab_;
};
// This must always be the first member so that it is destroyed last. This is
// because there are some instances where a caller may want to destroy a
// TabModel but keep the WebContents alive. There are other destructors such
// as TabFeatures that may require a valid `contents_` during destruction.
std::unique_ptr<content::WebContents> contents_owned_;
raw_ptr<content::WebContents> contents_;
// A back reference to the TabStripModel that contains this TabModel. The
// owning model can be nullptr if the tab has been detached from it's previous
// owning tabstrip model, and has yet to be transferred to a new tabstrip
// model or is in the process of being closed.
raw_ptr<TabStripModel> owning_model_ = nullptr;
raw_ptr<TabStripModel> soon_to_be_owning_model_ = nullptr;
raw_ptr<tabs::TabModel> opener_ = nullptr;
bool reset_opener_on_active_tab_change_ = false;
bool pinned_ = false;
bool blocked_ = false;
std::optional<tab_groups::TabGroupId> group_ = std::nullopt;
raw_ptr<TabCollection> parent_collection_ = nullptr;
using WillDiscardContentsCallbackList = base::RepeatingCallbackList<
void(TabInterface*, content::WebContents*, content::WebContents*)>;
WillDiscardContentsCallbackList will_discard_contents_callback_list_;
using DidEnterForegroundCallbackList =
base::RepeatingCallbackList<void(TabInterface*)>;
DidEnterForegroundCallbackList did_enter_foreground_callback_list_;
using WillEnterBackgroundCallbackList =
base::RepeatingCallbackList<void(TabInterface*)>;
WillEnterBackgroundCallbackList will_enter_background_callback_list_;
using WillDetachCallbackList =
base::RepeatingCallbackList<void(TabInterface*,
tabs::TabInterface::DetachReason)>;
WillDetachCallbackList will_detach_callback_list_;
// Tracks whether a modal UI is showing.
bool showing_modal_ui_ = false;
// Features that are per-tab will be owned by this class.
std::unique_ptr<TabFeatures> tab_features_;
};
using TabHandle = TabModel::Handle;
} // namespace tabs
#endif // CHROME_BROWSER_UI_TABS_TAB_MODEL_H_