0

Reland "Make ClientView a child of the NonClientFrameView"

This is a reland of ec39b1ddb4

Original change's description:
> Make ClientView a child of the NonClientFrameView
>
> This refactor changes the hierarchy of views under
> NonClientView. Previously, NonClientFrameView and ClientView were
> siblings under NonClientView. This will change the hierarchy to:
> NonClientView > NonClientFrameView > ClientView.
>
> This change also enables a cleaner implementation of the Window
> Controls Overlay (see before (https://crrev.com/c/2504573) vs
> after (https://crrev.com/c/2545685))
>
> Window controls overlay links:
> Explainer: https://github.com/WICG/window-controls-overlay/blob/master/explainer.md
> Design Doc: https://docs.google.com/document/d/1k0YL_-VMLIfjYCgJ2v6cMvuUv2qMKg4BgLI2tJ4qtyo/edit?usp=sharing
> I2P: https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/cper6nNLFRQ/hU91kfCWBQAJ
>
> Bug: 1175276, 937121
> Change-Id: If16de54a858f571c628b66c7801ef3777c6cc924
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2522616
> Commit-Queue: Amanda Baker <ambake@microsoft.com>
> Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
> Reviewed-by: Matt Giuca <mgiuca@chromium.org>
> Reviewed-by: David Tseng <dtseng@chromium.org>
> Reviewed-by: Leonard Grey <lgrey@chromium.org>
> Reviewed-by: Peter Kasting <pkasting@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#864068}

Bug: 1175276
Bug: 937121
Change-Id: Ib8919739dd685a4ea20b56fd241750678c38a9c0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2780032
Reviewed-by: Leonard Grey <lgrey@chromium.org>
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Reviewed-by: Peter Kasting <pkasting@chromium.org>
Reviewed-by: Matt Giuca <mgiuca@chromium.org>
Reviewed-by: David Tseng <dtseng@chromium.org>
Commit-Queue: Amanda Baker <ambake@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#871173}
This commit is contained in:
Amanda Baker
2021-04-10 00:00:15 +00:00
committed by Chromium LUCI CQ
parent c6591ab885
commit 5ec84dd81f
41 changed files with 273 additions and 443 deletions

@ -249,8 +249,11 @@ gfx::Size AppWindowFrameView::CalculatePreferredSize() const {
}
void AppWindowFrameView::Layout() {
NonClientFrameView::Layout();
if (!draw_frame_)
return;
gfx::Size close_size = close_button_->GetPreferredSize();
const int kButtonOffsetY = 0;
const int kButtonSpacing = 1;

@ -92,9 +92,9 @@ class SearchBoxViewTest : public views::test::WidgetTest,
}
void TearDown() override {
view_.reset();
app_list_view_->GetWidget()->Close();
widget_->CloseNow();
view_.reset();
views::test::WidgetTest::TearDown();
}

@ -277,7 +277,7 @@ TEST_F(DefaultFrameHeaderTest, ResizeAndReorderDuringAnimation) {
LayerDestroyedChecker checker(animating_layer);
// Change the view's stacking order should stop the animation.
ASSERT_EQ(2u, frame_view_1->children().size());
ASSERT_EQ(3u, frame_view_1->children().size());
frame_view_1->ReorderChildView(extra_view_1, 0);
EXPECT_EQ(

@ -327,16 +327,16 @@ gfx::Size NonClientFrameViewAsh::CalculatePreferredSize() const {
}
void NonClientFrameViewAsh::Layout() {
if (!GetEnabled())
return;
views::NonClientFrameView::Layout();
if (!GetFrameEnabled())
return;
aura::Window* frame_window = frame_->GetNativeWindow();
frame_window->SetProperty(aura::client::kTopViewInset,
NonClientTopBorderHeight());
}
gfx::Size NonClientFrameViewAsh::GetMinimumSize() const {
if (!GetEnabled())
if (!GetFrameEnabled())
return gfx::Size();
gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize());
@ -358,13 +358,6 @@ gfx::Size NonClientFrameViewAsh::GetMaximumSize() const {
return gfx::Size(width, height);
}
void NonClientFrameViewAsh::SetVisible(bool visible) {
overlay_view_->SetVisible(visible);
views::View::SetVisible(visible);
// We need to re-layout so that client view will occupy entire window.
InvalidateLayout();
}
void NonClientFrameViewAsh::SetShouldPaintHeader(bool paint) {
header_view_->SetShouldPaintHeader(paint);
}
@ -372,7 +365,7 @@ void NonClientFrameViewAsh::SetShouldPaintHeader(bool paint) {
int NonClientFrameViewAsh::NonClientTopBorderHeight() const {
// The frame should not occupy the window area when it's in fullscreen,
// not visible or disabled.
if (frame_->IsFullscreen() || !GetVisible() || !GetEnabled() ||
if (frame_->IsFullscreen() || !GetFrameEnabled() ||
header_view_->in_immersive_mode()) {
return 0;
}
@ -395,6 +388,15 @@ SkColor NonClientFrameViewAsh::GetInactiveFrameColorForTest() const {
return frame_->GetNativeWindow()->GetProperty(kFrameInactiveColorKey);
}
void NonClientFrameViewAsh::SetFrameEnabled(bool enabled) {
if (enabled == frame_enabled_)
return;
frame_enabled_ = enabled;
overlay_view_->SetVisible(frame_enabled_);
InvalidateLayout();
}
void NonClientFrameViewAsh::OnDidSchedulePaint(const gfx::Rect& r) {
// We may end up here before |header_view_| has been added to the Widget.
if (header_view_->GetWidget()) {
@ -410,9 +412,18 @@ void NonClientFrameViewAsh::OnDidSchedulePaint(const gfx::Rect& r) {
bool NonClientFrameViewAsh::DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const {
CHECK_EQ(target, this);
// NonClientView hit tests the NonClientFrameView first instead of going in
// z-order. Return false so that events get to the OverlayView.
return false;
// Give the OverlayView the first chance to handle events.
if (frame_enabled_ && overlay_view_->HitTestRect(rect))
return false;
// Handle the event if it's within the bounds of the ClientView.
gfx::RectF rect_in_client_view_coords_f(rect);
View::ConvertRectToTarget(this, frame_->client_view(),
&rect_in_client_view_coords_f);
gfx::Rect rect_in_client_view_coords =
gfx::ToEnclosingRect(rect_in_client_view_coords_f);
return frame_->client_view()->HitTestRect(rect_in_client_view_coords);
}
chromeos::FrameCaptionButtonContainerView*

@ -90,7 +90,6 @@ class ASH_EXPORT NonClientFrameViewAsh : public views::NonClientFrameView {
void Layout() override;
gfx::Size GetMinimumSize() const override;
gfx::Size GetMaximumSize() const override;
void SetVisible(bool visible) override;
// If |paint| is false, we should not paint the header. Used for overview mode
// with OnOverviewModeStarting() and OnOverviewModeEnded() to hide/show the
@ -111,6 +110,9 @@ class ASH_EXPORT NonClientFrameViewAsh : public views::NonClientFrameView {
views::Widget* frame() { return frame_; }
bool GetFrameEnabled() const { return frame_enabled_; }
virtual void SetFrameEnabled(bool enabled);
protected:
// views::View:
void OnDidSchedulePaint(const gfx::Rect& r) override;
@ -141,6 +143,8 @@ class ASH_EXPORT NonClientFrameViewAsh : public views::NonClientFrameView {
OverlayView* overlay_view_ = nullptr;
bool frame_enabled_ = true;
std::unique_ptr<NonClientFrameViewAshImmersiveHelper> immersive_helper_;
base::CallbackListSubscription paint_as_active_subscription_ =

@ -543,19 +543,19 @@ TEST_F(NonClientFrameViewAshTest, FrameVisibility) {
delegate->non_client_frame_view();
EXPECT_EQ(client_bounds, widget->client_view()->GetLocalBounds().size());
non_client_frame_view->SetVisible(false);
non_client_frame_view->SetFrameEnabled(false);
widget->GetRootView()->Layout();
EXPECT_EQ(gfx::Size(200, 100),
widget->client_view()->GetLocalBounds().size());
EXPECT_FALSE(widget->non_client_view()->frame_view()->GetVisible());
EXPECT_FALSE(non_client_frame_view->GetFrameEnabled());
EXPECT_EQ(
window_bounds,
non_client_frame_view->GetClientBoundsForWindowBounds(window_bounds));
non_client_frame_view->SetVisible(true);
non_client_frame_view->SetFrameEnabled(true);
widget->GetRootView()->Layout();
EXPECT_EQ(client_bounds, widget->client_view()->GetLocalBounds().size());
EXPECT_TRUE(widget->non_client_view()->frame_view()->GetVisible());
EXPECT_TRUE(non_client_frame_view->GetFrameEnabled());
EXPECT_EQ(32, delegate->GetNonClientFrameViewTopBorderHeight());
EXPECT_EQ(
gfx::Rect(gfx::Point(10, 42), client_bounds),

@ -6,6 +6,7 @@
#include "ash/fast_ink/view_tree_host_root_view.h"
#include "ash/fast_ink/view_tree_host_widget.h"
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/hud_display/graphs_container_view.h"
#include "ash/hud_display/hud_constants.h"
#include "ash/hud_display/hud_header_view.h"
@ -82,12 +83,11 @@ std::unique_ptr<views::ClientView> MakeClientView(views::Widget* widget) {
}
void InitializeFrameView(views::WidgetDelegate* delegate) {
auto* frame_view = delegate->GetWidget()->non_client_view()->frame_view();
auto* frame_view = static_cast<NonClientFrameViewAsh*>(
delegate->GetWidget()->non_client_view()->frame_view());
// TODO(oshima): support component type with TYPE_WINDOW_FLAMELESS widget.
if (frame_view) {
frame_view->SetEnabled(false);
frame_view->SetVisible(false);
}
if (frame_view)
frame_view->SetFrameEnabled(false);
}
} // namespace

@ -319,6 +319,8 @@ HoldingSpaceTrayBubble::HoldingSpaceTrayBubble(
// Create and customize bubble view.
TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
// Ensure bubble frame does not draw background behind bubble view.
bubble_view->set_color(SK_ColorTRANSPARENT);
child_bubble_container_ =
bubble_view->AddChildView(std::make_unique<ChildBubbleContainer>());
child_bubble_container_->SetMaxHeight(CalculateMaxHeight());
@ -339,12 +341,6 @@ HoldingSpaceTrayBubble::HoldingSpaceTrayBubble(
bubble_wrapper_ =
std::make_unique<TrayBubbleWrapper>(holding_space_tray, bubble_view);
// Set bubble frame to be invisible.
bubble_wrapper_->GetBubbleWidget()
->non_client_view()
->frame_view()
->SetVisible(false);
event_handler_ =
std::make_unique<HoldingSpaceTrayBubbleEventHandler>(this, &delegate_);

@ -547,11 +547,12 @@ TEST_F(SystemModalContainerLayoutManagerTest, ShowModalWhileHidden) {
TEST_F(SystemModalContainerLayoutManagerTest, ChangeCapture) {
std::unique_ptr<aura::Window> widget_window(ShowToplevelTestWindow(false));
views::test::CaptureTrackingView* view = new views::test::CaptureTrackingView;
views::View* contents_view =
views::View* client_view =
views::Widget::GetWidgetForNativeView(widget_window.get())
->GetContentsView();
contents_view->AddChildView(view);
view->SetBoundsRect(contents_view->bounds());
->non_client_view()
->client_view();
client_view->AddChildView(view);
view->SetBoundsRect(client_view->bounds());
gfx::Point center(view->width() / 2, view->height() / 2);
views::View::ConvertPointToScreen(view, &center);

@ -3049,7 +3049,7 @@ TEST_F('ChromeVoxBackgroundTest', 'ImageAnnotations', function() {
TEST_F('ChromeVoxBackgroundTest', 'VolumeChanges', function() {
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(``, function() {
this.runWithLoadedTree('<p>test</p>', function() {
const bounds = ChromeVoxState.instance.getFocusBounds();
mockFeedback.call(press(KeyCode.VOLUME_UP))
.expectSpeech('Volume', 'Slider', /\d+%/)

@ -89,21 +89,6 @@ class FullSizeBubbleFrameView : public views::BubbleFrameView {
~FullSizeBubbleFrameView() override = default;
private:
// Overridden from views::ViewTargeterDelegate:
bool DoesIntersectRect(const View* target,
const gfx::Rect& rect) const override {
// Make sure click events can still reach the close button, even if the
// ClientView overlaps it.
// NOTE: |rect| is in the mirrored coordinate space, so we must use the
// close button's mirrored bounds to correctly target the close button when
// in RTL mode.
if (IsCloseButtonVisible() &&
GetCloseButtonMirroredBounds().Intersects(rect)) {
return true;
}
return views::BubbleFrameView::DoesIntersectRect(target, rect);
}
// Overridden from views::BubbleFrameView:
bool ExtendClientIntoTitle() const override { return true; }
};

@ -66,6 +66,11 @@ BrowserNonClientFrameView::~BrowserNonClientFrameView() {
g_browser_process->profile_manager()->
GetProfileAttributesStorage().RemoveObserver(this);
}
// WebAppFrameToolbarView::ToolbarButtonContainer is an
// ImmersiveModeController::Observer, so it must be destroyed before the
// BrowserView destroys the ImmersiveModeController.
delete web_app_frame_toolbar_;
}
void BrowserNonClientFrameView::OnBrowserViewInitViewsComplete() {
@ -294,68 +299,6 @@ void BrowserNonClientFrameView::ChildPreferredSizeChanged(views::View* child) {
Layout();
}
bool BrowserNonClientFrameView::DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const {
DCHECK_EQ(target, this);
if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect)) {
// |rect| is outside the frame's bounds.
return false;
}
bool should_leave_to_top_container = false;
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
// In immersive mode, the caption buttons container is reparented to the
// TopContainerView and hence |rect| should not be claimed here. See
// BrowserNonClientFrameViewChromeOS::OnImmersiveRevealStarted().
should_leave_to_top_container =
browser_view_->immersive_mode_controller()->IsRevealed();
#endif
if (!browser_view_->GetTabStripVisible()) {
// Claim |rect| if it is above the top of the topmost client area view.
return !should_leave_to_top_container && (rect.y() < GetTopInset(false));
}
// If the rect is outside the bounds of the client area, claim it.
gfx::RectF rect_in_client_view_coords_f(rect);
View::ConvertRectToTarget(this, frame_->client_view(),
&rect_in_client_view_coords_f);
gfx::Rect rect_in_client_view_coords =
gfx::ToEnclosingRect(rect_in_client_view_coords_f);
if (!frame_->client_view()->HitTestRect(rect_in_client_view_coords))
return true;
// Otherwise, claim |rect| only if it is above the bottom of the tab strip
// region view in a non-tab portion.
TabStripRegionView* tab_strip_region_view =
browser_view_->tab_strip_region_view();
// The |tab_strip_region_view| may not be in a Widget (e.g. when switching
// into immersive reveal the BrowserView's TopContainerView is reparented).
if (tab_strip_region_view->GetWidget()) {
gfx::RectF rect_in_region_view_coords_f(rect);
View::ConvertRectToTarget(this, tab_strip_region_view,
&rect_in_region_view_coords_f);
gfx::Rect rect_in_region_view_coords =
gfx::ToEnclosingRect(rect_in_region_view_coords_f);
if (rect_in_region_view_coords.y() >=
tab_strip_region_view->GetLocalBounds().bottom()) {
// |rect| is below the tab_strip_region_view.
return false;
}
if (tab_strip_region_view->HitTestRect(rect_in_region_view_coords)) {
// Claim |rect| if it is in a non-tab portion of the tabstrip.
return tab_strip_region_view->IsRectInWindowCaption(
rect_in_region_view_coords);
}
}
// We claim |rect| because it is above the bottom of the tabstrip, but
// not in the tabstrip itself.
return !should_leave_to_top_container;
}
void BrowserNonClientFrameView::OnProfileAdded(
const base::FilePath& profile_path) {
OnProfileAvatarChanged(profile_path);

@ -157,8 +157,6 @@ class BrowserNonClientFrameView : public views::NonClientFrameView,
// views::NonClientFrameView:
void ChildPreferredSizeChanged(views::View* child) override;
bool DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const override;
// ProfileAttributesStorage::Observer:
void OnProfileAdded(const base::FilePath& profile_path) override;

@ -401,6 +401,27 @@ void BrowserNonClientFrameViewChromeOS::ChildPreferredSizeChanged(
}
}
bool BrowserNonClientFrameViewChromeOS::DoesIntersectRect(
const views::View* target,
const gfx::Rect& rect) const {
DCHECK_EQ(target, this);
if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect)) {
// |rect| is outside the frame's bounds.
return false;
}
bool should_leave_to_top_container = false;
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
// In immersive mode, the caption buttons container is reparented to the
// TopContainerView and hence |rect| should not be claimed here. See
// BrowserNonClientFrameViewChromeOS::OnImmersiveRevealStarted().
should_leave_to_top_container =
browser_view()->immersive_mode_controller()->IsRevealed();
#endif
return !should_leave_to_top_container;
}
SkColor BrowserNonClientFrameViewChromeOS::GetTitleColor() {
return browser_view()->GetRegularOrGuestSession()
? kNormalWindowTitleTextColor
@ -561,7 +582,13 @@ void BrowserNonClientFrameViewChromeOS::OnImmersiveRevealStarted() {
}
void BrowserNonClientFrameViewChromeOS::OnImmersiveRevealEnded() {
AddChildViewAt(caption_button_container_, 0);
// Ensure the caption button container receives events before the browser view
// by placing it higher in the z-order.
// [0] - FrameAnimatorView
// [1] - BrowserView
// [2] - FrameCaptionButtonContainerView
const int kCaptionButtonContainerIndex = 2;
AddChildViewAt(caption_button_container_, kCaptionButtonContainerIndex);
if (web_app_frame_toolbar())
AddChildViewAt(web_app_frame_toolbar(), 0);
Layout();

@ -79,6 +79,8 @@ class BrowserNonClientFrameViewChromeOS
gfx::Size GetMinimumSize() const override;
void OnThemeChanged() override;
void ChildPreferredSizeChanged(views::View* child) override;
bool DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const override;
// BrowserFrameHeaderChromeOS::AppearanceProvider:
SkColor GetTitleColor() override;

@ -338,6 +338,8 @@ void BrowserNonClientFrameViewMac::OnPaint(gfx::Canvas* canvas) {
}
void BrowserNonClientFrameViewMac::Layout() {
NonClientFrameView::Layout();
const int available_height = GetTopInset(true);
int leading_x = kFramePaddingLeft;
int trailing_x = width();

@ -61,12 +61,15 @@ class BrowserNonClientFrameViewPopupTest
#define MAYBE_HitTestPopupTopChrome HitTestPopupTopChrome
#endif
TEST_F(BrowserNonClientFrameViewPopupTest, MAYBE_HitTestPopupTopChrome) {
EXPECT_FALSE(frame_view_->HitTestRect(gfx::Rect(-1, 4, 1, 1)));
EXPECT_FALSE(frame_view_->HitTestRect(gfx::Rect(4, -1, 1, 1)));
constexpr gfx::Rect kLeftOfFrame(-1, 4, 1, 1);
EXPECT_FALSE(frame_view_->HitTestRect(kLeftOfFrame));
constexpr gfx::Rect kAboveFrame(4, -1, 1, 1);
EXPECT_FALSE(frame_view_->HitTestRect(kAboveFrame));
const int top_inset = frame_view_->GetTopInset(false);
EXPECT_FALSE(frame_view_->HitTestRect(gfx::Rect(4, top_inset, 1, 1)));
if (top_inset > 0)
EXPECT_TRUE(frame_view_->HitTestRect(gfx::Rect(4, top_inset - 1, 1, 1)));
const gfx::Rect in_browser_view(4, top_inset, 1, 1);
EXPECT_TRUE(frame_view_->HitTestRect(in_browser_view));
}
class BrowserNonClientFrameViewTabbedTest
@ -108,7 +111,9 @@ TEST_F(BrowserNonClientFrameViewTabbedTest, MAYBE_HitTestTabstrip) {
// Hits client portions of the tabstrip (near the bottom left corner of the
// first tab).
EXPECT_FALSE(frame_view_->HitTestRect(gfx::Rect(
EXPECT_TRUE(frame_view_->HitTestRect(gfx::Rect(
tabstrip_bounds.x() + 10, tabstrip_bounds.bottom() - 10, 1, 1)));
EXPECT_TRUE(frame_view_->browser_view()->HitTestRect(gfx::Rect(
tabstrip_bounds.x() + 10, tabstrip_bounds.bottom() - 10, 1, 1)));
// Tabs extend to the top of the tabstrip everywhere in this test context on

@ -2929,6 +2929,12 @@ void BrowserView::ViewHierarchyChanged(
}
void BrowserView::AddedToWidget() {
// BrowserView may be added to a widget more than once if the user changes
// themes after starting the browser. Do not re-initialize BrowserView in
// this case.
if (initialized_)
return;
views::ClientView::AddedToWidget();
widget_observation_.Observe(GetWidget());

@ -403,6 +403,7 @@ void GlassBrowserFrameView::Layout() {
LayoutCaptionButtons();
LayoutTitleBar();
LayoutClientView();
NonClientFrameView::Layout();
}
///////////////////////////////////////////////////////////////////////////////

@ -18,6 +18,7 @@
#include "ui/gfx/font.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/view_utils.h"
#include "ui/views/window/caption_button_layout_constants.h"
#include "ui/views/window/frame_caption_button.h"
@ -606,10 +607,20 @@ gfx::Size OpaqueBrowserFrameViewLayout::GetPreferredSize(
void OpaqueBrowserFrameViewLayout::ViewAdded(views::View* host,
views::View* view) {
if (views::IsViewClass<views::ClientView>(view)) {
client_view_ = static_cast<views::ClientView*>(view);
return;
}
SetView(view->GetID(), view);
}
void OpaqueBrowserFrameViewLayout::ViewRemoved(views::View* host,
views::View* view) {
if (views::IsViewClass<views::ClientView>(view)) {
client_view_ = nullptr;
return;
}
SetView(view->GetID(), nullptr);
}

@ -217,6 +217,8 @@ class OpaqueBrowserFrameViewLayout : public views::LayoutManager {
std::vector<views::FrameButton> leading_buttons_;
std::vector<views::FrameButton> trailing_buttons_;
views::ClientView* client_view_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(OpaqueBrowserFrameViewLayout);
};

@ -29,6 +29,7 @@ WebAppFrameToolbarView::WebAppFrameToolbarView(views::Widget* widget,
DCHECK(browser_view_);
DCHECK(web_app::AppBrowserController::IsWebApp(browser_view_->browser()));
SetID(VIEW_ID_WEB_APP_FRAME_TOOLBAR);
SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
{
// TODO(tluk) fix the need for both LayoutInContainer() and a layout
@ -219,6 +220,24 @@ ReloadButton* WebAppFrameToolbarView::GetReloadButton() {
return left_container_ ? left_container_->reload_button() : nullptr;
}
bool WebAppFrameToolbarView::DoesIntersectRect(const View* target,
const gfx::Rect& rect) const {
DCHECK_EQ(target, this);
if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect))
return false;
// If the rect is inside the bounds of the center_container, do not claim it.
// There is no actionable content in the center_container, and it overlaps
// tabs in tabbed PWA windows.
gfx::RectF rect_in_center_container_coords_f(rect);
View::ConvertRectToTarget(this, center_container_,
&rect_in_center_container_coords_f);
gfx::Rect rect_in_client_view_coords =
gfx::ToEnclosingRect(rect_in_center_container_coords_f);
return !center_container_->HitTestRect(rect_in_client_view_coords);
}
PageActionIconController*
WebAppFrameToolbarView::GetPageActionIconControllerForTesting() {
return right_container_->page_action_icon_controller();

@ -15,9 +15,11 @@
#include "ui/gfx/color_palette.h"
#include "ui/views/accessible_pane_view.h"
#include "ui/views/metadata/metadata_header_macros.h"
#include "ui/views/view_targeter_delegate.h"
namespace views {
class View;
class ViewTargeterDelegate;
class Widget;
} // namespace views
@ -29,7 +31,8 @@ class WebAppToolbarButtonContainer;
// A container for web app buttons in the title bar.
class WebAppFrameToolbarView : public views::AccessiblePaneView,
public ToolbarButtonProvider {
public ToolbarButtonProvider,
public views::ViewTargeterDelegate {
public:
METADATA_HEADER(WebAppFrameToolbarView);
WebAppFrameToolbarView(views::Widget* widget, BrowserView* browser_view);
@ -71,6 +74,10 @@ class WebAppFrameToolbarView : public views::AccessiblePaneView,
ToolbarButton* GetBackButton() override;
ReloadButton* GetReloadButton() override;
// views::ViewTargeterDelegate
bool DoesIntersectRect(const View* target,
const gfx::Rect& rect) const override;
WebAppNavigationButtonContainer* get_left_container_for_testing() {
return left_container_;
}

@ -1024,7 +1024,7 @@ void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) {
gfx::Rect ClientControlledShellSurface::GetShadowBounds() const {
gfx::Rect shadow_bounds = ShellSurfaceBase::GetShadowBounds();
const ash::NonClientFrameViewAsh* frame_view = GetFrameView();
if (frame_view->GetVisible()) {
if (frame_view->GetFrameEnabled()) {
// The client controlled geometry is only for the client
// area. When the chrome side frame is enabled, the shadow height
// has to include the height of the frame, and the total height is
@ -1083,7 +1083,7 @@ float ClientControlledShellSurface::GetScale() const {
base::Optional<gfx::Rect> ClientControlledShellSurface::GetWidgetBounds()
const {
const ash::NonClientFrameViewAsh* frame_view = GetFrameView();
if (frame_view->GetVisible()) {
if (frame_view->GetFrameEnabled()) {
gfx::Rect visible_bounds = ShellSurfaceBase::GetVisibleBounds();
if (widget_->IsMaximized() && frame_type_ == SurfaceFrameType::NORMAL) {
// When the widget is maximized in clamshell mode, client sends
@ -1262,7 +1262,7 @@ void ClientControlledShellSurface::UpdateFrame() {
.work_area();
ash::WindowState* window_state = GetWindowState();
bool enable_wide_frame = GetFrameView()->GetVisible() &&
bool enable_wide_frame = GetFrameView()->GetFrameEnabled() &&
window_state->IsMaximizedOrFullscreenOrPinned() &&
work_area.width() != geometry().width();
bool update_frame = state_changed_;

@ -517,7 +517,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
// Normal state.
widget->LayoutRootViewIfNecessary();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_EQ(normal_window_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(client_bounds,
frame_view->GetClientBoundsForWindowBounds(normal_window_bounds));
@ -528,7 +528,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(
gfx::Size(800, 568),
@ -541,7 +541,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_EQ(gfx::Rect(0, 200, 800, 400), widget->GetWindowBoundsInScreen());
display_manager->UpdateWorkAreaOfDisplay(display_id, gfx::Insets(0, 0, 0, 0));
@ -552,7 +552,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(fullscreen_bounds,
frame_view->GetClientBoundsForWindowBounds(fullscreen_bounds));
@ -562,7 +562,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(fullscreen_bounds,
frame_view->GetClientBoundsForWindowBounds(fullscreen_bounds));
@ -587,7 +587,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_EQ(normal_window_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(client_bounds,
frame_view->GetClientBoundsForWindowBounds(normal_window_bounds));
@ -599,7 +599,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_FALSE(frame_view->GetVisible());
EXPECT_FALSE(frame_view->GetFrameEnabled());
EXPECT_EQ(client_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(client_bounds,
frame_view->GetClientBoundsForWindowBounds(client_bounds));
@ -611,14 +611,14 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_TRUE(frame_view->GetHeaderView()->in_immersive_mode());
surface->SetFrame(SurfaceFrameType::NONE);
surface->Commit();
widget->LayoutRootViewIfNecessary();
EXPECT_FALSE(frame_view->GetVisible());
EXPECT_FALSE(frame_view->GetFrameEnabled());
EXPECT_FALSE(frame_view->GetHeaderView()->in_immersive_mode());
}
@ -2015,7 +2015,7 @@ TEST_F(ClientControlledShellSurfaceTest, SnappedInTabletMode) {
// Snapped window can also use auto hide.
surface->SetFrame(SurfaceFrameType::AUTOHIDE);
surface->Commit();
EXPECT_TRUE(frame_view->GetVisible());
EXPECT_TRUE(frame_view->GetFrameEnabled());
EXPECT_TRUE(frame_view->GetHeaderView()->in_immersive_mode());
}

@ -111,8 +111,7 @@ class CustomFrameView : public ash::NonClientFrameViewAsh {
ShellSurfaceBase* shell_surface,
bool enabled)
: NonClientFrameViewAsh(widget), shell_surface_(shell_surface) {
SetEnabled(enabled);
SetVisible(enabled);
SetFrameEnabled(enabled);
if (!enabled)
NonClientFrameViewAsh::SetShouldPaintHeader(false);
}
@ -121,7 +120,7 @@ class CustomFrameView : public ash::NonClientFrameViewAsh {
// Overridden from ash::NonClientFrameViewAsh:
void SetShouldPaintHeader(bool paint) override {
if (GetVisible()) {
if (GetFrameEnabled()) {
NonClientFrameViewAsh::SetShouldPaintHeader(paint);
return;
}
@ -129,46 +128,46 @@ class CustomFrameView : public ash::NonClientFrameViewAsh {
// Overridden from views::NonClientFrameView:
gfx::Rect GetBoundsForClientView() const override {
if (GetVisible())
if (GetFrameEnabled())
return ash::NonClientFrameViewAsh::GetBoundsForClientView();
return bounds();
}
gfx::Rect GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const override {
if (GetVisible()) {
if (GetFrameEnabled()) {
return ash::NonClientFrameViewAsh::GetWindowBoundsForClientBounds(
client_bounds);
}
return client_bounds;
}
int NonClientHitTest(const gfx::Point& point) override {
if (GetVisible() || shell_surface_->server_side_resize())
if (GetFrameEnabled() || shell_surface_->server_side_resize())
return ash::NonClientFrameViewAsh::NonClientHitTest(point);
return GetWidget()->client_view()->NonClientHitTest(point);
}
void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override {
if (GetVisible())
if (GetFrameEnabled())
return ash::NonClientFrameViewAsh::GetWindowMask(size, window_mask);
}
void ResetWindowControls() override {
if (GetVisible())
if (GetFrameEnabled())
return ash::NonClientFrameViewAsh::ResetWindowControls();
}
void UpdateWindowIcon() override {
if (GetVisible())
if (GetFrameEnabled())
return ash::NonClientFrameViewAsh::ResetWindowControls();
}
void UpdateWindowTitle() override {
if (GetVisible())
if (GetFrameEnabled())
return ash::NonClientFrameViewAsh::UpdateWindowTitle();
}
void SizeConstraintsChanged() override {
if (GetVisible())
if (GetFrameEnabled())
return ash::NonClientFrameViewAsh::SizeConstraintsChanged();
}
gfx::Size GetMinimumSize() const override {
gfx::Size minimum_size = shell_surface_->GetMinimumSize();
if (GetVisible()) {
if (GetFrameEnabled()) {
return ash::NonClientFrameViewAsh::GetWindowBoundsForClientBounds(
gfx::Rect(minimum_size))
.size();
@ -177,7 +176,7 @@ class CustomFrameView : public ash::NonClientFrameViewAsh {
}
gfx::Size GetMaximumSize() const override {
gfx::Size maximum_size = shell_surface_->GetMaximumSize();
if (GetVisible() && !maximum_size.IsEmpty()) {
if (GetFrameEnabled() && !maximum_size.IsEmpty()) {
return ash::NonClientFrameViewAsh::GetWindowBoundsForClientBounds(
gfx::Rect(maximum_size))
.size();
@ -710,11 +709,10 @@ void ShellSurfaceBase::OnSetFrame(SurfaceFrameType frame_type) {
CustomFrameView* frame_view =
static_cast<CustomFrameView*>(widget_->non_client_view()->frame_view());
if (frame_view->GetEnabled() == frame_enabled())
if (frame_view->GetFrameEnabled() == frame_enabled())
return;
frame_view->SetEnabled(frame_enabled());
frame_view->SetVisible(frame_enabled());
frame_view->SetFrameEnabled(frame_enabled());
frame_view->SetShouldPaintHeader(frame_enabled());
widget_->GetRootView()->Layout();
// TODO(oshima): We probably should wait applying these if the

@ -114,12 +114,14 @@ class OverlayAgentTest : public views::ViewsTestBase {
}
#endif
void CreateWidget(const gfx::Rect& bounds) {
void CreateWidget(const gfx::Rect& bounds,
views::Widget::InitParams::Type type) {
widget_ = std::make_unique<views::Widget>();
views::Widget::InitParams params;
params.delegate = nullptr;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = bounds;
params.type = type;
#if defined(USE_AURA)
params.parent = GetContext();
#endif
@ -129,7 +131,8 @@ class OverlayAgentTest : public views::ViewsTestBase {
void CreateWidget() {
// Create a widget with default bounds.
return CreateWidget(gfx::Rect(0, 0, 400, 400));
return CreateWidget(gfx::Rect(0, 0, 400, 400),
views::Widget::InitParams::Type::TYPE_WINDOW);
}
views::Widget* widget() { return widget_.get(); }
@ -176,13 +179,14 @@ TEST_F(OverlayAgentTest, FindElementIdTargetedByPointWindow) {
#endif
TEST_F(OverlayAgentTest, FindElementIdTargetedByPointViews) {
CreateWidget();
// Use a frameless window instead of deleting all children of |contents_view|
CreateWidget(gfx::Rect(0, 0, 400, 400),
views::Widget::InitParams::Type::TYPE_WINDOW_FRAMELESS);
std::unique_ptr<protocol::DOM::Node> root;
dom_agent()->getDocument(&root);
views::View* contents_view = widget()->GetContentsView();
contents_view->RemoveAllChildViews(true);
views::View* contents_view = widget()->GetRootView();
views::View* child_1 = new views::View;
views::View* child_2 = new views::View;
@ -203,7 +207,7 @@ TEST_F(OverlayAgentTest, FindElementIdTargetedByPointViews) {
child_1->SetBounds(20, 20, 100, 100);
child_2->SetBounds(90, 50, 100, 100);
EXPECT_EQ(GetViewAtPoint(1, 1), widget()->GetContentsView());
EXPECT_EQ(GetViewAtPoint(1, 1), widget()->GetRootView());
EXPECT_EQ(GetViewAtPoint(21, 21), child_1);
EXPECT_EQ(GetViewAtPoint(170, 130), child_2);
// At the overlap.
@ -237,7 +241,7 @@ TEST_F(OverlayAgentTest, HighlightRects) {
for (const auto& test_case : kTestCases) {
SCOPED_TRACE(testing::Message() << "Case: " << test_case.name);
CreateWidget(kWidgetBounds);
CreateWidget(kWidgetBounds, views::Widget::InitParams::Type::TYPE_WINDOW);
// Can't just use kWidgetBounds because of Mac's menu bar.
gfx::Vector2d widget_screen_offset =
widget()->GetClientAreaBoundsInScreen().OffsetFromOrigin();

@ -18,5 +18,5 @@ declare_args() {
# This may be set by Chromium packagers who do not wish to use the bundled
# wayland scanner.
use_system_wayland_scanner = (host_toolchain == default_toolchain && is_msan)
use_system_wayland_scanner = host_toolchain == default_toolchain && is_msan
}

@ -1196,7 +1196,6 @@ test("views_unittests") {
"window/dialog_delegate_unittest.cc",
"window/frame_caption_button_unittest.cc",
"window/hit_test_utils_unittest.cc",
"window/non_client_view_unittest.cc",
]
configs += [ "//build/config:precompiled_headers" ]

@ -115,7 +115,8 @@ class ViewAXPlatformNodeDelegateTest : public ViewsTestBase {
ui::AXPlatformNode::NotifyAddAXModeFlags(ui::kAXModeComplete);
widget_ = new Widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
Widget::InitParams params =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.bounds = gfx::Rect(0, 0, 200, 200);
widget_->Init(std::move(params));
@ -127,7 +128,7 @@ class ViewAXPlatformNodeDelegateTest : public ViewsTestBase {
label_->SetID(DEFAULT_VIEW_ID);
button_->AddChildView(label_);
widget_->GetContentsView()->AddChildView(button_);
widget_->GetRootView()->AddChildView(button_);
widget_->Show();
}
@ -163,7 +164,7 @@ class ViewAXPlatformNodeDelegateTest : public ViewsTestBase {
// child Views.
View::Views SetUpExtraViews() {
View* parent_view =
widget_->GetContentsView()->AddChildView(std::make_unique<View>());
widget_->GetRootView()->AddChildView(std::make_unique<View>());
View::Views views{parent_view};
for (int i = 0; i < 4; i++)
views.push_back(parent_view->AddChildView(std::make_unique<View>()));
@ -223,7 +224,7 @@ class ViewAXPlatformNodeDelegateTableTest
auto table =
std::make_unique<TableView>(model_.get(), columns, TEXT_ONLY, true);
table_ = table.get();
widget_->GetContentsView()->AddChildView(
widget_->GetRootView()->AddChildView(
TableView::CreateScrollViewWithTable(std::move(table)));
}
@ -536,12 +537,12 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigation) {
ViewAXPlatformNodeDelegate* child_view_3 = view_accessibility(extra_views[3]);
ViewAXPlatformNodeDelegate* child_view_4 = view_accessibility(extra_views[4]);
EXPECT_EQ(view_accessibility(widget_->GetContentsView())->GetNativeObject(),
EXPECT_EQ(view_accessibility(widget_->GetRootView())->GetNativeObject(),
parent_view->GetParent());
EXPECT_EQ(4, parent_view->GetChildCount());
EXPECT_EQ(2, button_accessibility()->GetIndexInParent());
EXPECT_EQ(3, parent_view->GetIndexInParent());
EXPECT_EQ(0, button_accessibility()->GetIndexInParent());
EXPECT_EQ(1, parent_view->GetIndexInParent());
EXPECT_EQ(child_view_1->GetNativeObject(), parent_view->ChildAtIndex(0));
EXPECT_EQ(child_view_2->GetNativeObject(), parent_view->ChildAtIndex(1));
@ -585,8 +586,6 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithLeafViews) {
// view is added as the next sibling of the already present button view.
//
// Widget
// ++NonClientView
// ++NonClientFrameView
// ++Button
// ++++Label
// 0 = ++ParentView
@ -596,7 +595,7 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithLeafViews) {
// 4 = ++++ChildView4
View::Views extra_views = SetUpExtraViews();
ViewAXPlatformNodeDelegate* contents_view =
view_accessibility(widget_->GetContentsView());
view_accessibility(widget_->GetRootView());
ViewAXPlatformNodeDelegate* parent_view = view_accessibility(extra_views[0]);
ViewAXPlatformNodeDelegate* child_view_1 = view_accessibility(extra_views[1]);
ViewAXPlatformNodeDelegate* child_view_2 = view_accessibility(extra_views[2]);
@ -610,12 +609,12 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithLeafViews) {
parent_view->OverrideIsLeaf(true);
child_view_2->OverrideIsLeaf(true);
EXPECT_EQ(4, contents_view->GetChildCount());
EXPECT_EQ(2, contents_view->GetChildCount());
EXPECT_EQ(contents_view->GetNativeObject(), parent_view->GetParent());
EXPECT_EQ(0, parent_view->GetChildCount());
EXPECT_EQ(2, button_accessibility()->GetIndexInParent());
EXPECT_EQ(3, parent_view->GetIndexInParent());
EXPECT_EQ(0, button_accessibility()->GetIndexInParent());
EXPECT_EQ(1, parent_view->GetIndexInParent());
EXPECT_FALSE(contents_view->IsIgnored());
EXPECT_FALSE(parent_view->IsIgnored());
@ -647,12 +646,12 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithLeafViews) {
// have no effect.
parent_view->OverrideIsLeaf(false);
EXPECT_EQ(4, contents_view->GetChildCount());
EXPECT_EQ(2, contents_view->GetChildCount());
EXPECT_EQ(contents_view->GetNativeObject(), parent_view->GetParent());
EXPECT_EQ(4, parent_view->GetChildCount());
EXPECT_EQ(2, button_accessibility()->GetIndexInParent());
EXPECT_EQ(3, parent_view->GetIndexInParent());
EXPECT_EQ(0, button_accessibility()->GetIndexInParent());
EXPECT_EQ(1, parent_view->GetIndexInParent());
EXPECT_FALSE(contents_view->IsIgnored());
EXPECT_FALSE(parent_view->IsIgnored());
@ -691,8 +690,6 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithIgnoredViews) {
// view is added as the next sibling of the already present button view.
//
// Widget
// ++NonClientView
// ++NonClientFrameView
// ++Button
// ++++Label
// 0 = ++ParentView
@ -702,7 +699,7 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithIgnoredViews) {
// 4 = ++++ChildView4
View::Views extra_views = SetUpExtraViews();
ViewAXPlatformNodeDelegate* contents_view =
view_accessibility(widget_->GetContentsView());
view_accessibility(widget_->GetRootView());
ViewAXPlatformNodeDelegate* parent_view = view_accessibility(extra_views[0]);
ViewAXPlatformNodeDelegate* child_view_1 = view_accessibility(extra_views[1]);
ViewAXPlatformNodeDelegate* child_view_2 = view_accessibility(extra_views[2]);
@ -716,7 +713,7 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithIgnoredViews) {
EXPECT_EQ(contents_view->GetNativeObject(), parent_view->GetParent());
EXPECT_EQ(3, parent_view->GetChildCount());
EXPECT_EQ(2, button_accessibility()->GetIndexInParent());
EXPECT_EQ(0, button_accessibility()->GetIndexInParent());
EXPECT_EQ(-1, parent_view->GetIndexInParent());
EXPECT_EQ(child_view_1->GetNativeObject(), parent_view->ChildAtIndex(0));
@ -724,17 +721,17 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithIgnoredViews) {
EXPECT_EQ(child_view_4->GetNativeObject(), parent_view->ChildAtIndex(2));
EXPECT_EQ(button_accessibility()->GetNativeObject(),
contents_view->ChildAtIndex(2));
EXPECT_EQ(child_view_1->GetNativeObject(), contents_view->ChildAtIndex(3));
EXPECT_EQ(child_view_3->GetNativeObject(), contents_view->ChildAtIndex(4));
EXPECT_EQ(child_view_4->GetNativeObject(), contents_view->ChildAtIndex(5));
contents_view->ChildAtIndex(0));
EXPECT_EQ(child_view_1->GetNativeObject(), contents_view->ChildAtIndex(1));
EXPECT_EQ(child_view_3->GetNativeObject(), contents_view->ChildAtIndex(2));
EXPECT_EQ(child_view_4->GetNativeObject(), contents_view->ChildAtIndex(3));
EXPECT_EQ(nullptr, parent_view->GetNextSibling());
EXPECT_EQ(nullptr, parent_view->GetPreviousSibling());
EXPECT_EQ(contents_view->GetNativeObject(), child_view_1->GetParent());
EXPECT_EQ(0, child_view_1->GetChildCount());
EXPECT_EQ(3, child_view_1->GetIndexInParent());
EXPECT_EQ(1, child_view_1->GetIndexInParent());
EXPECT_EQ(child_view_3->GetNativeObject(), child_view_1->GetNextSibling());
EXPECT_EQ(button_accessibility()->GetNativeObject(),
child_view_1->GetPreviousSibling());
@ -747,14 +744,14 @@ TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithIgnoredViews) {
EXPECT_EQ(contents_view->GetNativeObject(), child_view_3->GetParent());
EXPECT_EQ(0, child_view_3->GetChildCount());
EXPECT_EQ(4, child_view_3->GetIndexInParent());
EXPECT_EQ(2, child_view_3->GetIndexInParent());
EXPECT_EQ(child_view_4->GetNativeObject(), child_view_3->GetNextSibling());
EXPECT_EQ(child_view_1->GetNativeObject(),
child_view_3->GetPreviousSibling());
EXPECT_EQ(contents_view->GetNativeObject(), child_view_4->GetParent());
EXPECT_EQ(0, child_view_4->GetChildCount());
EXPECT_EQ(5, child_view_4->GetIndexInParent());
EXPECT_EQ(3, child_view_4->GetIndexInParent());
EXPECT_EQ(nullptr, child_view_4->GetNextSibling());
EXPECT_EQ(child_view_3->GetNativeObject(),
child_view_4->GetPreviousSibling());

@ -391,8 +391,20 @@ void BubbleFrameView::Layout() {
header_bottom = header_view_->bounds().bottom();
}
if (bounds.IsEmpty())
// Only account for footnote_container_'s height if it's visible, because
// content_margins_ adds extra padding even if all child views are invisible.
if (footnote_container_ && footnote_container_->GetVisible()) {
const int width = contents_bounds.width();
const int height = footnote_container_->GetHeightForWidth(width);
footnote_container_->SetBounds(
contents_bounds.x(), contents_bounds.bottom() - height, width, height);
}
NonClientFrameView::Layout();
if (bounds.IsEmpty()) {
return;
}
// The buttons are positioned somewhat closer to the edge of the bubble.
const int close_margin =
@ -442,15 +454,6 @@ void BubbleFrameView::Layout() {
title_icon_->SetBounds(bounds.x(), bounds.y(), title_icon_pref_size.width(),
title_height);
// Only account for footnote_container_'s height if it's visible, because
// content_margins_ adds extra padding even if all child views are invisible.
if (footnote_container_ && footnote_container_->GetVisible()) {
const int width = contents_bounds.width();
const int height = footnote_container_->GetHeightForWidth(width);
footnote_container_->SetBounds(
contents_bounds.x(), contents_bounds.bottom() - height, width, height);
}
}
void BubbleFrameView::OnThemeChanged() {

@ -195,7 +195,7 @@ class DragDropClientMacTest : public WidgetTest {
widget_->Show();
target_ = new DragDropView();
widget_->GetContentsView()->AddChildView(target_);
widget_->non_client_view()->frame_view()->AddChildView(target_);
target_->SetBoundsRect(bounds);
drag_drop_client()->source_operation_ = ui::DragDropTypes::DRAG_COPY;
@ -329,7 +329,7 @@ TEST_F(DragDropClientMacTest, CloseWidgetOnDrop) {
SetData(data);
target_ = new DragDropCloseView();
widget_->GetContentsView()->AddChildView(target_);
widget_->non_client_view()->frame_view()->AddChildView(target_);
target_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
target_->set_formats(ui::OSExchangeData::STRING | ui::OSExchangeData::URL);

@ -444,12 +444,13 @@ class TableViewTest : public ViewsTestBase,
helper_ = std::make_unique<TableViewTestHelper>(table_);
widget_ = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
Widget::InitParams params =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(0, 0, 650, 650);
params.delegate = GetWidgetDelegate(widget_.get());
widget_->Init(std::move(params));
widget_->GetContentsView()->AddChildView(std::move(scroll_view));
widget_->GetRootView()->AddChildView(std::move(scroll_view));
widget_->Show();
}

@ -117,7 +117,7 @@ class ViewMacTest : public test::WidgetTest {
view_ = new ThreeFingerSwipeView;
view_->SetSize(widget_->GetClientAreaBoundsInScreen().size());
widget_->GetContentsView()->AddChildView(view_);
widget_->non_client_view()->frame_view()->AddChildView(view_);
}
void TearDown() override {

@ -573,8 +573,10 @@ TEST_F(NativeWidgetMacTest, SetCursor) {
Widget* widget = CreateTopLevelPlatformWidget();
widget->SetBounds(gfx::Rect(0, 0, 300, 300));
widget->GetContentsView()->AddChildView(new CursorView(0, hand));
widget->GetContentsView()->AddChildView(new CursorView(100, ibeam));
widget->non_client_view()->frame_view()->AddChildView(
new CursorView(0, hand));
widget->non_client_view()->frame_view()->AddChildView(
new CursorView(100, ibeam));
widget->Show();
NSWindow* widget_window = widget->GetNativeWindow().GetNativeNSWindow();
@ -879,8 +881,8 @@ TEST_F(NativeWidgetMacTest, Tooltips) {
const std::u16string long_tooltip(2000, 'W');
// Create a nested layout to test corner cases.
LabelButton* back =
widget->GetContentsView()->AddChildView(std::make_unique<LabelButton>());
LabelButton* back = widget->non_client_view()->frame_view()->AddChildView(
std::make_unique<LabelButton>());
back->SetBounds(10, 10, 80, 80);
widget->Show();
@ -944,7 +946,7 @@ TEST_F(NativeWidgetMacTest, TwoWidgetTooltips) {
CustomTooltipView* view_below = new CustomTooltipView(u"Back", view_above);
view_below->SetBoundsRect(widget_below->GetContentsView()->bounds());
widget_below->GetContentsView()->AddChildView(view_below);
widget_below->non_client_view()->frame_view()->AddChildView(view_below);
widget_below->Show();
widget_above->Show();

@ -3773,7 +3773,7 @@ TEST_F(WidgetTest, MouseWheelEvent) {
WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
widget->SetBounds(gfx::Rect(0, 0, 600, 600));
EventCountView* event_count_view = new EventCountView();
widget->GetContentsView()->AddChildView(event_count_view);
widget->client_view()->AddChildView(event_count_view);
event_count_view->SetBounds(0, 0, 600, 600);
widget->Show();
@ -3784,81 +3784,6 @@ TEST_F(WidgetTest, MouseWheelEvent) {
EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSEWHEEL));
}
class LayoutCountingView : public View {
public:
LayoutCountingView() = default;
~LayoutCountingView() override = default;
void set_layout_closure(base::OnceClosure layout_closure) {
layout_closure_ = std::move(layout_closure);
}
size_t GetAndClearLayoutCount() {
const size_t count = layout_count_;
layout_count_ = 0u;
return count;
}
// View:
void Layout() override {
++layout_count_;
View::Layout();
if (layout_closure_)
std::move(layout_closure_).Run();
}
private:
size_t layout_count_ = 0u;
// If valid, this is run when Layout() is called.
base::OnceClosure layout_closure_;
DISALLOW_COPY_AND_ASSIGN(LayoutCountingView);
};
using WidgetInvalidateLayoutTest = ViewsTestBaseWithNativeWidgetType;
TEST_P(WidgetInvalidateLayoutTest, InvalidateLayout) {
std::unique_ptr<Widget> widget =
CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
LayoutCountingView* view =
widget->widget_delegate()->GetContentsView()->AddChildView(
std::make_unique<LayoutCountingView>());
view->parent()->SetLayoutManager(std::make_unique<FillLayout>());
// Force an initial Layout().
// TODO(sky): this shouldn't be necessary, adding a child view should trigger
// ScheduleLayout().
view->Layout();
widget->Show();
ui::Compositor* compositor = widget->GetCompositor();
ASSERT_TRUE(compositor);
compositor->ScheduleDraw();
ui::DrawWaiterForTest::WaitForCompositingEnded(compositor);
base::RunLoop run_loop;
view->GetAndClearLayoutCount();
// Don't use WaitForCompositingEnded() here as it's entirely possible nothing
// will be drawn (which means WaitForCompositingEnded() isn't run). Instead
// wait for Layout() to be called.
view->set_layout_closure(run_loop.QuitClosure());
EXPECT_FALSE(ViewTestApi(view).needs_layout());
EXPECT_FALSE(ViewTestApi(widget->GetRootView()).needs_layout());
view->InvalidateLayout();
EXPECT_TRUE(ViewTestApi(view).needs_layout());
EXPECT_TRUE(ViewTestApi(widget->GetRootView()).needs_layout());
run_loop.Run();
EXPECT_EQ(1u, view->GetAndClearLayoutCount());
EXPECT_FALSE(ViewTestApi(view).needs_layout());
EXPECT_FALSE(ViewTestApi(widget->GetRootView()).needs_layout());
}
INSTANTIATE_TEST_SUITE_P(
WidgetInvalidateLayoutTest,
WidgetInvalidateLayoutTest,
::testing::Values(ViewsTestBase::NativeWidgetType::kDefault,
ViewsTestBase::NativeWidgetType::kDesktop));
class WidgetShadowTest : public WidgetTest {
public:
WidgetShadowTest() = default;

@ -87,8 +87,6 @@ void ClientView::ViewHierarchyChanged(
// TODO(weili): This seems fragile and can be refactored.
// Tracked at https://crbug.com/1012466.
AddChildViewAt(contents_view_, 0);
} else if (!details.is_add && details.child == contents_view_) {
contents_view_ = nullptr;
}
}

@ -211,6 +211,7 @@ void CustomFrameView::Layout() {
}
LayoutClientView();
NonClientFrameView::Layout();
}
gfx::Size CustomFrameView::CalculatePreferredSize() const {

@ -11,6 +11,7 @@
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/hit_test.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/metadata/metadata_impl_macros.h"
#include "ui/views/rect_based_targeting_utils.h"
#include "ui/views/view_targeter.h"
@ -24,18 +25,6 @@
namespace views {
namespace {
// The frame view and the client view are always at these specific indices,
// because the RootView message dispatch sends messages to items higher in the
// z-order first and we always want the client view to have first crack at
// handling mouse messages.
constexpr int kFrameViewIndex = 0;
constexpr int kClientViewIndex = 1;
// The overlay view is always on top (view == children().back()).
} // namespace
NonClientFrameView::~NonClientFrameView() = default;
bool NonClientFrameView::ShouldPaintAsActive() const {
@ -129,18 +118,19 @@ void NonClientFrameView::OnThemeChanged() {
SchedulePaint();
}
NonClientFrameView::NonClientFrameView() {
SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
void NonClientFrameView::Layout() {
if (GetLayoutManager())
GetLayoutManager()->Layout(this);
views::ClientView* client_view = GetWidget()->client_view();
client_view->SetBoundsRect(GetBoundsForClientView());
SkPath client_clip;
if (GetClientMask(client_view->size(), &client_clip))
client_view->SetClipPath(client_clip);
}
// ViewTargeterDelegate:
bool NonClientFrameView::DoesIntersectRect(const View* target,
const gfx::Rect& rect) const {
CHECK_EQ(target, this);
// For the default case, we assume the non-client frame view never overlaps
// the client view.
return !GetWidget()->client_view()->bounds().Intersects(rect);
NonClientFrameView::NonClientFrameView() {
SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
}
#if defined(OS_WIN)
@ -165,13 +155,18 @@ NonClientView::~NonClientView() {
void NonClientView::SetFrameView(
std::unique_ptr<NonClientFrameView> frame_view) {
// See comment in header about ownership.
frame_view->set_owned_by_client();
if (frame_view_.get())
// If there is an existing frame view, remove the client view before removing
// the frame view to prevent the client view from being deleted.
if (frame_view_.get()) {
frame_view_->RemoveChildView(client_view_);
RemoveChildView(frame_view_.get());
}
frame_view_ = std::move(frame_view);
if (parent())
AddChildViewAt(frame_view_.get(), kFrameViewIndex);
if (parent()) {
AddChildViewAt(frame_view_.get(), 0);
frame_view_->AddChildViewAt(client_view_, 0);
}
}
void NonClientView::SetOverlayView(View* view) {
@ -262,11 +257,6 @@ void NonClientView::Layout() {
// into a View hierarchy once" ( http://codereview.chromium.org/27317 ), but
// where that is still the case it should simply be fixed.
frame_view_->SetBoundsRect(GetLocalBounds());
client_view_->SetBoundsRect(frame_view_->GetBoundsForClientView());
SkPath client_clip;
if (frame_view_->GetClientMask(client_view_->size(), &client_clip))
client_view_->SetClipPath(client_clip);
if (overlay_view_)
overlay_view_->SetBoundsRect(GetLocalBounds());
@ -302,8 +292,8 @@ void NonClientView::ViewHierarchyChanged(
// the various setters, and create and add children directly in the
// constructor.
if (details.is_add && GetWidget() && details.child == this) {
AddChildViewAt(frame_view_.get(), kFrameViewIndex);
AddChildViewAt(client_view_, kClientViewIndex);
AddChildViewAt(frame_view_.get(), 0);
frame_view_->AddChildViewAt(client_view_, 0);
if (overlay_view_)
AddChildView(overlay_view_);
}

@ -101,11 +101,7 @@ class VIEWS_EXPORT NonClientFrameView : public View,
// View:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
void OnThemeChanged() override;
protected:
// ViewTargeterDelegate:
bool DoesIntersectRect(const View* target,
const gfx::Rect& rect) const override;
void Layout() override;
private:
#if defined(OS_WIN)
@ -121,10 +117,11 @@ class VIEWS_EXPORT NonClientFrameView : public View,
//
// The NonClientView is the logical root of all Views contained within a
// Window, except for the RootView which is its parent and of which it is the
// sole child. The NonClientView has two children, the NonClientFrameView which
// sole child. The NonClientView has one child, the NonClientFrameView which
// is responsible for painting and responding to events from the non-client
// portions of the window, and the ClientView, which is responsible for the
// same for the client area of the window:
// portions of the window, and for forwarding events to its child, the
// ClientView, which is responsible for the same for the client area of the
// window:
//
// +- views::Widget ------------------------------------+
// | +- views::RootView ------------------------------+ |
@ -135,23 +132,17 @@ class VIEWS_EXPORT NonClientFrameView : public View,
// | | | | << of the non-client areas of a >> | | | |
// | | | | << views::Widget. >> | | | |
// | | | | | | | |
// | | | +----------------------------------------+ | | |
// | | | +- views::ClientView or subclass --------+ | | |
// | | | | | | | |
// | | | | << all painting and event receiving >> | | | |
// | | | | << of the client areas of a >> | | | |
// | | | | << views::Widget. >> | | | |
// | | | | | | | |
// | | | | +- views::ClientView or subclass ----+ | | | |
// | | | | | | | | | |
// | | | | | << all painting and event >> | | | | |
// | | | | | << receiving of the client >> | | | | |
// | | | | | << areas of a views::Widget. >> | | | | |
// | | | | +----------------------------------+ | | | | |
// | | | +----------------------------------------+ | | |
// | | +--------------------------------------------+ | |
// | +------------------------------------------------+ |
// +----------------------------------------------------+
//
// The NonClientFrameView and ClientView are siblings because due to theme
// changes the NonClientFrameView may be replaced with different
// implementations (e.g. during the switch from DWM/Aero-Glass to Vista Basic/
// Classic rendering).
//
class VIEWS_EXPORT NonClientView : public View, public ViewTargeterDelegate {
public:
METADATA_HEADER(NonClientView);

@ -1,102 +0,0 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/window/non_client_view.h"
#include <memory>
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/client_view.h"
#include "ui/views/window/native_frame_view.h"
namespace views {
namespace test {
namespace {
class NonClientFrameTestView : public NativeFrameView {
public:
using NativeFrameView::NativeFrameView;
int layout_count() const { return layout_count_; }
// NativeFrameView:
void Layout() override {
NativeFrameView::Layout();
++layout_count_;
}
private:
int layout_count_ = 0;
};
class ClientTestView : public ClientView {
public:
using ClientView::ClientView;
int layout_count() const { return layout_count_; }
// ClientView:
void Layout() override {
ClientView::Layout();
++layout_count_;
}
private:
int layout_count_ = 0;
};
class TestWidgetDelegate : public WidgetDelegateView {
public:
// WidgetDelegateView:
std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
Widget* widget) override {
return std::make_unique<NonClientFrameTestView>(widget);
}
views::ClientView* CreateClientView(Widget* widget) override {
return new ClientTestView(widget, this);
}
};
class NonClientViewTest : public ViewsTestBase {
public:
Widget::InitParams CreateParams(Widget::InitParams::Type type) override {
Widget::InitParams params = ViewsTestBase::CreateParams(type);
params.delegate = new TestWidgetDelegate;
return params;
}
};
} // namespace
// Ensure Layout() is not called excessively on a ClientView when Widget bounds
// are changing.
TEST_F(NonClientViewTest, OnlyLayoutChildViewsOnce) {
std::unique_ptr<views::Widget> widget =
CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
NonClientView* non_client_view = widget->non_client_view();
non_client_view->Layout();
auto* frame_view =
static_cast<NonClientFrameTestView*>(non_client_view->frame_view());
auto* client_view =
static_cast<ClientTestView*>(non_client_view->client_view());
int initial_frame_view_layouts = frame_view->layout_count();
int initial_client_view_layouts = client_view->layout_count();
// Make sure it does no layout when nothing has changed.
non_client_view->Layout();
EXPECT_EQ(frame_view->layout_count(), initial_frame_view_layouts);
EXPECT_EQ(client_view->layout_count(), initial_client_view_layouts);
// Ensure changing bounds triggers a (single) layout.
widget->SetBounds(gfx::Rect(0, 0, 161, 100));
EXPECT_EQ(frame_view->layout_count(), initial_frame_view_layouts + 1);
EXPECT_EQ(client_view->layout_count(), initial_client_view_layouts + 1);
}
} // namespace test
} // namespace views