blob: 2c78c20f5de442694f28e0152e71e3b63a35c3ae [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/frame/browser_view_ash.h"
#include <algorithm>
#include "base/check.h"
#include "chrome/browser/ui/sad_tab_helper.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/contents_container_view.h"
#include "chrome/browser/ui/views/frame/multi_contents_view.h"
#include "chrome/browser/ui/views/frame/scrim_view.h"
#include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
#include "chrome/browser/ui/views/sad_tab_view.h"
#include "chrome/browser/ui/views/side_panel/side_panel.h"
#include "components/search/ntp_features.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/controls/webview/webview.h"
namespace {
void SetRoundedCornersOnHost(views::NativeViewHost* host,
const gfx::RoundedCornersF& radii) {
auto* layer = host->GetUILayer();
if (layer && layer->rounded_corner_radii() != radii) {
host->SetCornerRadii(radii);
}
}
} // namespace
BrowserViewAsh::BrowserViewAsh(Browser* browser) : BrowserView(browser) {}
void BrowserViewAsh::Layout(PassKey) {
LayoutSuperclass<BrowserView>(this);
// In ChromeOS ash we round the bottom two corners of the browser frame by
// rounding the respective corners of visible client contents i.e main web
// contents, devtools web contents and side panel. When ever there is change
// in the layout or visibility of these contents (devtools opened, devtools
// docked placement change, side panel open etc), we might need to update
// which corners are currently rounded. See
// `BrowserFrameViewChromeOS::UpdateWindowRoundedCorners()` for more
// details.
DCHECK(GetWidget());
GetWidget()->non_client_view()->frame_view()->UpdateWindowRoundedCorners();
}
void BrowserViewAsh::UpdateWindowRoundedCorners(
const gfx::RoundedCornersF& window_radii) {
SidePanel* side_panel = contents_height_side_panel();
const bool right_aligned_side_panel_showing =
side_panel->GetVisible() && side_panel->IsRightAligned();
const bool left_aligned_side_panel_showing =
side_panel->GetVisible() && !side_panel->IsRightAligned();
// If side panel is visible, round one of the bottom two corners of the side
// panel based on its alignment w.r.t to web contents.
const gfx::RoundedCornersF side_panel_radii(
0, 0, right_aligned_side_panel_showing ? window_radii.lower_right() : 0,
left_aligned_side_panel_showing ? window_radii.lower_left() : 0);
if (side_panel_radii != side_panel->background_radii()) {
side_panel->SetBackgroundRadii(side_panel_radii);
}
window_scrim_view()->SetRoundedCorners(window_radii);
if (base::FeatureList::IsEnabled(features::kSideBySide)) {
const gfx::RoundedCornersF multi_contents_radii(
0, 0, right_aligned_side_panel_showing ? 0 : window_radii.lower_right(),
left_aligned_side_panel_showing ? 0 : window_radii.lower_left());
if (multi_contents_view()->background_radii() != multi_contents_radii) {
multi_contents_view()->SetBackgroundRadii(multi_contents_radii);
}
if (IsInSplitView()) {
// In a non-split view, browser's content (main web content, DevTools, NTP
// footer, etc.) extends into the rounded corners. However, in split view,
// the content is bordered, making it sufficient to simply round the
// background painted by multi_contents_view().
return;
}
}
views::WebView *devtools_webview =
GetActiveContentsContainerView()->devtools_web_view();
CHECK(devtools_webview);
CHECK(devtools_webview->holder());
// If devtools are visible, round one of the bottom two corners of the
// the devtools context based on the alignment of the side panel. Since
// devtools cover the full bounds of the web contents container, if the side
// panel is not visible, we have to round the bottom two corners of side panel
// irrespective of its docked placement.
const gfx::RoundedCornersF devtools_webview_radii(
0, 0, right_aligned_side_panel_showing ? 0 : window_radii.lower_right(),
left_aligned_side_panel_showing ? 0 : window_radii.lower_left());
SetRoundedCornersOnHost(devtools_webview->holder(), devtools_webview_radii);
GetActiveContentsContainerView()->devtools_scrim_view()->SetRoundedCorners(
devtools_webview_radii);
const ContentsContainerView::DevToolsDockedPlacement devtools_placement =
GetActiveContentsContainerView()->devtools_docked_placement();
CHECK_NE(devtools_placement,
ContentsContainerView::DevToolsDockedPlacement::kUnknown);
// Rounded the contents webview.
std::vector<ContentsWebView*> contents_views =
GetAllVisibleContentsWebViews();
ContentsWebView* contents_webview = contents_views.front();
const gfx::Rect contents_bounds =
GetActiveContentsContainerView()->GetContentsViewBounds();
const gfx::Rect container_bounds =
GetActiveContentsContainerView()->GetLocalBounds();
const bool devtools_showing = contents_bounds != container_bounds;
// With window controls overlay enabled, the web content extends over the
// entire window height, overlapping the window's top-two rounded corners.
// Consequently, we need to make the top two corners of the web_view
// rounded as well.
const bool round_content_webview_top_corner =
IsWindowControlsOverlayEnabled();
auto* ntp_footer = GetActiveContentsContainerView()->new_tab_footer_view();
bool is_ntp_footer_showing = false;
if (base::FeatureList::IsEnabled(ntp_features::kNtpFooter)) {
is_ntp_footer_showing = ntp_footer->GetVisible();
}
if (is_ntp_footer_showing) {
const gfx::RoundedCornersF ntp_footer_radii(
0, 0,
right_aligned_side_panel_showing ||
(devtools_showing &&
devtools_placement !=
ContentsContainerView::DevToolsDockedPlacement::kLeft)
? 0
: window_radii.lower_right(),
left_aligned_side_panel_showing ||
(devtools_showing &&
devtools_placement !=
ContentsContainerView::DevToolsDockedPlacement::kRight)
? 0
: window_radii.lower_left());
SetRoundedCornersOnHost(ntp_footer->holder(), ntp_footer_radii);
}
const gfx::RoundedCornersF contents_webview_radii(
round_content_webview_top_corner ? window_radii.upper_left() : 0,
round_content_webview_top_corner ? window_radii.upper_right() : 0,
is_ntp_footer_showing || right_aligned_side_panel_showing ||
(devtools_showing &&
devtools_placement !=
ContentsContainerView::DevToolsDockedPlacement::kLeft)
? 0
: window_radii.lower_right(),
is_ntp_footer_showing || left_aligned_side_panel_showing ||
(devtools_showing &&
devtools_placement !=
ContentsContainerView::DevToolsDockedPlacement::kRight)
? 0
: window_radii.lower_left());
CHECK(contents_webview);
CHECK(contents_webview->holder());
if (contents_webview->web_contents()) {
// SideTabView is shown when the renderer crashes. Initially the SabTabView
// gets the same corners as the contents webview it gets attached to but its
// radii needs to be updated as it is unaware of the client view layout
// changes.
if (auto* sad_tab_helper =
SadTabHelper::FromWebContents(contents_webview->web_contents());
sad_tab_helper && sad_tab_helper->sad_tab()) {
SadTabView* sad_tab_view =
static_cast<SadTabView*>(sad_tab_helper->sad_tab());
if (sad_tab_view->GetBackgroundRadii() != contents_webview_radii) {
sad_tab_view->SetBackgroundRadii(contents_webview_radii);
}
} else {
// We only round contents_webview, if SadTabView is not showing.
SetRoundedCornersOnHost(contents_webview->holder(),
contents_webview_radii);
}
}
if (contents_webview->GetBackgroundRadii() != contents_webview_radii) {
contents_webview->SetBackgroundRadii(contents_webview_radii);
}
const gfx::RoundedCornersF contents_scrim_radii(
round_content_webview_top_corner ? window_radii.upper_left() : 0,
round_content_webview_top_corner ? window_radii.upper_right() : 0,
right_aligned_side_panel_showing ? 0 : window_radii.lower_right(),
left_aligned_side_panel_showing ? 0 : window_radii.lower_left());
GetActiveContentsContainerView()->contents_scrim_view()->SetRoundedCorners(
contents_scrim_radii);
}