blob: 7cf53c4f7d0e73ddebd2e583f97fe1f736b72cc4 [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_window/public/browser_window_interface.h"
#include "chrome/browser/ui/bubble_anchor_util.h"
#include "chrome/browser/ui/page_info/page_info_dialog.h"
#include "chrome/browser/ui/tab_sharing/tab_sharing_ui.h"
#include "chrome/browser/ui/views/screen_sharing_util.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/tabs/public/tab_interface.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 "media/capture/capture_switches.h"
#include "net/base/url_util.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "ui/base/base_window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
namespace {
using TabRole = ::TabSharingInfoBarDelegate::TabRole;
constexpr int kCapturedSurfaceControlIndicatorButtonIconHeight = 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""; }
};
using TabSharingInfoBarDelegateButton =
::TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton;
class TabSharingInfoBarDelegate::StopButton
: public TabSharingInfoBarDelegateButton {
public:
StopButton(TabSharingUI* ui,
TabSharingInfoBarDelegate::TabShareType capture_type)
: ui_(ui), capture_type_(capture_type) {}
~StopButton() override = default;
void Click(infobars::InfoBar* infobar) override { ui_->StopSharing(); }
std::u16string GetLabel() const override {
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);
}
NOTREACHED();
}
private:
const raw_ptr<TabSharingUI, AcrossTasksDanglingUntriaged> ui_;
const TabShareType capture_type_;
};
// 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 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();
}
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 TabSharingInfoBarDelegateButton {
public:
SwitchToTabButton(content::GlobalRenderFrameHostId 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_);
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);
tabs::TabInterface* tab = tabs::TabInterface::GetFromContents(web_contents);
BrowserWindowInterface* browser = tab->GetBrowserWindowInterface();
if (browser && browser->GetWindow()) {
browser->GetWindow()->Activate();
}
}
std::u16string GetLabel() const override {
// TODO(crbug.com/40188004): Hard-code this text into the button.
content::RenderFrameHost* const rfh =
content::RenderFrameHost::FromID(focus_target_);
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));
}
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 content::GlobalRenderFrameHostId focus_target_;
const bool focus_target_is_capturer_;
};
class TabSharingInfoBarDelegate::CscIndicatorButton
: public TabSharingInfoBarDelegateButton {
public:
explicit CscIndicatorButton(content::WebContents* web_contents)
: web_contents_(web_contents ? web_contents->GetWeakPtr() : nullptr) {}
~CscIndicatorButton() override = default;
void Click(infobars::InfoBar* infobar) override {
if (!web_contents_) {
return;
}
ShowPageInfoDialog(web_contents_.get(), base::DoNothing(),
bubble_anchor_util::Anchor::kLocationBar,
ContentSettingsType::CAPTURED_SURFACE_CONTROL);
}
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,
kCapturedSurfaceControlIndicatorButtonIconHeight);
}
private:
const base::WeakPtr<content::WebContents> web_contents_;
};
bool TabSharingInfoBarDelegate::IsCapturedTab(TabRole role) {
switch (role) {
case TabRole::kCapturingTab:
case TabRole::kOtherTab:
return false;
case TabRole::kCapturedTab:
case TabRole::kSelfCapturingTab:
return true;
}
NOTREACHED();
}
bool TabSharingInfoBarDelegate::IsCapturingTab(TabRole role) {
switch (role) {
case TabRole::kCapturingTab:
case TabRole::kSelfCapturingTab:
return true;
case TabRole::kCapturedTab:
case TabRole::kOtherTab:
return false;
}
NOTREACHED();
}
// static
infobars::InfoBar* TabSharingInfoBarDelegate::Create(
infobars::InfoBarManager* infobar_manager,
infobars::InfoBar* old_infobar,
content::GlobalRenderFrameHostId shared_tab_id,
content::GlobalRenderFrameHostId capturer_id,
const std::u16string& shared_tab_name,
const std::u16string& capturer_name,
content::WebContents* web_contents,
TabRole role,
ButtonState share_this_tab_instead_button_state,
content::GlobalRenderFrameHostId focus_target,
bool captured_surface_control_active,
TabSharingUI* ui,
TabShareType capture_type) {
CHECK(infobar_manager);
CHECK(ui);
std::unique_ptr<infobars::InfoBar> new_infobar = CreateTabSharingInfoBar(
base::WrapUnique(new TabSharingInfoBarDelegate(
web_contents, role, share_this_tab_instead_button_state, focus_target,
captured_surface_control_active, ui, capture_type)),
shared_tab_id, capturer_id, shared_tab_name, capturer_name, role,
capture_type, ui->GetUmaLogger().GetWeakPtr());
return old_infobar ? infobar_manager->ReplaceInfoBar(old_infobar,
std::move(new_infobar))
: infobar_manager->AddInfoBar(std::move(new_infobar));
}
TabSharingInfoBarDelegate::TabSharingInfoBarDelegate(
content::WebContents* web_contents,
TabRole role,
ButtonState share_this_tab_instead_button_state,
content::GlobalRenderFrameHostId focus_target,
bool captured_surface_control_active,
TabSharingUI* ui,
TabShareType capture_type)
: ui_(ui), capture_type_(capture_type) {
stop_button_ = std::make_unique<StopButton>(ui_, 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) {
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) {
csc_indicator_button_ = std::make_unique<CscIndicatorButton>(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::GetButtonLabel(
TabSharingInfoBarButton button) const {
return GetButton(button).GetLabel();
}
ui::ImageModel TabSharingInfoBarDelegate::GetButtonImage(
TabSharingInfoBarButton button) const {
return GetButton(button).GetImage();
}
bool TabSharingInfoBarDelegate::IsButtonEnabled(
TabSharingInfoBarButton button) const {
return GetButton(button).IsEnabled();
}
std::u16string TabSharingInfoBarDelegate::GetButtonTooltip(
TabSharingInfoBarButton button) const {
return GetButton(button).GetTooltip();
}
int TabSharingInfoBarDelegate::GetButtons() const {
return (stop_button_ ? kStop : 0) |
(share_this_tab_instead_button_ ? kShareThisTabInstead : 0) |
(quick_nav_button_ ? kQuickNav : 0) |
(csc_indicator_button_ ? kCapturedSurfaceControlIndicator : 0);
}
void TabSharingInfoBarDelegate::Stop() {
GetButton(kStop).Click(infobar());
}
void TabSharingInfoBarDelegate::ShareThisTabInstead() {
GetButton(kShareThisTabInstead).Click(infobar());
}
void TabSharingInfoBarDelegate::QuickNav() {
GetButton(kQuickNav).Click(infobar());
}
void TabSharingInfoBarDelegate::
OnCapturedSurfaceControlActivityIndicatorPressed() {
GetButton(kCapturedSurfaceControlIndicator).Click(infobar());
}
bool TabSharingInfoBarDelegate::IsCloseable() const {
return false;
}
const gfx::VectorIcon& TabSharingInfoBarDelegate::GetVectorIcon() const {
return base::FeatureList::IsEnabled(features::kTabCaptureInfobarLinks)
? vector_icons::kScreenShareIcon
: vector_icons::kScreenShareOldIcon;
}
const TabSharingInfoBarDelegateButton& TabSharingInfoBarDelegate::GetButton(
TabSharingInfoBarButton button) const {
switch (button) {
case TabSharingInfoBarButton::kNone:
break;
case TabSharingInfoBarButton::kStop:
return *stop_button_;
case TabSharingInfoBarButton::kShareThisTabInstead:
return *share_this_tab_instead_button_;
case TabSharingInfoBarButton::kQuickNav:
return *quick_nav_button_;
case TabSharingInfoBarButton::kCapturedSurfaceControlIndicator:
return *csc_indicator_button_;
}
NOTREACHED();
}
TabSharingInfoBarDelegateButton& TabSharingInfoBarDelegate::GetButton(
TabSharingInfoBarButton button) {
return const_cast<TabSharingInfoBarDelegateButton&>(
const_cast<const TabSharingInfoBarDelegate*>(this)->GetButton(button));
}