[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 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 | |
oshima | 136691a | 2014-10-24 21:54:11 | [diff] [blame] | 5 | #include "components/constrained_window/constrained_window_views.h" |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 6 | |
dcheng | a0ee5fb | 2016-04-26 02:46:55 | [diff] [blame] | 7 | #include <memory> |
kylixrd | 51116b9 | 2016-12-16 16:48:10 | [diff] [blame] | 8 | #include <vector> |
dcheng | a0ee5fb | 2016-04-26 02:46:55 | [diff] [blame] | 9 | |
avi | bc5337b | 2015-12-25 23:16:33 | [diff] [blame] | 10 | #include "base/macros.h" |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 11 | #include "components/constrained_window/constrained_window_views_client.h" |
[email protected] | 127c6a1 | 2013-10-17 12:57:04 | [diff] [blame] | 12 | #include "components/web_modal/test_web_contents_modal_dialog_host.h" |
kylixrd | 51116b9 | 2016-12-16 16:48:10 | [diff] [blame] | 13 | #include "ui/display/display.h" |
| 14 | #include "ui/display/screen.h" |
tfarina | 655f81d | 2014-12-23 02:38:50 | [diff] [blame] | 15 | #include "ui/gfx/geometry/point.h" |
tfarina | 3b0452d | 2014-12-31 15:20:09 | [diff] [blame] | 16 | #include "ui/gfx/geometry/rect.h" |
tfarina | ebe974f0 | 2015-01-03 04:25:32 | [diff] [blame] | 17 | #include "ui/gfx/geometry/size.h" |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 18 | #include "ui/gfx/native_widget_types.h" |
[email protected] | 51170c0 | 2013-12-11 02:58:53 | [diff] [blame] | 19 | #include "ui/views/border.h" |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 20 | #include "ui/views/test/views_test_base.h" |
| 21 | #include "ui/views/widget/widget.h" |
| 22 | #include "ui/views/window/dialog_delegate.h" |
| 23 | |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 24 | using views::Widget; |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 25 | |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 26 | namespace constrained_window { |
| 27 | namespace { |
| 28 | |
| 29 | class DialogContents : public views::DialogDelegateView { |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 30 | public: |
| 31 | DialogContents() {} |
dcheng | 30a1b154 | 2014-10-29 21:27:50 | [diff] [blame] | 32 | ~DialogContents() override {} |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 33 | |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 34 | void set_modal_type(ui::ModalType modal_type) { modal_type_ = modal_type; } |
| 35 | |
| 36 | // DialogDelegateView: |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 37 | views::View* GetContentsView() override { return this; } |
dcheng | 30a1b154 | 2014-10-29 21:27:50 | [diff] [blame] | 38 | gfx::Size GetMinimumSize() const override { return gfx::Size(); } |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 39 | |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 40 | // WidgetDelegate: |
| 41 | ui::ModalType GetModalType() const override { return modal_type_; } |
| 42 | |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 43 | private: |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 44 | ui::ModalType modal_type_ = ui::MODAL_TYPE_NONE; |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 45 | |
| 46 | DISALLOW_COPY_AND_ASSIGN(DialogContents); |
| 47 | }; |
| 48 | |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 49 | // Dummy client that returns a null modal dialog host and host view. |
| 50 | class 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(). |
| 71 | class 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 | |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 92 | class ConstrainedWindowViewsTest : public views::ViewsTestBase { |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 93 | public: |
tapted | e37016b | 2014-12-16 04:06:42 | [diff] [blame] | 94 | ConstrainedWindowViewsTest() : contents_(nullptr), dialog_(nullptr) {} |
dcheng | 30a1b154 | 2014-10-29 21:27:50 | [diff] [blame] | 95 | ~ConstrainedWindowViewsTest() override {} |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 96 | |
dcheng | 30a1b154 | 2014-10-29 21:27:50 | [diff] [blame] | 97 | void SetUp() override { |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 98 | 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)); |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 105 | views::ViewsTestBase::SetUp(); |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 106 | views_delegate_weak->set_context(GetContext()); |
| 107 | |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 108 | contents_ = new DialogContents; |
tapted | e37016b | 2014-12-16 04:06:42 | [diff] [blame] | 109 | dialog_ = views::DialogDelegate::CreateDialogWidget( |
| 110 | contents_, GetContext(), nullptr); |
[email protected] | 127c6a1 | 2013-10-17 12:57:04 | [diff] [blame] | 111 | dialog_host_.reset(new web_modal::TestWebContentsModalDialogHost( |
| 112 | dialog_->GetNativeView())); |
| 113 | dialog_host_->set_max_dialog_size(gfx::Size(5000, 5000)); |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 114 | |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 115 | // 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 Stade | e8c9c59 | 2017-05-25 20:09:53 | [diff] [blame] | 119 | contents()->SetPreferredSize(preferred_size); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 120 | } |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 121 | |
dcheng | 30a1b154 | 2014-10-29 21:27:50 | [diff] [blame] | 122 | void TearDown() override { |
tapted | e37016b | 2014-12-16 04:06:42 | [diff] [blame] | 123 | contents_ = nullptr; |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 124 | dialog_host_.reset(); |
tapted | e37016b | 2014-12-16 04:06:42 | [diff] [blame] | 125 | dialog_->CloseNow(); |
| 126 | ViewsTestBase::TearDown(); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | gfx::Size GetDialogSize() { |
| 130 | return dialog()->GetRootView()->GetBoundsInScreen().size(); |
| 131 | } |
| 132 | |
| 133 | DialogContents* contents() { return contents_; } |
[email protected] | 127c6a1 | 2013-10-17 12:57:04 | [diff] [blame] | 134 | web_modal::TestWebContentsModalDialogHost* dialog_host() { |
| 135 | return dialog_host_.get(); |
| 136 | } |
tapted | e37016b | 2014-12-16 04:06:42 | [diff] [blame] | 137 | Widget* dialog() { return dialog_; } |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 138 | |
| 139 | private: |
| 140 | DialogContents* contents_; |
dcheng | a0ee5fb | 2016-04-26 02:46:55 | [diff] [blame] | 141 | std::unique_ptr<web_modal::TestWebContentsModalDialogHost> dialog_host_; |
tapted | e37016b | 2014-12-16 04:06:42 | [diff] [blame] | 142 | Widget* dialog_; |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 143 | |
| 144 | DISALLOW_COPY_AND_ASSIGN(ConstrainedWindowViewsTest); |
| 145 | }; |
| 146 | |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 147 | } // namespace |
| 148 | |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 149 | // Make sure a dialog that increases its preferred size grows on the next |
| 150 | // position update. |
| 151 | TEST_F(ConstrainedWindowViewsTest, GrowModalDialogSize) { |
oshima | 136691a | 2014-10-24 21:54:11 | [diff] [blame] | 152 | UpdateWidgetModalDialogPosition(dialog(), dialog_host()); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 153 | gfx::Size expected_size = GetDialogSize(); |
| 154 | gfx::Size preferred_size = contents()->GetPreferredSize(); |
| 155 | expected_size.Enlarge(50, 50); |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 156 | preferred_size.Enlarge(50, 50); |
Evan Stade | e8c9c59 | 2017-05-25 20:09:53 | [diff] [blame] | 157 | contents()->SetPreferredSize(preferred_size); |
oshima | 136691a | 2014-10-24 21:54:11 | [diff] [blame] | 158 | UpdateWidgetModalDialogPosition(dialog(), dialog_host()); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 159 | EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString()); |
| 160 | } |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 161 | |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 162 | // Make sure a dialog that reduces its preferred size shrinks on the next |
| 163 | // position update. |
| 164 | TEST_F(ConstrainedWindowViewsTest, ShrinkModalDialogSize) { |
oshima | 136691a | 2014-10-24 21:54:11 | [diff] [blame] | 165 | UpdateWidgetModalDialogPosition(dialog(), dialog_host()); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 166 | 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 Stade | e8c9c59 | 2017-05-25 20:09:53 | [diff] [blame] | 170 | contents()->SetPreferredSize(preferred_size); |
oshima | 136691a | 2014-10-24 21:54:11 | [diff] [blame] | 171 | UpdateWidgetModalDialogPosition(dialog(), dialog_host()); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 172 | EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString()); |
| 173 | } |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 174 | |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 175 | // Make sure browser modal dialogs are not affected by restrictions on web |
| 176 | // content modal dialog maximum sizes. |
| 177 | TEST_F(ConstrainedWindowViewsTest, MaximumBrowserDialogSize) { |
oshima | 136691a | 2014-10-24 21:54:11 | [diff] [blame] | 178 | UpdateWidgetModalDialogPosition(dialog(), dialog_host()); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 179 | 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); |
oshima | 136691a | 2014-10-24 21:54:11 | [diff] [blame] | 183 | UpdateWidgetModalDialogPosition(dialog(), dialog_host()); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 184 | EXPECT_EQ(dialog_size.ToString(), GetDialogSize().ToString()); |
| 185 | } |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 186 | |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 187 | // Web content modal dialogs should not get a size larger than what the dialog |
| 188 | // host gives as the maximum size. |
| 189 | TEST_F(ConstrainedWindowViewsTest, MaximumWebContentsDialogSize) { |
| 190 | UpdateWebContentsModalDialogPosition(dialog(), dialog_host()); |
| 191 | gfx::Size full_dialog_size = GetDialogSize(); |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 192 | gfx::Size max_dialog_size = full_dialog_size; |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 193 | max_dialog_size.Enlarge(-50, -50); |
| 194 | dialog_host()->set_max_dialog_size(max_dialog_size); |
| 195 | UpdateWebContentsModalDialogPosition(dialog(), dialog_host()); |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 196 | // 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] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 199 | gfx::Size expected_size = max_dialog_size; |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 200 | views::Border* border = dialog()->non_client_view()->frame_view()->border(); |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 201 | if (border) |
| 202 | expected_size.Enlarge(0, border->GetInsets().top()); |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 203 | EXPECT_EQ(expected_size.ToString(), GetDialogSize().ToString()); |
[email protected] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 204 | |
[email protected] | 00517a8 | 2013-10-09 22:19:30 | [diff] [blame] | 205 | // 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] | 0026387d | 2013-09-06 05:08:43 | [diff] [blame] | 211 | } |
| 212 | |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 213 | // Ensure CreateBrowserModalDialogViews() works correctly with a null parent. |
| 214 | TEST_F(ConstrainedWindowViewsTest, NullModalParent) { |
| 215 | // Use desktop widgets (except on ChromeOS) for extra coverage. |
kylixrd | 4e8cac4 | 2017-04-13 17:15:56 | [diff] [blame] | 216 | test_views_delegate()->set_use_desktop_native_widgets(true); |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 217 | |
| 218 | SetConstrainedWindowViewsClient( |
Gyuyoung Kim | 6afb508 | 2018-01-19 13:35:57 | [diff] [blame] | 219 | std::make_unique<TestConstrainedWindowViewsClient>()); |
tapted | 82c125e | 2016-10-19 22:10:46 | [diff] [blame] | 220 | 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 | |
kylixrd | 51116b9 | 2016-12-16 16:48:10 | [diff] [blame] | 228 | // Make sure dialogs presented off-screen are properly clamped to the nearest |
| 229 | // screen. |
| 230 | TEST_F(ConstrainedWindowViewsTest, ClampDialogToNearestDisplay) { |
| 231 | // Make sure the dialog will fit fully on the display |
Evan Stade | e8c9c59 | 2017-05-25 20:09:53 | [diff] [blame] | 232 | contents()->SetPreferredSize(gfx::Size(200, 100)); |
kylixrd | 51116b9 | 2016-12-16 16:48:10 | [diff] [blame] | 233 | |
| 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 | |
oshima | dd3db6a | 2014-11-10 22:21:23 | [diff] [blame] | 262 | } // namespace constrained_window |