blob: 959a5f260096678ef1146477638eb169659b9f1d [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.
#include "chrome/browser/ui/tabs/tab_model.h"
#include "chrome/browser/ui/tabs/tab_features.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/ui_features.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
namespace tabs {
TabModel::TabModel(std::unique_ptr<content::WebContents> contents,
TabStripModel* owning_model)
: contents_owned_(std::move(contents)),
contents_(contents_owned_.get()),
owning_model_(owning_model) {
// When a TabModel is constructed it must be attached to a TabStripModel. This
// may later change if the Tab is detached.
CHECK(owning_model);
owning_model_->AddObserver(this);
tab_features_ = TabFeatures::CreateTabFeatures();
tab_features_->Init(this);
}
TabModel::~TabModel() = default;
void TabModel::OnAddedToModel(TabStripModel* owning_model) {
CHECK(!owning_model_);
CHECK(owning_model);
owning_model_ = owning_model;
owning_model_->AddObserver(this);
// Being detached is equivalent to being in the background. So after
// detachment, if the tab is in the foreground, we must send a notification.
if (IsInForeground()) {
did_enter_foreground_callback_list_.Notify(this);
}
}
void TabModel::OnRemovedFromModel() {
// Going through each field here:
// Keep `contents_`, obviously.
// We are now unowned. In this case no UI is shown, which is functionally
// equivalent to being in the background.
did_enter_background_callback_list_.Notify(this);
owning_model_->RemoveObserver(this);
owning_model_ = nullptr;
// Opener stuff doesn't make sense to transfer between browsers.
opener_ = nullptr;
reset_opener_on_active_tab_change_ = false;
// Pinned state, blocked state, and group membership are all preserved, at
// least in some cases, but for now let's leave that to the existing
// mechanisms that were handling that.
// TODO(tbergquist): Decide whether to stick with this approach or not.
pinned_ = false;
blocked_ = false;
group_ = std::nullopt;
}
TabCollection* TabModel::GetParentCollection(
base::PassKey<TabCollection>) const {
CHECK(base::FeatureList::IsEnabled(features::kTabStripCollectionStorage));
return parent_collection_;
}
void TabModel::OnReparented(TabCollection* parent,
base::PassKey<TabCollection>) {
CHECK(base::FeatureList::IsEnabled(features::kTabStripCollectionStorage));
parent_collection_ = parent;
}
content::WebContents* TabModel::GetContents() const {
return contents();
}
base::CallbackListSubscription TabModel::RegisterDidAddContents(
TabInterface::DidAddContentsCallback callback) {
return did_add_contents_callback_list_.Add(std::move(callback));
}
base::CallbackListSubscription TabModel::RegisterWillRemoveContents(
TabInterface::WillRemoveContentsCallback callback) {
return will_remove_contents_callback_list_.Add(std::move(callback));
}
bool TabModel::IsInForeground() const {
return owning_model()->GetActiveTab() == this;
}
base::CallbackListSubscription TabModel::RegisterDidEnterForeground(
TabInterface::DidEnterForegroundCallback callback) {
return did_enter_foreground_callback_list_.Add(std::move(callback));
}
base::CallbackListSubscription TabModel::RegisterDidEnterBackground(
TabInterface::DidEnterBackgroundCallback callback) {
return did_enter_background_callback_list_.Add(std::move(callback));
}
bool TabModel::CanShowModalUI() const {
return !showing_modal_ui_;
}
std::unique_ptr<ScopedTabModalUI> TabModel::ShowModalUI() {
return std::make_unique<ScopedTabModalUIImpl>(this);
}
void TabModel::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
if (!selection.active_tab_changed()) {
return;
}
if (selection.new_contents == contents()) {
did_enter_foreground_callback_list_.Notify(this);
return;
}
if (selection.old_contents == contents()) {
did_enter_background_callback_list_.Notify(this);
}
}
TabModel::ScopedTabModalUIImpl::ScopedTabModalUIImpl(TabModel* tab)
: tab_(tab) {
CHECK(!tab_->showing_modal_ui_);
tab_->showing_modal_ui_ = true;
}
TabModel::ScopedTabModalUIImpl::~ScopedTabModalUIImpl() {
tab_->showing_modal_ui_ = false;
}
void TabModel::WriteIntoTrace(perfetto::TracedValue context) const {
auto dict = std::move(context).WriteDictionary();
dict.Add("web_contents", contents());
dict.Add("pinned", pinned());
dict.Add("blocked", blocked());
}
std::unique_ptr<content::WebContents> TabModel::ReplaceContents(
std::unique_ptr<content::WebContents> contents) {
std::unique_ptr<content::WebContents> old_contents = RemoveContents();
SetContents(std::move(contents));
return old_contents;
}
std::unique_ptr<content::WebContents> TabModel::RemoveContents() {
for (auto& obs : observers_) {
obs.WillRemoveContents(this, contents_.get());
}
will_remove_contents_callback_list_.Notify(this, contents_.get());
contents_ = nullptr;
return std::move(contents_owned_);
}
// static
std::unique_ptr<content::WebContents> TabModel::DestroyAndTakeWebContents(
std::unique_ptr<TabModel> tab_model) {
std::unique_ptr<content::WebContents> contents =
std::move(tab_model->contents_owned_);
return contents;
}
void TabModel::SetContents(std::unique_ptr<content::WebContents> contents) {
CHECK(!contents_);
CHECK(contents);
contents_owned_ = std::move(contents);
contents_ = contents_owned_.get();
for (auto& obs : observers_) {
obs.DidAddContents(this, contents_.get());
}
did_add_contents_callback_list_.Notify(this, contents_.get());
}
} // namespace tabs