window_preview_view_unittest.cc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/wm/window_preview_view.h"
  5. #include "ash/constants/app_types.h"
  6. #include "ash/test/ash_test_base.h"
  7. #include "ash/wm/window_preview_view_test_api.h"
  8. #include "ui/aura/client/aura_constants.h"
  9. #include "ui/views/widget/widget.h"
  10. #include "ui/wm/core/window_util.h"
  11. namespace ash {
  12. namespace {
  13. using WindowPreviewViewTest = AshTestBase;
  14. // Creates and returns a widget whose type is the given |type|, which is added
  15. // as a transient child of the given |parent_widget|.
  16. std::unique_ptr<views::Widget> CreateTransientChild(
  17. views::Widget* parent_widget,
  18. views::Widget::InitParams::Type type) {
  19. auto widget = std::make_unique<views::Widget>();
  20. views::Widget::InitParams params{type};
  21. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  22. params.bounds = gfx::Rect{40, 50};
  23. params.context = params.parent = parent_widget->GetNativeWindow();
  24. params.init_properties_container.SetProperty(
  25. aura::client::kAppType, static_cast<int>(ash::AppType::ARC_APP));
  26. widget->Init(std::move(params));
  27. widget->Show();
  28. return widget;
  29. }
  30. // Test that if we have two widgets whos windows are linked together by
  31. // transience, WindowPreviewView's internal collection will contain both those
  32. // two windows.
  33. TEST_F(WindowPreviewViewTest, Basic) {
  34. auto widget1 = CreateTestWidget();
  35. auto widget2 = CreateTestWidget();
  36. ::wm::AddTransientChild(widget1->GetNativeWindow(),
  37. widget2->GetNativeWindow());
  38. auto preview_view = std::make_unique<WindowPreviewView>(
  39. widget1->GetNativeWindow(), /*trilinear_filtering_on_init=*/false);
  40. WindowPreviewViewTestApi test_api(preview_view.get());
  41. EXPECT_EQ(2u, test_api.GetMirrorViews().size());
  42. EXPECT_TRUE(test_api.GetMirrorViews().contains(widget1->GetNativeWindow()));
  43. EXPECT_TRUE(test_api.GetMirrorViews().contains(widget2->GetNativeWindow()));
  44. }
  45. // Tests that the preview view preferred size is the same aspect ratio as the
  46. // window's non client view.
  47. TEST_F(WindowPreviewViewTest, AspectRatio) {
  48. // Default frame header is 32dp, so we expect a window of size 300, 300 to
  49. // have a preview of 1:1 ratio.
  50. auto window = CreateAppWindow(gfx::Rect(300, 332));
  51. auto preview_view = std::make_unique<WindowPreviewView>(
  52. window.get(), /*trilinear_filtering_on_init=*/false);
  53. const gfx::SizeF preferred_size(preview_view->GetPreferredSize());
  54. EXPECT_EQ(1.f, preferred_size.width() / preferred_size.height());
  55. }
  56. // Tests that WindowPreviewView behaves as expected when we add or remove
  57. // transient children.
  58. TEST_F(WindowPreviewViewTest, TransientChildAddedAndRemoved) {
  59. auto widget1 = CreateTestWidget();
  60. auto widget2 = CreateTestWidget();
  61. auto widget3 = CreateTestWidget();
  62. ::wm::AddTransientChild(widget1->GetNativeWindow(),
  63. widget2->GetNativeWindow());
  64. auto preview_view = std::make_unique<WindowPreviewView>(
  65. widget1->GetNativeWindow(), /*trilinear_filtering_on_init=*/false);
  66. WindowPreviewViewTestApi test_api(preview_view.get());
  67. ASSERT_EQ(2u, test_api.GetMirrorViews().size());
  68. ::wm::AddTransientChild(widget1->GetNativeWindow(),
  69. widget3->GetNativeWindow());
  70. EXPECT_EQ(3u, test_api.GetMirrorViews().size());
  71. ::wm::RemoveTransientChild(widget1->GetNativeWindow(),
  72. widget3->GetNativeWindow());
  73. EXPECT_EQ(2u, test_api.GetMirrorViews().size());
  74. }
  75. // Tests that init'ing a Widget with a native window as a transient child before
  76. // it is parented to a parent window doesn't cause a crash while the
  77. // WindowPreviewView is observing transient windows additions.
  78. // https://crbug.com/1003544.
  79. TEST_F(WindowPreviewViewTest, NoCrashWithTransientChildWithNoWindowState) {
  80. auto widget1 = CreateTestWidget();
  81. auto transient_child1 = CreateTransientChild(
  82. widget1.get(), views::Widget::InitParams::TYPE_WINDOW);
  83. EXPECT_EQ(widget1->GetNativeWindow(),
  84. wm::GetTransientParent(transient_child1->GetNativeWindow()));
  85. auto preview_view = std::make_unique<WindowPreviewView>(
  86. widget1->GetNativeWindow(), /*trilinear_filtering_on_init=*/false);
  87. WindowPreviewViewTestApi test_api(preview_view.get());
  88. ASSERT_EQ(2u, test_api.GetMirrorViews().size());
  89. // The popup and bubble transient child should be ignored (they result in a
  90. // native window type WINDOW_TYPE_POPUP), while the TYPE_WINDOW one will be
  91. // added as a transient child before it is parented to a container. This
  92. // should not cause a crash.
  93. auto transient_child2 = CreateTransientChild(
  94. widget1.get(), views::Widget::InitParams::TYPE_POPUP);
  95. auto transient_child3 = CreateTransientChild(
  96. widget1.get(), views::Widget::InitParams::TYPE_BUBBLE);
  97. auto transient_child4 = CreateTransientChild(
  98. widget1.get(), views::Widget::InitParams::TYPE_WINDOW);
  99. EXPECT_EQ(widget1->GetNativeWindow(),
  100. wm::GetTransientParent(transient_child2->GetNativeWindow()));
  101. EXPECT_EQ(widget1->GetNativeWindow(),
  102. wm::GetTransientParent(transient_child3->GetNativeWindow()));
  103. EXPECT_EQ(widget1->GetNativeWindow(),
  104. wm::GetTransientParent(transient_child4->GetNativeWindow()));
  105. EXPECT_EQ(3u, test_api.GetMirrorViews().size());
  106. transient_child3.reset();
  107. EXPECT_EQ(3u, test_api.GetMirrorViews().size());
  108. transient_child4.reset();
  109. EXPECT_EQ(2u, test_api.GetMirrorViews().size());
  110. }
  111. // Tests that if cycling stops before a transient popup child is destroyed
  112. // doesn't introduce a crash. https://crbug.com/1014543.
  113. TEST_F(WindowPreviewViewTest,
  114. NoCrashWhenWindowCyclingIsCanceledWithATransientPopup) {
  115. auto widget1 = CreateTestWidget();
  116. auto preview_view = std::make_unique<WindowPreviewView>(
  117. widget1->GetNativeWindow(), /*trilinear_filtering_on_init=*/false);
  118. WindowPreviewViewTestApi test_api(preview_view.get());
  119. ASSERT_EQ(1u, test_api.GetMirrorViews().size());
  120. auto transient_popup = CreateTransientChild(
  121. widget1.get(), views::Widget::InitParams::TYPE_POPUP);
  122. ASSERT_EQ(1u, test_api.GetMirrorViews().size());
  123. // Simulate canceling window cycling now, there should be no crashes.
  124. preview_view.reset();
  125. }
  126. TEST_F(WindowPreviewViewTest, LayoutChildWithinParentBounds) {
  127. UpdateDisplay("1000x900");
  128. // Create two widgets linked transiently. The child window is within the
  129. // bounds of the parent window.
  130. auto widget1 = CreateTestWidget();
  131. auto widget2 = CreateTestWidget();
  132. widget1->GetNativeWindow()->SetBounds(gfx::Rect(0, -20, 100, 120));
  133. widget1->GetNativeWindow()->SetProperty(aura::client::kTopViewInset, 20);
  134. widget2->GetNativeWindow()->SetBounds(gfx::Rect(20, 20, 50, 50));
  135. widget2->GetNativeWindow()->SetProperty(aura::client::kTopViewInset, 10);
  136. ::wm::AddTransientChild(widget1->GetNativeWindow(),
  137. widget2->GetNativeWindow());
  138. // The top inset is excluded from GetUnionRect() calculations.
  139. auto preview_view = std::make_unique<WindowPreviewView>(
  140. widget1->GetNativeWindow(), /*trilinear_filtering_on_init=*/false);
  141. WindowPreviewViewTestApi test_api(preview_view.get());
  142. EXPECT_EQ(gfx::RectF(100.f, 100.f), test_api.GetUnionRect());
  143. // Test that the ratio between the two windows is maintained.
  144. preview_view->SetBoundsRect(gfx::Rect(500, 500));
  145. EXPECT_EQ(gfx::Rect(500, 500),
  146. test_api.GetMirrorViewForWidget(widget1.get())->bounds());
  147. // The original rect (20, 20, 50, 50) should now be (100, 100, 250, 250).
  148. // However, the top inset of 10 is now 50, and is excluded from calculations.
  149. EXPECT_EQ(gfx::Rect(100, 150, 250, 200),
  150. test_api.GetMirrorViewForWidget(widget2.get())->bounds());
  151. }
  152. // Test that WindowPreviewView layouts the transient tree correctly when each
  153. // transient child is outside the bounds of its transient parent.
  154. TEST_F(WindowPreviewViewTest, LayoutChildOutsideParentBounds) {
  155. UpdateDisplay("1000x900");
  156. // Create two widgets linked transiently. The child window is outside of the
  157. // bounds of the parent window.
  158. auto widget1 = CreateTestWidget();
  159. auto widget2 = CreateTestWidget();
  160. widget1->GetNativeWindow()->SetBounds(gfx::Rect(0, -20, 200, 220));
  161. widget1->GetNativeWindow()->SetProperty(aura::client::kTopViewInset, 20);
  162. widget2->GetNativeWindow()->SetBounds(gfx::Rect(300, 300, 100, 100));
  163. widget2->GetNativeWindow()->SetProperty(aura::client::kTopViewInset, 20);
  164. ::wm::AddTransientChild(widget1->GetNativeWindow(),
  165. widget2->GetNativeWindow());
  166. // Get the union rect of the two windows. The top inset is excluded from
  167. // calculations.
  168. auto preview_view = std::make_unique<WindowPreviewView>(
  169. widget1->GetNativeWindow(), /*trilinear_filtering_on_init=*/false);
  170. WindowPreviewViewTestApi test_api(preview_view.get());
  171. EXPECT_EQ(gfx::RectF(400.f, 400.f), test_api.GetUnionRect());
  172. // Test that the ratio between the two windows, relative to the smallest
  173. // rectangle which encompasses them both (0, 0, 400, 400) is maintained. The
  174. // first window is positioned at the origin, the second window is positioned
  175. // such that it is 25% larger and the bottom right corner is aligned with
  176. // |preview_view|'s bottom right corner.
  177. preview_view->SetBoundsRect(gfx::Rect(500, 500));
  178. EXPECT_EQ(gfx::Rect(250, 250),
  179. test_api.GetMirrorViewForWidget(widget1.get())->bounds());
  180. EXPECT_EQ(gfx::Rect(375, 400, 125, 100),
  181. test_api.GetMirrorViewForWidget(widget2.get())->bounds());
  182. }
  183. } // namespace
  184. } // namespace ash