Remove DocumentOverlayWindowViews
This class used to support Document Picture in Picture, but has been replaced by a custom browser frame instead. This CL removes it. Change-Id: Ib89044c96dba403f3e87a09071425300e98c1a85 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3988524 Reviewed-by: Fr <beaufort.francois@gmail.com> Reviewed-by: Avi Drissman <avi@chromium.org> Commit-Queue: Frank Liberato <liberato@chromium.org> Cr-Commit-Position: refs/heads/main@{#1064874}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
f0690e8201
commit
db9980ce11
chrome
content/public/browser
@ -6019,18 +6019,6 @@ ChromeContentBrowserClient::CreateWindowForVideoPictureInPicture(
|
||||
return content::VideoOverlayWindow::Create(controller);
|
||||
}
|
||||
|
||||
std::unique_ptr<content::DocumentOverlayWindow>
|
||||
ChromeContentBrowserClient::CreateWindowForDocumentPictureInPicture(
|
||||
content::DocumentPictureInPictureWindowController* controller) {
|
||||
// Note: content::DocumentOverlayWindow::Create() is defined by
|
||||
// platform-specific implementation in chrome/browser/ui/views. This layering
|
||||
// hack, which goes through //content and ContentBrowserClient, allows us to
|
||||
// work around the dependency constraints that disallow directly calling
|
||||
// chrome/browser/ui/views code either from here or from other code in
|
||||
// chrome/browser.
|
||||
return content::DocumentOverlayWindow::Create(controller);
|
||||
}
|
||||
|
||||
void ChromeContentBrowserClient::RegisterRendererPreferenceWatcher(
|
||||
content::BrowserContext* browser_context,
|
||||
mojo::PendingRemote<blink::mojom::RendererPreferenceWatcher> watcher) {
|
||||
|
@ -654,9 +654,6 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
|
||||
std::unique_ptr<content::VideoOverlayWindow>
|
||||
CreateWindowForVideoPictureInPicture(
|
||||
content::VideoPictureInPictureWindowController* controller) override;
|
||||
std::unique_ptr<content::DocumentOverlayWindow>
|
||||
CreateWindowForDocumentPictureInPicture(
|
||||
content::DocumentPictureInPictureWindowController* controller) override;
|
||||
void RegisterRendererPreferenceWatcher(
|
||||
content::BrowserContext* browser_context,
|
||||
mojo::PendingRemote<blink::mojom::RendererPreferenceWatcher> watcher)
|
||||
|
@ -1,7 +1,4 @@
|
||||
specific_include_rules = {
|
||||
"document_picture_in_picture_window_controller_browsertest\.cc": [
|
||||
"+chrome/browser/ui/views/overlay/document_overlay_window_views.h",
|
||||
],
|
||||
"video_picture_in_picture_window_controller_browsertest\.cc": [
|
||||
"+chrome/browser/ui/views/overlay/hang_up_button.h",
|
||||
"+chrome/browser/ui/views/overlay/overlay_window_views.h",
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "chrome/browser/ui/browser.h"
|
||||
#include "chrome/browser/ui/browser_commands.h"
|
||||
#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
||||
#include "chrome/browser/ui/views/overlay/document_overlay_window_views.h"
|
||||
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
|
||||
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
|
||||
#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
|
||||
|
@ -4688,8 +4688,6 @@ static_library("ui") {
|
||||
"views/overlay/close_image_button.cc",
|
||||
"views/overlay/close_image_button.h",
|
||||
"views/overlay/constants.h",
|
||||
"views/overlay/document_overlay_window_views.cc",
|
||||
"views/overlay/document_overlay_window_views.h",
|
||||
"views/overlay/hang_up_button.cc",
|
||||
"views/overlay/hang_up_button.h",
|
||||
"views/overlay/overlay_window_image_button.cc",
|
||||
|
@ -1,640 +0,0 @@
|
||||
// Copyright 2022 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/views/overlay/document_overlay_window_views.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/app/vector_icons/vector_icons.h"
|
||||
#include "chrome/browser/command_updater_delegate.h"
|
||||
#include "chrome/browser/command_updater_impl.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/themes/theme_service.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/toolbar/chrome_location_bar_model_delegate.h"
|
||||
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
|
||||
#include "chrome/browser/ui/views/overlay/back_to_tab_image_button.h"
|
||||
#include "chrome/browser/ui/views/overlay/close_image_button.h"
|
||||
#include "chrome/browser/ui/views/overlay/resize_handle_button.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "components/omnibox/browser/location_bar_model_impl.h"
|
||||
#include "components/vector_icons/vector_icons.h"
|
||||
#include "content/public/browser/document_picture_in_picture_window_controller.h"
|
||||
#include "content/public/browser/picture_in_picture_window_controller.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/content_constants.h"
|
||||
#include "media/base/media_switches.h"
|
||||
#include "media/base/video_util.h"
|
||||
#include "third_party/skia/include/core/SkColor.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/compositor/layer.h"
|
||||
#include "ui/gfx/color_palette.h"
|
||||
#include "ui/gfx/geometry/resize_utils.h"
|
||||
#include "ui/gfx/paint_vector_icon.h"
|
||||
#include "ui/views/controls/button/image_button.h"
|
||||
#include "ui/views/controls/webview/webview.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
#include "ui/views/vector_icons.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
#include "ash/public/cpp/ash_constants.h"
|
||||
#include "ash/public/cpp/rounded_corner_utils.h"
|
||||
#include "ash/public/cpp/window_properties.h" // nogncheck
|
||||
#include "ui/aura/window.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "chrome/browser/shell_integration_win.h"
|
||||
#include "ui/aura/window.h"
|
||||
#include "ui/aura/window_tree_host.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_LACROS)
|
||||
#include "ui/aura/window_tree_host.h"
|
||||
#include "ui/platform_window/extensions/wayland_extension.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
// The opacity of the resize handle control.
|
||||
constexpr double kResizeHandleOpacity = 0.38;
|
||||
#endif
|
||||
|
||||
// The height of the controls bar at the top of the window.
|
||||
// This includes the location bar and the close/back-to-tab buttons.
|
||||
constexpr int kTopControlsHeight = 30;
|
||||
|
||||
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 {
|
||||
public:
|
||||
METADATA_HEADER(WindowBackgroundView);
|
||||
|
||||
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, views::View)
|
||||
END_METADATA
|
||||
|
||||
} // namespace
|
||||
|
||||
OverlayLocationBarViewProxy::~OverlayLocationBarViewProxy() = default;
|
||||
|
||||
class OverlayLocationBarViewImpl : public OverlayLocationBarViewProxy,
|
||||
public ChromeLocationBarModelDelegate,
|
||||
public LocationBarView::Delegate,
|
||||
public CommandUpdaterDelegate {
|
||||
public:
|
||||
OverlayLocationBarViewImpl(Profile* profile,
|
||||
content::WebContents* web_contents)
|
||||
: web_contents_(web_contents),
|
||||
location_bar_model_(std::make_unique<LocationBarModelImpl>(
|
||||
this,
|
||||
content::kMaxURLDisplayChars)),
|
||||
command_updater_(this) {
|
||||
view_holder_ = std::make_unique<LocationBarView>(
|
||||
/*browser=*/nullptr, profile, &command_updater_, this, true);
|
||||
view_ = view_holder_.get();
|
||||
}
|
||||
~OverlayLocationBarViewImpl() override = default;
|
||||
void Init() override { view_->Init(); }
|
||||
std::unique_ptr<views::View> ReleaseView() override {
|
||||
return std::move(view_holder_);
|
||||
}
|
||||
|
||||
// CommandUpdaterDelegate
|
||||
void ExecuteCommandWithDisposition(int id, WindowOpenDisposition disposition)
|
||||
override {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
// ChromeLocationBarModelDelegate
|
||||
content::WebContents* GetActiveWebContents() const final {
|
||||
return web_contents_;
|
||||
}
|
||||
|
||||
// LocationBarView::Delegate
|
||||
content::WebContents* GetWebContents() override { return web_contents_; }
|
||||
LocationBarModel* GetLocationBarModel() override {
|
||||
return location_bar_model_.get();
|
||||
}
|
||||
const LocationBarModel* GetLocationBarModel() const override {
|
||||
return location_bar_model_.get();
|
||||
}
|
||||
ContentSettingBubbleModelDelegate* GetContentSettingBubbleModelDelegate()
|
||||
override {
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
raw_ptr<content::WebContents> web_contents_;
|
||||
std::unique_ptr<LocationBarView> view_holder_;
|
||||
raw_ptr<LocationBarView> view_;
|
||||
#if 0
|
||||
raw_ptr<DocumentOverlayWindowViews> overlay_;
|
||||
#endif
|
||||
const std::unique_ptr<LocationBarModelImpl> location_bar_model_;
|
||||
CommandUpdaterImpl command_updater_;
|
||||
};
|
||||
|
||||
// static
|
||||
std::unique_ptr<DocumentOverlayWindowViews> DocumentOverlayWindowViews::Create(
|
||||
content::DocumentPictureInPictureWindowController* controller,
|
||||
std::unique_ptr<OverlayLocationBarViewProxy>
|
||||
location_bar_view_proxy_for_testing) {
|
||||
DVLOG(1) << __func__ << ": DocumentOverlayWindowViews::Create";
|
||||
// Can't use make_unique(), which doesn't have access to the private
|
||||
// constructor. It's important that the constructor be private, because it
|
||||
// doesn't initialize the object fully.
|
||||
auto overlay_window =
|
||||
base::WrapUnique(new DocumentOverlayWindowViews(controller));
|
||||
|
||||
if (location_bar_view_proxy_for_testing) {
|
||||
overlay_window->set_location_bar_view_proxy(
|
||||
std::move(location_bar_view_proxy_for_testing));
|
||||
}
|
||||
|
||||
overlay_window->CalculateAndUpdateWindowBounds();
|
||||
overlay_window->SetUpViews();
|
||||
|
||||
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
|
||||
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
||||
// Just to have any non-empty bounds as required by Init(). The window is
|
||||
// resized to fit the WebView that is embedded right afterwards, anyway.
|
||||
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 = OverlayWindowViews::CreateDelegate();
|
||||
|
||||
overlay_window->Init(std::move(params));
|
||||
overlay_window->OnRootViewReady();
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
std::wstring app_user_model_id;
|
||||
Browser* browser =
|
||||
chrome::FindBrowserWithWebContents(controller->GetWebContents());
|
||||
if (browser) {
|
||||
const base::FilePath& profile_path = browser->profile()->GetPath();
|
||||
// Set the window app id to GetAppUserModelIdForApp if the original window
|
||||
// is an app window, GetAppUserModelIdForBrowser if it's a browser window.
|
||||
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());
|
||||
}
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
// Set the controls to be permanently visible. This means that calls such as
|
||||
// UpdateControlsVisibility(false) will have no effect, the controls remain
|
||||
// visible.
|
||||
overlay_window->ForceControlsVisible(true);
|
||||
|
||||
return overlay_window;
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<content::DocumentOverlayWindow>
|
||||
content::DocumentOverlayWindow::Create(
|
||||
content::DocumentPictureInPictureWindowController* controller) {
|
||||
return DocumentOverlayWindowViews::Create(controller);
|
||||
}
|
||||
|
||||
DocumentOverlayWindowViews::DocumentOverlayWindowViews(
|
||||
content::DocumentPictureInPictureWindowController* controller)
|
||||
: controller_(controller) {}
|
||||
|
||||
DocumentOverlayWindowViews::~DocumentOverlayWindowViews() = default;
|
||||
|
||||
bool DocumentOverlayWindowViews::ControlsHitTestContainsPoint(
|
||||
const gfx::Point& point) {
|
||||
if (web_view_->GetMirroredBounds().Contains(point)) {
|
||||
// Always allow interactions with the WebView
|
||||
return true;
|
||||
}
|
||||
if (!AreControlsVisible())
|
||||
return false;
|
||||
if (GetBackToTabControlsBounds().Contains(point) ||
|
||||
GetCloseControlsBounds().Contains(point)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
content::PictureInPictureWindowController*
|
||||
DocumentOverlayWindowViews::GetController() const {
|
||||
return controller_;
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::set_location_bar_view_proxy(
|
||||
std::unique_ptr<OverlayLocationBarViewProxy> proxy) {
|
||||
location_bar_view_proxy_ = std::move(proxy);
|
||||
}
|
||||
|
||||
views::View* DocumentOverlayWindowViews::GetWindowBackgroundView() const {
|
||||
return window_background_view_;
|
||||
}
|
||||
|
||||
views::View* DocumentOverlayWindowViews::GetControlsContainerView() const {
|
||||
return controls_container_view_;
|
||||
}
|
||||
|
||||
const ui::ThemeProvider* DocumentOverlayWindowViews::GetThemeProvider() const {
|
||||
// FIXME: is there a way to use a dark theme just for this window?
|
||||
DCHECK(profile_for_theme_);
|
||||
return &ThemeService::GetThemeProviderForProfile(profile_for_theme_);
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::SetUpViews() {
|
||||
// The window content consists of the fixed-height controls_container_view at
|
||||
// the top which is a box layout, and the remainder of the view is filled with
|
||||
// the content web view. ChromeOS adds a resize handle and changes the order,
|
||||
// see UpdateResizeHandleBounds(quadrant) for more information.
|
||||
//
|
||||
// +----------------------+------+-------+
|
||||
// | location bar | back | close |
|
||||
// +----------------------+------+-------+
|
||||
// | |
|
||||
// | web view |
|
||||
// | |
|
||||
// +-------------------------------------+
|
||||
|
||||
content::WebContents* pip_contents = controller_->GetChildWebContents();
|
||||
auto* profile =
|
||||
Profile::FromBrowserContext(pip_contents->GetBrowserContext());
|
||||
profile_for_theme_ = profile;
|
||||
|
||||
// In testing, the location bar view proxy is provided via Create.
|
||||
// In production, it's created here.
|
||||
if (!location_bar_view_proxy_) {
|
||||
location_bar_view_proxy_ = std::make_unique<OverlayLocationBarViewImpl>(
|
||||
profile, controller_->GetWebContents());
|
||||
}
|
||||
|
||||
auto web_view = std::make_unique<views::WebView>(profile);
|
||||
DVLOG(2) << __func__ << ": content WebView=" << web_view.get();
|
||||
web_view->SetWebContents(pip_contents);
|
||||
|
||||
// View that is displayed when WebView is hidden. ----------------------------
|
||||
// Adding an extra pixel to width/height makes sure controls background cover
|
||||
// entirely window when platform has fractional scale applied.
|
||||
auto window_background_view = std::make_unique<WindowBackgroundView>();
|
||||
|
||||
auto controls_container_view = std::make_unique<views::View>();
|
||||
|
||||
auto controls_box_owner = std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kHorizontal);
|
||||
controls_box_owner->set_cross_axis_alignment(
|
||||
views::BoxLayout::CrossAxisAlignment::kCenter);
|
||||
auto* controls_box =
|
||||
controls_container_view->SetLayoutManager(std::move(controls_box_owner));
|
||||
|
||||
auto close_controls_view =
|
||||
std::make_unique<CloseImageButton>(base::BindRepeating(
|
||||
[](DocumentOverlayWindowViews* overlay) {
|
||||
const bool should_pause = true;
|
||||
overlay->controller_->Close(should_pause);
|
||||
},
|
||||
base::Unretained(this)));
|
||||
|
||||
std::unique_ptr<BackToTabImageButton> back_to_tab_image_button;
|
||||
auto back_to_tab_callback = base::BindRepeating(
|
||||
[](DocumentOverlayWindowViews* overlay) {
|
||||
overlay->controller_->CloseAndFocusInitiator();
|
||||
},
|
||||
base::Unretained(this));
|
||||
back_to_tab_image_button =
|
||||
std::make_unique<BackToTabImageButton>(std::move(back_to_tab_callback));
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
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");
|
||||
|
||||
// view::View that holds the WebView. ---------------------------------------
|
||||
web_view->SetPaintToLayer(ui::LAYER_TEXTURED);
|
||||
web_view->layer()->SetMasksToBounds(true);
|
||||
web_view->layer()->SetFillsBoundsOpaquely(false);
|
||||
web_view->layer()->SetName("WebView");
|
||||
|
||||
// views::View that is a parent of all the controls. Makes hiding and showing
|
||||
// all the controls at once easier.
|
||||
controls_container_view->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
|
||||
controls_container_view->layer()->SetFillsBoundsOpaquely(false);
|
||||
controls_container_view->layer()->SetName("ControlsContainerView");
|
||||
|
||||
// views::View that closes the window. --------------------------------------
|
||||
close_controls_view->SetPaintToLayer(ui::LAYER_TEXTURED);
|
||||
close_controls_view->layer()->SetFillsBoundsOpaquely(false);
|
||||
close_controls_view->layer()->SetName("CloseControlsView");
|
||||
|
||||
// views::View that closes the window and focuses initiator tab. ------------
|
||||
back_to_tab_image_button->SetPaintToLayer(ui::LAYER_TEXTURED);
|
||||
back_to_tab_image_button->layer()->SetFillsBoundsOpaquely(false);
|
||||
back_to_tab_image_button->layer()->SetName("BackToTabControlsView");
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
// views::View that shows the affordance that the window can be resized. ----
|
||||
resize_handle_view->SetPaintToLayer(ui::LAYER_TEXTURED);
|
||||
resize_handle_view->layer()->SetFillsBoundsOpaquely(false);
|
||||
resize_handle_view->layer()->SetName("ResizeHandleView");
|
||||
resize_handle_view->layer()->SetOpacity(kResizeHandleOpacity);
|
||||
#endif
|
||||
|
||||
// Set up view::Views hierarchy. --------------------------------------------
|
||||
window_background_view_ =
|
||||
AddChildView(&view_holder_, std::move(window_background_view));
|
||||
web_view_ = AddChildView(&view_holder_, std::move(web_view));
|
||||
|
||||
location_bar_view_ = controls_container_view->AddChildView(
|
||||
location_bar_view_proxy_->ReleaseView());
|
||||
controls_box->SetFlexForView(location_bar_view_, 1);
|
||||
|
||||
back_to_tab_image_button_ = controls_container_view->AddChildView(
|
||||
std::move(back_to_tab_image_button));
|
||||
controls_box->SetFlexForView(back_to_tab_image_button_, 0);
|
||||
|
||||
close_controls_view_ =
|
||||
controls_container_view->AddChildView(std::move(close_controls_view));
|
||||
controls_box->SetFlexForView(close_controls_view_, 0);
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
resize_handle_view_ =
|
||||
controls_container_view->AddChildView(std::move(resize_handle_view));
|
||||
controls_box->SetFlexForView(resize_handle_view_, 0);
|
||||
#endif
|
||||
|
||||
controls_container_view_ =
|
||||
AddChildView(&view_holder_, std::move(controls_container_view));
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::OnRootViewReady() {
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
GetNativeWindow()->SetProperty(ash::kWindowPipTypeKey, true);
|
||||
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
|
||||
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();
|
||||
|
||||
location_bar_view_proxy_->Init();
|
||||
location_bar_view_->SetVisible(true);
|
||||
|
||||
// Don't show the controls until the mouse hovers over the window.
|
||||
UpdateControlsVisibility(false);
|
||||
|
||||
// FIXME, get aspect/size via PiP API
|
||||
UpdateNaturalSize({400, 300});
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::UpdateLayerBoundsWithLetterboxing(
|
||||
gfx::Size window_size) {
|
||||
// This is the case when the window is initially created or the video surface
|
||||
// id has not been embedded.
|
||||
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;
|
||||
|
||||
// To avoid black stripes in the window when integer window dimensions don't
|
||||
// correspond to the content aspect ratio exactly (e.g. 854x480 for 16:9
|
||||
// video) force the letterbox region size to be equal to the window size.
|
||||
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 content_bounds(
|
||||
gfx::Point((window_size.width() - letterbox_region.size().width()) / 2,
|
||||
(window_size.height() - letterbox_region.size().height()) / 2),
|
||||
letterbox_region.size());
|
||||
|
||||
// Update the layout of the controls. (Do this immediately, bypassing
|
||||
// UpdateControlsBounds from the parent class.)
|
||||
OnUpdateControlsBounds();
|
||||
|
||||
// Update the surface layer bounds to scale with window size changes.
|
||||
window_background_view_->SetBoundsRect(
|
||||
gfx::Rect(gfx::Point(0, 0), GetBounds().size()));
|
||||
gfx::Rect webview_bounds(
|
||||
content_bounds.x(), content_bounds.y() + kTopControlsHeight,
|
||||
content_bounds.width(), content_bounds.height() - kTopControlsHeight);
|
||||
web_view_->SetBoundsRect(webview_bounds);
|
||||
if (web_view_->layer()->has_external_content())
|
||||
web_view_->layer()->SetSurfaceSize(webview_bounds.size());
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::OnUpdateControlsBounds() {
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
OverlayWindowViews::WindowQuadrant quadrant =
|
||||
OverlayWindowViews::GetCurrentWindowQuadrant(GetBounds(), controller_);
|
||||
UpdateResizeHandleBounds(quadrant);
|
||||
#endif
|
||||
|
||||
controls_container_view_->SetSize(
|
||||
{GetBounds().size().width(), kTopControlsHeight});
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
void DocumentOverlayWindowViews::UpdateResizeHandleBounds(
|
||||
OverlayWindowViews::WindowQuadrant quadrant) {
|
||||
//
|
||||
if (quadrant == WindowQuadrant::kBottomLeft ||
|
||||
quadrant == WindowQuadrant::kTopLeft) {
|
||||
// Controls layout when the window is in the left half of the screen:
|
||||
//
|
||||
// +-------+------+-------------------+--------+
|
||||
// | close | back | location bar | resize |
|
||||
// +-------+------+-------------------+--------+
|
||||
// | web view |
|
||||
// +-------------------------------------------+
|
||||
if (controls_container_view_->GetIndexOf(close_controls_view_) != 0u) {
|
||||
resize_handle_view_->SetQuadrant(quadrant);
|
||||
controls_container_view_->ReorderChildView(close_controls_view_, 0);
|
||||
controls_container_view_->ReorderChildView(back_to_tab_image_button_, 1);
|
||||
controls_container_view_->ReorderChildView(
|
||||
resize_handle_view_, controls_container_view_->children().size());
|
||||
// FIXME: controls_container_view_->InvalidateLayout() isn't sufficient?
|
||||
controls_container_view_->Layout();
|
||||
}
|
||||
} else {
|
||||
// Controls layout when the window is in the right half of the screen:
|
||||
//
|
||||
// +--------+-------------------+------+-------+
|
||||
// | resize | location bar | back | close |
|
||||
// +--------+-------------------+------+-------+
|
||||
// | web view |
|
||||
// +-------------------------------------------+
|
||||
if (controls_container_view_->GetIndexOf(resize_handle_view_) != 0u) {
|
||||
resize_handle_view_->SetQuadrant(quadrant);
|
||||
controls_container_view_->ReorderChildView(
|
||||
back_to_tab_image_button_,
|
||||
controls_container_view_->children().size());
|
||||
controls_container_view_->ReorderChildView(
|
||||
close_controls_view_, controls_container_view_->children().size());
|
||||
controls_container_view_->ReorderChildView(resize_handle_view_, 0);
|
||||
// FIXME: controls_container_view_->InvalidateLayout() isn't sufficient?
|
||||
controls_container_view_->Layout();
|
||||
}
|
||||
}
|
||||
GetNativeWindow()->SetProperty(
|
||||
ash::kWindowPipResizeHandleBoundsKey,
|
||||
new gfx::Rect(GetResizeHandleControlsBounds()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool DocumentOverlayWindowViews::IsActive() {
|
||||
return views::Widget::IsActive();
|
||||
}
|
||||
|
||||
bool DocumentOverlayWindowViews::IsActive() const {
|
||||
return views::Widget::IsActive();
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::Close() {
|
||||
views::Widget::Close();
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::ShowInactive() {
|
||||
DoShowInactive();
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::Hide() {
|
||||
views::Widget::Hide();
|
||||
}
|
||||
|
||||
bool DocumentOverlayWindowViews::IsVisible() {
|
||||
return views::Widget::IsVisible();
|
||||
}
|
||||
|
||||
bool DocumentOverlayWindowViews::IsVisible() const {
|
||||
return views::Widget::IsVisible();
|
||||
}
|
||||
|
||||
bool DocumentOverlayWindowViews::IsAlwaysOnTop() {
|
||||
return true;
|
||||
}
|
||||
|
||||
gfx::Rect DocumentOverlayWindowViews::GetBounds() {
|
||||
return views::Widget::GetRestoredBounds();
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::UpdateNaturalSize(
|
||||
const gfx::Size& natural_size) {
|
||||
DoUpdateNaturalSize(natural_size);
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::OnNativeWidgetMove() {
|
||||
OverlayWindowViews::OnNativeWidgetMove();
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
// Update the positioning of some icons when the window is moved.
|
||||
WindowQuadrant quadrant =
|
||||
GetCurrentWindowQuadrant(GetRestoredBounds(), GetController());
|
||||
UpdateResizeHandleBounds(quadrant);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::OnNativeWidgetDestroyed() {
|
||||
views::Widget::OnNativeWidgetDestroyed();
|
||||
controller_->OnWindowDestroyed(
|
||||
/*should_pause_video=*/true);
|
||||
}
|
||||
|
||||
void DocumentOverlayWindowViews::OnGestureEvent(ui::GestureEvent* event) {
|
||||
if (OverlayWindowViews::OnGestureEventHandledOrIgnored(event))
|
||||
return;
|
||||
|
||||
if (GetBackToTabControlsBounds().Contains(event->location())) {
|
||||
controller_->CloseAndFocusInitiator();
|
||||
event->SetHandled();
|
||||
} else if (GetCloseControlsBounds().Contains(event->location())) {
|
||||
controller_->Close(/*should_pause_video=*/true);
|
||||
event->SetHandled();
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Rect DocumentOverlayWindowViews::GetBackToTabControlsBounds() {
|
||||
return back_to_tab_image_button_->GetMirroredBounds();
|
||||
}
|
||||
|
||||
gfx::Rect DocumentOverlayWindowViews::GetCloseControlsBounds() {
|
||||
return close_controls_view_->GetMirroredBounds();
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
gfx::Rect DocumentOverlayWindowViews::GetResizeHandleControlsBounds() {
|
||||
return resize_handle_view_->GetMirroredBounds();
|
||||
}
|
||||
|
||||
int DocumentOverlayWindowViews::GetResizeHTComponent() const {
|
||||
return resize_handle_view_->GetHTComponent();
|
||||
}
|
||||
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
|
||||
CloseImageButton* DocumentOverlayWindowViews::close_button_for_testing() const {
|
||||
return close_controls_view_;
|
||||
}
|
||||
|
||||
ui::Layer* DocumentOverlayWindowViews::document_layer_for_testing() const {
|
||||
return web_view_->layer();
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
// Copyright 2022 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_UI_VIEWS_OVERLAY_DOCUMENT_OVERLAY_WINDOW_VIEWS_H_
|
||||
#define CHROME_BROWSER_UI_VIEWS_OVERLAY_DOCUMENT_OVERLAY_WINDOW_VIEWS_H_
|
||||
|
||||
#include "build/chromeos_buildflags.h"
|
||||
#include "chrome/browser/ui/views/overlay/overlay_window_views.h"
|
||||
|
||||
class BackToTabImageButton;
|
||||
class CloseImageButton;
|
||||
class Profile;
|
||||
class ResizeHandleButton;
|
||||
|
||||
class OverlayLocationBarViewProxy {
|
||||
public:
|
||||
OverlayLocationBarViewProxy() = default;
|
||||
OverlayLocationBarViewProxy(const OverlayLocationBarViewProxy&) = delete;
|
||||
OverlayLocationBarViewProxy& operator=(const OverlayLocationBarViewProxy&) =
|
||||
delete;
|
||||
virtual ~OverlayLocationBarViewProxy();
|
||||
virtual void Init() = 0;
|
||||
virtual std::unique_ptr<views::View> ReleaseView() = 0;
|
||||
};
|
||||
|
||||
class DocumentOverlayWindowViews : public OverlayWindowViews,
|
||||
public content::DocumentOverlayWindow {
|
||||
public:
|
||||
// Constructs and initializes an instance. Since it includes a location bar
|
||||
// view which is rather testing hostile due to its many dependencies, the
|
||||
// optional argument supports supplying a fake implementation.
|
||||
static std::unique_ptr<DocumentOverlayWindowViews> Create(
|
||||
content::DocumentPictureInPictureWindowController* controller,
|
||||
std::unique_ptr<OverlayLocationBarViewProxy>
|
||||
location_bar_view_proxy_for_testing = nullptr);
|
||||
|
||||
DocumentOverlayWindowViews(const DocumentOverlayWindowViews&) = delete;
|
||||
DocumentOverlayWindowViews& operator=(const DocumentOverlayWindowViews&) =
|
||||
delete;
|
||||
|
||||
~DocumentOverlayWindowViews() override;
|
||||
|
||||
// OverlayWindow:
|
||||
bool IsActive() override;
|
||||
void Close() override;
|
||||
void ShowInactive() override;
|
||||
void Hide() override;
|
||||
bool IsVisible() override;
|
||||
bool IsAlwaysOnTop() override;
|
||||
gfx::Rect GetBounds() override;
|
||||
void UpdateNaturalSize(const gfx::Size& natural_size) override;
|
||||
|
||||
// views::Widget:
|
||||
bool IsActive() const override;
|
||||
bool IsVisible() const override;
|
||||
void OnNativeWidgetMove() override;
|
||||
void OnNativeWidgetDestroyed() override;
|
||||
const ui::ThemeProvider* GetThemeProvider() const override;
|
||||
|
||||
// OverlayWindowViews
|
||||
bool ControlsHitTestContainsPoint(const gfx::Point& point) override;
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
int GetResizeHTComponent() const override;
|
||||
gfx::Rect GetResizeHandleControlsBounds() override;
|
||||
void UpdateResizeHandleBounds(WindowQuadrant quadrant) override;
|
||||
#endif
|
||||
void OnUpdateControlsBounds() override;
|
||||
void OnGestureEvent(ui::GestureEvent* event) override;
|
||||
void SetUpViews() override;
|
||||
void OnRootViewReady() override;
|
||||
void UpdateLayerBoundsWithLetterboxing(gfx::Size window_size) override;
|
||||
content::PictureInPictureWindowController* GetController() const override;
|
||||
views::View* GetWindowBackgroundView() const override;
|
||||
views::View* GetControlsContainerView() const override;
|
||||
|
||||
// Gets the bounds of the controls.
|
||||
gfx::Rect GetBackToTabControlsBounds();
|
||||
gfx::Rect GetCloseControlsBounds();
|
||||
|
||||
// Unit test support.
|
||||
CloseImageButton* close_button_for_testing() const;
|
||||
ui::Layer* document_layer_for_testing() const;
|
||||
|
||||
void set_location_bar_view_proxy(
|
||||
std::unique_ptr<OverlayLocationBarViewProxy> proxy);
|
||||
|
||||
private:
|
||||
explicit DocumentOverlayWindowViews(
|
||||
content::DocumentPictureInPictureWindowController* controller);
|
||||
|
||||
// Calculate and set the bounds of the controls.
|
||||
gfx::Rect CalculateControlsBounds(int x, const gfx::Size& size);
|
||||
|
||||
// Not owned; |controller_| owns |this|.
|
||||
raw_ptr<content::DocumentPictureInPictureWindowController> controller_ =
|
||||
nullptr;
|
||||
|
||||
std::unique_ptr<OverlayLocationBarViewProxy> location_bar_view_proxy_;
|
||||
|
||||
raw_ptr<Profile> profile_for_theme_ = nullptr;
|
||||
|
||||
// Temporary storage for child Views. Used during the time between
|
||||
// construction and initialization, when the views::View pointer members must
|
||||
// already be initialized, but there is no root view to add them to yet.
|
||||
std::vector<std::unique_ptr<views::View>> view_holder_;
|
||||
|
||||
// Views to be shown. The views are first temporarily owned by view_holder_,
|
||||
// then passed to this widget's ContentsView which takes ownership.
|
||||
raw_ptr<views::View> window_background_view_ = nullptr;
|
||||
raw_ptr<views::View> web_view_ = nullptr;
|
||||
raw_ptr<views::View> controls_container_view_ = nullptr;
|
||||
raw_ptr<views::View> location_bar_view_ = nullptr;
|
||||
raw_ptr<CloseImageButton> close_controls_view_ = nullptr;
|
||||
raw_ptr<BackToTabImageButton> back_to_tab_image_button_ = nullptr;
|
||||
raw_ptr<ResizeHandleButton> resize_handle_view_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_UI_VIEWS_OVERLAY_DOCUMENT_OVERLAY_WINDOW_VIEWS_H_
|
@ -1,450 +0,0 @@
|
||||
// Copyright 2022 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/views/overlay/document_overlay_window_views.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "build/build_config.h"
|
||||
#include "build/chromeos_buildflags.h"
|
||||
#include "chrome/browser/ui/views/overlay/close_image_button.h"
|
||||
#include "chrome/test/base/testing_profile.h"
|
||||
#include "chrome/test/views/chrome_views_test_base.h"
|
||||
#include "content/public/browser/document_picture_in_picture_window_controller.h"
|
||||
#include "content/public/browser/overlay_window.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/test/test_web_contents_factory.h"
|
||||
#include "content/public/test/web_contents_tester.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "ui/base/hit_test.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/display/test/scoped_screen_override.h"
|
||||
#include "ui/display/test/test_screen.h"
|
||||
#include "ui/events/base_event_utils.h"
|
||||
#include "ui/events/event.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
#include "ui/gfx/geometry/vector2d.h"
|
||||
#include "ui/views/test/button_test_api.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr gfx::Size kMinWindowSize(200, 100);
|
||||
|
||||
} // namespace
|
||||
|
||||
class FakeOverlayLocationBarView : public OverlayLocationBarViewProxy {
|
||||
public:
|
||||
FakeOverlayLocationBarView() {
|
||||
view_holder_ = std::make_unique<views::View>();
|
||||
}
|
||||
~FakeOverlayLocationBarView() override = default;
|
||||
void Init() override {}
|
||||
std::unique_ptr<views::View> ReleaseView() override {
|
||||
return std::move(view_holder_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<views::View> view_holder_;
|
||||
};
|
||||
|
||||
class TestDocumentPictureInPictureWindowController
|
||||
: public content::DocumentPictureInPictureWindowController {
|
||||
public:
|
||||
TestDocumentPictureInPictureWindowController() = default;
|
||||
|
||||
// PictureInPictureWindowController:
|
||||
void Show() override {}
|
||||
void FocusInitiator() override {}
|
||||
MOCK_METHOD(void, Close, (bool));
|
||||
void CloseAndFocusInitiator() override {}
|
||||
MOCK_METHOD(void, OnWindowDestroyed, (bool));
|
||||
content::WebContents* GetWebContents() override { return web_contents_; }
|
||||
content::WebContents* GetChildWebContents() override {
|
||||
return child_web_contents_;
|
||||
}
|
||||
|
||||
// DocumentPictureInPictureWindowController
|
||||
void SetChildWebContents(content::WebContents* child) override {
|
||||
child_web_contents_ = child;
|
||||
}
|
||||
|
||||
void set_web_contents(content::WebContents* web_contents) {
|
||||
web_contents_ = web_contents;
|
||||
}
|
||||
|
||||
void destroy() { child_web_contents_ = nullptr; }
|
||||
absl::optional<gfx::Rect> GetWindowBounds() override { return absl::nullopt; }
|
||||
|
||||
private:
|
||||
raw_ptr<content::WebContents> web_contents_;
|
||||
raw_ptr<content::WebContents> child_web_contents_;
|
||||
};
|
||||
|
||||
class DocumentOverlayWindowViewsTest : public ChromeViewsTestBase {
|
||||
public:
|
||||
DocumentOverlayWindowViewsTest() = default;
|
||||
// ChromeViewsTestBase:
|
||||
void SetUp() override {
|
||||
// Purposely skip ChromeViewsTestBase::SetUp() as that creates ash::Shell
|
||||
// on ChromeOS, which we don't want.
|
||||
ViewsTestBase::SetUp();
|
||||
// web_contents_ needs to be created after the constructor, so that
|
||||
// |feature_list_| can be initialized before other threads check if a
|
||||
// feature is enabled.
|
||||
web_contents_ = web_contents_factory_.CreateWebContents(&profile_);
|
||||
pip_window_controller_.set_web_contents(web_contents_);
|
||||
|
||||
// The child web contents will be owned by the WebView, so create them
|
||||
// separately. (WebContentsFactory owns its created WebContents which isn't
|
||||
// compatible with this usage.)
|
||||
auto child =
|
||||
content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
|
||||
|
||||
pip_window_controller_.SetChildWebContents(child.get());
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
test_views_delegate()->set_context(GetContext());
|
||||
#endif
|
||||
test_views_delegate()->set_use_desktop_native_widgets(true);
|
||||
|
||||
// The default work area must be big enough to fit the minimum
|
||||
// DocumentOverlayWindowViews size.
|
||||
SetDisplayWorkArea({0, 0, 1000, 1000});
|
||||
|
||||
auto fake_location_bar = std::make_unique<FakeOverlayLocationBarView>();
|
||||
overlay_window_ = DocumentOverlayWindowViews::Create(
|
||||
&pip_window_controller_, std::move(fake_location_bar));
|
||||
overlay_window_->set_minimum_size_for_testing(kMinWindowSize);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// AutocompleteClassifierFactory::GetInstance()->Disassociate(&profile_);
|
||||
base::RunLoop().RunUntilIdle();
|
||||
pip_window_controller_.destroy();
|
||||
overlay_window_.reset();
|
||||
ViewsTestBase::TearDown();
|
||||
}
|
||||
|
||||
void SetDisplayWorkArea(const gfx::Rect& work_area) {
|
||||
display::Display display = test_screen_.GetPrimaryDisplay();
|
||||
display.set_work_area(work_area);
|
||||
test_screen_.display_list().UpdateDisplay(display);
|
||||
}
|
||||
|
||||
DocumentOverlayWindowViews& overlay_window() { return *overlay_window_; }
|
||||
|
||||
content::WebContents* web_contents() { return web_contents_; }
|
||||
|
||||
TestDocumentPictureInPictureWindowController& pip_window_controller() {
|
||||
return pip_window_controller_;
|
||||
}
|
||||
|
||||
private:
|
||||
TestingProfile profile_;
|
||||
content::TestWebContentsFactory web_contents_factory_;
|
||||
raw_ptr<content::WebContents> web_contents_;
|
||||
TestDocumentPictureInPictureWindowController pip_window_controller_;
|
||||
|
||||
display::test::TestScreen test_screen_;
|
||||
display::test::ScopedScreenOverride scoped_screen_override_{&test_screen_};
|
||||
|
||||
std::unique_ptr<DocumentOverlayWindowViews> overlay_window_;
|
||||
};
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, InitialWindowSize_Square) {
|
||||
// Fit the window taking 1/5 (both dimensions) of the work area as the
|
||||
// starting size, and applying the size and aspect ratio constraints.
|
||||
overlay_window().UpdateNaturalSize({400, 400});
|
||||
EXPECT_EQ(gfx::Size(200, 200), overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(gfx::Size(200, 170),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, InitialWindowSize_Horizontal) {
|
||||
// Set an arbitrary starting position in the top left corner. Otherwise, since
|
||||
// OnRootViewReady has already set a content size, the default window position
|
||||
// would be at the bottom right which results in a ResizeEdge::kTopLeft that
|
||||
// is considered a horizontal resize which shrinks the window. If it starts
|
||||
// out at the top left, we get ResizeEdge::kBottomRight which is a vertical
|
||||
// resize that enlarges the window.
|
||||
overlay_window().SetBounds({0, 0, 200, 200});
|
||||
|
||||
// Fit the window taking 1/5 (both dimensions) of the work area as the
|
||||
// starting size, and applying the size and aspect ratio constraints.
|
||||
overlay_window().UpdateNaturalSize({400, 200});
|
||||
EXPECT_EQ(gfx::Size(400, 200), overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(gfx::Size(400, 170),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, InitialWindowSize_Vertical) {
|
||||
// Fit the window taking 1/5 (both dimensions) of the work area as the
|
||||
// starting size, and applying the size and aspect ratio constraints.
|
||||
overlay_window().UpdateNaturalSize({400, 500});
|
||||
EXPECT_EQ(gfx::Size(200, 250), overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(gfx::Size(200, 220),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, Letterboxing) {
|
||||
overlay_window().UpdateNaturalSize({400, 10});
|
||||
|
||||
// Must fit within the minimum height of 146. But with the aspect ratio of
|
||||
// 40:1 the width gets exceedingly big and must be limited to the maximum of
|
||||
// 800. Thus, letterboxing is unavoidable.
|
||||
EXPECT_EQ(gfx::Size(800, 100), overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(gfx::Size(800, 0),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, Pillarboxing) {
|
||||
overlay_window().UpdateNaturalSize({10, 400});
|
||||
|
||||
// Must fit within the minimum width of 260. But with the aspect ratio of
|
||||
// 1:40 the height gets exceedingly big and must be limited to the maximum of
|
||||
// 800. Thus, pillarboxing is unavoidable.
|
||||
EXPECT_EQ(gfx::Size(200, 800), overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(gfx::Size(20, 770),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, Pillarboxing_Square) {
|
||||
overlay_window().UpdateNaturalSize({100, 100});
|
||||
|
||||
// Pillarboxing also occurs on Linux even with the square aspect ratio,
|
||||
// because the user is allowed to size the window to the rectangular minimum
|
||||
// size.
|
||||
overlay_window().SetSize({200, 100});
|
||||
EXPECT_EQ(gfx::Size(100, 70),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, ApproximateAspectRatio_Horizontal) {
|
||||
// "Horizontal" video.
|
||||
overlay_window().UpdateNaturalSize({320, 240});
|
||||
|
||||
// The user drags the window resizer horizontally and now the integer window
|
||||
// dimensions can't reproduce the video aspect ratio exactly. The video
|
||||
// should still fill the entire window area.
|
||||
overlay_window().SetSize({320, 240});
|
||||
EXPECT_EQ(gfx::Size(320, 210),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
overlay_window().SetSize({321, 241});
|
||||
EXPECT_EQ(gfx::Size(321, 211),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
// Wide video.
|
||||
overlay_window().UpdateNaturalSize({1600, 900});
|
||||
|
||||
overlay_window().SetSize({444, 250});
|
||||
EXPECT_EQ(gfx::Size(444, 220),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
overlay_window().SetSize({445, 250});
|
||||
EXPECT_EQ(gfx::Size(445, 220),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
// Very wide video.
|
||||
overlay_window().UpdateNaturalSize({400, 100});
|
||||
|
||||
overlay_window().SetSize({478, 120});
|
||||
EXPECT_EQ(gfx::Size(478, 90),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
overlay_window().SetSize({481, 120});
|
||||
EXPECT_EQ(gfx::Size(481, 90),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, ApproximateAspectRatio_Vertical) {
|
||||
// "Vertical" video.
|
||||
overlay_window().UpdateNaturalSize({240, 320});
|
||||
|
||||
// The user dragged the window resizer vertically and now the integer window
|
||||
// dimensions can't reproduce the video aspect ratio exactly. The video
|
||||
// should still fill the entire window area.
|
||||
overlay_window().SetSize({240, 320});
|
||||
EXPECT_EQ(gfx::Size(240, 290),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
overlay_window().SetSize({239, 319});
|
||||
EXPECT_EQ(gfx::Size(239, 289),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
// Narrow video.
|
||||
overlay_window().UpdateNaturalSize({900, 1600});
|
||||
|
||||
overlay_window().SetSize({250, 444});
|
||||
EXPECT_EQ(gfx::Size(250, 414),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
overlay_window().SetSize({250, 445});
|
||||
EXPECT_EQ(gfx::Size(250, 415),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
// Very narrow video.
|
||||
// NOTE: Window width is bounded by the minimum size.
|
||||
overlay_window().UpdateNaturalSize({100, 400});
|
||||
|
||||
overlay_window().SetSize({200, 478});
|
||||
EXPECT_EQ(gfx::Size(120, 448),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
|
||||
overlay_window().SetSize({200, 481});
|
||||
EXPECT_EQ(gfx::Size(120, 451),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, UpdateMaximumSize) {
|
||||
SetDisplayWorkArea({0, 0, 4000, 4000});
|
||||
|
||||
overlay_window().UpdateNaturalSize({480, 320});
|
||||
|
||||
// The initial size is determined by the work area and the video natural size
|
||||
// (aspect ratio).
|
||||
EXPECT_EQ(gfx::Size(1200, 800), overlay_window().GetBounds().size());
|
||||
// The initial maximum size is 80% of the work area.
|
||||
EXPECT_EQ(gfx::Size(3200, 3200), overlay_window().GetMaximumSize());
|
||||
|
||||
// If the maximum size increases then we should keep the existing window size.
|
||||
SetDisplayWorkArea({0, 0, 8000, 8000});
|
||||
EXPECT_EQ(gfx::Size(1200, 800), overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(gfx::Size(6400, 6400), overlay_window().GetMaximumSize());
|
||||
|
||||
// If the maximum size decreases then we should shrink to fit.
|
||||
SetDisplayWorkArea({0, 0, 1000, 2000});
|
||||
EXPECT_EQ(gfx::Size(800, 800), overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(gfx::Size(800, 1600), overlay_window().GetMaximumSize());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, IgnoreInvalidMaximumSize) {
|
||||
ASSERT_EQ(gfx::Size(800, 800), overlay_window().GetMaximumSize());
|
||||
|
||||
SetDisplayWorkArea({0, 0, 0, 0});
|
||||
EXPECT_EQ(gfx::Size(800, 800), overlay_window().GetMaximumSize());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, UpdateNaturalSizeDoesNotMoveWindow) {
|
||||
// Enter PiP.
|
||||
overlay_window().UpdateNaturalSize({300, 200});
|
||||
overlay_window().ShowInactive();
|
||||
|
||||
// Resize the window and move it toward the top-left corner of the work area.
|
||||
// In production, resizing preserves the aspect ratio if possible, so we
|
||||
// preserve it here too.
|
||||
overlay_window().SetBounds({100, 100, 450, 300});
|
||||
|
||||
// Simulate a new surface layer and a change in the aspect ratio.
|
||||
overlay_window().UpdateNaturalSize({400, 200});
|
||||
|
||||
// The window should not move.
|
||||
// The window size will be adjusted according to the new aspect ratio, and
|
||||
// clamped to 600x300 to fit within the maximum size for the work area of
|
||||
// 1000x1000.
|
||||
EXPECT_EQ(gfx::Rect(100, 100, 600, 300), overlay_window().GetBounds());
|
||||
}
|
||||
|
||||
// Tests that the OverlayWindowFrameView does not accept events so they can
|
||||
// propagate to the overlay.
|
||||
TEST_F(DocumentOverlayWindowViewsTest, HitTestFrameView) {
|
||||
// Since the NonClientFrameView is the only non-custom direct descendent of
|
||||
// the NonClientView, we can assume that if the frame does not accept the
|
||||
// point but the NonClientView does, then it will be handled by one of the
|
||||
// custom overlay views.
|
||||
auto point = gfx::Point(50, 50);
|
||||
views::NonClientView* non_client_view = overlay_window().non_client_view();
|
||||
EXPECT_EQ(non_client_view->frame_view()->HitTestPoint(point), false);
|
||||
EXPECT_EQ(non_client_view->HitTestPoint(point), true);
|
||||
}
|
||||
|
||||
// Tests that hit tests on various areas of the window have the expected
|
||||
// hit test type.
|
||||
TEST_F(DocumentOverlayWindowViewsTest, HitTestTypesByLocation) {
|
||||
views::NonClientView* view = overlay_window().non_client_view();
|
||||
|
||||
overlay_window().UpdateNaturalSize({200, 200});
|
||||
|
||||
// The corners and edges of the frame support resizing.
|
||||
EXPECT_EQ(view->NonClientHitTest({1, 1}), HTTOPLEFT);
|
||||
EXPECT_EQ(view->NonClientHitTest({198, 1}), HTTOPRIGHT);
|
||||
EXPECT_EQ(view->NonClientHitTest({1, 100}), HTLEFT);
|
||||
EXPECT_EQ(view->NonClientHitTest({198, 100}), HTRIGHT);
|
||||
EXPECT_EQ(view->NonClientHitTest({1, 198}), HTBOTTOMLEFT);
|
||||
EXPECT_EQ(view->NonClientHitTest({198, 198}), HTBOTTOMRIGHT);
|
||||
|
||||
// The middle of the controls bar allows dragging the window.
|
||||
EXPECT_EQ(view->NonClientHitTest({100, 15}), HTCAPTION);
|
||||
|
||||
// The right side of the control bar contains the close button
|
||||
// which is interactive.
|
||||
EXPECT_EQ(view->NonClientHitTest({185, 15}), HTNOWHERE);
|
||||
|
||||
// Clicks on the web content area must not be intercepted so that
|
||||
// interactions remain possible.
|
||||
EXPECT_EQ(view->NonClientHitTest({100, 100}), HTNOWHERE);
|
||||
}
|
||||
|
||||
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
|
||||
// With pillarboxing, the close button doesn't cover the video area. Make sure
|
||||
// hovering the button doesn't get handled like normal mouse exit events
|
||||
// causing the controls to hide.
|
||||
TEST_F(DocumentOverlayWindowViewsTest, NoMouseExitWithinWindowBounds) {
|
||||
overlay_window().UpdateNaturalSize({10, 400});
|
||||
|
||||
const auto close_button_bounds = overlay_window().GetCloseControlsBounds();
|
||||
const auto video_bounds =
|
||||
overlay_window().document_layer_for_testing()->bounds();
|
||||
ASSERT_FALSE(video_bounds.Contains(close_button_bounds));
|
||||
|
||||
const gfx::Point moved_location(video_bounds.origin() + gfx::Vector2d(5, 5));
|
||||
ui::MouseEvent moved_event(ui::ET_MOUSE_MOVED, moved_location, moved_location,
|
||||
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
|
||||
overlay_window().OnMouseEvent(&moved_event);
|
||||
ASSERT_TRUE(overlay_window().AreControlsVisible());
|
||||
|
||||
const gfx::Point exited_location(close_button_bounds.CenterPoint());
|
||||
ui::MouseEvent exited_event(ui::ET_MOUSE_EXITED, exited_location,
|
||||
exited_location, ui::EventTimeForNow(),
|
||||
ui::EF_NONE, ui::EF_NONE);
|
||||
overlay_window().OnMouseEvent(&exited_event);
|
||||
EXPECT_TRUE(overlay_window().AreControlsVisible());
|
||||
}
|
||||
|
||||
#endif // !BUILDFLAG(IS_CHROMEOS_LACROS)
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, PauseOnCloseButton) {
|
||||
views::test::ButtonTestApi close_button_clicker(
|
||||
overlay_window().close_button_for_testing());
|
||||
ui::MouseEvent dummy_event(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0),
|
||||
gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0);
|
||||
|
||||
// Closing via the close button should pause the content.
|
||||
EXPECT_CALL(pip_window_controller(), Close(true));
|
||||
close_button_clicker.NotifyClick(dummy_event);
|
||||
testing::Mock::VerifyAndClearExpectations(&pip_window_controller());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, PauseOnWidgetClose) {
|
||||
// When the native widget is destroyed we should pause the underlying content.
|
||||
EXPECT_CALL(pip_window_controller(), OnWindowDestroyed(true));
|
||||
overlay_window().CloseNow();
|
||||
testing::Mock::VerifyAndClearExpectations(&pip_window_controller());
|
||||
}
|
||||
|
||||
TEST_F(DocumentOverlayWindowViewsTest, SmallDisplayWorkAreaDoesNotCrash) {
|
||||
SetDisplayWorkArea({0, 0, 240, 120});
|
||||
overlay_window().UpdateNaturalSize({400, 300});
|
||||
|
||||
// Since the work area would force a max size smaller than the minimum size,
|
||||
// the size is fixed at the minimum size.
|
||||
EXPECT_EQ(kMinWindowSize, overlay_window().GetBounds().size());
|
||||
EXPECT_EQ(kMinWindowSize, overlay_window().GetMaximumSize());
|
||||
|
||||
// The video should still be letterboxed to the correct aspect ratio.
|
||||
EXPECT_EQ(gfx::Size(133, 70),
|
||||
overlay_window().document_layer_for_testing()->size());
|
||||
}
|
@ -455,10 +455,6 @@ void OverlayWindowViews::RecordButtonPressed(
|
||||
}
|
||||
|
||||
void OverlayWindowViews::ForceControlsVisibleForTesting(bool visible) {
|
||||
ForceControlsVisible(visible);
|
||||
}
|
||||
|
||||
void OverlayWindowViews::ForceControlsVisible(bool visible) {
|
||||
force_controls_visible_ = visible;
|
||||
UpdateControlsVisibility(visible);
|
||||
}
|
||||
|
@ -16,9 +16,9 @@
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
// Base class for the Chrome desktop implementation of VideoOverlayWindow and
|
||||
// DocumentOverlayWindow. This will only be implemented in views, which will
|
||||
// support all desktop platforms.
|
||||
// Base class for the Chrome desktop implementation of VideoOverlayWindow.
|
||||
// This will only be implemented in views, which will support all desktop
|
||||
// platforms.
|
||||
//
|
||||
// This class is a views::Widget. The subclasses implement the needed
|
||||
// methods for their corresponding OverlayWindow subclass.
|
||||
@ -89,9 +89,7 @@ class OverlayWindowViews : public views::Widget,
|
||||
// Updates the controls view::Views to reflect |is_visible|.
|
||||
void UpdateControlsVisibility(bool is_visible);
|
||||
|
||||
// DocumentOverlayWindowViews currently always forces the controls
|
||||
// to be visible. VideoOverlayWindowViews does so for testing.
|
||||
void ForceControlsVisible(bool visible);
|
||||
// VideoOverlayWindowViews does this for testing.
|
||||
void ForceControlsVisibleForTesting(bool visible);
|
||||
|
||||
// Determines whether a layout of the window controls has been scheduled but
|
||||
|
@ -8835,7 +8835,6 @@ test("unit_tests") {
|
||||
"../browser/ui/views/omnibox/omnibox_match_cell_view_unittest.cc",
|
||||
"../browser/ui/views/omnibox/omnibox_result_view_unittest.cc",
|
||||
"../browser/ui/views/omnibox/omnibox_view_views_unittest.cc",
|
||||
"../browser/ui/views/overlay/document_overlay_window_views_unittest.cc",
|
||||
"../browser/ui/views/overlay/video_overlay_window_views_unittest.cc",
|
||||
"../browser/ui/views/page_action/page_action_icon_view_unittest.cc",
|
||||
"../browser/ui/views/page_info/page_info_bubble_view_unittest.cc",
|
||||
|
@ -1109,12 +1109,6 @@ ContentBrowserClient::CreateWindowForVideoPictureInPicture(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<DocumentOverlayWindow>
|
||||
ContentBrowserClient::CreateWindowForDocumentPictureInPicture(
|
||||
DocumentPictureInPictureWindowController* controller) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ContentBrowserClient::RegisterRendererPreferenceWatcher(
|
||||
BrowserContext* browser_context,
|
||||
mojo::PendingRemote<blink::mojom::RendererPreferenceWatcher> watcher) {
|
||||
|
@ -205,8 +205,6 @@ class ClientCertificateDelegate;
|
||||
class ControllerPresentationServiceDelegate;
|
||||
class DevToolsManagerDelegate;
|
||||
class DirectSocketsDelegate;
|
||||
class DocumentOverlayWindow;
|
||||
class DocumentPictureInPictureWindowController;
|
||||
class FeatureObserverClient;
|
||||
class FontAccessDelegate;
|
||||
class HidDelegate;
|
||||
@ -1964,18 +1962,15 @@ class CONTENT_EXPORT ContentBrowserClient {
|
||||
RenderFrameHost* initiator_document,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory);
|
||||
|
||||
// Creates an OverlayWindow to be used for video or document
|
||||
// Picture-in-Picture respectively. This window will house the content shown
|
||||
// when in Picture-in-Picture mode. This will return a new OverlayWindow.
|
||||
// Creates an OverlayWindow to be used for video or Picture-in-Picture.
|
||||
// This window will house the content shown when in Picture-in-Picture mode.
|
||||
// This will return a new OverlayWindow.
|
||||
//
|
||||
// May return nullptr if embedder does not support this functionality. The
|
||||
// default implementation provides nullptr OverlayWindow.
|
||||
virtual std::unique_ptr<VideoOverlayWindow>
|
||||
CreateWindowForVideoPictureInPicture(
|
||||
VideoPictureInPictureWindowController* controller);
|
||||
virtual std::unique_ptr<DocumentOverlayWindow>
|
||||
CreateWindowForDocumentPictureInPicture(
|
||||
DocumentPictureInPictureWindowController* controller);
|
||||
|
||||
// Registers the watcher to observe updates in RendererPreferences.
|
||||
virtual void RegisterRendererPreferenceWatcher(
|
||||
|
Reference in New Issue
Block a user