#include "chrome/browser/ui/views/overlay/video_overlay_window_views.h"
#include <memory>
#include <string>
#include "base/check.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/browser/media/media_engagement_service.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_occlusion_tracker.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/views/overlay/back_to_tab_button.h"
#include "chrome/browser/ui/views/overlay/back_to_tab_label_button.h"
#include "chrome/browser/ui/views/overlay/close_image_button.h"
#include "chrome/browser/ui/views/overlay/constants.h"
#include "chrome/browser/ui/views/overlay/hang_up_button.h"
#include "chrome/browser/ui/views/overlay/minimize_button.h"
#include "chrome/browser/ui/views/overlay/overlay_controls_fade_animation.h"
#include "chrome/browser/ui/views/overlay/overlay_window_live_caption_button.h"
#include "chrome/browser/ui/views/overlay/overlay_window_live_caption_dialog.h"
#include "chrome/browser/ui/views/overlay/playback_image_button.h"
#include "chrome/browser/ui/views/overlay/resize_handle_button.h"
#include "chrome/browser/ui/views/overlay/simple_overlay_window_image_button.h"
#include "chrome/browser/ui/views/overlay/skip_ad_label_button.h"
#include "chrome/browser/ui/views/overlay/toggle_camera_button.h"
#include "chrome/browser/ui/views/overlay/toggle_microphone_button.h"
#include "chrome/browser/ui/views/picture_in_picture/picture_in_picture_tucker.h"
#include "chrome/grit/generated_resources.h"
#include "components/global_media_controls/public/format_duration.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/media_session.h"
#include "content/public/browser/picture_in_picture_window_controller.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "media/base/media_switches.h"
#include "media/base/video_util.h"
#include "services/media_session/public/cpp/media_image_manager.h"
#include "ui/base/hit_test.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/geometry/resize_utils.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/frame_view.h"
#include "url/origin.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "ash/public/cpp/window_properties.h"
#include "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/chromeos_ui_constants.h"
#include "chromeos/ui/base/window_properties.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/shell_integration_win.h"
#include "content/public/browser/render_widget_host_view.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/ime/win/tsf_input_scope.h"
#include "ui/base/win/shell.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "chrome/browser/ui/views/overlay/video_overlay_window_native_widget_mac.h"
#endif
namespace {
constexpr gfx::Size kMinWindowSize(284, 160);
constexpr int kOverlayBorderThickness = 10;
constexpr gfx::Size kOverlayViewPadding(64, 46);
constexpr gfx::Size kActionButtonSize(28, 28);
constexpr base::TimeDelta kSeekTime = base::Seconds(10);
constexpr gfx::Size kFaviconSize(24, 24);
constexpr gfx::Size kFaviconIconSize(16, 16);
gfx::Size ScaleImageSizeToFitView(const gfx::Size& image_size,
const gfx::Size& view_size) {
const float scale =
std::max(view_size.width() / static_cast<float>(image_size.width()),
view_size.height() / static_cast<float>(image_size.height()));
return gfx::ScaleToFlooredSize(image_size, scale);
}
gfx::ImageSkia GetCorrectColorTypeImage(const SkBitmap& bitmap) {
if (bitmap.info().colorType() == kN32_SkColorType) {
return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
}
SkImageInfo color_type_info = bitmap.info().makeColorType(kN32_SkColorType);
SkBitmap color_type_copy;
if (!color_type_copy.tryAllocPixels(color_type_info)) {
return gfx::ImageSkia();
}
if (!bitmap.readPixels(color_type_info, color_type_copy.getPixels(),
color_type_copy.rowBytes(), 0, 0)) {
return gfx::ImageSkia();
}
return gfx::ImageSkia::CreateFrom1xBitmap(color_type_copy);
}
VideoOverlayWindowViews::WindowQuadrant GetCurrentWindowQuadrant(
const gfx::Rect window_bounds,
content::PictureInPictureWindowController* controller) {
const gfx::Rect work_area =
display::Screen::Get()
->GetDisplayNearestWindow(
controller->GetWebContents()->GetTopLevelNativeWindow())
.work_area();
const gfx::Point window_center = window_bounds.CenterPoint();
const bool top = window_center.y() < work_area.height() / 2;
if (window_center.x() < work_area.width() / 2) {
return top ? VideoOverlayWindowViews::WindowQuadrant::kTopLeft
: VideoOverlayWindowViews::WindowQuadrant::kBottomLeft;
}
return top ? VideoOverlayWindowViews::WindowQuadrant::kTopRight
: VideoOverlayWindowViews::WindowQuadrant::kBottomRight;
}
template <typename T>
T* AddChildView(std::vector<std::unique_ptr<views::View>>* views,
std::unique_ptr<T> child) {
views->push_back(std::move(child));
return static_cast<T*>(views->back().get());
}
class WindowBackgroundView : public views::View {
METADATA_HEADER(WindowBackgroundView, views::View)
public:
WindowBackgroundView() = default;
WindowBackgroundView(const WindowBackgroundView&) = delete;
WindowBackgroundView& operator=(const WindowBackgroundView&) = delete;
~WindowBackgroundView() override = default;
void OnThemeChanged() override {
views::View::OnThemeChanged();
layer()->SetColor(GetColorProvider()->GetColor(kColorPipWindowBackground));
}
};
BEGIN_METADATA(WindowBackgroundView)
END_METADATA
class ControlsBackgroundView : public views::View {
METADATA_HEADER(ControlsBackgroundView, views::View)
public:
ControlsBackgroundView() = default;
ControlsBackgroundView(const ControlsBackgroundView&) = delete;
ControlsBackgroundView& operator=(const ControlsBackgroundView&) = delete;
~ControlsBackgroundView() override = default;
void OnThemeChanged() override {
views::View::OnThemeChanged();
SetBackground(views::CreateSolidBackground(
GetColorProvider()->GetColor(kColorPipWindowScrimFull)));
}
};
BEGIN_METADATA(ControlsBackgroundView)
END_METADATA
class GradientBackground : public views::Background {
public:
GradientBackground(SkColor4f top_color, SkColor4f bottom_color)
: top_color_(top_color), bottom_color_(bottom_color) {}
GradientBackground(const GradientBackground&) = delete;
GradientBackground& operator=(const GradientBackground&) = delete;
~GradientBackground() override = default;
void Paint(gfx::Canvas* canvas, views::View* view) const override {
gfx::Rect draw_bounds = view->GetContentsBounds();
const SkColor4f colors[2] = {top_color_, bottom_color_};
const SkPoint points[2] = {
gfx::PointToSkPoint(draw_bounds.top_center()),
gfx::PointToSkPoint(draw_bounds.bottom_center())};
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setShader(cc::PaintShader::MakeLinearGradient(points, colors, nullptr,
2, SkTileMode::kClamp));
canvas->DrawRect(draw_bounds, flags);
}
const SkColor4f top_color_;
const SkColor4f bottom_color_;
};
}
class OverlayWindowFrameView : public views::FrameView {
METADATA_HEADER(OverlayWindowFrameView, views::FrameView)
public:
explicit OverlayWindowFrameView(views::Widget* widget) : widget_(widget) {}
OverlayWindowFrameView(const OverlayWindowFrameView&) = delete;
OverlayWindowFrameView& operator=(const OverlayWindowFrameView&) = delete;
~OverlayWindowFrameView() override = default;
gfx::Rect GetBoundsForClientView() const override { return bounds(); }
gfx::Rect GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const override {
return bounds();
}
int NonClientHitTest(const gfx::Point& point) override {
if (!bounds().Contains(point)) {
return HTNOWHERE;
}
constexpr int kResizeAreaCornerSize = 16;
int window_component = GetHTComponentForFrame(
point, gfx::Insets(kOverlayBorderThickness), kResizeAreaCornerSize,
kResizeAreaCornerSize, GetWidget()->widget_delegate()->CanResize());
VideoOverlayWindowViews* window =
static_cast<VideoOverlayWindowViews*>(widget_);
if (window->ControlsHitTestContainsPoint(point)) {
return window_component;
}
#if BUILDFLAG(IS_CHROMEOS)
if (window->AreControlsVisible() &&
window->GetResizeHandleControlsBounds().Contains(point)) {
return window->GetResizeHTComponent();
}
#endif
if (!window->GetLiveCaptionDialogBounds().IsEmpty()) {
return window_component;
}
return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
}
#if BUILDFLAG(IS_CHROMEOS)
void UpdateWindowRoundedCorners() override {
ui::Layer* root_view_layer = GetWidget()->GetRootView()->layer();
if (root_view_layer) {
const gfx::RoundedCornersF window_radii(
chromeos::kPipRoundedCornerRadius);
root_view_layer->SetRoundedCornerRadius(window_radii);
root_view_layer->SetIsFastRoundedCorner(true);
}
}
#endif
bool DoesIntersectRect(const View* target,
const gfx::Rect& rect) const override {
DCHECK_EQ(target, this);
return false;
}
private:
raw_ptr<views::Widget> widget_;
};
BEGIN_METADATA(OverlayWindowFrameView)
END_METADATA
class OverlayWindowWidgetDelegate : public views::WidgetDelegate {
public:
OverlayWindowWidgetDelegate() {
SetCanResize(true);
SetModalType(ui::mojom::ModalType::kNone);
SetShowTitle(false);
SetTitle(IDS_PICTURE_IN_PICTURE_TITLE_TEXT);
SetOwnedByWidget(OwnedByWidgetPassKey());
}
OverlayWindowWidgetDelegate(const OverlayWindowWidgetDelegate&) = delete;
OverlayWindowWidgetDelegate& operator=(const OverlayWindowWidgetDelegate&) =
delete;
~OverlayWindowWidgetDelegate() override = default;
std::unique_ptr<views::FrameView> CreateFrameView(
views::Widget* widget) override {
return std::make_unique<OverlayWindowFrameView>(widget);
}
};
std::unique_ptr<VideoOverlayWindowViews> VideoOverlayWindowViews::Create(
content::VideoPictureInPictureWindowController* controller) {
auto overlay_window =
base::WrapUnique(new VideoOverlayWindowViews(controller));
overlay_window->SetColorModeOverride(ui::ColorProviderKey::ColorMode::kDark);
overlay_window->CalculateAndUpdateWindowBounds();
overlay_window->SetUpViews();
views::Widget::InitParams params(
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(overlay_window->GetMinimumSize());
params.z_order = ui::ZOrderLevel::kFloatingWindow;
params.visible_on_all_workspaces = true;
params.remove_standard_frame = true;
params.name = "PictureInPictureWindow";
params.layer_type = ui::LAYER_NOT_DRAWN;
params.delegate = new OverlayWindowWidgetDelegate();
#if !BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(
media::kPictureInPictureShowWindowAnimation)) {
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
}
#endif
#if BUILDFLAG(IS_MAC)
params.native_widget =
new VideoOverlayWindowNativeWidgetMac(overlay_window.get());
#endif
#if BUILDFLAG(IS_CHROMEOS)
params.init_properties_container.SetProperty(chromeos::kAppTypeKey,
chromeos::AppType::BROWSER);
params.rounded_corners =
gfx::RoundedCornersF(chromeos::kPipRoundedCornerRadius);
#endif
overlay_window->Init(std::move(params));
overlay_window->OnRootViewReady();
#if BUILDFLAG(IS_WIN)
std::wstring app_user_model_id;
Browser* browser = chrome::FindBrowserWithTab(controller->GetWebContents());
if (browser) {
const base::FilePath& profile_path = browser->profile()->GetPath();
app_user_model_id =
browser->is_type_app()
? shell_integration::win::GetAppUserModelIdForApp(
base::UTF8ToWide(browser->app_name()), profile_path)
: shell_integration::win::GetAppUserModelIdForBrowser(profile_path);
if (!app_user_model_id.empty()) {
ui::win::SetAppIdForWindow(
app_user_model_id,
overlay_window->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
}
}
InputScope input_scope = IS_PRIVATE;
ui::TextInputClient* text_input_client = overlay_window->GetController()
->GetWebContents()
->GetRenderWidgetHostView()
->GetTextInputClient();
if (text_input_client && text_input_client->ShouldDoLearning()) {
input_scope = IS_DEFAULT;
}
ui::tsf_inputscope::SetInputScope(
overlay_window->GetNativeWindow()->GetHost()->GetAcceleratedWidget(),
input_scope);
#endif
PictureInPictureOcclusionTracker* tracker =
PictureInPictureWindowManager::GetInstance()->GetOcclusionTracker();
if (tracker) {
tracker->OnPictureInPictureWidgetOpened(overlay_window.get());
}
return overlay_window;
}
std::unique_ptr<content::VideoOverlayWindow>
content::VideoOverlayWindow::Create(
content::VideoPictureInPictureWindowController* controller) {
return VideoOverlayWindowViews::Create(controller);
}
VideoOverlayWindowViews::VideoOverlayWindowViews(
content::VideoPictureInPictureWindowController* controller)
: controller_(controller),
min_size_(kMinWindowSize),
hide_controls_timer_(
FROM_HERE,
base::Milliseconds(2500),
base::BindRepeating(
&VideoOverlayWindowViews::UpdateControlsVisibility,
base::Unretained(this),
false ,
true )),
enable_controls_after_move_timer_(
FROM_HERE,
VideoOverlayWindowViews::kControlHideDelayAfterMove,
base::BindRepeating(
&VideoOverlayWindowViews::ReEnableControlsAfterMove,
base::Unretained(this))) {
display::Screen::Get()->AddObserver(this);
}
VideoOverlayWindowViews::~VideoOverlayWindowViews() {
if (overlay_view_) {
overlay_view_->RemoveObserver(this);
}
display::Screen::Get()->RemoveObserver(this);
PictureInPictureWindowManager::GetInstance()->OnPictureInPictureWindowHidden(
this);
}
gfx::Size& VideoOverlayWindowViews::GetNaturalSize() {
return natural_size_;
}
gfx::Rect VideoOverlayWindowViews::CalculateAndUpdateWindowBounds() {
gfx::Rect work_area = GetWorkAreaForWindow();
UpdateMaxSize(work_area);
const gfx::Rect bounds = GetBounds();
gfx::Size window_size = bounds.size();
if (!has_been_shown_) {
window_size = gfx::Size(work_area.width() / 5, work_area.height() / 5);
}
window_size.SetToMin(max_size_);
window_size.SetToMax(GetMinimumSize());
if (!natural_size_.IsEmpty()) {
float aspect_ratio = (float)natural_size_.width() / natural_size_.height();
WindowQuadrant quadrant = GetCurrentWindowQuadrant(bounds, GetController());
gfx::ResizeEdge resize_edge;
switch (quadrant) {
case WindowQuadrant::kBottomRight:
resize_edge = gfx::ResizeEdge::kTopLeft;
break;
case WindowQuadrant::kBottomLeft:
resize_edge = gfx::ResizeEdge::kTopRight;
break;
case WindowQuadrant::kTopLeft:
resize_edge = gfx::ResizeEdge::kBottomRight;
break;
case WindowQuadrant::kTopRight:
resize_edge = gfx::ResizeEdge::kBottomLeft;
break;
}
gfx::Rect window_rect(bounds.origin(), window_size);
gfx::SizeRectToAspectRatio(resize_edge, aspect_ratio, GetMinimumSize(),
max_size_, &window_rect);
window_size = window_rect.size();
UpdateLayerBoundsWithLetterboxing(window_size);
}
gfx::Point origin = bounds.origin();
int window_diff_width = work_area.right() - window_size.width();
int window_diff_height = work_area.bottom() - window_size.height();
int buffer = (window_diff_width + window_diff_height) / 2 * 0.02;
gfx::Point default_origin =
gfx::Point(window_diff_width - buffer, window_diff_height - buffer);
if (has_been_shown_) {
origin.SetToMin(default_origin);
} else {
origin = default_origin;
}
return gfx::Rect(origin, window_size);
}
void VideoOverlayWindowViews::OnNativeFocus() {
UpdateControlsVisibility(true);
views::Widget::OnNativeFocus();
}
void VideoOverlayWindowViews::OnNativeBlur() {
UpdateControlsVisibility(false);
views::Widget::OnNativeBlur();
}
gfx::Size VideoOverlayWindowViews::GetMinimumSize() const {
if (IsOverlayViewShown()) {
gfx::Size overlay_size =
overlay_view_->GetBubbleSize() + kOverlayViewPadding;
overlay_size.SetToMax(min_size_);
return overlay_size;
}
return min_size_;
}
gfx::Size VideoOverlayWindowViews::GetMaximumSize() const {
return max_size_;
}
void VideoOverlayWindowViews::OnNativeWidgetMove() {
if (!is_moving_) {
UpdateControlsVisibility(false, false);
}
is_moving_ = true;
enable_controls_after_move_timer_.Reset();
UpdateMaxSize(GetWorkAreaForWindow());
#if BUILDFLAG(IS_CHROMEOS)
WindowQuadrant quadrant =
GetCurrentWindowQuadrant(GetBounds(), GetController());
UpdateResizeHandleBounds(quadrant);
#endif
views::Widget::OnNativeWidgetMove();
}
void VideoOverlayWindowViews::OnNativeWidgetSizeChanged(
const gfx::Size& new_size) {
UpdateControlsVisibility(false);
UpdateLayerBoundsWithLetterboxing(new_size);
views::Widget::OnNativeWidgetSizeChanged(new_size);
}
void VideoOverlayWindowViews::OnKeyEvent(ui::KeyEvent* event) {
hide_controls_timer_.Reset();
if (event->type() == ui::EventType::kKeyPressed ||
event->key_code() == ui::VKEY_TAB) {
UpdateControlsVisibility(true);
}
#if BUILDFLAG(IS_WIN)
if (event->type() == ui::EventType::kKeyPressed && event->IsAltDown() &&
event->key_code() == ui::VKEY_F4) {
CloseAndPauseIfAvailable();
event->SetHandled();
}
#endif
views::View* focused_view = GetFocusManager()->GetFocusedView();
if (!focused_view && event->type() == ui::EventType::kKeyPressed &&
event->key_code() == ui::VKEY_SPACE && show_play_pause_button_) {
TogglePlayPause();
event->SetHandled();
}
MaybeUpdateMeetsUserInteraction(*event);
views::Widget::OnKeyEvent(event);
}
void VideoOverlayWindowViews::OnMouseEvent(ui::MouseEvent* event) {
switch (event->type()) {
case ui::EventType::kMouseMoved:
case ui::EventType::kMouseEntered:
UpdateControlsVisibility(true);
break;
case ui::EventType::kMouseExited: {
const bool should_update_control_visibility =
!GetWindowBackgroundView()->bounds().Contains(event->location()) &&
progress_view_drag_state_ ==
global_media_controls::DragState::kDragEnded;
if (should_update_control_visibility) {
UpdateControlsVisibility(false);
}
break;
}
case ui::EventType::kMousePressed:
if (live_caption_dialog_ && live_caption_dialog_->GetVisible() &&
!GetLiveCaptionDialogBounds().Contains(event->location()) &&
!GetLiveCaptionButtonBounds().Contains(event->location())) {
SetLiveCaptionDialogVisibility(false);
return;
}
break;
default:
break;
}
hide_controls_timer_.Reset();
MaybeUpdateMeetsUserInteraction(*event);
views::Widget::OnMouseEvent(event);
}
bool VideoOverlayWindowViews::ShowControlsForGestureIfNecessary(
ui::GestureEvent* event) {
if (event->type() != ui::EventType::kGestureTap) {
return false;
}
if (!AreControlsVisible()) {
UpdateControlsVisibility(true);
return true;
}
return false;
}
bool VideoOverlayWindowViews::HideLiveCaptionDialogForGestureIfNecessary(
ui::GestureEvent* event) {
if (event->type() != ui::EventType::kGestureTap) {
return false;
}
if (!live_caption_dialog_->GetVisible()) {
return false;
}
if (!GetLiveCaptionDialogBounds().Contains(event->location())) {
SetLiveCaptionDialogVisibility(false);
event->SetHandled();
return true;
}
return false;
}
void VideoOverlayWindowViews::ReEnableControlsAfterMove() {
is_moving_ = false;
if (queued_controls_visibility_status_) {
UpdateControlsVisibility(
queued_controls_visibility_status_->is_visible,
queued_controls_visibility_status_->should_animate);
}
queued_controls_visibility_status_.reset();
}
void VideoOverlayWindowViews::ForceControlsVisibleForTesting(
bool controls_visible,
std::optional<bool> title_and_scrim_visible) {
force_controls_visible_ = controls_visible;
force_title_and_scrim_visible_ = title_and_scrim_visible;
UpdateControlsVisibility(controls_visible, false);
}
void VideoOverlayWindowViews::StopForcingControlsVisibleForTesting() {
force_controls_visible_.reset();
force_title_and_scrim_visible_.reset();
}
void VideoOverlayWindowViews::FireEnableControlsAfterMoveTimerForTesting() {
if (!enable_controls_after_move_timer_.IsRunning()) {
return;
}
enable_controls_after_move_timer_.Stop();
ReEnableControlsAfterMove();
}
bool VideoOverlayWindowViews::AreControlsVisible() const {
if (fade_animation_) {
return (fade_animation_->type() ==
OverlayControlsFadeAnimation::Type::kToShown);
}
return GetControlsContainerView()->layer()->opacity() > 0;
}
void VideoOverlayWindowViews::UpdateControlsVisibility(bool is_visible,
bool should_animate) {
if (is_moving_) {
if (queued_controls_visibility_status_.has_value() &&
queued_controls_visibility_status_->is_visible == is_visible) {
queued_controls_visibility_status_->should_animate =
queued_controls_visibility_status_->should_animate && should_animate;
} else {
queued_controls_visibility_status_ = {is_visible, should_animate};
}
return;
}
if (progress_view_drag_state_ ==
global_media_controls::DragState::kDragStarted) {
return;
}
const bool wanted_visibility =
!IsOverlayViewShown() && force_controls_visible_.value_or(is_visible);
const bool can_hide_title_and_scrim =
IsTrustedForMediaPlayback() || meets_user_interaction_;
if (wanted_visibility && can_hide_title_and_scrim) {
initial_title_hide_timer_.Stop();
}
const bool title_is_visible =
force_title_and_scrim_visible_.has_value()
? force_title_and_scrim_visible_.value()
: wanted_visibility || initial_title_hide_timer_.IsRunning() ||
!can_hide_title_and_scrim;
if (should_animate) {
if (title_is_visible != AreTitleAndScrimVisible()) {
const std::vector<raw_ptr<views::View>> title_and_scrim = {
title_view_, controls_top_scrim_view_};
title_and_top_scrim_fade_animation_ =
std::make_unique<OverlayControlsFadeAnimation>(
title_and_scrim,
title_is_visible ? OverlayControlsFadeAnimation::Type::kToShown
: OverlayControlsFadeAnimation::Type::kToHidden);
title_and_top_scrim_fade_animation_->Start();
}
if (wanted_visibility != AreControlsVisible()) {
const std::vector<raw_ptr<views::View>> controls = {
controls_container_view_, controls_scrim_view_,
controls_bottom_scrim_view_};
fade_animation_ = std::make_unique<OverlayControlsFadeAnimation>(
controls, wanted_visibility
? OverlayControlsFadeAnimation::Type::kToShown
: OverlayControlsFadeAnimation::Type::kToHidden);
fade_animation_->Start();
}
} else {
title_and_top_scrim_fade_animation_.reset();
fade_animation_.reset();
GetTitleView()->layer()->SetOpacity(title_is_visible ? 1.0 : 0.0);
GetControlsTopScrimView()->layer()->SetOpacity(title_is_visible ? 1.0
: 0.0);
controls_scrim_view_->layer()->SetOpacity(wanted_visibility ? 1.0 : 0.0);
controls_bottom_scrim_view_->layer()->SetOpacity(wanted_visibility ? 1.0
: 0.0);
GetControlsContainerView()->layer()->SetOpacity(wanted_visibility ? 1.0
: 0.0);
GetControlsContainerView()->SetVisible(wanted_visibility);
}
}
void VideoOverlayWindowViews::UpdateControlsBounds() {
if (!AreControlsVisible()) {
OnUpdateControlsBounds();
return;
}
update_controls_bounds_timer_ = std::make_unique<base::OneShotTimer>();
update_controls_bounds_timer_->Start(
FROM_HERE, base::Seconds(1),
base::BindOnce(&VideoOverlayWindowViews::OnUpdateControlsBounds,
base::Unretained(this)));
}
bool VideoOverlayWindowViews::IsLayoutPendingForTesting() const {
return update_controls_bounds_timer_ &&
update_controls_bounds_timer_->IsRunning();
}
void VideoOverlayWindowViews::FinishTuckAnimationForTesting() {
if (tucker_) {
tucker_->FinishAnimationForTesting();
}
}
bool VideoOverlayWindowViews::AreTitleAndScrimVisibleForTesting() const {
return AreTitleAndScrimVisible();
}
void VideoOverlayWindowViews::OnDisplayMetricsChanged(
const display::Display& display,
uint32_t changed_metrics) {
if (changed_metrics & display::DisplayObserver::DISPLAY_METRIC_WORK_AREA &&
display.id() == display::Screen::Get()
->GetDisplayNearestWindow(GetNativeWindow())
.id()) {
UpdateMaxSize(GetWorkAreaForWindow());
}
}
void VideoOverlayWindowViews::OnViewVisibilityChanged(
views::View* observed_view,
views::View* starting_view,
bool visible) {
if (starting_view != overlay_view_) {
return;
}
OnSizeConstraintsChanged();
}
void VideoOverlayWindowViews::SetForcedTucking(bool tuck) {
if (!tucker_) {
tucker_ = std::make_unique<PictureInPictureTucker>(*this);
}
is_tucking_forced_ = tuck;
if (tuck) {
tucker_->Tuck();
} else {
tucker_->Untuck();
}
}
void VideoOverlayWindowViews::OnAutoPipSettingOverlayViewHidden() {
RemoveOverlayViewIfExists();
}
gfx::Rect VideoOverlayWindowViews::GetWorkAreaForWindow() const {
return display::Screen::Get()
->GetDisplayNearestWindow(
native_widget() && IsVisible()
? GetNativeWindow()
: GetController()->GetWebContents()->GetTopLevelNativeWindow())
.work_area();
}
void VideoOverlayWindowViews::UpdateMaxSize(const gfx::Rect& work_area) {
if (work_area.IsEmpty()) {
return;
}
auto new_max_size =
gfx::Size(work_area.width() * 0.8, work_area.height() * 0.8);
new_max_size.SetToMax(min_size_);
if (new_max_size == max_size_) {
return;
}
max_size_ = new_max_size;
if (!native_widget()) {
return;
}
OnSizeConstraintsChanged();
if (GetBounds().width() <= max_size_.width() &&
GetBounds().height() <= max_size_.height()) {
return;
}
gfx::Size clamped_size = GetBounds().size();
clamped_size.SetToMin(max_size_);
SetSize(clamped_size);
}
bool VideoOverlayWindowViews::ControlsHitTestContainsPoint(
const gfx::Point& point) {
if (overlay_view_) {
gfx::Point point_in_screen =
views::View::ConvertPointToScreen(non_client_view(), point);
return overlay_view_->WantsEvent(point_in_screen);
}
if (!AreControlsVisible()) {
return false;
}
if (GetBackToTabControlsBounds().Contains(point) ||
GetSkipAdControlsBounds().Contains(point) ||
GetCloseControlsBounds().Contains(point) ||
GetMinimizeControlsBounds().Contains(point) ||
GetPlayPauseControlsBounds().Contains(point) ||
GetReplay10SecondsButtonBounds().Contains(point) ||
GetForward10SecondsButtonBounds().Contains(point) ||
GetNextTrackControlsBounds().Contains(point) ||
GetPreviousTrackControlsBounds().Contains(point) ||
GetToggleMicrophoneButtonBounds().Contains(point) ||
GetToggleCameraButtonBounds().Contains(point) ||
GetHangUpButtonBounds().Contains(point) ||
GetProgressViewBounds().Contains(point) ||
GetLiveCaptionButtonBounds().Contains(point) ||
GetLiveCaptionDialogBounds().Contains(point)) {
return true;
}
return false;
}
content::PictureInPictureWindowController*
VideoOverlayWindowViews::GetController() const {
return controller_;
}
views::View* VideoOverlayWindowViews::GetWindowBackgroundView() const {
return window_background_view_;
}
views::View* VideoOverlayWindowViews::GetControlsContainerView() const {
return controls_container_view_;
}
views::View* VideoOverlayWindowViews::GetTitleView() const {
return title_view_;
}
views::View* VideoOverlayWindowViews::GetControlsTopScrimView() const {
return controls_top_scrim_view_;
}
void VideoOverlayWindowViews::SetUpViews() {
auto window_background_view = std::make_unique<WindowBackgroundView>();
auto video_view = std::make_unique<views::View>();
auto controls_scrim_view = std::make_unique<ControlsBackgroundView>();
auto controls_container_view = std::make_unique<views::View>();
auto title_view = std::make_unique<views::View>();
auto close_controls_view = std::make_unique<CloseImageButton>(
base::BindRepeating(&VideoOverlayWindowViews::CloseAndPauseIfAvailable,
base::Unretained(this)));
auto play_pause_controls_view =
std::make_unique<PlaybackImageButton>(base::BindRepeating(
[](VideoOverlayWindowViews* overlay) { overlay->TogglePlayPause(); },
base::Unretained(this)));
play_pause_controls_view->SetSize({kCenterButtonSize, kCenterButtonSize});
auto playback_controls_container_view = std::make_unique<views::View>();
auto vc_controls_container_view = std::make_unique<views::View>();
auto controls_top_scrim_view = std::make_unique<views::View>();
controls_top_scrim_view->SetBackground(std::make_unique<GradientBackground>(
SkColor4f::FromColor(
GetColorProvider()->GetColor(kColorPipWindowScrimTopGradientStart)),
SkColor4f::FromColor(
GetColorProvider()->GetColor(kColorPipWindowScrimTopGradientEnd))));
auto controls_bottom_scrim_view = std::make_unique<views::View>();
controls_bottom_scrim_view->SetBackground(
std::make_unique<GradientBackground>(
SkColor4f::FromColor(GetColorProvider()->GetColor(
kColorPipWindowScrimBottomGradientStart)),
SkColor4f::FromColor(GetColorProvider()->GetColor(
kColorPipWindowScrimBottomGradientEnd))));
auto favicon_view = std::make_unique<views::ImageView>();
favicon_view->SetSize(kFaviconSize);
auto origin = std::make_unique<views::Label>(std::u16string(),
views::style::CONTEXT_LABEL,
views::style::STYLE_BODY_4);
origin->SetEnabledColor(ui::kColorSysOnSurface);
origin->SetBackgroundColor(SK_ColorTRANSPARENT);
origin->SetHorizontalAlignment(gfx::ALIGN_LEFT);
origin->SetElideBehavior(gfx::ELIDE_HEAD);
auto minimize_button = std::make_unique<
OverlayWindowMinimizeButton>(base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
PictureInPictureWindowManager::GetInstance()
->ExitPictureInPictureViaWindowUi(
PictureInPictureWindowManager::UiBehavior::kCloseWindowOnly);
},
base::Unretained(this)));
auto back_to_tab_button =
std::make_unique<OverlayWindowBackToTabButton>(base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
PictureInPictureWindowManager::GetInstance()
->ExitPictureInPictureViaWindowUi(
PictureInPictureWindowManager::UiBehavior::
kCloseWindowAndFocusOpener);
},
base::Unretained(this)));
auto replay_10_seconds_button =
std::make_unique<SimpleOverlayWindowImageButton>(
base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
overlay->Replay10Seconds();
},
base::Unretained(this)),
vector_icons::kReplay10Icon,
l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_REPLAY_10_TEXT));
replay_10_seconds_button->SetSize(kActionButtonSize);
auto forward_10_seconds_button =
std::make_unique<SimpleOverlayWindowImageButton>(
base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
overlay->Forward10Seconds();
},
base::Unretained(this)),
vector_icons::kForward10Icon,
l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_FORWARD_10_TEXT));
forward_10_seconds_button->SetSize(kActionButtonSize);
auto previous_track_controls_view =
std::make_unique<SimpleOverlayWindowImageButton>(
base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
if (overlay->show_previous_track_button_) {
overlay->controller_->PreviousTrack();
} else if (overlay->show_previous_slide_button_) {
overlay->controller_->PreviousSlide();
}
},
base::Unretained(this)),
vector_icons::kSkipPreviousIcon,
l10n_util::GetStringUTF16(
IDS_PICTURE_IN_PICTURE_PREVIOUS_TRACK_CONTROL_ACCESSIBLE_TEXT));
previous_track_controls_view->SetSize(kActionButtonSize);
auto next_track_controls_view =
std::make_unique<SimpleOverlayWindowImageButton>(
base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
if (overlay->show_next_track_button_) {
overlay->controller_->NextTrack();
} else if (overlay->show_next_slide_button_) {
overlay->controller_->NextSlide();
}
},
base::Unretained(this)),
vector_icons::kSkipNextIcon,
l10n_util::GetStringUTF16(
IDS_PICTURE_IN_PICTURE_NEXT_TRACK_CONTROL_ACCESSIBLE_TEXT));
next_track_controls_view->SetSize(kActionButtonSize);
auto progress_view =
std::make_unique<global_media_controls::MediaProgressView>(
false,
ui::kColorSysPrimary,
ui::kColorSysStateDisabled,
ui::kColorSysStateDisabled,
ui::kColorSysStateDisabled,
ui::kColorSysStateFocusRing,
base::BindRepeating(
&VideoOverlayWindowViews::OnProgressDragStateChanged,
base::Unretained(this)),
base::BindRepeating(
&VideoOverlayWindowViews::ChangePlaybackStateForProgressDrag,
base::Unretained(this)),
base::BindRepeating(
&VideoOverlayWindowViews::SeekForProgressBarInteraction,
base::Unretained(this)),
base::BindRepeating(
&VideoOverlayWindowViews::OnProgressViewUpdateCurrentTime,
base::Unretained(this)));
auto timestamp = std::make_unique<views::Label>(std::u16string(),
views::style::CONTEXT_LABEL,
views::style::STYLE_BODY_4);
timestamp->SetEnabledColor(ui::kColorSysOnSurfaceSubtle);
timestamp->SetBackgroundColor(SK_ColorTRANSPARENT);
timestamp->SetHorizontalAlignment(gfx::ALIGN_LEFT);
auto live_status = std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_LIVE_STATUS_TEXT),
views::style::CONTEXT_LABEL, views::style::STYLE_CAPTION_BOLD);
live_status->SetEnabledColor(ui::kColorSysTonalContainer);
live_status->SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(0, 4)));
live_status->SetBackground(
views::CreateRoundedRectBackground(ui::kColorSysOnTonalContainer, 4));
live_status->SetVisible(false);
auto live_caption_button = std::make_unique<OverlayWindowLiveCaptionButton>(
base::BindRepeating(&VideoOverlayWindowViews::OnLiveCaptionButtonPressed,
base::Unretained(this)));
live_caption_button->SetSize(kActionButtonSize);
live_caption_button->SetIsLiveCaptionDialogOpen(false);
auto live_caption_dialog = std::make_unique<OverlayWindowLiveCaptionDialog>(
Profile::FromBrowserContext(
controller_->GetWebContents()->GetBrowserContext()));
live_caption_dialog->SetVisible(false);
auto toggle_microphone_button =
std::make_unique<ToggleMicrophoneButton>(base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
overlay->controller_->ToggleMicrophone();
},
base::Unretained(this)));
toggle_microphone_button->SetVisible(false);
toggle_microphone_button->SetSize(kActionButtonSize);
auto toggle_camera_button =
std::make_unique<ToggleCameraButton>(base::BindRepeating(
[](VideoOverlayWindowViews* overlay) {
overlay->controller_->ToggleCamera();
},
base::Unretained(this)));
toggle_camera_button->SetVisible(false);
toggle_camera_button->SetSize(kActionButtonSize);
auto hang_up_button = std::make_unique<HangUpButton>(base::BindRepeating(
[](VideoOverlayWindowViews* overlay) { overlay->controller_->HangUp(); },
base::Unretained(this)));
hang_up_button->SetSize({kCenterButtonSize, kCenterButtonSize});
hang_up_button->SetVisible(false);
#if BUILDFLAG(IS_CHROMEOS)
auto resize_handle_view =
std::make_unique<ResizeHandleButton>(views::Button::PressedCallback());
#endif
window_background_view->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
window_background_view->layer()->SetName("WindowBackgroundView");
video_view->SetPaintToLayer(ui::LAYER_TEXTURED);
video_view->layer()->SetMasksToBounds(true);
video_view->layer()->SetFillsBoundsOpaquely(false);
video_view->layer()->SetName("VideoView");
controls_scrim_view->SetPaintToLayer(ui::LAYER_TEXTURED);
controls_scrim_view->layer()->SetFillsBoundsOpaquely(false);
controls_scrim_view->layer()->SetName("ControlsScrimView");
controls_container_view->SetPaintToLayer(ui::LAYER_TEXTURED);
controls_container_view->layer()->SetFillsBoundsOpaquely(false);
controls_container_view->layer()->SetName("ControlsContainerView");
controls_top_scrim_view->SetPaintToLayer(ui::LAYER_TEXTURED);
controls_top_scrim_view->layer()->SetFillsBoundsOpaquely(false);
controls_top_scrim_view->layer()->SetName("ControlsTopScrimView");
controls_bottom_scrim_view->SetPaintToLayer(ui::LAYER_TEXTURED);
controls_bottom_scrim_view->layer()->SetFillsBoundsOpaquely(false);
controls_bottom_scrim_view->layer()->SetName("ControlsBottomScrimView");
title_view->SetPaintToLayer(ui::LAYER_TEXTURED);
title_view->layer()->SetFillsBoundsOpaquely(false);
title_view->layer()->SetName("TitleView");
window_background_view_ =
AddChildView(&view_holder_, std::move(window_background_view));
video_view_ = AddChildView(&view_holder_, std::move(video_view));
controls_scrim_view_ =
AddChildView(&view_holder_, std::move(controls_scrim_view));
controls_top_scrim_view_ =
AddChildView(&view_holder_, std::move(controls_top_scrim_view));
controls_bottom_scrim_view_ =
AddChildView(&view_holder_, std::move(controls_bottom_scrim_view));
title_view_ = AddChildView(&view_holder_, std::move(title_view));
controls_container_view_ =
AddChildView(&view_holder_, std::move(controls_container_view));
playback_controls_container_view_ = controls_container_view_->AddChildView(
std::move(playback_controls_container_view));
vc_controls_container_view_ = controls_container_view_->AddChildView(
std::move(vc_controls_container_view));
favicon_view_ = title_view_->AddChildView(std::move(favicon_view));
UpdateFavicon(gfx::ImageSkia());
origin_ = title_view_->AddChildView(std::move(origin));
minimize_button_ =
controls_container_view_->AddChildView(std::move(minimize_button));
back_to_tab_button_ =
controls_container_view_->AddChildView(std::move(back_to_tab_button));
close_controls_view_ =
controls_container_view_->AddChildView(std::move(close_controls_view));
replay_10_seconds_button_ = playback_controls_container_view_->AddChildView(
std::move(replay_10_seconds_button));
play_pause_controls_view_ = playback_controls_container_view_->AddChildView(
std::move(play_pause_controls_view));
forward_10_seconds_button_ = playback_controls_container_view_->AddChildView(
std::move(forward_10_seconds_button));
previous_track_controls_view_ =
playback_controls_container_view_->AddChildView(
std::move(previous_track_controls_view));
progress_view_ =
playback_controls_container_view_->AddChildView(std::move(progress_view));
next_track_controls_view_ = playback_controls_container_view_->AddChildView(
std::move(next_track_controls_view));
timestamp_ =
playback_controls_container_view_->AddChildView(std::move(timestamp));
live_status_ =
playback_controls_container_view_->AddChildView(std::move(live_status));
live_caption_button_ = playback_controls_container_view_->AddChildView(
std::move(live_caption_button));
live_caption_dialog_ =
controls_container_view_->AddChildView(std::move(live_caption_dialog));
toggle_camera_button_ = vc_controls_container_view_->AddChildView(
std::move(toggle_camera_button));
hang_up_button_ =
vc_controls_container_view_->AddChildView(std::move(hang_up_button));
toggle_microphone_button_ = vc_controls_container_view_->AddChildView(
std::move(toggle_microphone_button));
#if BUILDFLAG(IS_CHROMEOS)
resize_handle_view_ =
controls_container_view_->AddChildView(std::move(resize_handle_view));
#endif
}
void VideoOverlayWindowViews::OnRootViewReady() {
#if BUILDFLAG(IS_CHROMEOS)
GetNativeWindow()->SetProperty(ash::kWindowPipTypeKey, true);
highlight_border_overlay_ =
std::make_unique<HighlightBorderOverlay>(this, nullptr);
#endif
GetRootView()->SetPaintToLayer(ui::LAYER_TEXTURED);
GetRootView()->layer()->SetName("RootView");
GetRootView()->layer()->SetMasksToBounds(true);
views::View* const contents_view = GetContentsView();
for (std::unique_ptr<views::View>& child : view_holder_) {
contents_view->AddChildView(std::move(child));
}
view_holder_.clear();
UpdateControlsVisibility(false, false);
}
void VideoOverlayWindowViews::UpdateLayerBoundsWithLetterboxing(
gfx::Size window_size) {
if (!native_widget() || GetBounds().IsEmpty() || GetNaturalSize().IsEmpty()) {
return;
}
gfx::Rect letterbox_region = media::ComputeLetterboxRegion(
gfx::Rect(gfx::Point(0, 0), window_size), GetNaturalSize());
if (letterbox_region.IsEmpty()) {
return;
}
const float aspect_ratio =
static_cast<float>(GetNaturalSize().width()) / GetNaturalSize().height();
if (aspect_ratio > 1 && window_size.height() == letterbox_region.height()) {
const int height_from_width =
base::ClampRound(window_size.width() / aspect_ratio);
if (height_from_width == window_size.height()) {
letterbox_region.set_width(window_size.width());
}
} else if (aspect_ratio <= 1 &&
window_size.width() == letterbox_region.width()) {
const int width_from_height =
base::ClampRound(window_size.height() * aspect_ratio);
if (width_from_height == window_size.width()) {
letterbox_region.set_height(window_size.height());
}
}
const gfx::Rect video_bounds(
gfx::Point((window_size.width() - letterbox_region.size().width()) / 2,
(window_size.height() - letterbox_region.size().height()) / 2),
letterbox_region.size());
UpdateControlsBounds();
window_background_view_->SetBoundsRect(
gfx::Rect(gfx::Point(0, 0), GetBounds().size()));
video_view_->SetBoundsRect(video_bounds);
if (video_view_->layer()->has_external_content()) {
video_view_->layer()->SetSurfaceSize(video_bounds.size());
}
if (IsOverlayViewShown()) {
overlay_view_->SetBoundsRect(gfx::Rect(GetBounds().size()));
}
controller_->UpdateLayerBounds();
}
void VideoOverlayWindowViews::OnUpdateControlsBounds() {
controls_container_view_->SetSize(GetBounds().size());
gfx::Rect larger_window_bounds = gfx::Rect(GetBounds().size());
larger_window_bounds.Inset(-1);
controls_scrim_view_->SetBoundsRect(larger_window_bounds);
WindowQuadrant quadrant = GetCurrentWindowQuadrant(GetBounds(), controller_);
close_controls_view_->SetPosition(GetBounds().size(), quadrant);
#if BUILDFLAG(IS_CHROMEOS)
UpdateResizeHandleBounds(quadrant);
#endif
constexpr int kTopControlsHeight = 34;
constexpr int kBottomControlsHeight = 64;
constexpr int kTopScrimHeight = 160;
constexpr int kBottomScrimHeight = 160;
constexpr int kFaviconLeftMargin = 8;
constexpr int kFaviconTopMargin = 5;
constexpr int kFaviconRightMargin = 4;
constexpr int kOriginTopMargin = 5;
constexpr int kOriginHeight = 24;
constexpr int kOriginRightMargin = 80;
constexpr int kProgressBarHeight = 26;
constexpr int kCenterControlMargin = 16;
constexpr int kBottomControlsHorizontalMargin = 12;
constexpr int kBottomControlsVerticalMargin = 4;
constexpr int kTimestampHorizontalMargin = 16;
constexpr int kTimestampVerticalMargin = 10;
constexpr int kTimestampHeight = 16;
gfx::Rect bounds = GetBounds();
bounds.set_origin({0, 0});
gfx::Rect top_controls_bounds(bounds.x(), bounds.y(), bounds.width(),
kTopControlsHeight);
gfx::Rect bottom_controls_bounds(
bounds.x(), bounds.y() + bounds.height() - kBottomControlsHeight,
bounds.width(), kBottomControlsHeight);
gfx::Rect middle_controls_bounds = gfx::BoundingRect(
top_controls_bounds.bottom_left(), bottom_controls_bounds.top_right());
gfx::Rect vc_middle_controls_bounds(
top_controls_bounds.bottom_left(),
{bounds.width(), bounds.height() - (2 * top_controls_bounds.height())});
title_view_->SetSize(bounds.size());
playback_controls_container_view_->SetSize(bounds.size());
vc_controls_container_view_->SetSize(bounds.size());
controls_top_scrim_view_->SetBoundsRect(
{top_controls_bounds.x(), top_controls_bounds.y(),
top_controls_bounds.width(), kTopScrimHeight});
controls_bottom_scrim_view_->SetBoundsRect(
{bottom_controls_bounds.x(),
bottom_controls_bounds.y() + bottom_controls_bounds.height() -
kBottomScrimHeight,
bottom_controls_bounds.width(), kBottomScrimHeight});
gfx::Rect favicon_view_bounds({top_controls_bounds.x() + kFaviconLeftMargin,
top_controls_bounds.y() + kFaviconTopMargin},
kFaviconSize);
favicon_view_->SetPosition(favicon_view_bounds.origin());
gfx::Point origin_position(favicon_view_bounds.right() + kFaviconRightMargin,
kOriginTopMargin);
origin_->SetPosition(origin_position);
origin_->SetSize(
{top_controls_bounds.width() - origin_position.x() - kOriginRightMargin,
kOriginHeight});
minimize_button_->SetPosition(GetBounds().size());
back_to_tab_button_->SetPosition(GetBounds().size());
const bool showing_vc_controls = show_toggle_camera_button_ ||
show_toggle_microphone_button_ ||
show_hang_up_button_;
gfx::Rect effective_middle_controls_bounds =
showing_vc_controls ? vc_middle_controls_bounds : middle_controls_bounds;
const gfx::Point center_control_position(
effective_middle_controls_bounds.CenterPoint().x() -
kCenterButtonSize / 2,
effective_middle_controls_bounds.CenterPoint().y() -
kCenterButtonSize / 2);
const gfx::Point center_left_control_position(
center_control_position.x() - kCenterControlMargin -
kActionButtonSize.width(),
effective_middle_controls_bounds.CenterPoint().y() -
kActionButtonSize.height() / 2);
const gfx::Point center_right_control_position(
center_control_position.x() + kCenterButtonSize + kCenterControlMargin,
effective_middle_controls_bounds.CenterPoint().y() -
kActionButtonSize.height() / 2);
hang_up_button_->SetVisible(show_hang_up_button_);
toggle_camera_button_->SetVisible(show_toggle_camera_button_);
toggle_microphone_button_->SetVisible(show_toggle_microphone_button_);
if (showing_vc_controls) {
hang_up_button_->SetPosition(center_control_position);
toggle_camera_button_->SetPosition(center_left_control_position);
toggle_microphone_button_->SetPosition(center_right_control_position);
vc_controls_container_view_->SetVisible(true);
playback_controls_container_view_->SetVisible(false);
return;
}
playback_controls_container_view_->SetVisible(true);
vc_controls_container_view_->SetVisible(false);
play_pause_controls_view_->SetPosition(center_control_position);
replay_10_seconds_button_->SetPosition(center_left_control_position);
forward_10_seconds_button_->SetPosition(center_right_control_position);
previous_track_controls_view_->SetPosition(
{bottom_controls_bounds.x() + kBottomControlsHorizontalMargin,
bottom_controls_bounds.y() + kBottomControlsVerticalMargin});
next_track_controls_view_->SetPosition(
{bottom_controls_bounds.x() + bottom_controls_bounds.width() -
(kBottomControlsHorizontalMargin + kActionButtonSize.width()),
bottom_controls_bounds.y() + kBottomControlsVerticalMargin});
const bool should_enable_prev =
show_previous_track_button_ || show_previous_slide_button_;
const bool should_enable_next =
show_next_track_button_ || show_next_slide_button_;
const bool should_show_prev_next = should_enable_prev || should_enable_next;
previous_track_controls_view_->SetVisible(should_show_prev_next);
next_track_controls_view_->SetVisible(should_show_prev_next);
previous_track_controls_view_->SetEnabled(should_enable_prev);
next_track_controls_view_->SetEnabled(should_enable_next);
constexpr int kPreviousNextTrackWidthPlusHorizontalMargins =
kBottomControlsHorizontalMargin + kActionButtonSize.width();
const int used_horizontal_space_left_of_progress_bar =
should_show_prev_next ? kPreviousNextTrackWidthPlusHorizontalMargins
: kBottomControlsHorizontalMargin;
progress_view_->SetPosition(
{bottom_controls_bounds.x() + used_horizontal_space_left_of_progress_bar,
bottom_controls_bounds.y() + kBottomControlsVerticalMargin});
progress_view_->SetSize(
{bounds.width() - (2 * used_horizontal_space_left_of_progress_bar),
kProgressBarHeight});
gfx::Point timestamp_position(
bottom_controls_bounds.x() + kTimestampHorizontalMargin,
bottom_controls_bounds.y() + bottom_controls_bounds.height() -
kTimestampVerticalMargin - kTimestampHeight);
const int max_timestamp_width =
bottom_controls_bounds.width() - (2 * kTimestampHorizontalMargin);
timestamp_->SetPosition(timestamp_position);
timestamp_->SetSize({max_timestamp_width, kTimestampHeight});
timestamp_->SetVisible(!is_live_);
live_status_->SetPosition(timestamp_position);
live_status_->SetMaximumWidthSingleLine(max_timestamp_width);
live_status_->SetSize(
{live_status_->GetPreferredSize({max_timestamp_width, kTimestampHeight})
.width(),
kTimestampHeight});
live_status_->SetVisible(is_live_);
gfx::Rect live_caption_button_bounds(
bottom_controls_bounds.right() - kBottomControlsHorizontalMargin -
kActionButtonSize.width(),
bottom_controls_bounds.bottom() - kBottomControlsVerticalMargin -
kActionButtonSize.height(),
live_caption_button_->width(), live_caption_button_->height());
live_caption_button_->SetPosition(live_caption_button_bounds.origin());
live_caption_dialog_->SetPosition(
{live_caption_button_bounds.right() - live_caption_dialog_->width(),
live_caption_button_bounds.y() - live_caption_dialog_->height()});
const bool is_dragging_progress_bar =
progress_view_drag_state_ ==
global_media_controls::DragState::kDragStarted;
play_pause_controls_view_->SetVisible(show_play_pause_button_ &&
!is_dragging_progress_bar);
replay_10_seconds_button_->SetVisible(!is_dragging_progress_bar && !is_live_);
forward_10_seconds_button_->SetVisible(!is_dragging_progress_bar &&
!is_live_);
}
#if BUILDFLAG(IS_CHROMEOS)
void VideoOverlayWindowViews::UpdateResizeHandleBounds(
WindowQuadrant quadrant) {
resize_handle_view_->SetPosition(GetBounds().size(), quadrant);
GetNativeWindow()->SetProperty(
ash::kWindowPipResizeHandleBoundsKey,
new gfx::Rect(GetResizeHandleControlsBounds()));
}
#endif
bool VideoOverlayWindowViews::IsActive() const {
return views::Widget::IsActive();
}
void VideoOverlayWindowViews::Close() {
views::Widget::Close();
MaybeUnregisterFrameSinkHierarchy();
if (fade_animator_) {
fade_animator_->CancelAndReset();
}
PictureInPictureWindowManager::GetInstance()->OnPictureInPictureWindowHidden(
this);
}
void VideoOverlayWindowViews::ShowInactive() {
#if BUILDFLAG(IS_WIN)
views::Widget::ShowInactive();
#else
if (base::FeatureList::IsEnabled(
media::kPictureInPictureShowWindowAnimation)) {
if (!fade_animator_) {
fade_animator_ = std::make_unique<PictureInPictureWidgetFadeAnimator>();
}
fade_animator_->AnimateShowWindow(
this,
PictureInPictureWidgetFadeAnimator::WidgetShowType::kShowInactive);
} else {
views::Widget::ShowInactive();
}
#endif
views::Widget::SetVisibleOnAllWorkspaces(true);
#if BUILDFLAG(IS_CHROMEOS)
non_client_view()->frame_view()->UpdateWindowRoundedCorners();
#endif
RemoveOverlayViewIfExists();
auto overlay_view =
get_overlay_view_cb_
? get_overlay_view_cb_.Run()
: PictureInPictureWindowManager::GetInstance()->GetOverlayView(
window_background_view_, views::BubbleBorder::Arrow::FLOAT);
if (overlay_view) {
overlay_view_ = GetContentsView()->AddChildView(std::move(overlay_view));
overlay_view_->views::View::AddObserver(this);
overlay_view_->set_delegate(this);
overlay_view_->SetBoundsRect(gfx::Rect(GetBounds().size()));
overlay_view_->ShowBubble(GetNativeView());
SetBounds(CalculateAndUpdateWindowBounds());
}
initial_title_hide_timer_.Start(
FROM_HERE, kTitleShowDuration,
base::BindOnce(&VideoOverlayWindowViews::OnInitialTitleTimerFired,
base::Unretained(this)));
UpdateControlsVisibility(false);
has_been_shown_ = true;
if (is_tucking_forced_ && !PictureInPictureWindowManager::GetInstance()
->IsPictureInPictureForceTucked()) {
SetForcedTucking(false);
}
PictureInPictureWindowManager::GetInstance()->OnPictureInPictureWindowShown(
this);
}
void VideoOverlayWindowViews::Hide() {
RemoveOverlayViewIfExists();
views::Widget::Hide();
if (fade_animator_) {
fade_animator_->CancelAndReset();
}
MaybeUnregisterFrameSinkHierarchy();
PictureInPictureWindowManager::GetInstance()->OnPictureInPictureWindowHidden(
this);
}
bool VideoOverlayWindowViews::IsVisible() const {
return views::Widget::IsVisible();
}
gfx::Rect VideoOverlayWindowViews::GetBounds() {
if (!native_widget()) {
return gfx::Rect();
}
return base::FeatureList::IsEnabled(media::kUseWindowBoundsForPip)
? GetWindowBoundsInScreen()
: GetRestoredBounds();
}
void VideoOverlayWindowViews::UpdateNaturalSize(const gfx::Size& natural_size) {
DCHECK(!natural_size.IsEmpty());
natural_size_ = natural_size;
SetAspectRatio(gfx::SizeF(natural_size_));
SetBounds(CalculateAndUpdateWindowBounds());
if (is_tucking_forced_) {
tucker_->Tuck();
}
}
void VideoOverlayWindowViews::SetPlaybackState(PlaybackState playback_state) {
playback_state_for_testing_ = playback_state;
play_pause_controls_view_->SetPlaybackState(playback_state);
}
void VideoOverlayWindowViews::SetPlayPauseButtonVisibility(bool is_visible) {
if (show_play_pause_button_ == is_visible) {
return;
}
show_play_pause_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetSkipAdButtonVisibility(bool is_visible) {
if (show_skip_ad_button_ == is_visible) {
return;
}
show_skip_ad_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetPreviousSlideButtonVisibility(
bool is_visible) {
if (show_previous_slide_button_ == is_visible) {
return;
}
show_previous_slide_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetNextSlideButtonVisibility(bool is_visible) {
if (show_next_slide_button_ == is_visible) {
return;
}
show_next_slide_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetNextTrackButtonVisibility(bool is_visible) {
if (show_next_track_button_ == is_visible) {
return;
}
show_next_track_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetPreviousTrackButtonVisibility(
bool is_visible) {
if (show_previous_track_button_ == is_visible) {
return;
}
show_previous_track_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetMicrophoneMuted(bool muted) {
toggle_microphone_button_->SetMutedState(muted);
}
void VideoOverlayWindowViews::SetCameraState(bool turned_on) {
toggle_camera_button_->SetCameraState(turned_on);
}
void VideoOverlayWindowViews::SetToggleMicrophoneButtonVisibility(
bool is_visible) {
if (show_toggle_microphone_button_ == is_visible) {
return;
}
show_toggle_microphone_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetToggleCameraButtonVisibility(bool is_visible) {
if (show_toggle_camera_button_ == is_visible) {
return;
}
show_toggle_camera_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetHangUpButtonVisibility(bool is_visible) {
if (show_hang_up_button_ == is_visible) {
return;
}
show_hang_up_button_ = is_visible;
UpdateControlsBounds();
}
void VideoOverlayWindowViews::SetMediaPosition(
const media_session::MediaPosition& position) {
position_ = position;
progress_view_->UpdateProgress(position);
UpdateTimestampLabel(position_.GetPosition(), position_.duration());
}
void VideoOverlayWindowViews::SetSourceTitle(
const std::u16string& source_title) {
origin_->SetText(source_title);
}
void VideoOverlayWindowViews::SetFaviconImages(
const std::vector<media_session::MediaImage>& images) {
media_session::MediaImageManager manager(gfx::kFaviconSize,
gfx::kFaviconSize);
std::optional<media_session::MediaImage> image = manager.SelectImage(images);
if (!image) {
UpdateFavicon(gfx::ImageSkia());
return;
}
controller_->GetMediaImage(
*image, gfx::kFaviconSize, gfx::kFaviconSize,
base::BindOnce(&VideoOverlayWindowViews::OnFaviconReceived,
weak_factory_.GetWeakPtr()));
}
void VideoOverlayWindowViews::SetSurfaceId(const viz::SurfaceId& surface_id) {
MaybeUnregisterFrameSinkHierarchy();
GetCompositor()->AddChildFrameSink(surface_id.frame_sink_id());
has_registered_frame_sink_hierarchy_ = true;
video_view_->layer()->SetShowSurface(
surface_id, GetBounds().size(),
GetColorProvider()->GetColor(kColorPipWindowBackground),
cc::DeadlinePolicy::UseDefaultDeadline(),
true );
}
void VideoOverlayWindowViews::OnNativeWidgetDestroying() {
views::Widget::OnNativeWidgetDestroying();
MaybeUnregisterFrameSinkHierarchy();
}
void VideoOverlayWindowViews::OnNativeWidgetDestroyed() {
views::Widget::OnNativeWidgetDestroyed();
controller_->OnWindowDestroyed(
show_play_pause_button_);
}
void VideoOverlayWindowViews::OnNativeWidgetAddedToCompositor() {
if (!has_registered_frame_sink_hierarchy_ && GetCurrentFrameSinkId()) {
GetCompositor()->AddChildFrameSink(*GetCurrentFrameSinkId());
has_registered_frame_sink_hierarchy_ = true;
}
}
void VideoOverlayWindowViews::OnNativeWidgetRemovingFromCompositor() {
MaybeUnregisterFrameSinkHierarchy();
}
void VideoOverlayWindowViews::OnGestureEvent(ui::GestureEvent* event) {
MaybeUpdateMeetsUserInteraction(*event);
hide_controls_timer_.Reset();
if (ShowControlsForGestureIfNecessary(event)) {
return;
}
if (HideLiveCaptionDialogForGestureIfNecessary(event)) {
return;
}
views::Widget::OnGestureEvent(event);
}
gfx::Rect VideoOverlayWindowViews::GetBackToTabControlsBounds() {
return back_to_tab_button_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetSkipAdControlsBounds() {
return gfx::Rect();
}
gfx::Rect VideoOverlayWindowViews::GetCloseControlsBounds() {
return close_controls_view_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetMinimizeControlsBounds() {
return minimize_button_->GetMirroredBounds();
}
#if BUILDFLAG(IS_CHROMEOS)
gfx::Rect VideoOverlayWindowViews::GetResizeHandleControlsBounds() {
return resize_handle_view_->GetMirroredBounds();
}
#endif
gfx::Rect VideoOverlayWindowViews::GetPlayPauseControlsBounds() {
return play_pause_controls_view_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetReplay10SecondsButtonBounds() {
return replay_10_seconds_button_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetForward10SecondsButtonBounds() {
return forward_10_seconds_button_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetNextTrackControlsBounds() {
return next_track_controls_view_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetPreviousTrackControlsBounds() {
return previous_track_controls_view_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetToggleMicrophoneButtonBounds() {
return toggle_microphone_button_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetToggleCameraButtonBounds() {
return toggle_camera_button_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetHangUpButtonBounds() {
return hang_up_button_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetProgressViewBounds() {
return progress_view_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetLiveCaptionButtonBounds() {
return live_caption_button_->GetMirroredBounds();
}
gfx::Rect VideoOverlayWindowViews::GetLiveCaptionDialogBounds() {
if (!live_caption_dialog_->GetVisible()) {
return gfx::Rect();
}
return live_caption_dialog_->GetMirroredBounds();
}
bool VideoOverlayWindowViews::HasHighMediaEngagement(
const url::Origin& origin) const {
MediaEngagementService* service =
MediaEngagementService::Get(Profile::FromBrowserContext(
GetController()->GetWebContents()->GetBrowserContext()));
if (!service) {
return false;
}
return service->HasHighEngagement(origin);
}
bool VideoOverlayWindowViews::IsTrustedForMediaPlayback() const {
content::MediaSession* media_session =
content::MediaSession::GetIfExists(GetController()->GetWebContents());
if (!media_session) {
return false;
}
content::RenderFrameHost* rfh = media_session->GetRoutedFrame();
if (rfh == nullptr) {
return false;
}
if (!rfh->IsInPrimaryMainFrame()) {
return false;
}
const url::Origin origin = rfh->GetLastCommittedOrigin();
if (origin.GetURL().SchemeIsFile()) {
return true;
}
return HasHighMediaEngagement(origin);
}
#if BUILDFLAG(IS_CHROMEOS)
int VideoOverlayWindowViews::GetResizeHTComponent() const {
return resize_handle_view_->GetHTComponent();
}
#endif
void VideoOverlayWindowViews::TogglePlayPause() {
bool is_active = controller_->TogglePlayPause();
play_pause_controls_view_->SetPlaybackState(is_active ? kPlaying : kPaused);
}
void VideoOverlayWindowViews::Replay10Seconds() {
controller_->SeekTo(
std::max(base::Seconds(0), position_.GetPosition() - kSeekTime));
}
void VideoOverlayWindowViews::Forward10Seconds() {
controller_->SeekTo(
std::min(position_.GetPosition() + kSeekTime, position_.duration()));
}
void VideoOverlayWindowViews::CloseAndPauseIfAvailable() {
const bool should_pause_video = !!show_play_pause_button_;
PictureInPictureWindowManager::GetInstance()->ExitPictureInPictureViaWindowUi(
should_pause_video
? PictureInPictureWindowManager::UiBehavior::kCloseWindowAndPauseVideo
: PictureInPictureWindowManager::UiBehavior::kCloseWindowOnly);
}
PlaybackImageButton*
VideoOverlayWindowViews::play_pause_controls_view_for_testing() const {
return play_pause_controls_view_;
}
SimpleOverlayWindowImageButton*
VideoOverlayWindowViews::replay_10_seconds_button_for_testing() const {
return replay_10_seconds_button_;
}
SimpleOverlayWindowImageButton*
VideoOverlayWindowViews::forward_10_seconds_button_for_testing() const {
return forward_10_seconds_button_;
}
SimpleOverlayWindowImageButton*
VideoOverlayWindowViews::next_track_controls_view_for_testing() const {
return next_track_controls_view_;
}
SimpleOverlayWindowImageButton*
VideoOverlayWindowViews::previous_track_controls_view_for_testing() const {
return previous_track_controls_view_;
}
SkipAdLabelButton* VideoOverlayWindowViews::skip_ad_controls_view_for_testing()
const {
return skip_ad_controls_view_;
}
ToggleMicrophoneButton*
VideoOverlayWindowViews::toggle_microphone_button_for_testing() const {
return toggle_microphone_button_;
}
ToggleCameraButton* VideoOverlayWindowViews::toggle_camera_button_for_testing()
const {
return toggle_camera_button_;
}
HangUpButton* VideoOverlayWindowViews::hang_up_button_for_testing() const {
return hang_up_button_;
}
global_media_controls::MediaProgressView*
VideoOverlayWindowViews::progress_view_for_testing() const {
return progress_view_;
}
views::Label* VideoOverlayWindowViews::timestamp_for_testing() const {
return timestamp_;
}
views::Label* VideoOverlayWindowViews::live_status_for_testing() const {
return live_status_;
}
OverlayWindowLiveCaptionButton*
VideoOverlayWindowViews::live_caption_button_for_testing() const {
return live_caption_button_;
}
OverlayWindowLiveCaptionDialog*
VideoOverlayWindowViews::live_caption_dialog_for_testing() const {
return live_caption_dialog_;
}
views::ImageView* VideoOverlayWindowViews::favicon_view_for_testing() const {
return favicon_view_;
}
views::Label* VideoOverlayWindowViews::origin_for_testing() const {
return origin_;
}
CloseImageButton* VideoOverlayWindowViews::close_button_for_testing() const {
return close_controls_view_;
}
OverlayWindowMinimizeButton*
VideoOverlayWindowViews::minimize_button_for_testing() const {
return minimize_button_;
}
OverlayWindowBackToTabButton*
VideoOverlayWindowViews::back_to_tab_button_for_testing() const {
return back_to_tab_button_;
}
gfx::Point VideoOverlayWindowViews::close_image_position_for_testing() const {
return close_controls_view_->origin();
}
#if BUILDFLAG(IS_CHROMEOS)
gfx::Point VideoOverlayWindowViews::resize_handle_position_for_testing() const {
return resize_handle_view_->origin();
}
#endif
VideoOverlayWindowViews::PlaybackState
VideoOverlayWindowViews::playback_state_for_testing() const {
return playback_state_for_testing_;
}
ui::Layer* VideoOverlayWindowViews::video_layer_for_testing() const {
return video_view_->layer();
}
views::View* VideoOverlayWindowViews::title_view_for_testing() const {
return title_view_;
}
views::View* VideoOverlayWindowViews::controls_top_scrim_view_for_testing()
const {
return controls_top_scrim_view_;
}
base::OneShotTimer&
VideoOverlayWindowViews::initial_title_hide_timer_for_testing() {
return initial_title_hide_timer_;
}
const viz::FrameSinkId* VideoOverlayWindowViews::GetCurrentFrameSinkId() const {
if (auto* surface = video_view_->layer()->GetSurfaceId()) {
return &surface->frame_sink_id();
}
return nullptr;
}
void VideoOverlayWindowViews::MaybeUnregisterFrameSinkHierarchy() {
if (has_registered_frame_sink_hierarchy_) {
DCHECK(GetCurrentFrameSinkId());
GetCompositor()->RemoveChildFrameSink(*GetCurrentFrameSinkId());
has_registered_frame_sink_hierarchy_ = false;
}
}
bool VideoOverlayWindowViews::IsOverlayViewShown() const {
return overlay_view_ && overlay_view_->GetVisible();
}
void VideoOverlayWindowViews::RemoveOverlayViewIfExists() {
if (overlay_view_) {
overlay_view_->RemoveObserver(this);
GetContentsView()->RemoveChildViewT(overlay_view_.ExtractAsDangling());
OnSizeConstraintsChanged();
}
}
void VideoOverlayWindowViews::OnProgressDragStateChanged(
global_media_controls::DragState drag_state) {
progress_view_drag_state_ = drag_state;
OnUpdateControlsBounds();
}
void VideoOverlayWindowViews::ChangePlaybackStateForProgressDrag(
global_media_controls::PlaybackStateChangeForDragging
playback_state_change) {
if (playback_state_change ==
global_media_controls::PlaybackStateChangeForDragging::
kPauseForDraggingStarted) {
controller_->Pause();
} else {
controller_->Play();
}
}
void VideoOverlayWindowViews::SeekForProgressBarInteraction(
double seek_progress) {
controller_->SeekTo(seek_progress * position_.duration());
}
void VideoOverlayWindowViews::OnProgressViewUpdateCurrentTime(
base::TimeDelta current_time) {
UpdateTimestampLabel(current_time, position_.duration());
}
void VideoOverlayWindowViews::UpdateTimestampLabel(base::TimeDelta current_time,
base::TimeDelta duration) {
bool was_live = is_live_;
is_live_ = duration.is_max();
if (!is_live_) {
timestamp_->SetText(base::StrCat(
{global_media_controls::GetFormattedDuration(current_time), u" / ",
global_media_controls::GetFormattedDuration(duration)}));
}
if (was_live != is_live_) {
OnUpdateControlsBounds();
}
}
void VideoOverlayWindowViews::OnLiveCaptionButtonPressed() {
SetLiveCaptionDialogVisibility(!live_caption_dialog_->GetVisible());
}
void VideoOverlayWindowViews::SetLiveCaptionDialogVisibility(
bool wanted_visibility) {
if (wanted_visibility == live_caption_dialog_->GetVisible()) {
return;
}
live_caption_dialog_->SetVisible(wanted_visibility);
live_caption_button_->SetIsLiveCaptionDialogOpen(wanted_visibility);
views::View* controls_to_be_disabled_when_live_caption_is_open[] = {
minimize_button_.get(),
back_to_tab_button_.get(),
close_controls_view_.get(),
replay_10_seconds_button_.get(),
play_pause_controls_view_.get(),
forward_10_seconds_button_.get(),
previous_track_controls_view_.get(),
progress_view_.get(),
next_track_controls_view_.get(),
toggle_camera_button_.get(),
toggle_microphone_button_.get(),
hang_up_button_.get()};
for (auto* control : controls_to_be_disabled_when_live_caption_is_open) {
control->SetEnabled(!wanted_visibility);
}
}
void VideoOverlayWindowViews::OnFaviconReceived(const SkBitmap& image) {
UpdateFavicon(GetCorrectColorTypeImage(image));
}
void VideoOverlayWindowViews::UpdateFavicon(const gfx::ImageSkia& favicon) {
if (favicon.isNull()) {
favicon_view_->SetImage(ui::ImageModel::FromVectorIcon(
vector_icons::kGlobeIcon, ui::kColorSysOnSurface,
kFaviconIconSize.width()));
} else {
favicon_view_->SetImageSize(
ScaleImageSizeToFitView(favicon.size(), kFaviconIconSize));
favicon_view_->SetImage(ui::ImageModel::FromImageSkia(favicon));
}
}
void VideoOverlayWindowViews::OnInitialTitleTimerFired() {
if (user_interacted_before_timer_fired_) {
meets_user_interaction_ = true;
}
UpdateControlsVisibility(false);
}
bool VideoOverlayWindowViews::AreTitleAndScrimVisible() const {
if (title_and_top_scrim_fade_animation_) {
return (title_and_top_scrim_fade_animation_->type() ==
OverlayControlsFadeAnimation::Type::kToShown);
}
DCHECK_EQ(GetTitleView()->layer()->opacity(),
GetControlsTopScrimView()->layer()->opacity());
return GetTitleView()->layer()->opacity() > 0;
}
void VideoOverlayWindowViews::MaybeUpdateMeetsUserInteraction(
const ui::Event& event) {
if (meets_user_interaction_) {
return;
}
if (event.type() != ui::EventType::kKeyPressed &&
event.type() != ui::EventType::kGestureTap &&
event.type() != ui::EventType::kMousePressed) {
return;
}
if (initial_title_hide_timer_.IsRunning()) {
user_interacted_before_timer_fired_ = true;
} else {
meets_user_interaction_ = true;
}
}