blob: b228f8bc840bd0bd0f9e7ac4c742edb208581d45 [file] [log] [blame]
// 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/widget/desktop_aura/desktop_window_tree_host_platform.h"
#include <memory>
#include <utility>
#include "base/run_loop.h"
#include "ui/aura/window_tree_host.h"
#include "ui/aura/window_tree_host_observer.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/widget_observer.h"
namespace views {
namespace {
class TestWidgetObserver : public WidgetObserver {
public:
enum class Change {
kVisibility,
kDestroying,
};
explicit TestWidgetObserver(Widget* widget) : widget_(widget) {
DCHECK(widget_);
widget_->AddObserver(this);
}
~TestWidgetObserver() override {
// This might have been destroyed by the widget destroying delegate call.
if (widget_)
widget_->RemoveObserver(this);
}
// Waits for notification changes for the |change|. |old_value| must be
// provided to be sure that this is not called after the change has already
// happened - e.g. synchronous change.
void WaitForChange(Change change, bool old_value) {
switch (change) {
case Change::kVisibility:
if (old_value == visible_)
Wait();
break;
case Change::kDestroying:
if (old_value == on_widget_destroying_)
Wait();
break;
default:
NOTREACHED() << "unknown value";
break;
}
}
bool widget_destroying() const { return on_widget_destroying_; }
bool visible() const { return visible_; }
private:
// views::WidgetObserver overrides:
void OnWidgetDestroying(Widget* widget) override {
DCHECK_EQ(widget_, widget);
widget_->RemoveObserver(this);
widget_ = nullptr;
on_widget_destroying_ = true;
StopWaiting();
}
void OnWidgetVisibilityChanged(Widget* widget, bool visible) override {
DCHECK_EQ(widget_, widget);
visible_ = visible;
StopWaiting();
}
void Wait() {
ASSERT_FALSE(run_loop_);
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
run_loop_.reset();
}
void StopWaiting() {
if (!run_loop_)
return;
ASSERT_TRUE(run_loop_->running());
run_loop_->Quit();
}
Widget* widget_;
std::unique_ptr<base::RunLoop> run_loop_;
bool on_widget_destroying_ = false;
bool visible_ = false;
DISALLOW_COPY_AND_ASSIGN(TestWidgetObserver);
};
std::unique_ptr<Widget> CreateWidgetWithNativeWidget() {
std::unique_ptr<Widget> widget(new Widget);
Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
params.delegate = nullptr;
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.remove_standard_frame = true;
params.native_widget = new DesktopNativeWidgetAura(widget.get());
params.bounds = gfx::Rect(100, 100, 100, 100);
widget->Init(std::move(params));
return widget;
}
} // namespace
class DesktopWindowTreeHostPlatformTest : public ViewsTestBase {
public:
DesktopWindowTreeHostPlatformTest() {}
~DesktopWindowTreeHostPlatformTest() override {}
private:
DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostPlatformTest);
};
TEST_F(DesktopWindowTreeHostPlatformTest, CallOnNativeWidgetDestroying) {
std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
TestWidgetObserver observer(widget->native_widget_private()->GetWidget());
widget->CloseNow();
observer.WaitForChange(TestWidgetObserver::Change::kDestroying,
false /* old_value */);
EXPECT_TRUE(observer.widget_destroying());
}
// Calling show/hide/show triggers changing visibility of the native widget.
TEST_F(DesktopWindowTreeHostPlatformTest, CallOnNativeWidgetVisibilityChanged) {
std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
TestWidgetObserver observer(widget->native_widget_private()->GetWidget());
EXPECT_FALSE(observer.visible());
widget->Show();
EXPECT_TRUE(observer.visible());
widget->Hide();
EXPECT_FALSE(observer.visible());
widget->Show();
EXPECT_TRUE(observer.visible());
}
// Tests that the minimization information is propagated to the content window.
TEST_F(DesktopWindowTreeHostPlatformTest,
ToggleMinimizePropogateToContentWindow) {
std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
widget->Show();
auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
ASSERT_TRUE(host_platform);
EXPECT_TRUE(widget->GetNativeWindow()->IsVisible());
// Pretend a PlatformWindow enters the minimized state.
host_platform->OnWindowStateChanged(ui::PlatformWindowState::kMinimized);
EXPECT_FALSE(widget->GetNativeWindow()->IsVisible());
// Pretend a PlatformWindow exits the minimized state.
host_platform->OnWindowStateChanged(ui::PlatformWindowState::kNormal);
EXPECT_TRUE(widget->GetNativeWindow()->IsVisible());
}
// Tests that the window shape is updated from the
// |NonClientView::GetWindowMask|.
TEST_F(DesktopWindowTreeHostPlatformTest, UpdateWindowShapeFromWindowMask) {
std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
widget->Show();
auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
ASSERT_TRUE(host_platform);
auto* content_window =
DesktopWindowTreeHostPlatform::GetContentWindowForWidget(
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
ASSERT_TRUE(content_window);
// alpha_shape for the layer of content window is updated from the
// |NonClientView::GetWindowMask|.
EXPECT_TRUE(host_platform
->GetWindowMaskForWindowShape(content_window->bounds().size())
.has_value());
EXPECT_TRUE(content_window->layer()->alpha_shape());
// When fullscreen mode, alpha_shape is set to empty since there is no
// |NonClientView::GetWindowMask|.
host_platform->SetFullscreen(true);
widget->SetBounds(gfx::Rect(800, 800));
EXPECT_FALSE(
host_platform
->GetWindowMaskForWindowShape(content_window->bounds().size())
.has_value());
EXPECT_FALSE(content_window->layer()->alpha_shape());
}
// A Widget that allows setting the min/max size for the widget.
class CustomSizeWidget : public Widget {
public:
CustomSizeWidget() = default;
~CustomSizeWidget() override = default;
void set_min_size(const gfx::Size& size) { min_size_ = size; }
void set_max_size(const gfx::Size& size) { max_size_ = size; }
// Widget:
gfx::Size GetMinimumSize() const override { return min_size_; }
gfx::Size GetMaximumSize() const override { return max_size_; }
private:
gfx::Size min_size_;
gfx::Size max_size_;
DISALLOW_COPY_AND_ASSIGN(CustomSizeWidget);
};
TEST_F(DesktopWindowTreeHostPlatformTest, SetBoundsWithMinMax) {
CustomSizeWidget widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(200, 100);
widget.Init(std::move(params));
widget.Show();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(gfx::Size(200, 100).ToString(),
widget.GetWindowBoundsInScreen().size().ToString());
widget.SetBounds(gfx::Rect(300, 200));
EXPECT_EQ(gfx::Size(300, 200).ToString(),
widget.GetWindowBoundsInScreen().size().ToString());
widget.set_min_size(gfx::Size(100, 100));
widget.SetBounds(gfx::Rect(50, 500));
EXPECT_EQ(gfx::Size(100, 500).ToString(),
widget.GetWindowBoundsInScreen().size().ToString());
}
class ResizeObserver : public aura::WindowTreeHostObserver {
public:
explicit ResizeObserver(aura::WindowTreeHost* host) : host_(host) {
host_->AddObserver(this);
}
ResizeObserver(const ResizeObserver&) = delete;
ResizeObserver& operator=(const ResizeObserver&) = delete;
~ResizeObserver() override { host_->RemoveObserver(this); }
int bounds_change_count() const { return bounds_change_count_; }
int resize_count() const { return resize_count_; }
// aura::WindowTreeHostObserver:
void OnHostResized(aura::WindowTreeHost* host) override { resize_count_++; }
void OnHostWillProcessBoundsChange(aura::WindowTreeHost* host) override {
bounds_change_count_++;
}
private:
aura::WindowTreeHost* const host_;
int resize_count_ = 0;
int bounds_change_count_ = 0;
};
// Verifies that setting widget bounds, just after creating it, with the same
// size passed in InitParams does not lead to a "bounds change" event. Prevents
// regressions, such as https://crbug.com/1151092.
TEST_F(DesktopWindowTreeHostPlatformTest, SetBoundsWithUnchangedSize) {
auto widget = CreateWidgetWithNativeWidget();
widget->Show();
EXPECT_EQ(gfx::Size(100, 100), widget->GetWindowBoundsInScreen().size());
auto* host = widget->GetNativeWindow()->GetHost();
ResizeObserver observer(host);
auto* dwth_platform = DesktopWindowTreeHostPlatform::GetHostForWidget(
widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
ASSERT_TRUE(dwth_platform);
// Check with different origin.
dwth_platform->SetBoundsInPixels(gfx::Rect(2, 2, 100, 100));
EXPECT_EQ(1, observer.bounds_change_count());
EXPECT_EQ(0, observer.resize_count());
}
} // namespace views