blob: 21ea2eb9f7d8a5636a23d25f0a2a036d3073a0e3 [file] [log] [blame]
[email protected]0026387d2013-09-06 05:08:431// 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
oshima136691a2014-10-24 21:54:115#include "components/constrained_window/constrained_window_views.h"
[email protected]0026387d2013-09-06 05:08:436
dchenga0ee5fb2016-04-26 02:46:557#include <memory>
kylixrd51116b92016-12-16 16:48:108#include <vector>
dchenga0ee5fb2016-04-26 02:46:559
avibc5337b2015-12-25 23:16:3310#include "base/macros.h"
tapted82c125e2016-10-19 22:10:4611#include "components/constrained_window/constrained_window_views_client.h"
[email protected]127c6a12013-10-17 12:57:0412#include "components/web_modal/test_web_contents_modal_dialog_host.h"
kylixrd51116b92016-12-16 16:48:1013#include "ui/display/display.h"
14#include "ui/display/screen.h"
tfarina655f81d2014-12-23 02:38:5015#include "ui/gfx/geometry/point.h"
tfarina3b0452d2014-12-31 15:20:0916#include "ui/gfx/geometry/rect.h"
tfarinaebe974f02015-01-03 04:25:3217#include "ui/gfx/geometry/size.h"
[email protected]0026387d2013-09-06 05:08:4318#include "ui/gfx/native_widget_types.h"
[email protected]51170c02013-12-11 02:58:5319#include "ui/views/border.h"
[email protected]0026387d2013-09-06 05:08:4320#include "ui/views/test/views_test_base.h"
21#include "ui/views/widget/widget.h"
22#include "ui/views/window/dialog_delegate.h"
23
oshimadd3db6a2014-11-10 22:21:2324using views::Widget;
[email protected]0026387d2013-09-06 05:08:4325
oshimadd3db6a2014-11-10 22:21:2326namespace constrained_window {
27namespace {
28
29class DialogContents : public views::DialogDelegateView {
[email protected]0026387d2013-09-06 05:08:4330 public:
31 DialogContents() {}
dcheng30a1b1542014-10-29 21:27:5032 ~DialogContents() override {}
[email protected]0026387d2013-09-06 05:08:4333
tapted82c125e2016-10-19 22:10:4634 void set_modal_type(ui::ModalType modal_type) { modal_type_ = modal_type; }
35
36 // DialogDelegateView:
oshimadd3db6a2014-11-10 22:21:2337 views::View* GetContentsView() override { return this; }
dcheng30a1b1542014-10-29 21:27:5038 gfx::Size GetMinimumSize() const override { return gfx::Size(); }
[email protected]0026387d2013-09-06 05:08:4339
tapted82c125e2016-10-19 22:10:4640 // WidgetDelegate:
41 ui::ModalType GetModalType() const override { return modal_type_; }
42
[email protected]0026387d2013-09-06 05:08:4343 private:
tapted82c125e2016-10-19 22:10:4644 ui::ModalType modal_type_ = ui::MODAL_TYPE_NONE;
[email protected]0026387d2013-09-06 05:08:4345
46 DISALLOW_COPY_AND_ASSIGN(DialogContents);
47};
48
tapted82c125e2016-10-19 22:10:4649// Dummy client that returns a null modal dialog host and host view.
50class TestConstrainedWindowViewsClient
51 : public constrained_window::ConstrainedWindowViewsClient {
52 public:
53 TestConstrainedWindowViewsClient() {}
54
55 // ConstrainedWindowViewsClient:
56 web_modal::ModalDialogHost* GetModalDialogHost(
57 gfx::NativeWindow parent) override {
58 return nullptr;
59 }
60 gfx::NativeView GetDialogHostView(gfx::NativeWindow parent) override {
61 return nullptr;
62 }
63
64 private:
65 DISALLOW_COPY_AND_ASSIGN(TestConstrainedWindowViewsClient);
66};
67
68// ViewsDelegate to provide context to dialog creation functions such as
69// CreateBrowserModalDialogViews() which do not allow InitParams to be set, and
70// pass a null |context| argument to DialogDelegate::CreateDialogWidget().
71class TestViewsDelegateWithContext : public views::TestViewsDelegate {
72 public:
73 TestViewsDelegateWithContext() {}
74
75 void set_context(gfx::NativeWindow context) { context_ = context; }
76
77 // ViewsDelegate:
78 void OnBeforeWidgetInit(
79 views::Widget::InitParams* params,
80 views::internal::NativeWidgetDelegate* delegate) override {
81 if (!params->context)
82 params->context = context_;
83 TestViewsDelegate::OnBeforeWidgetInit(params, delegate);
84 }
85
86 private:
87 gfx::NativeWindow context_ = nullptr;
88
89 DISALLOW_COPY_AND_ASSIGN(TestViewsDelegateWithContext);
90};
91
oshimadd3db6a2014-11-10 22:21:2392class ConstrainedWindowViewsTest : public views::ViewsTestBase {
[email protected]00517a82013-10-09 22:19:3093 public:
taptede37016b2014-12-16 04:06:4294 ConstrainedWindowViewsTest() : contents_(nullptr), dialog_(nullptr) {}
dcheng30a1b1542014-10-29 21:27:5095 ~ConstrainedWindowViewsTest() override {}
[email protected]0026387d2013-09-06 05:08:4396
dcheng30a1b1542014-10-29 21:27:5097 void SetUp() override {
tapted82c125e2016-10-19 22:10:4698 std::unique_ptr<TestViewsDelegateWithContext> views_delegate(
99 new TestViewsDelegateWithContext);
100
101 // set_views_delegate() must be called before SetUp(), and GetContext() is
102 // null before that, so take a reference.
103 TestViewsDelegateWithContext* views_delegate_weak = views_delegate.get();
104 set_views_delegate(std::move(views_delegate));
oshimadd3db6a2014-11-10 22:21:23105 views::ViewsTestBase::SetUp();
tapted82c125e2016-10-19 22:10:46106 views_delegate_weak->set_context(GetContext());
107
[email protected]00517a82013-10-09 22:19:30108 contents_ = new DialogContents;
taptede37016b2014-12-16 04:06:42109 dialog_ = views::DialogDelegate::CreateDialogWidget(
110 contents_, GetContext(), nullptr);
[email protected]127c6a12013-10-17 12:57:04111 dialog_host_.reset(new web_modal::TestWebContentsModalDialogHost(
112 dialog_->GetNativeView()));
113 dialog_host_->set_max_dialog_size(gfx::Size(5000, 5000));
[email protected]0026387d2013-09-06 05:08:43114
[email protected]00517a82013-10-09 22:19:30115 // Make sure the dialog size is dominated by the preferred size of the
116 // contents.
117 gfx::Size preferred_size = dialog()->GetRootView()->GetPreferredSize();
118 preferred_size.Enlarge(500, 500);
Evan Stadee8c9c592017-05-25 20:09:53119 contents()->SetPreferredSize(preferred_size);
[email protected]00517a82013-10-09 22:19:30120 }
[email protected]0026387d2013-09-06 05:08:43121
dcheng30a1b1542014-10-29 21:27:50122 void TearDown() override {
taptede37016b2014-12-16 04:06:42123 contents_ = nullptr;
[email protected]00517a82013-10-09 22:19:30124 dialog_host_.reset();
taptede37016b2014-12-16 04:06:42125 dialog_->CloseNow();
126 ViewsTestBase::TearDown();
[email protected]00517a82013-10-09 22:19:30127 }
128
129 gfx::Size GetDialogSize() {
130 return dialog()->GetRootView()->GetBoundsInScreen().size();
131 }
132
133 DialogContents* contents() { return contents_; }
[email protected]127c6a12013-10-17 12:57:04134 web_modal::TestWebContentsModalDialogHost* dialog_host() {
135 return dialog_host_.get();
136 }
taptede37016b2014-12-16 04:06:42137 Widget* dialog() { return dialog_; }
[email protected]00517a82013-10-09 22:19:30138
139 private:
140 DialogContents* contents_;
dchenga0ee5fb2016-04-26 02:46:55141 std::unique_ptr<web_modal::TestWebContentsModalDialogHost> dialog_host_;
taptede37016b2014-12-16 04:06:42142 Widget* dialog_;
[email protected]00517a82013-10-09 22:19:30143
144 DISALLOW_COPY_AND_ASSIGN(ConstrainedWindowViewsTest);
145};
146
oshimadd3db6a2014-11-10 22:21:23147} // namespace
148
[email protected]00517a82013-10-09 22:19:30149// Make sure a dialog that increases its preferred size grows on the next
150// position update.
151TEST_F(ConstrainedWindowViewsTest, GrowModalDialogSize) {
oshima136691a2014-10-24 21:54:11152 UpdateWidgetModalDialogPosition(dialog(), dialog_host());
[email protected]00517a82013-10-09 22:19:30153 gfx::Size expected_size = GetDialogSize();
154 gfx::Size preferred_size = contents()->GetPreferredSize();
155 expected_size.Enlarge(50, 50);
[email protected]0026387d2013-09-06 05:08:43156 preferred_size.Enlarge(50, 50);
Evan Stadee8c9c592017-05-25 20:09:53157 contents()->SetPreferredSize(preferred_size);
oshima136691a2014-10-24 21:54:11158 UpdateWidgetModalDialogPosition(dialog(), dialog_host());
[email protected]00517a82013-10-09 22:19:30159 EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString());
160}
[email protected]0026387d2013-09-06 05:08:43161
[email protected]00517a82013-10-09 22:19:30162// Make sure a dialog that reduces its preferred size shrinks on the next
163// position update.
164TEST_F(ConstrainedWindowViewsTest, ShrinkModalDialogSize) {
oshima136691a2014-10-24 21:54:11165 UpdateWidgetModalDialogPosition(dialog(), dialog_host());
[email protected]00517a82013-10-09 22:19:30166 gfx::Size expected_size = GetDialogSize();
167 gfx::Size preferred_size = contents()->GetPreferredSize();
168 expected_size.Enlarge(-50, -50);
169 preferred_size.Enlarge(-50, -50);
Evan Stadee8c9c592017-05-25 20:09:53170 contents()->SetPreferredSize(preferred_size);
oshima136691a2014-10-24 21:54:11171 UpdateWidgetModalDialogPosition(dialog(), dialog_host());
[email protected]00517a82013-10-09 22:19:30172 EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString());
173}
[email protected]0026387d2013-09-06 05:08:43174
[email protected]00517a82013-10-09 22:19:30175// Make sure browser modal dialogs are not affected by restrictions on web
176// content modal dialog maximum sizes.
177TEST_F(ConstrainedWindowViewsTest, MaximumBrowserDialogSize) {
oshima136691a2014-10-24 21:54:11178 UpdateWidgetModalDialogPosition(dialog(), dialog_host());
[email protected]00517a82013-10-09 22:19:30179 gfx::Size dialog_size = GetDialogSize();
180 gfx::Size max_dialog_size = dialog_size;
181 max_dialog_size.Enlarge(-50, -50);
182 dialog_host()->set_max_dialog_size(max_dialog_size);
oshima136691a2014-10-24 21:54:11183 UpdateWidgetModalDialogPosition(dialog(), dialog_host());
[email protected]00517a82013-10-09 22:19:30184 EXPECT_EQ(dialog_size.ToString(), GetDialogSize().ToString());
185}
[email protected]0026387d2013-09-06 05:08:43186
[email protected]00517a82013-10-09 22:19:30187// Web content modal dialogs should not get a size larger than what the dialog
188// host gives as the maximum size.
189TEST_F(ConstrainedWindowViewsTest, MaximumWebContentsDialogSize) {
190 UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
191 gfx::Size full_dialog_size = GetDialogSize();
[email protected]0026387d2013-09-06 05:08:43192 gfx::Size max_dialog_size = full_dialog_size;
[email protected]00517a82013-10-09 22:19:30193 max_dialog_size.Enlarge(-50, -50);
194 dialog_host()->set_max_dialog_size(max_dialog_size);
195 UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
[email protected]0026387d2013-09-06 05:08:43196 // The top border of the dialog is intentionally drawn outside the area
197 // specified by the dialog host, so add it to the size the dialog is expected
198 // to occupy.
[email protected]00517a82013-10-09 22:19:30199 gfx::Size expected_size = max_dialog_size;
oshimadd3db6a2014-11-10 22:21:23200 views::Border* border = dialog()->non_client_view()->frame_view()->border();
[email protected]0026387d2013-09-06 05:08:43201 if (border)
202 expected_size.Enlarge(0, border->GetInsets().top());
[email protected]00517a82013-10-09 22:19:30203 EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString());
[email protected]0026387d2013-09-06 05:08:43204
[email protected]00517a82013-10-09 22:19:30205 // Increasing the maximum dialog size should bring the dialog back to its
206 // original size.
207 max_dialog_size.Enlarge(100, 100);
208 dialog_host()->set_max_dialog_size(max_dialog_size);
209 UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
210 EXPECT_EQ(full_dialog_size.ToString(), GetDialogSize().ToString());
[email protected]0026387d2013-09-06 05:08:43211}
212
tapted82c125e2016-10-19 22:10:46213// Ensure CreateBrowserModalDialogViews() works correctly with a null parent.
214TEST_F(ConstrainedWindowViewsTest, NullModalParent) {
215 // Use desktop widgets (except on ChromeOS) for extra coverage.
kylixrd4e8cac42017-04-13 17:15:56216 test_views_delegate()->set_use_desktop_native_widgets(true);
tapted82c125e2016-10-19 22:10:46217
218 SetConstrainedWindowViewsClient(
Gyuyoung Kim6afb5082018-01-19 13:35:57219 std::make_unique<TestConstrainedWindowViewsClient>());
tapted82c125e2016-10-19 22:10:46220 DialogContents* contents = new DialogContents;
221 contents->set_modal_type(ui::MODAL_TYPE_WINDOW);
222 views::Widget* widget = CreateBrowserModalDialogViews(contents, nullptr);
223 widget->Show();
224 EXPECT_TRUE(widget->IsVisible());
225 widget->CloseNow();
226}
227
kylixrd51116b92016-12-16 16:48:10228// Make sure dialogs presented off-screen are properly clamped to the nearest
229// screen.
230TEST_F(ConstrainedWindowViewsTest, ClampDialogToNearestDisplay) {
231 // Make sure the dialog will fit fully on the display
Evan Stadee8c9c592017-05-25 20:09:53232 contents()->SetPreferredSize(gfx::Size(200, 100));
kylixrd51116b92016-12-16 16:48:10233
234 // First, make sure the host and dialog are sized and positioned.
235 UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
236
237 const display::Screen* screen = display::Screen::GetScreen();
238 const display::Display display = screen->GetPrimaryDisplay();
239 // Within the tests there is only 1 display. Error if that ever changes.
240 EXPECT_EQ(screen->GetNumDisplays(), 1);
241 const gfx::Rect extents = display.work_area();
242
243 // Move the host completely off the screen.
244 views::Widget* host_widget =
245 views::Widget::GetWidgetForNativeView(dialog_host()->GetHostView());
246 gfx::Rect host_bounds = host_widget->GetWindowBoundsInScreen();
247 host_bounds.set_origin(gfx::Point(extents.right(), extents.bottom()));
248 host_widget->SetBounds(host_bounds);
249
250 // Make sure the host is fully off the screen.
251 EXPECT_FALSE(extents.Intersects(host_widget->GetWindowBoundsInScreen()));
252
253 // Now reposition the modal dialog into the display.
254 UpdateWebContentsModalDialogPosition(dialog(), dialog_host());
255
256 const gfx::Rect dialog_bounds = dialog()->GetRootView()->GetBoundsInScreen();
257
258 // The dialog should now be fully on the display.
259 EXPECT_TRUE(extents.Contains(dialog_bounds));
260}
261
oshimadd3db6a2014-11-10 22:21:23262} // namespace constrained_window