blob: 18589036cae4275badf86b197d7f79e07acf0b03 [file] [log] [blame]
// Copyright 2019 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/tab_sharing/tab_sharing_infobar_delegate.h"
#include <string>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/page_info/page_info_dialog.h"
#include "chrome/browser/ui/tab_sharing/tab_sharing_ui.h"
#include "chrome/grit/generated_resources.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
#include "components/strings/grit/components_strings.h"
#include "components/url_formatter/elide_url.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/content_features.h"
#include "net/base/url_util.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
namespace {
using TabRole = ::TabSharingInfoBarDelegate::TabRole;
constexpr int kCscPermissionButtonIconHeight = 16;
} // namespace
class TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton {
public:
TabSharingInfoBarDelegateButton() = default;
// Deleted copy and assignment operator.
TabSharingInfoBarDelegateButton(const TabSharingInfoBarDelegateButton&) =
delete;
TabSharingInfoBarDelegateButton& operator=(
const TabSharingInfoBarDelegateButton&) = delete;
virtual ~TabSharingInfoBarDelegateButton() = default;
virtual void Click(infobars::InfoBar* infobar) = 0;
virtual std::u16string GetLabel() const = 0;
virtual ui::ImageModel GetImage() const { return {}; }
virtual bool IsEnabled() const { return true; }
virtual std::u16string GetTooltip() const { return u""; }
};
// Represents a button which, when clicked, changes the tab being shared/cast to
// be the current tab (the one associated with this infobar.)
class TabSharingInfoBarDelegate::ShareTabInsteadButton
: public TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton {
public:
ShareTabInsteadButton(TabSharingUI* ui,
TabSharingInfoBarDelegate::ButtonState button_state,
TabSharingInfoBarDelegate::TabShareType capture_type)
: ui_(ui), button_state_(button_state), capture_type_(capture_type) {}
~ShareTabInsteadButton() override = default;
void Click(infobars::InfoBar* infobar) override {
DCHECK(ui_); // Not verified in ctor to keep tests simple.
ui_->StartSharing(infobar);
}
std::u16string GetLabel() const override {
switch (capture_type_) {
case TabSharingInfoBarDelegate::TabShareType::CAST:
return l10n_util::GetStringUTF16(IDS_TAB_CASTING_INFOBAR_CAST_BUTTON);
case TabSharingInfoBarDelegate::TabShareType::CAPTURE:
return l10n_util::GetStringUTF16(IDS_TAB_SHARING_INFOBAR_SHARE_BUTTON);
}
NOTREACHED();
return std::u16string();
}
bool IsEnabled() const override {
return button_state_ == TabSharingInfoBarDelegate::ButtonState::ENABLED;
}
std::u16string GetTooltip() const override {
return button_state_ == TabSharingInfoBarDelegate::ButtonState::DISABLED
? l10n_util::GetStringUTF16(
IDS_POLICY_DLP_SCREEN_SHARE_BLOCKED_TITLE)
: u"";
}
private:
const raw_ptr<TabSharingUI, AcrossTasksDanglingUntriaged> ui_;
const TabSharingInfoBarDelegate::ButtonState button_state_;
const TabSharingInfoBarDelegate::TabShareType capture_type_;
};
// Represents a button which, when clicked, changes the activated tab to be
// the one which was hard-coded into this infobar. The intended use for this
// class is for the captured tab to activate the capturing tab, and vice versa.
class TabSharingInfoBarDelegate::SwitchToTabButton
: public TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton {
public:
SwitchToTabButton(const TabSharingInfoBarDelegate::FocusTarget& focus_target,
bool focus_target_is_capturer)
: focus_target_(focus_target),
focus_target_is_capturer_(focus_target_is_capturer) {}
~SwitchToTabButton() override = default;
void Click(infobars::InfoBar* infobar) override {
content::RenderFrameHost* const rfh =
content::RenderFrameHost::FromID(focus_target_.id);
if (!rfh) {
return;
}
page_load_metrics::MetricsWebContentsObserver::RecordFeatureUsage(
rfh, focus_target_is_capturer_
? blink::mojom::WebFeature::kTabSharingBarSwitchToCapturer
: blink::mojom::WebFeature::kTabSharingBarSwitchToCapturee);
content::WebContents* const web_contents =
content::WebContents::FromRenderFrameHost(rfh);
DCHECK(web_contents);
web_contents->GetDelegate()->ActivateContents(web_contents);
Browser* const browser = chrome::FindBrowserWithTab(web_contents);
if (browser && browser->window()) {
browser->window()->Activate();
}
}
std::u16string GetLabel() const override {
// TODO(crbug.com/1224363): Hard-code this text into the button.
content::RenderFrameHost* const rfh =
content::RenderFrameHost::FromID(focus_target_.id);
if (!rfh) {
return GetDefaultLabel();
}
return l10n_util::GetStringFUTF16(
IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON,
url_formatter::FormatOriginForSecurityDisplay(
rfh->GetLastCommittedOrigin(),
url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS));
}
ui::ImageModel GetImage() const override { return focus_target_.icon; }
private:
std::u16string GetDefaultLabel() const {
return l10n_util::GetStringUTF16(
focus_target_is_capturer_
? IDS_TAB_SHARING_INFOBAR_SWITCH_TO_CAPTURER_BUTTON
: IDS_TAB_SHARING_INFOBAR_SWITCH_TO_CAPTURED_BUTTON);
}
const TabSharingInfoBarDelegate::FocusTarget focus_target_;
const bool focus_target_is_capturer_;
};
class TabSharingInfoBarDelegate::CscPermissionButton
: public TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton {
public:
explicit CscPermissionButton(content::WebContents* web_contents)
: web_contents_(web_contents ? web_contents->GetWeakPtr() : nullptr) {}
~CscPermissionButton() override = default;
void Click(infobars::InfoBar* infobar) override {
if (!web_contents_) {
return;
}
ShowPageInfoDialog(web_contents_.get(), base::DoNothing());
}
std::u16string GetLabel() const override {
return l10n_util::GetStringUTF16(
IDS_TAB_SHARING_INFOBAR_CAPTURED_SURFACE_CONTROL_PERMISSION_BUTTON);
}
bool IsEnabled() const override { return true; }
ui::ImageModel GetImage() const override {
return ui::ImageModel::FromVectorIcon(vector_icons::kTouchpadMouseIcon,
ui::kColorSysPrimary,
kCscPermissionButtonIconHeight);
}
private:
const base::WeakPtr<content::WebContents> web_contents_;
};
namespace {
std::u16string GetMessageTextCastingNoSinkName(
bool shared_tab,
const std::u16string& shared_tab_name) {
if (shared_tab) {
return l10n_util::GetStringUTF16(
IDS_TAB_CASTING_INFOBAR_CASTING_CURRENT_TAB_NO_DEVICE_NAME_LABEL);
}
return shared_tab_name.empty()
? l10n_util::GetStringUTF16(
IDS_TAB_CASTING_INFOBAR_CASTING_ANOTHER_UNTITLED_TAB_NO_DEVICE_NAME_LABEL)
: l10n_util::GetStringFUTF16(
IDS_TAB_CASTING_INFOBAR_CASTING_ANOTHER_TAB_NO_DEVICE_NAME_LABEL,
shared_tab_name);
}
std::u16string GetMessageTextCasting(bool shared_tab,
const std::u16string& shared_tab_name,
const std::u16string& sink_name) {
if (sink_name.empty()) {
return GetMessageTextCastingNoSinkName(shared_tab, shared_tab_name);
}
if (shared_tab) {
return l10n_util::GetStringFUTF16(
IDS_TAB_CASTING_INFOBAR_CASTING_CURRENT_TAB_LABEL, sink_name);
}
return shared_tab_name.empty()
? l10n_util::GetStringFUTF16(
IDS_TAB_CASTING_INFOBAR_CASTING_ANOTHER_UNTITLED_TAB_LABEL,
sink_name)
: l10n_util::GetStringFUTF16(
IDS_TAB_CASTING_INFOBAR_CASTING_ANOTHER_TAB_LABEL,
shared_tab_name, sink_name);
}
std::u16string GetMessageTextCapturing(bool shared_tab,
const std::u16string& shared_tab_name,
const std::u16string& app_name) {
if (shared_tab) {
return l10n_util::GetStringFUTF16(
IDS_TAB_SHARING_INFOBAR_SHARING_CURRENT_TAB_LABEL, app_name);
}
return !shared_tab_name.empty()
? l10n_util::GetStringFUTF16(
IDS_TAB_SHARING_INFOBAR_SHARING_ANOTHER_TAB_LABEL,
shared_tab_name, app_name)
: l10n_util::GetStringFUTF16(
IDS_TAB_SHARING_INFOBAR_SHARING_ANOTHER_UNTITLED_TAB_LABEL,
app_name);
}
bool IsCapturedTab(TabRole role) {
switch (role) {
case TabRole::kCapturingTab:
case TabRole::kOtherTab:
return false;
case TabRole::kCapturedTab:
case TabRole::kSelfCapturingTab:
return true;
}
NOTREACHED_NORETURN();
}
} // namespace
// static
infobars::InfoBar* TabSharingInfoBarDelegate::Create(
infobars::ContentInfoBarManager* infobar_manager,
const std::u16string& shared_tab_name,
const std::u16string& capturer_name,
content::WebContents* web_contents,
TabRole role,
ButtonState share_this_tab_instead_button_state,
std::optional<FocusTarget> focus_target,
bool captured_surface_control_active,
TabSharingUI* ui,
TabShareType capture_type,
bool favicons_used_for_switch_to_tab_button) {
DCHECK(infobar_manager);
return infobar_manager->AddInfoBar(
CreateTabSharingInfoBar(base::WrapUnique(new TabSharingInfoBarDelegate(
shared_tab_name, capturer_name, web_contents, role,
share_this_tab_instead_button_state, focus_target,
captured_surface_control_active, ui, capture_type,
favicons_used_for_switch_to_tab_button))));
}
TabSharingInfoBarDelegate::TabSharingInfoBarDelegate(
std::u16string shared_tab_name,
std::u16string capturer_name,
content::WebContents* web_contents,
TabRole role,
ButtonState share_this_tab_instead_button_state,
std::optional<FocusTarget> focus_target,
bool captured_surface_control_active,
TabSharingUI* ui,
TabShareType capture_type,
bool favicons_used_for_switch_to_tab_button)
: shared_tab_name_(std::move(shared_tab_name)),
role_(role),
capturer_name_(std::move(capturer_name)),
ui_(ui),
favicons_used_for_switch_to_tab_button_(
favicons_used_for_switch_to_tab_button),
capture_type_(capture_type) {
if (share_this_tab_instead_button_state != ButtonState::NOT_SHOWN) {
share_this_tab_instead_button_ = std::make_unique<ShareTabInsteadButton>(
ui_, share_this_tab_instead_button_state, capture_type_);
}
if (focus_target.has_value()) {
quick_nav_button_ = std::make_unique<SwitchToTabButton>(
*focus_target, IsCapturedTab(role_));
}
// Note that kSelfCapturingTab is intentionally disregarded,
// because write-access CapturedSurfaceControl APIs are disallowed
// in that case anyway.
//
// TODO(crbug.com/324468211): Hide the button if Captured Surface Control
// is set to BLOCKED or ASK through the user's interaction with PageInfo.
if (role_ == TabRole::kCapturingTab && captured_surface_control_active &&
base::FeatureList::IsEnabled(
features::kCapturedSurfaceControlStickyPermissions)) {
csc_permission_button_ =
std::make_unique<CscPermissionButton>(web_contents);
}
}
TabSharingInfoBarDelegate::~TabSharingInfoBarDelegate() = default;
bool TabSharingInfoBarDelegate::EqualsDelegate(
InfoBarDelegate* delegate) const {
return false;
}
bool TabSharingInfoBarDelegate::ShouldExpire(
const NavigationDetails& details) const {
return false;
}
infobars::InfoBarDelegate::InfoBarIdentifier
TabSharingInfoBarDelegate::GetIdentifier() const {
return TAB_SHARING_INFOBAR_DELEGATE;
}
std::u16string TabSharingInfoBarDelegate::GetMessageText() const {
switch (capture_type_) {
case TabShareType::CAST:
return GetMessageTextCasting(IsCapturedTab(role_), shared_tab_name_,
capturer_name_);
case TabShareType::CAPTURE:
return GetMessageTextCapturing(IsCapturedTab(role_), shared_tab_name_,
capturer_name_);
}
NOTREACHED_NORETURN();
}
std::u16string TabSharingInfoBarDelegate::GetButtonLabel(
InfoBarButton button) const {
switch (button) {
case kNone:
break;
case kStop:
switch (capture_type_) {
case TabSharingInfoBarDelegate::TabShareType::CAST:
return l10n_util::GetStringUTF16(IDS_TAB_CASTING_INFOBAR_STOP_BUTTON);
case TabSharingInfoBarDelegate::TabShareType::CAPTURE:
return l10n_util::GetStringUTF16(IDS_TAB_SHARING_INFOBAR_STOP_BUTTON);
}
break;
case kShareThisTabInstead:
DCHECK(share_this_tab_instead_button_);
return share_this_tab_instead_button_->GetLabel();
case kQuickNav:
DCHECK(quick_nav_button_);
return quick_nav_button_->GetLabel();
case kCscPermission:
DCHECK(csc_permission_button_);
return csc_permission_button_->GetLabel();
}
NOTREACHED_NORETURN();
}
ui::ImageModel TabSharingInfoBarDelegate::GetButtonImage(
InfoBarButton button) const {
switch (button) {
case kNone:
break;
case kStop:
return ui::ImageModel();
case kShareThisTabInstead:
DCHECK(share_this_tab_instead_button_);
return favicons_used_for_switch_to_tab_button_
? share_this_tab_instead_button_->GetImage()
: ui::ImageModel();
case kQuickNav:
DCHECK(quick_nav_button_);
return quick_nav_button_->GetImage();
case kCscPermission:
DCHECK(csc_permission_button_);
return csc_permission_button_->GetImage();
}
NOTREACHED_NORETURN();
}
bool TabSharingInfoBarDelegate::GetButtonEnabled(InfoBarButton button) const {
switch (button) {
case kNone:
break;
case kStop:
return true;
case kShareThisTabInstead:
DCHECK(share_this_tab_instead_button_);
return share_this_tab_instead_button_->IsEnabled();
case kQuickNav:
DCHECK(quick_nav_button_);
return quick_nav_button_->IsEnabled();
case kCscPermission:
DCHECK(csc_permission_button_);
return csc_permission_button_->IsEnabled();
}
NOTREACHED_NORETURN();
}
std::u16string TabSharingInfoBarDelegate::GetButtonTooltip(
InfoBarButton button) const {
switch (button) {
case kNone:
break;
case kStop:
return std::u16string();
case kShareThisTabInstead:
DCHECK(share_this_tab_instead_button_);
return share_this_tab_instead_button_->GetTooltip();
case kQuickNav:
DCHECK(quick_nav_button_);
return quick_nav_button_->GetTooltip();
case kCscPermission:
DCHECK(csc_permission_button_);
return csc_permission_button_->GetTooltip();
}
NOTREACHED_NORETURN();
}
int TabSharingInfoBarDelegate::GetButtons() const {
return kStop | (share_this_tab_instead_button_ ? kShareThisTabInstead : 0) |
(quick_nav_button_ ? kQuickNav : 0) |
(csc_permission_button_ ? kCscPermission : 0);
}
bool TabSharingInfoBarDelegate::Stop() {
ui_->StopSharing();
return false;
}
bool TabSharingInfoBarDelegate::ShareThisTabInstead() {
DCHECK(share_this_tab_instead_button_);
share_this_tab_instead_button_->Click(infobar());
return false;
}
bool TabSharingInfoBarDelegate::QuickNav() {
DCHECK(quick_nav_button_);
quick_nav_button_->Click(infobar());
return false;
}
void TabSharingInfoBarDelegate::
OnCapturedSurfaceControlActivityIndicatorPressed() {
DCHECK(csc_permission_button_);
csc_permission_button_->Click(infobar());
}
bool TabSharingInfoBarDelegate::IsCloseable() const {
return false;
}
const gfx::VectorIcon& TabSharingInfoBarDelegate::GetVectorIcon() const {
return vector_icons::kScreenShareIcon;
}