constrained_window_views_unittest.cc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright 2013 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 "components/constrained_window/constrained_window_views.h"
  5. #include "base/memory/raw_ptr.h"
  6. #include <memory>
  7. #include "build/build_config.h"
  8. #include "components/constrained_window/constrained_window_views_client.h"
  9. #include "components/web_modal/test_web_contents_modal_dialog_host.h"
  10. #include "ui/display/display.h"
  11. #include "ui/display/screen.h"
  12. #include "ui/gfx/geometry/point.h"
  13. #include "ui/gfx/geometry/rect.h"
  14. #include "ui/gfx/geometry/size.h"
  15. #include "ui/gfx/native_widget_types.h"
  16. #include "ui/views/test/test_views.h"
  17. #include "ui/views/test/views_test_base.h"
  18. #include "ui/views/widget/widget.h"
  19. #include "ui/views/window/dialog_delegate.h"
  20. using views::Widget;
  21. namespace constrained_window {
  22. namespace {
  23. // Dummy client that returns a null modal dialog host and host view.
  24. class TestConstrainedWindowViewsClient
  25. : public constrained_window::ConstrainedWindowViewsClient {
  26. public:
  27. TestConstrainedWindowViewsClient() = default;
  28. TestConstrainedWindowViewsClient(const TestConstrainedWindowViewsClient&) =
  29. delete;
  30. TestConstrainedWindowViewsClient& operator=(
  31. const TestConstrainedWindowViewsClient&) = delete;
  32. // ConstrainedWindowViewsClient:
  33. web_modal::ModalDialogHost* GetModalDialogHost(
  34. gfx::NativeWindow parent) override {
  35. return nullptr;
  36. }
  37. gfx::NativeView GetDialogHostView(gfx::NativeWindow parent) override {
  38. return nullptr;
  39. }
  40. };
  41. // ViewsDelegate to provide context to dialog creation functions such as
  42. // CreateBrowserModalDialogViews() which do not allow InitParams to be set, and
  43. // pass a null |context| argument to DialogDelegate::CreateDialogWidget().
  44. class TestViewsDelegateWithContext : public views::TestViewsDelegate {
  45. public:
  46. TestViewsDelegateWithContext() = default;
  47. TestViewsDelegateWithContext(const TestViewsDelegateWithContext&) = delete;
  48. TestViewsDelegateWithContext& operator=(const TestViewsDelegateWithContext&) =
  49. delete;
  50. void set_context(gfx::NativeWindow context) { context_ = context; }
  51. // ViewsDelegate:
  52. void OnBeforeWidgetInit(
  53. views::Widget::InitParams* params,
  54. views::internal::NativeWidgetDelegate* delegate) override {
  55. if (!params->context)
  56. params->context = context_;
  57. TestViewsDelegate::OnBeforeWidgetInit(params, delegate);
  58. }
  59. private:
  60. gfx::NativeWindow context_ = nullptr;
  61. };
  62. class ConstrainedWindowViewsTest : public views::ViewsTestBase {
  63. public:
  64. ConstrainedWindowViewsTest() = default;
  65. ConstrainedWindowViewsTest(const ConstrainedWindowViewsTest&) = delete;
  66. ConstrainedWindowViewsTest& operator=(const ConstrainedWindowViewsTest&) =
  67. delete;
  68. ~ConstrainedWindowViewsTest() override = default;
  69. void SetUp() override {
  70. auto views_delegate = std::make_unique<TestViewsDelegateWithContext>();
  71. // set_views_delegate() must be called before SetUp(), and GetContext() is
  72. // null before that, so take a reference.
  73. TestViewsDelegateWithContext* views_delegate_weak = views_delegate.get();
  74. set_views_delegate(std::move(views_delegate));
  75. views::ViewsTestBase::SetUp();
  76. views_delegate_weak->set_context(GetContext());
  77. delegate_ = std::make_unique<views::DialogDelegate>();
  78. auto contents = std::make_unique<views::StaticSizedView>();
  79. contents_ = delegate_->SetContentsView(std::move(contents));
  80. dialog_ = views::DialogDelegate::CreateDialogWidget(delegate_.get(),
  81. GetContext(), nullptr);
  82. dialog_host_ = std::make_unique<web_modal::TestWebContentsModalDialogHost>(
  83. dialog_->GetNativeView());
  84. dialog_host_->set_max_dialog_size(gfx::Size(5000, 5000));
  85. // Make sure the dialog size is dominated by the preferred size of the
  86. // contents.
  87. gfx::Size preferred_size = dialog()->GetRootView()->GetPreferredSize();
  88. preferred_size.Enlarge(500, 500);
  89. contents_->SetPreferredSize(preferred_size);
  90. }
  91. void TearDown() override {
  92. contents_ = nullptr;
  93. dialog_host_.reset();
  94. dialog_->CloseNow();
  95. ViewsTestBase::TearDown();
  96. }
  97. gfx::Size GetDialogSize() {
  98. return dialog()->GetRootView()->GetBoundsInScreen().size();
  99. }
  100. views::DialogDelegate* delegate() { return delegate_.get(); }
  101. views::View* contents() { return contents_; }
  102. web_modal::TestWebContentsModalDialogHost* dialog_host() {
  103. return dialog_host_.get();
  104. }
  105. Widget* dialog() { return dialog_; }
  106. private:
  107. std::unique_ptr<views::DialogDelegate> delegate_;
  108. raw_ptr<views::View> contents_ = nullptr;
  109. std::unique_ptr<web_modal::TestWebContentsModalDialogHost> dialog_host_;
  110. raw_ptr<Widget> dialog_ = nullptr;
  111. };
  112. } // namespace
  113. // Make sure a dialog that increases its preferred size grows on the next
  114. // position update.
  115. TEST_F(ConstrainedWindowViewsTest, GrowModalDialogSize) {
  116. UpdateWidgetModalDialogPosition(dialog(), dialog_host());
  117. gfx::Size expected_size = GetDialogSize();
  118. gfx::Size preferred_size = contents()->GetPreferredSize();
  119. expected_size.Enlarge(50, 50);
  120. preferred_size.Enlarge(50, 50);
  121. contents()->SetPreferredSize(preferred_size);
  122. UpdateWidgetModalDialogPosition(dialog(), dialog_host());
  123. EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString());
  124. }
  125. // Make sure a dialog that reduces its preferred size shrinks on the next
  126. // position update.
  127. TEST_F(ConstrainedWindowViewsTest, ShrinkModalDialogSize) {
  128. UpdateWidgetModalDialogPosition(dialog(), dialog_host());
  129. gfx::Size expected_size = GetDialogSize();
  130. gfx::Size preferred_size = contents()->GetPreferredSize();
  131. expected_size.Enlarge(-50, -50);
  132. preferred_size.Enlarge(-50, -50);
  133. contents()->SetPreferredSize(preferred_size);
  134. UpdateWidgetModalDialogPosition(dialog(), dialog_host());
  135. EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString());
  136. }
  137. // Make sure browser modal dialogs are not affected by restrictions on web
  138. // content modal dialog maximum sizes.
  139. TEST_F(ConstrainedWindowViewsTest, MaximumBrowserDialogSize) {
  140. UpdateWidgetModalDialogPosition(dialog(), dialog_host());
  141. gfx::Size dialog_size = GetDialogSize();
  142. gfx::Size max_dialog_size = dialog_size;
  143. max_dialog_size.Enlarge(-50, -50);
  144. dialog_host()->set_max_dialog_size(max_dialog_size);
  145. UpdateWidgetModalDialogPosition(dialog(), dialog_host());
  146. EXPECT_EQ(dialog_size.ToString(), GetDialogSize().ToString());
  147. }
  148. // Web content modal dialogs should not get a size larger than what the dialog
  149. // host gives as the maximum size.
  150. TEST_F(ConstrainedWindowViewsTest, MaximumWebContentsDialogSize) {
  151. UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
  152. gfx::Size full_dialog_size = GetDialogSize();
  153. gfx::Size max_dialog_size = full_dialog_size;
  154. max_dialog_size.Enlarge(-50, -50);
  155. dialog_host()->set_max_dialog_size(max_dialog_size);
  156. UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
  157. // The top border of the dialog is intentionally drawn outside the area
  158. // specified by the dialog host, so add it to the size the dialog is expected
  159. // to occupy.
  160. gfx::Size expected_size = max_dialog_size;
  161. expected_size.Enlarge(
  162. 0, dialog()->non_client_view()->frame_view()->GetInsets().top());
  163. EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString());
  164. // Increasing the maximum dialog size should bring the dialog back to its
  165. // original size.
  166. max_dialog_size.Enlarge(100, 100);
  167. dialog_host()->set_max_dialog_size(max_dialog_size);
  168. UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
  169. EXPECT_EQ(full_dialog_size.ToString(), GetDialogSize().ToString());
  170. }
  171. // Ensure CreateBrowserModalDialogViews() works correctly with a null parent.
  172. // Flaky on Win10. https://crbug.com/1009182
  173. #if BUILDFLAG(IS_WIN)
  174. #define MAYBE_NullModalParent DISABLED_NullModalParent
  175. #else
  176. #define MAYBE_NullModalParent NullModalParent
  177. #endif
  178. TEST_F(ConstrainedWindowViewsTest, MAYBE_NullModalParent) {
  179. // Use desktop widgets (except on ChromeOS) for extra coverage.
  180. test_views_delegate()->set_use_desktop_native_widgets(true);
  181. SetConstrainedWindowViewsClient(
  182. std::make_unique<TestConstrainedWindowViewsClient>());
  183. auto delegate = std::make_unique<views::DialogDelegate>();
  184. delegate->SetModalType(ui::MODAL_TYPE_WINDOW);
  185. views::Widget* widget =
  186. CreateBrowserModalDialogViews(delegate.get(), nullptr);
  187. widget->Show();
  188. EXPECT_TRUE(widget->IsVisible());
  189. widget->CloseNow();
  190. }
  191. // Make sure dialogs presented off-screen are properly clamped to the nearest
  192. // screen.
  193. TEST_F(ConstrainedWindowViewsTest, ClampDialogToNearestDisplay) {
  194. // Make sure the dialog will fit fully on the display
  195. contents()->SetPreferredSize(gfx::Size(200, 100));
  196. // First, make sure the host and dialog are sized and positioned.
  197. UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
  198. const display::Screen* screen = display::Screen::GetScreen();
  199. const display::Display display = screen->GetPrimaryDisplay();
  200. // Within the tests there is only 1 display. Error if that ever changes.
  201. EXPECT_EQ(screen->GetNumDisplays(), 1);
  202. const gfx::Rect extents = display.work_area();
  203. // Move the host completely off the screen.
  204. views::Widget* host_widget =
  205. views::Widget::GetWidgetForNativeView(dialog_host()->GetHostView());
  206. gfx::Rect host_bounds = host_widget->GetWindowBoundsInScreen();
  207. host_bounds.set_origin(gfx::Point(extents.right(), extents.bottom()));
  208. host_widget->SetBounds(host_bounds);
  209. // Make sure the host is fully off the screen.
  210. EXPECT_FALSE(extents.Intersects(host_widget->GetWindowBoundsInScreen()));
  211. // Now reposition the modal dialog into the display.
  212. UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
  213. const gfx::Rect dialog_bounds = dialog()->GetRootView()->GetBoundsInScreen();
  214. // The dialog should now be fully on the display.
  215. EXPECT_TRUE(extents.Contains(dialog_bounds));
  216. }
  217. } // namespace constrained_window