1 // Copyright 2016 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
5 #include "ui/ozone/platform/wayland/host/wayland_window.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include <cursor-shapes-unstable-v1-client-protocol.h>
11 #include <linux/input.h>
12 #include <wayland-server-core.h>
13 #include <xdg-shell-server-protocol.h>
14 #include <xdg-shell-unstable-v6-server-protocol.h>
15
16 #include "base/files/file_util.h"
17 #include "base/memory/scoped_refptr.h"
18 #include "base/run_loop.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
23 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
24 #include "ui/base/hit_test.h"
25 #include "ui/events/base_event_utils.h"
26 #include "ui/events/event.h"
27 #include "ui/gfx/native_widget_types.h"
28 #include "ui/gfx/overlay_transform.h"
29 #include "ui/ozone/platform/wayland/common/wayland_util.h"
30 #include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
31 #include "ui/ozone/platform/wayland/host/wayland_connection_test_api.h"
32 #include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
33 #include "ui/ozone/platform/wayland/host/wayland_zcr_cursor_shapes.h"
34 #include "ui/ozone/platform/wayland/test/mock_pointer.h"
35 #include "ui/ozone/platform/wayland/test/mock_surface.h"
36 #include "ui/ozone/platform/wayland/test/test_keyboard.h"
37 #include "ui/ozone/platform/wayland/test/test_region.h"
38 #include "ui/ozone/platform/wayland/test/test_touch.h"
39 #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
40 #include "ui/ozone/platform/wayland/test/wayland_test.h"
41 #include "ui/ozone/test/mock_platform_window_delegate.h"
42 #include "ui/platform_window/platform_window.h"
43 #include "ui/platform_window/platform_window_init_properties.h"
44 #include "ui/platform_window/wm/wm_move_resize_handler.h"
45
46 using ::testing::_;
47 using ::testing::Eq;
48 using ::testing::Mock;
49 using ::testing::Return;
50 using ::testing::SaveArg;
51 using ::testing::StrEq;
52
53 namespace ui {
54
55 namespace {
56
57 struct PopupPosition {
58 gfx::Rect anchor_rect;
59 gfx::Size size;
60 uint32_t anchor = 0;
61 uint32_t gravity = 0;
62 uint32_t constraint_adjustment = 0;
63 };
64
65 class ScopedWlArray {
66 public:
ScopedWlArray()67 ScopedWlArray() { wl_array_init(&array_); }
68
ScopedWlArray(ScopedWlArray && rhs)69 ScopedWlArray(ScopedWlArray&& rhs) {
70 array_ = rhs.array_;
71 // wl_array_init sets rhs.array_'s fields to nullptr, so that
72 // the free() in wl_array_release() is a no-op.
73 wl_array_init(&rhs.array_);
74 }
75
~ScopedWlArray()76 ~ScopedWlArray() { wl_array_release(&array_); }
77
operator =(ScopedWlArray && rhs)78 ScopedWlArray& operator=(ScopedWlArray&& rhs) {
79 wl_array_release(&array_);
80 array_ = rhs.array_;
81 // wl_array_init sets rhs.array_'s fields to nullptr, so that
82 // the free() in wl_array_release() is a no-op.
83 wl_array_init(&rhs.array_);
84 return *this;
85 }
86
get()87 wl_array* get() { return &array_; }
88
89 private:
90 wl_array array_;
91 };
92
MakeFD()93 base::ScopedFD MakeFD() {
94 base::FilePath temp_path;
95 EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
96 auto file =
97 base::File(temp_path, base::File::FLAG_READ | base::File::FLAG_WRITE |
98 base::File::FLAG_CREATE_ALWAYS);
99 return base::ScopedFD(file.TakePlatformFile());
100 }
101
102 class MockZcrCursorShapes : public WaylandZcrCursorShapes {
103 public:
MockZcrCursorShapes()104 MockZcrCursorShapes() : WaylandZcrCursorShapes(nullptr, nullptr) {}
105 MockZcrCursorShapes(const MockZcrCursorShapes&) = delete;
106 MockZcrCursorShapes& operator=(const MockZcrCursorShapes&) = delete;
107 ~MockZcrCursorShapes() override = default;
108
109 MOCK_METHOD(void, SetCursorShape, (int32_t), (override));
110 };
111
112 } // namespace
113
114 class WaylandWindowTest : public WaylandTest {
115 public:
WaylandWindowTest()116 WaylandWindowTest()
117 : test_mouse_event_(ET_MOUSE_PRESSED,
118 gfx::Point(10, 15),
119 gfx::Point(10, 15),
120 ui::EventTimeStampFromSeconds(123456),
121 EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON,
122 EF_LEFT_MOUSE_BUTTON) {}
123
SetUp()124 void SetUp() override {
125 WaylandTest::SetUp();
126
127 xdg_surface_ = surface_->xdg_surface();
128 ASSERT_TRUE(xdg_surface_);
129 }
130
131 protected:
SendConfigureEventPopup(WaylandWindow * menu_window,const gfx::Rect bounds)132 void SendConfigureEventPopup(WaylandWindow* menu_window,
133 const gfx::Rect bounds) {
134 auto* popup = GetPopupByWindow(menu_window);
135 ASSERT_TRUE(popup);
136 if (GetParam() == kXdgShellV6) {
137 zxdg_popup_v6_send_configure(popup->resource(), bounds.x(), bounds.y(),
138 bounds.width(), bounds.height());
139 } else {
140 xdg_popup_send_configure(popup->resource(), bounds.x(), bounds.y(),
141 bounds.width(), bounds.height());
142 }
143 }
144
GetXdgToplevel()145 wl::MockXdgTopLevel* GetXdgToplevel() { return xdg_surface_->xdg_toplevel(); }
146
AddStateToWlArray(uint32_t state,wl_array * states)147 void AddStateToWlArray(uint32_t state, wl_array* states) {
148 *static_cast<uint32_t*>(wl_array_add(states, sizeof state)) = state;
149 }
150
InitializeWlArrayWithActivatedState()151 ScopedWlArray InitializeWlArrayWithActivatedState() {
152 ScopedWlArray states;
153 AddStateToWlArray(XDG_TOPLEVEL_STATE_ACTIVATED, states.get());
154 return states;
155 }
156
MakeStateArray(const std::vector<int32_t> states)157 ScopedWlArray MakeStateArray(const std::vector<int32_t> states) {
158 ScopedWlArray result;
159 for (const auto state : states)
160 AddStateToWlArray(state, result.get());
161 return result;
162 }
163
CreateWaylandWindowWithParams(PlatformWindowType type,gfx::AcceleratedWidget parent_widget,const gfx::Rect bounds,MockPlatformWindowDelegate * delegate)164 std::unique_ptr<WaylandWindow> CreateWaylandWindowWithParams(
165 PlatformWindowType type,
166 gfx::AcceleratedWidget parent_widget,
167 const gfx::Rect bounds,
168 MockPlatformWindowDelegate* delegate) {
169 PlatformWindowInitProperties properties;
170 // TODO(msisov): use a fancy method to calculate position of a popup window.
171 properties.bounds = bounds;
172 properties.type = type;
173 properties.parent_widget = parent_widget;
174
175 auto window = WaylandWindow::Create(delegate, connection_.get(),
176 std::move(properties));
177 if (window)
178 window->Show(false);
179 return window;
180 }
181
InitializeWithSupportedHitTestValues(std::vector<int> * hit_tests)182 void InitializeWithSupportedHitTestValues(std::vector<int>* hit_tests) {
183 hit_tests->push_back(static_cast<int>(HTBOTTOM));
184 hit_tests->push_back(static_cast<int>(HTBOTTOMLEFT));
185 hit_tests->push_back(static_cast<int>(HTBOTTOMRIGHT));
186 hit_tests->push_back(static_cast<int>(HTLEFT));
187 hit_tests->push_back(static_cast<int>(HTRIGHT));
188 hit_tests->push_back(static_cast<int>(HTTOP));
189 hit_tests->push_back(static_cast<int>(HTTOPLEFT));
190 hit_tests->push_back(static_cast<int>(HTTOPRIGHT));
191 }
192
InstallMockZcrCursorShapes()193 MockZcrCursorShapes* InstallMockZcrCursorShapes() {
194 auto zcr_cursor_shapes = std::make_unique<MockZcrCursorShapes>();
195 MockZcrCursorShapes* mock_cursor_shapes = zcr_cursor_shapes.get();
196 WaylandConnectionTestApi test_api(connection_.get());
197 test_api.SetZcrCursorShapes(std::move(zcr_cursor_shapes));
198 return mock_cursor_shapes;
199 }
200
VerifyAndClearExpectations()201 void VerifyAndClearExpectations() {
202 Mock::VerifyAndClearExpectations(xdg_surface_);
203 Mock::VerifyAndClearExpectations(&delegate_);
204 }
205
VerifyXdgPopupPosition(WaylandWindow * menu_window,const PopupPosition & position)206 void VerifyXdgPopupPosition(WaylandWindow* menu_window,
207 const PopupPosition& position) {
208 auto* popup = GetPopupByWindow(menu_window);
209 ASSERT_TRUE(popup);
210
211 EXPECT_EQ(popup->anchor_rect(), position.anchor_rect);
212 EXPECT_EQ(popup->size(), position.size);
213 EXPECT_EQ(popup->anchor(), position.anchor);
214 EXPECT_EQ(popup->gravity(), position.gravity);
215 EXPECT_EQ(popup->constraint_adjustment(), position.constraint_adjustment);
216 }
217
VerifyCanDispatchMouseEvents(const std::vector<WaylandWindow * > & dispatching_windows,const std::vector<WaylandWindow * > & non_dispatching_windows)218 void VerifyCanDispatchMouseEvents(
219 const std::vector<WaylandWindow*>& dispatching_windows,
220 const std::vector<WaylandWindow*>& non_dispatching_windows) {
221 for (auto* window : dispatching_windows)
222 EXPECT_TRUE(window->CanDispatchEvent(&test_mouse_event_));
223 for (auto* window : non_dispatching_windows)
224 EXPECT_FALSE(window->CanDispatchEvent(&test_mouse_event_));
225 }
226
VerifyCanDispatchTouchEvents(const std::vector<WaylandWindow * > & dispatching_windows,const std::vector<WaylandWindow * > & non_dispatching_windows)227 void VerifyCanDispatchTouchEvents(
228 const std::vector<WaylandWindow*>& dispatching_windows,
229 const std::vector<WaylandWindow*>& non_dispatching_windows) {
230 PointerDetails pointer_details(EventPointerType::kTouch, 1);
231 TouchEvent test_touch_event(ET_TOUCH_PRESSED, {1, 1}, base::TimeTicks(),
232 pointer_details);
233 for (auto* window : dispatching_windows)
234 EXPECT_TRUE(window->CanDispatchEvent(&test_touch_event));
235 for (auto* window : non_dispatching_windows)
236 EXPECT_FALSE(window->CanDispatchEvent(&test_touch_event));
237 }
238
VerifyCanDispatchKeyEvents(const std::vector<WaylandWindow * > & dispatching_windows,const std::vector<WaylandWindow * > & non_dispatching_windows)239 void VerifyCanDispatchKeyEvents(
240 const std::vector<WaylandWindow*>& dispatching_windows,
241 const std::vector<WaylandWindow*>& non_dispatching_windows) {
242 KeyEvent test_key_event(ET_KEY_PRESSED, VKEY_0, 0);
243 for (auto* window : dispatching_windows)
244 EXPECT_TRUE(window->CanDispatchEvent(&test_key_event));
245 for (auto* window : non_dispatching_windows)
246 EXPECT_FALSE(window->CanDispatchEvent(&test_key_event));
247 }
248
GetPopupByWindow(WaylandWindow * window)249 wl::TestXdgPopup* GetPopupByWindow(WaylandWindow* window) {
250 wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>(
251 window->root_surface()->GetSurfaceId());
252 if (mock_surface) {
253 auto* mock_xdg_surface = mock_surface->xdg_surface();
254 if (mock_xdg_surface)
255 return mock_xdg_surface->xdg_popup();
256 }
257 return nullptr;
258 }
259
260 wl::MockXdgSurface* xdg_surface_;
261
262 MouseEvent test_mouse_event_;
263
264 private:
265 DISALLOW_COPY_AND_ASSIGN(WaylandWindowTest);
266 };
267
TEST_P(WaylandWindowTest,SetTitle)268 TEST_P(WaylandWindowTest, SetTitle) {
269 EXPECT_CALL(*GetXdgToplevel(), SetTitle(StrEq("hello")));
270 window_->SetTitle(base::ASCIIToUTF16("hello"));
271 }
272
TEST_P(WaylandWindowTest,MaximizeAndRestore)273 TEST_P(WaylandWindowTest, MaximizeAndRestore) {
274 const auto kNormalBounds = gfx::Rect{0, 0, 500, 300};
275 const auto kMaximizedBounds = gfx::Rect{0, 0, 800, 600};
276
277 uint32_t serial = 0;
278
279 // Make sure the window has normal state initially.
280 EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds));
281 window_->SetBounds(kNormalBounds);
282 EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
283 VerifyAndClearExpectations();
284
285 // Deactivate the surface.
286 auto empty_state = MakeStateArray({});
287 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get());
288
289 Sync();
290
291 auto active_maximized = MakeStateArray(
292 {XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_MAXIMIZED});
293 EXPECT_CALL(*GetXdgToplevel(), SetMaximized());
294 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kMaximizedBounds.width(),
295 kMaximizedBounds.height()));
296 EXPECT_CALL(delegate_, OnActivationChanged(Eq(true)));
297 EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds));
298 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
299 window_->Maximize();
300 SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
301 kMaximizedBounds.height(), ++serial,
302 active_maximized.get());
303 Sync();
304 VerifyAndClearExpectations();
305
306 auto inactive_maximized = MakeStateArray({XDG_TOPLEVEL_STATE_MAXIMIZED});
307 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kMaximizedBounds.width(),
308 kMaximizedBounds.height()));
309 EXPECT_CALL(delegate_, OnActivationChanged(Eq(false)));
310 EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0);
311 SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
312 kMaximizedBounds.height(), ++serial,
313 inactive_maximized.get());
314 Sync();
315 VerifyAndClearExpectations();
316
317 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kMaximizedBounds.width(),
318 kMaximizedBounds.height()));
319 EXPECT_CALL(delegate_, OnActivationChanged(Eq(true)));
320 EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0);
321 SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
322 kMaximizedBounds.height(), ++serial,
323 active_maximized.get());
324 Sync();
325 VerifyAndClearExpectations();
326
327 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(),
328 kNormalBounds.height()));
329 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
330 EXPECT_CALL(delegate_, OnActivationChanged(_)).Times(0);
331 EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds));
332 EXPECT_CALL(*GetXdgToplevel(), UnsetMaximized());
333 window_->Restore();
334 // Reinitialize wl_array, which removes previous old states.
335 auto active = InitializeWlArrayWithActivatedState();
336 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, active.get());
337 Sync();
338 }
339
TEST_P(WaylandWindowTest,Minimize)340 TEST_P(WaylandWindowTest, Minimize) {
341 ScopedWlArray states;
342
343 // Make sure the window is initialized to normal state from the beginning.
344 EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
345 SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
346 Sync();
347
348 EXPECT_CALL(*GetXdgToplevel(), SetMinimized());
349 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
350 window_->Minimize();
351 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMinimized);
352
353 // Reinitialize wl_array, which removes previous old states.
354 states = ScopedWlArray();
355 SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
356 Sync();
357
358 // Wayland compositor doesn't notify clients about minimized state, but rather
359 // if a window is not activated. Thus, a WaylandToplevelWindow marks itself as
360 // being minimized and and sets state to minimized. Thus, the state mustn't
361 // change after the configuration event is sent.
362 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMinimized);
363
364 // Send one additional empty configuration event (which means the surface is
365 // not maximized, fullscreen or activated) to ensure, WaylandWindow stays in
366 // the same minimized state and doesn't notify its delegate.
367 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
368 SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
369 Sync();
370
371 // And one last time to ensure the behaviour.
372 SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
373 Sync();
374 }
375
TEST_P(WaylandWindowTest,SetFullscreenAndRestore)376 TEST_P(WaylandWindowTest, SetFullscreenAndRestore) {
377 // Make sure the window is initialized to normal state from the beginning.
378 EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
379
380 ScopedWlArray states = InitializeWlArrayWithActivatedState();
381 SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
382 Sync();
383
384 AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
385
386 EXPECT_CALL(*GetXdgToplevel(), SetFullscreen());
387 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
388 window_->ToggleFullscreen();
389 // Make sure than WaylandWindow manually handles fullscreen states. Check the
390 // comment in the WaylandWindow::ToggleFullscreen.
391 EXPECT_EQ(window_->GetPlatformWindowState(),
392 PlatformWindowState::kFullScreen);
393 SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
394 Sync();
395
396 EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen());
397 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
398 window_->Restore();
399 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal);
400 // Reinitialize wl_array, which removes previous old states.
401 states = InitializeWlArrayWithActivatedState();
402 SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
403 Sync();
404 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal);
405 }
406
TEST_P(WaylandWindowTest,StartWithFullscreen)407 TEST_P(WaylandWindowTest, StartWithFullscreen) {
408 MockPlatformWindowDelegate delegate;
409 PlatformWindowInitProperties properties;
410 properties.bounds = gfx::Rect(0, 0, 100, 100);
411 properties.type = PlatformWindowType::kWindow;
412 // We need to create a window avoid calling Show() on it as it is what upper
413 // views layer does - when Widget initialize DesktopWindowTreeHost, the Show()
414 // is called later down the road, but Maximize may be called earlier. We
415 // cannot process them and set a pending state instead, because ShellSurface
416 // is not created by that moment.
417 auto window = WaylandWindow::Create(&delegate, connection_.get(),
418 std::move(properties));
419
420 Sync();
421
422 // Make sure the window is initialized to normal state from the beginning.
423 EXPECT_EQ(PlatformWindowState::kNormal, window->GetPlatformWindowState());
424
425 // The state must not be changed to the fullscreen before the surface is
426 // activated.
427 auto* mock_surface = server_.GetObject<wl::MockSurface>(
428 window->root_surface()->GetSurfaceId());
429 EXPECT_FALSE(mock_surface->xdg_surface());
430 EXPECT_CALL(delegate, OnWindowStateChanged(_)).Times(0);
431 window->ToggleFullscreen();
432 // The state of the window must already be fullscreen one.
433 EXPECT_EQ(window->GetPlatformWindowState(), PlatformWindowState::kFullScreen);
434
435 Sync();
436
437 // We mustn't receive any state changes if that does not differ from the last
438 // state.
439 EXPECT_CALL(delegate, OnWindowStateChanged(_)).Times(0);
440
441 // Activate the surface.
442 ScopedWlArray states = InitializeWlArrayWithActivatedState();
443 AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
444 SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
445
446 Sync();
447
448 // It must be still the same state.
449 EXPECT_EQ(window->GetPlatformWindowState(), PlatformWindowState::kFullScreen);
450 }
451
TEST_P(WaylandWindowTest,StartMaximized)452 TEST_P(WaylandWindowTest, StartMaximized) {
453 MockPlatformWindowDelegate delegate;
454 PlatformWindowInitProperties properties;
455 properties.bounds = gfx::Rect(0, 0, 100, 100);
456 properties.type = PlatformWindowType::kWindow;
457 // We need to create a window avoid calling Show() on it as it is what upper
458 // views layer does - when Widget initialize DesktopWindowTreeHost, the Show()
459 // is called later down the road, but Maximize may be called earlier. We
460 // cannot process them and set a pending state instead, because ShellSurface
461 // is not created by that moment.
462 auto window = WaylandWindow::Create(&delegate, connection_.get(),
463 std::move(properties));
464
465 Sync();
466
467 // Make sure the window is initialized to normal state from the beginning.
468 EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
469
470 // The state must not be changed to the fullscreen before the surface is
471 // activated.
472 auto* mock_surface = server_.GetObject<wl::MockSurface>(
473 window->root_surface()->GetSurfaceId());
474 EXPECT_FALSE(mock_surface->xdg_surface());
475 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
476
477 window_->Maximize();
478 // The state of the window must already be fullscreen one.
479 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized);
480
481 Sync();
482
483 // Once the surface will be activated, the window state mustn't be changed
484 // and retain the same.
485 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
486 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized);
487
488 // Activate the surface.
489 ScopedWlArray states = InitializeWlArrayWithActivatedState();
490 AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
491 SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
492
493 Sync();
494
495 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized);
496 }
497
TEST_P(WaylandWindowTest,CompositorSideStateChanges)498 TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
499 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal);
500 auto normal_bounds = window_->GetBounds();
501
502 ScopedWlArray states = InitializeWlArrayWithActivatedState();
503 AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
504 SendConfigureEvent(xdg_surface_, 2000, 2000, 1, states.get());
505
506 EXPECT_CALL(delegate_,
507 OnWindowStateChanged(Eq(PlatformWindowState::kMaximized)))
508 .Times(1);
509 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2000, 2000));
510
511 Sync();
512
513 EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized);
514
515 // Unmaximize
516 states = InitializeWlArrayWithActivatedState();
517 SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
518
519 EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal)))
520 .Times(1);
521 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(),
522 normal_bounds.height()));
523
524 // Now, set to fullscreen.
525 AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
526 SendConfigureEvent(xdg_surface_, 2005, 2005, 3, states.get());
527 EXPECT_CALL(delegate_,
528 OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen)))
529 .Times(1);
530 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2005, 2005));
531
532 Sync();
533
534 // Unfullscreen
535 states = InitializeWlArrayWithActivatedState();
536 SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
537
538 EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal)))
539 .Times(1);
540 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(),
541 normal_bounds.height()));
542
543 Sync();
544
545 // Now, maximize, fullscreen and restore.
546 states = InitializeWlArrayWithActivatedState();
547 AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
548 SendConfigureEvent(xdg_surface_, 2000, 2000, 1, states.get());
549
550 EXPECT_CALL(delegate_,
551 OnWindowStateChanged(Eq(PlatformWindowState::kMaximized)))
552 .Times(1);
553 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2000, 2000));
554
555 Sync();
556
557 AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
558 SendConfigureEvent(xdg_surface_, 2005, 2005, 1, states.get());
559
560 EXPECT_CALL(delegate_,
561 OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen)))
562 .Times(1);
563 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 2005, 2005));
564
565 // Restore
566 states = InitializeWlArrayWithActivatedState();
567 SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
568
569 EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal)))
570 .Times(1);
571 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(),
572 normal_bounds.height()));
573
574 Sync();
575 }
576
TEST_P(WaylandWindowTest,SetMaximizedFullscreenAndRestore)577 TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) {
578 const auto kNormalBounds = gfx::Rect{0, 0, 500, 300};
579 const auto kMaximizedBounds = gfx::Rect{0, 0, 800, 600};
580
581 uint32_t serial = 0;
582
583 // Make sure the window has normal state initially.
584 EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds));
585 window_->SetBounds(kNormalBounds);
586 EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
587 VerifyAndClearExpectations();
588
589 // Deactivate the surface.
590 ScopedWlArray empty_state;
591 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get());
592 Sync();
593
594 auto active_maximized = MakeStateArray(
595 {XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_MAXIMIZED});
596 EXPECT_CALL(*GetXdgToplevel(), SetMaximized());
597 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kMaximizedBounds.width(),
598 kMaximizedBounds.height()));
599 EXPECT_CALL(delegate_, OnActivationChanged(Eq(true)));
600 EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds));
601 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
602 window_->Maximize();
603 // State changes are synchronous.
604 EXPECT_EQ(PlatformWindowState::kMaximized, window_->GetPlatformWindowState());
605 SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
606 kMaximizedBounds.height(), ++serial,
607 active_maximized.get());
608 Sync();
609 // Verify that the state has not been changed.
610 EXPECT_EQ(PlatformWindowState::kMaximized, window_->GetPlatformWindowState());
611 VerifyAndClearExpectations();
612
613 EXPECT_CALL(*GetXdgToplevel(), SetFullscreen());
614 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kMaximizedBounds.width(),
615 kMaximizedBounds.height()));
616 EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0);
617 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
618 window_->ToggleFullscreen();
619 // State changes are synchronous.
620 EXPECT_EQ(PlatformWindowState::kFullScreen,
621 window_->GetPlatformWindowState());
622 AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, active_maximized.get());
623 SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
624 kMaximizedBounds.height(), ++serial,
625 active_maximized.get());
626 Sync();
627 // Verify that the state has not been changed.
628 EXPECT_EQ(PlatformWindowState::kFullScreen,
629 window_->GetPlatformWindowState());
630 VerifyAndClearExpectations();
631
632 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, kNormalBounds.width(),
633 kNormalBounds.height()));
634 EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen());
635 EXPECT_CALL(*GetXdgToplevel(), UnsetMaximized());
636 EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds));
637 EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
638 window_->Restore();
639 EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
640 // Reinitialize wl_array, which removes previous old states.
641 auto active = InitializeWlArrayWithActivatedState();
642 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, active.get());
643 Sync();
644 EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
645 }
646
TEST_P(WaylandWindowTest,RestoreBoundsAfterMaximize)647 TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) {
648 const gfx::Rect current_bounds = window_->GetBounds();
649
650 ScopedWlArray states = InitializeWlArrayWithActivatedState();
651
652 gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels();
653 EXPECT_TRUE(restored_bounds.IsEmpty());
654 gfx::Rect bounds = window_->GetBounds();
655
656 const gfx::Rect maximized_bounds = gfx::Rect(0, 0, 1024, 768);
657 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds)));
658 window_->Maximize();
659 AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
660 SendConfigureEvent(xdg_surface_, maximized_bounds.width(),
661 maximized_bounds.height(), 1, states.get());
662 Sync();
663 restored_bounds = window_->GetRestoredBoundsInPixels();
664 EXPECT_EQ(bounds, restored_bounds);
665
666 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(current_bounds)));
667 // Both in XdgV5 and XdgV6, surfaces implement SetWindowGeometry method.
668 // Thus, using a toplevel object in XdgV6 case is not right thing. Use a
669 // surface here instead.
670 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, current_bounds.width(),
671 current_bounds.height()));
672 window_->Restore();
673 // Reinitialize wl_array, which removes previous old states.
674 states = InitializeWlArrayWithActivatedState();
675 SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
676 Sync();
677 bounds = window_->GetBounds();
678 EXPECT_EQ(bounds, restored_bounds);
679 restored_bounds = window_->GetRestoredBoundsInPixels();
680 EXPECT_EQ(restored_bounds, gfx::Rect());
681 }
682
TEST_P(WaylandWindowTest,RestoreBoundsAfterFullscreen)683 TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) {
684 const gfx::Rect current_bounds = window_->GetBounds();
685
686 ScopedWlArray states = InitializeWlArrayWithActivatedState();
687 SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
688 Sync();
689
690 gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels();
691 EXPECT_EQ(restored_bounds, gfx::Rect());
692 gfx::Rect bounds = window_->GetBounds();
693
694 const gfx::Rect fullscreen_bounds = gfx::Rect(0, 0, 1280, 720);
695 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds)));
696 window_->ToggleFullscreen();
697 AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
698 SendConfigureEvent(xdg_surface_, fullscreen_bounds.width(),
699 fullscreen_bounds.height(), 2, states.get());
700 Sync();
701 restored_bounds = window_->GetRestoredBoundsInPixels();
702 EXPECT_EQ(bounds, restored_bounds);
703
704 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(current_bounds)));
705 // Both in XdgV5 and XdgV6, surfaces implement SetWindowGeometry method.
706 // Thus, using a toplevel object in XdgV6 case is not right thing. Use a
707 // surface here instead.
708 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, current_bounds.width(),
709 current_bounds.height()));
710 window_->Restore();
711 // Reinitialize wl_array, which removes previous old states.
712 states = InitializeWlArrayWithActivatedState();
713 SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
714 Sync();
715 bounds = window_->GetBounds();
716 EXPECT_EQ(bounds, restored_bounds);
717 restored_bounds = window_->GetRestoredBoundsInPixels();
718 EXPECT_EQ(restored_bounds, gfx::Rect());
719 }
720
TEST_P(WaylandWindowTest,RestoreBoundsAfterMaximizeAndFullscreen)721 TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) {
722 const gfx::Rect current_bounds = window_->GetBounds();
723
724 ScopedWlArray states = InitializeWlArrayWithActivatedState();
725
726 gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels();
727 EXPECT_EQ(restored_bounds, gfx::Rect());
728 gfx::Rect bounds = window_->GetBounds();
729
730 const gfx::Rect maximized_bounds = gfx::Rect(0, 0, 1024, 768);
731 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds)));
732 window_->Maximize();
733 AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
734 SendConfigureEvent(xdg_surface_, maximized_bounds.width(),
735 maximized_bounds.height(), 1, states.get());
736 Sync();
737 restored_bounds = window_->GetRestoredBoundsInPixels();
738 EXPECT_EQ(bounds, restored_bounds);
739
740 const gfx::Rect fullscreen_bounds = gfx::Rect(0, 0, 1280, 720);
741 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds)));
742 window_->ToggleFullscreen();
743 AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
744 SendConfigureEvent(xdg_surface_, fullscreen_bounds.width(),
745 fullscreen_bounds.height(), 2, states.get());
746 Sync();
747 gfx::Rect fullscreen_restore_bounds = window_->GetRestoredBoundsInPixels();
748 EXPECT_EQ(restored_bounds, fullscreen_restore_bounds);
749
750 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds)));
751 window_->Maximize();
752 // Reinitialize wl_array, which removes previous old states.
753 states = InitializeWlArrayWithActivatedState();
754 AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
755 SendConfigureEvent(xdg_surface_, maximized_bounds.width(),
756 maximized_bounds.height(), 3, states.get());
757 Sync();
758 restored_bounds = window_->GetRestoredBoundsInPixels();
759 EXPECT_EQ(restored_bounds, fullscreen_restore_bounds);
760
761 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(current_bounds)));
762 // Both in XdgV5 and XdgV6, surfaces implement SetWindowGeometry method.
763 // Thus, using a toplevel object in XdgV6 case is not right thing. Use a
764 // surface here instead.
765 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, current_bounds.width(),
766 current_bounds.height()));
767 window_->Restore();
768 // Reinitialize wl_array, which removes previous old states.
769 states = InitializeWlArrayWithActivatedState();
770 SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
771 Sync();
772 bounds = window_->GetBounds();
773 EXPECT_EQ(bounds, restored_bounds);
774 restored_bounds = window_->GetRestoredBoundsInPixels();
775 EXPECT_EQ(restored_bounds, gfx::Rect());
776 }
777
TEST_P(WaylandWindowTest,SendsBoundsOnRequest)778 TEST_P(WaylandWindowTest, SendsBoundsOnRequest) {
779 const gfx::Rect initial_bounds = window_->GetBounds();
780
781 const gfx::Rect new_bounds = gfx::Rect(0, 0, initial_bounds.width() + 10,
782 initial_bounds.height() + 10);
783 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(new_bounds)));
784 window_->SetBounds(new_bounds);
785
786 ScopedWlArray states = InitializeWlArrayWithActivatedState();
787
788 // First case is when Wayland sends a configure event with 0,0 height and
789 // width.
790 EXPECT_CALL(*xdg_surface_,
791 SetWindowGeometry(0, 0, new_bounds.width(), new_bounds.height()))
792 .Times(2);
793 SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
794 Sync();
795
796 // Restored bounds should keep empty value.
797 gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels();
798 EXPECT_EQ(restored_bounds, gfx::Rect());
799
800 // Second case is when Wayland sends a configure event with 1, 1 height and
801 // width. It looks more like a bug in Gnome Shell with Wayland as long as the
802 // documentation says it must be set to 0, 0, when wayland requests bounds.
803 SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
804 Sync();
805
806 // Restored bounds should keep empty value.
807 restored_bounds = window_->GetRestoredBoundsInPixels();
808 EXPECT_EQ(restored_bounds, gfx::Rect());
809 }
810
TEST_P(WaylandWindowTest,CanDispatchMouseEventDefault)811 TEST_P(WaylandWindowTest, CanDispatchMouseEventDefault) {
812 EXPECT_FALSE(window_->CanDispatchEvent(&test_mouse_event_));
813 }
814
TEST_P(WaylandWindowTest,CanDispatchMouseEventFocus)815 TEST_P(WaylandWindowTest, CanDispatchMouseEventFocus) {
816 // SetPointerFocus(true) requires a WaylandPointer.
817 wl_seat_send_capabilities(server_.seat()->resource(),
818 WL_SEAT_CAPABILITY_POINTER);
819 Sync();
820 ASSERT_TRUE(connection_->pointer());
821 window_->SetPointerFocus(true);
822 EXPECT_TRUE(window_->CanDispatchEvent(&test_mouse_event_));
823 }
824
TEST_P(WaylandWindowTest,CanDispatchMouseEventUnfocus)825 TEST_P(WaylandWindowTest, CanDispatchMouseEventUnfocus) {
826 EXPECT_FALSE(window_->has_pointer_focus());
827 EXPECT_FALSE(window_->CanDispatchEvent(&test_mouse_event_));
828 }
829
TEST_P(WaylandWindowTest,SetCursorUsesZcrCursorShapesForCommonTypes)830 TEST_P(WaylandWindowTest, SetCursorUsesZcrCursorShapesForCommonTypes) {
831 MockZcrCursorShapes* mock_cursor_shapes = InstallMockZcrCursorShapes();
832
833 // Verify some commonly-used cursors.
834 EXPECT_CALL(*mock_cursor_shapes,
835 SetCursorShape(ZCR_CURSOR_SHAPES_V1_CURSOR_SHAPE_TYPE_POINTER));
836 auto pointer_cursor =
837 base::MakeRefCounted<BitmapCursorOzone>(mojom::CursorType::kPointer);
838 window_->SetCursor(pointer_cursor.get());
839
840 EXPECT_CALL(*mock_cursor_shapes,
841 SetCursorShape(ZCR_CURSOR_SHAPES_V1_CURSOR_SHAPE_TYPE_HAND));
842 auto hand_cursor =
843 base::MakeRefCounted<BitmapCursorOzone>(mojom::CursorType::kHand);
844 window_->SetCursor(hand_cursor.get());
845
846 EXPECT_CALL(*mock_cursor_shapes,
847 SetCursorShape(ZCR_CURSOR_SHAPES_V1_CURSOR_SHAPE_TYPE_IBEAM));
848 auto ibeam_cursor =
849 base::MakeRefCounted<BitmapCursorOzone>(mojom::CursorType::kIBeam);
850 window_->SetCursor(ibeam_cursor.get());
851 }
852
TEST_P(WaylandWindowTest,SetCursorCallsZcrCursorShapesOncePerCursor)853 TEST_P(WaylandWindowTest, SetCursorCallsZcrCursorShapesOncePerCursor) {
854 MockZcrCursorShapes* mock_cursor_shapes = InstallMockZcrCursorShapes();
855 auto hand_cursor =
856 base::MakeRefCounted<BitmapCursorOzone>(mojom::CursorType::kHand);
857 // Setting the same cursor twice on the client only calls the server once.
858 EXPECT_CALL(*mock_cursor_shapes, SetCursorShape(_)).Times(1);
859 window_->SetCursor(hand_cursor.get());
860 window_->SetCursor(hand_cursor.get());
861 }
862
TEST_P(WaylandWindowTest,SetCursorDoesNotUseZcrCursorShapesForNoneCursor)863 TEST_P(WaylandWindowTest, SetCursorDoesNotUseZcrCursorShapesForNoneCursor) {
864 MockZcrCursorShapes* mock_cursor_shapes = InstallMockZcrCursorShapes();
865 EXPECT_CALL(*mock_cursor_shapes, SetCursorShape(_)).Times(0);
866 // The "none" cursor is represented by nullptr.
867 window_->SetCursor(nullptr);
868 }
869
TEST_P(WaylandWindowTest,SetCursorDoesNotUseZcrCursorShapesForCustomCursors)870 TEST_P(WaylandWindowTest, SetCursorDoesNotUseZcrCursorShapesForCustomCursors) {
871 MockZcrCursorShapes* mock_cursor_shapes = InstallMockZcrCursorShapes();
872
873 // Custom cursors require bitmaps, so they do not use server-side cursors.
874 EXPECT_CALL(*mock_cursor_shapes, SetCursorShape(_)).Times(0);
875 auto custom_cursor = base::MakeRefCounted<BitmapCursorOzone>(
876 mojom::CursorType::kCustom, SkBitmap(), gfx::Point());
877 window_->SetCursor(custom_cursor.get());
878 }
879
ACTION_P(CloneEvent,ptr)880 ACTION_P(CloneEvent, ptr) {
881 *ptr = Event::Clone(*arg0);
882 }
883
TEST_P(WaylandWindowTest,DispatchEvent)884 TEST_P(WaylandWindowTest, DispatchEvent) {
885 std::unique_ptr<Event> event;
886 EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event));
887 window_->DispatchEvent(&test_mouse_event_);
888 ASSERT_TRUE(event);
889 ASSERT_TRUE(event->IsMouseEvent());
890 auto* mouse_event = event->AsMouseEvent();
891 EXPECT_EQ(mouse_event->location_f(), test_mouse_event_.location_f());
892 EXPECT_EQ(mouse_event->root_location_f(),
893 test_mouse_event_.root_location_f());
894 EXPECT_EQ(mouse_event->time_stamp(), test_mouse_event_.time_stamp());
895 EXPECT_EQ(mouse_event->button_flags(), test_mouse_event_.button_flags());
896 EXPECT_EQ(mouse_event->changed_button_flags(),
897 test_mouse_event_.changed_button_flags());
898 }
899
TEST_P(WaylandWindowTest,ConfigureEvent)900 TEST_P(WaylandWindowTest, ConfigureEvent) {
901 ScopedWlArray states;
902
903 // The surface must react on each configure event and send bounds to its
904 // delegate.
905
906 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1000, 1000))));
907 // Responding to a configure event, the window geometry in here must respect
908 // the sizing negotiations specified by the configure event.
909 // |xdg_surface_| must receive the following calls in both xdg_shell_v5 and
910 // xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by
911 // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5.
912 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1000, 1000)).Times(1);
913 EXPECT_CALL(*xdg_surface_, AckConfigure(12));
914 SendConfigureEvent(xdg_surface_, 1000, 1000, 12, states.get());
915
916 Sync();
917
918 EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000))));
919 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1500, 1000)).Times(1);
920 EXPECT_CALL(*xdg_surface_, AckConfigure(13));
921 SendConfigureEvent(xdg_surface_, 1500, 1000, 13, states.get());
922 }
923
TEST_P(WaylandWindowTest,ConfigureEventWithNulledSize)924 TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) {
925 ScopedWlArray states;
926
927 // If Wayland sends configure event with 0 width and 0 size, client should
928 // call back with desired sizes. In this case, that's the actual size of
929 // the window.
930 SendConfigureEvent(xdg_surface_, 0, 0, 14, states.get());
931 // |xdg_surface_| must receive the following calls in both xdg_shell_v5 and
932 // xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by
933 // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5.
934 EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 800, 600));
935 EXPECT_CALL(*xdg_surface_, AckConfigure(14));
936 }
937
TEST_P(WaylandWindowTest,OnActivationChanged)938 TEST_P(WaylandWindowTest, OnActivationChanged) {
939 uint32_t serial = 0;
940
941 // Deactivate the surface.
942 ScopedWlArray empty_state;
943 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get());
944 Sync();
945
946 {
947 ScopedWlArray states = InitializeWlArrayWithActivatedState();
948 EXPECT_CALL(delegate_, OnActivationChanged(Eq(true)));
949 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, states.get());
950 Sync();
951 }
952
953 ScopedWlArray states;
954 EXPECT_CALL(delegate_, OnActivationChanged(Eq(false)));
955 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, states.get());
956 Sync();
957 }
958
TEST_P(WaylandWindowTest,OnAcceleratedWidgetDestroy)959 TEST_P(WaylandWindowTest, OnAcceleratedWidgetDestroy) {
960 window_.reset();
961 }
962
TEST_P(WaylandWindowTest,CanCreateMenuWindow)963 TEST_P(WaylandWindowTest, CanCreateMenuWindow) {
964 MockPlatformWindowDelegate menu_window_delegate;
965
966 // SetPointerFocus(true) requires a WaylandPointer.
967 wl_seat_send_capabilities(
968 server_.seat()->resource(),
969 WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH);
970 Sync();
971 ASSERT_TRUE(connection_->pointer() && connection_->touch());
972 window_->SetPointerFocus(true);
973
974 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
975 PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget,
976 gfx::Rect(0, 0, 10, 10), &menu_window_delegate);
977 EXPECT_TRUE(menu_window);
978
979 Sync();
980
981 window_->SetPointerFocus(false);
982 window_->set_touch_focus(false);
983
984 // Given that there is no parent passed and we don't have any focused windows,
985 // Wayland must still create a window.
986 menu_window = CreateWaylandWindowWithParams(
987 PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget,
988 gfx::Rect(0, 0, 10, 10), &menu_window_delegate);
989 EXPECT_TRUE(menu_window);
990
991 Sync();
992
993 window_->set_touch_focus(true);
994
995 menu_window = CreateWaylandWindowWithParams(
996 PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget,
997 gfx::Rect(0, 0, 10, 10), &menu_window_delegate);
998 EXPECT_TRUE(menu_window);
999
1000 Sync();
1001 }
1002
TEST_P(WaylandWindowTest,CreateAndDestroyNestedMenuWindow)1003 TEST_P(WaylandWindowTest, CreateAndDestroyNestedMenuWindow) {
1004 MockPlatformWindowDelegate menu_window_delegate;
1005 gfx::AcceleratedWidget menu_window_widget;
1006 EXPECT_CALL(menu_window_delegate, OnAcceleratedWidgetAvailable(_))
1007 .WillOnce(SaveArg<0>(&menu_window_widget));
1008
1009 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1010 PlatformWindowType::kMenu, widget_, gfx::Rect(0, 0, 10, 10),
1011 &menu_window_delegate);
1012 EXPECT_TRUE(menu_window);
1013 ASSERT_NE(menu_window_widget, gfx::kNullAcceleratedWidget);
1014
1015 Sync();
1016
1017 MockPlatformWindowDelegate nested_menu_window_delegate;
1018 std::unique_ptr<WaylandWindow> nested_menu_window =
1019 CreateWaylandWindowWithParams(
1020 PlatformWindowType::kMenu, menu_window_widget,
1021 gfx::Rect(20, 0, 10, 10), &nested_menu_window_delegate);
1022 EXPECT_TRUE(nested_menu_window);
1023
1024 Sync();
1025 }
1026
TEST_P(WaylandWindowTest,DispatchesLocatedEventsToCapturedWindow)1027 TEST_P(WaylandWindowTest, DispatchesLocatedEventsToCapturedWindow) {
1028 MockPlatformWindowDelegate menu_window_delegate;
1029 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1030 PlatformWindowType::kMenu, widget_, gfx::Rect(10, 10, 10, 10),
1031 &menu_window_delegate);
1032 EXPECT_TRUE(menu_window);
1033
1034 wl_seat_send_capabilities(server_.seat()->resource(),
1035 WL_SEAT_CAPABILITY_POINTER);
1036 Sync();
1037 ASSERT_TRUE(connection_->pointer());
1038 window_->SetPointerFocus(true);
1039
1040 // Make sure the events are handled by the window that has the pointer focus.
1041 VerifyCanDispatchMouseEvents({window_.get()}, {menu_window.get()});
1042
1043 // The |window_| that has the pointer focus must receive the event.
1044 EXPECT_CALL(menu_window_delegate, DispatchEvent(_)).Times(0);
1045 std::unique_ptr<Event> event;
1046 EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event));
1047
1048 // The event is send in local surface coordinates of the |window|.
1049 wl_pointer_send_motion(server_.seat()->pointer()->resource(), 1002,
1050 wl_fixed_from_double(10.75),
1051 wl_fixed_from_double(20.375));
1052
1053 Sync();
1054
1055 ASSERT_TRUE(event->IsLocatedEvent());
1056 EXPECT_EQ(event->AsLocatedEvent()->location(), gfx::Point(10, 20));
1057
1058 // Set capture to menu window now.
1059 menu_window->SetCapture();
1060
1061 // It's still the |window_| that can dispatch the events, but it will reroute
1062 // the event to correct window and fix the location.
1063 VerifyCanDispatchMouseEvents({window_.get()}, {menu_window.get()});
1064
1065 // The |window_| that has the pointer focus must receive the event.
1066 EXPECT_CALL(delegate_, DispatchEvent(_)).Times(0);
1067 std::unique_ptr<Event> event2;
1068 EXPECT_CALL(menu_window_delegate, DispatchEvent(_))
1069 .WillOnce(CloneEvent(&event2));
1070
1071 // The event is send in local surface coordinates of the |window|.
1072 wl_pointer_send_motion(server_.seat()->pointer()->resource(), 1002,
1073 wl_fixed_from_double(10.75),
1074 wl_fixed_from_double(20.375));
1075
1076 Sync();
1077
1078 ASSERT_TRUE(event2->IsLocatedEvent());
1079 EXPECT_EQ(event2->AsLocatedEvent()->location(), gfx::Point(0, 10));
1080
1081 // The event is send in local surface coordinates of the |window|.
1082 wl_pointer_send_motion(server_.seat()->pointer()->resource(), 1002,
1083 wl_fixed_from_double(2.75),
1084 wl_fixed_from_double(8.375));
1085
1086 EXPECT_CALL(delegate_, DispatchEvent(_)).Times(0);
1087 std::unique_ptr<Event> event3;
1088 EXPECT_CALL(menu_window_delegate, DispatchEvent(_))
1089 .WillOnce(CloneEvent(&event3));
1090
1091 Sync();
1092
1093 ASSERT_TRUE(event3->IsLocatedEvent());
1094 EXPECT_EQ(event3->AsLocatedEvent()->location(), gfx::Point(-8, -2));
1095
1096 // If nested menu window is added, the events are still correctly translated
1097 // to the captured window.
1098 MockPlatformWindowDelegate nested_menu_window_delegate;
1099 std::unique_ptr<WaylandWindow> nested_menu_window =
1100 CreateWaylandWindowWithParams(
1101 PlatformWindowType::kMenu, menu_window->GetWidget(),
1102 gfx::Rect(15, 18, 10, 10), &nested_menu_window_delegate);
1103 EXPECT_TRUE(nested_menu_window);
1104
1105 Sync();
1106
1107 window_->SetPointerFocus(false);
1108 nested_menu_window->SetPointerFocus(true);
1109
1110 // The event is processed by the window that has the pointer focus, but
1111 // dispatched by the window that has the capture.
1112 VerifyCanDispatchMouseEvents({nested_menu_window.get()},
1113 {window_.get(), menu_window.get()});
1114 EXPECT_TRUE(menu_window->HasCapture());
1115
1116 EXPECT_CALL(delegate_, DispatchEvent(_)).Times(0);
1117 EXPECT_CALL(nested_menu_window_delegate, DispatchEvent(_)).Times(0);
1118 std::unique_ptr<Event> event4;
1119 EXPECT_CALL(menu_window_delegate, DispatchEvent(_))
1120 .WillOnce(CloneEvent(&event4));
1121
1122 // The event is send in local surface coordinates of the |nested_menu_window|.
1123 wl_pointer_send_motion(server_.seat()->pointer()->resource(), 1002,
1124 wl_fixed_from_double(2.75),
1125 wl_fixed_from_double(8.375));
1126
1127 Sync();
1128
1129 ASSERT_TRUE(event4->IsLocatedEvent());
1130 EXPECT_EQ(event4->AsLocatedEvent()->location(), gfx::Point(7, 16));
1131
1132 menu_window.reset();
1133 }
1134
1135 // Tests that the event grabber gets the events processed by its toplevel parent
1136 // window iff they belong to the same "family". Otherwise, events mustn't be
1137 // rerouted from another toplevel window to the event grabber.
TEST_P(WaylandWindowTest,DispatchesLocatedEventsToCapturedWindowInTheSameStack)1138 TEST_P(WaylandWindowTest,
1139 DispatchesLocatedEventsToCapturedWindowInTheSameStack) {
1140 MockPlatformWindowDelegate menu_window_delegate;
1141 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1142 PlatformWindowType::kMenu, widget_, gfx::Rect(30, 40, 20, 50),
1143 &menu_window_delegate);
1144 EXPECT_TRUE(menu_window);
1145
1146 // Second toplevel window has the same bounds as the |window_|.
1147 MockPlatformWindowDelegate toplevel_window2_delegate;
1148 std::unique_ptr<WaylandWindow> toplevel_window2 =
1149 CreateWaylandWindowWithParams(
1150 PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
1151 window_->GetBounds(), &toplevel_window2_delegate);
1152 EXPECT_TRUE(toplevel_window2);
1153
1154 wl_seat_send_capabilities(server_.seat()->resource(),
1155 WL_SEAT_CAPABILITY_POINTER);
1156 Sync();
1157 ASSERT_TRUE(connection_->pointer());
1158 window_->SetPointerFocus(true);
1159
1160 // Make sure the events are handled by the window that has the pointer focus.
1161 VerifyCanDispatchMouseEvents({window_.get()},
1162 {menu_window.get(), toplevel_window2.get()});
1163
1164 menu_window->SetCapture();
1165
1166 // The |menu_window| that has capture must receive the event.
1167 EXPECT_CALL(delegate_, DispatchEvent(_)).Times(0);
1168 EXPECT_CALL(toplevel_window2_delegate, DispatchEvent(_)).Times(0);
1169 std::unique_ptr<Event> event;
1170 EXPECT_CALL(menu_window_delegate, DispatchEvent(_))
1171 .WillOnce(CloneEvent(&event));
1172
1173 // The event is send in local surface coordinates of the |window|.
1174 wl_pointer_send_motion(server_.seat()->pointer()->resource(), 1002,
1175 wl_fixed_from_double(10.75),
1176 wl_fixed_from_double(20.375));
1177
1178 Sync();
1179
1180 ASSERT_TRUE(event->IsLocatedEvent());
1181 EXPECT_EQ(event->AsLocatedEvent()->location(), gfx::Point(-20, -20));
1182
1183 // Now, pretend that the second toplevel window gets the pointer focus - the
1184 // event grabber must be disragerder now.
1185 window_->SetPointerFocus(false);
1186 toplevel_window2->SetPointerFocus(true);
1187
1188 VerifyCanDispatchMouseEvents({toplevel_window2.get()},
1189 {menu_window.get(), window_.get()});
1190
1191 // The |toplevel_window2| that has capture and must receive the event.
1192 EXPECT_CALL(delegate_, DispatchEvent(_)).Times(0);
1193 EXPECT_CALL(menu_window_delegate, DispatchEvent(_)).Times(0);
1194 event.reset();
1195 EXPECT_CALL(toplevel_window2_delegate, DispatchEvent(_))
1196 .WillOnce(CloneEvent(&event));
1197
1198 // The event is send in local surface coordinates of the |toplevel_window2|
1199 // (they're basically the same as the |window| has.)
1200 wl_pointer_send_motion(server_.seat()->pointer()->resource(), 1002,
1201 wl_fixed_from_double(10.75),
1202 wl_fixed_from_double(20.375));
1203
1204 Sync();
1205
1206 ASSERT_TRUE(event->IsLocatedEvent());
1207 EXPECT_EQ(event->AsLocatedEvent()->location(), gfx::Point(10, 20));
1208 }
1209
TEST_P(WaylandWindowTest,DispatchesKeyboardEventToToplevelWindow)1210 TEST_P(WaylandWindowTest, DispatchesKeyboardEventToToplevelWindow) {
1211 MockPlatformWindowDelegate menu_window_delegate;
1212 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1213 PlatformWindowType::kMenu, widget_, gfx::Rect(10, 10, 10, 10),
1214 &menu_window_delegate);
1215 EXPECT_TRUE(menu_window);
1216
1217 wl_seat_send_capabilities(server_.seat()->resource(),
1218 WL_SEAT_CAPABILITY_KEYBOARD);
1219 Sync();
1220 ASSERT_TRUE(connection_->keyboard());
1221 menu_window->set_keyboard_focus(true);
1222
1223 // Even though the menu window has the keyboard focus, the keyboard events are
1224 // dispatched by the root parent wayland window in the end.
1225 VerifyCanDispatchKeyEvents({menu_window.get()}, {window_.get()});
1226 EXPECT_CALL(menu_window_delegate, DispatchEvent(_)).Times(0);
1227 std::unique_ptr<Event> event;
1228 EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event));
1229
1230 wl_keyboard_send_key(server_.seat()->keyboard()->resource(), 2, 0, 30 /* a */,
1231 WL_KEYBOARD_KEY_STATE_PRESSED);
1232
1233 Sync();
1234
1235 ASSERT_TRUE(event->IsKeyEvent());
1236
1237 // Setting capture doesn't affect the kbd events.
1238 menu_window->SetCapture();
1239 VerifyCanDispatchKeyEvents({menu_window.get()}, {window_.get()});
1240
1241 wl_keyboard_send_key(server_.seat()->keyboard()->resource(), 2, 0, 30 /* a */,
1242 WL_KEYBOARD_KEY_STATE_PRESSED);
1243
1244 EXPECT_CALL(menu_window_delegate, DispatchEvent(_)).Times(0);
1245 event.reset();
1246 EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event));
1247
1248 Sync();
1249
1250 ASSERT_TRUE(event->IsKeyEvent());
1251
1252 menu_window.reset();
1253 }
1254
1255 // Tests that event is processed by the surface that has the focus. More
1256 // extensive tests are located in wayland touch/keyboard/pointer unittests.
TEST_P(WaylandWindowTest,CanDispatchEvent)1257 TEST_P(WaylandWindowTest, CanDispatchEvent) {
1258 MockPlatformWindowDelegate menu_window_delegate;
1259 gfx::AcceleratedWidget menu_window_widget;
1260 EXPECT_CALL(menu_window_delegate, OnAcceleratedWidgetAvailable(_))
1261 .WillOnce(SaveArg<0>(&menu_window_widget));
1262
1263 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1264 PlatformWindowType::kMenu, widget_, gfx::Rect(0, 0, 10, 10),
1265 &menu_window_delegate);
1266 EXPECT_TRUE(menu_window);
1267
1268 Sync();
1269
1270 MockPlatformWindowDelegate nested_menu_window_delegate;
1271 std::unique_ptr<WaylandWindow> nested_menu_window =
1272 CreateWaylandWindowWithParams(
1273 PlatformWindowType::kMenu, menu_window_widget,
1274 gfx::Rect(20, 0, 10, 10), &nested_menu_window_delegate);
1275 EXPECT_TRUE(nested_menu_window);
1276
1277 Sync();
1278
1279 wl_seat_send_capabilities(server_.seat()->resource(),
1280 WL_SEAT_CAPABILITY_POINTER |
1281 WL_SEAT_CAPABILITY_KEYBOARD |
1282 WL_SEAT_CAPABILITY_TOUCH);
1283 Sync();
1284 ASSERT_TRUE(connection_->pointer());
1285 ASSERT_TRUE(connection_->touch());
1286 ASSERT_TRUE(connection_->keyboard());
1287
1288 uint32_t serial = 0;
1289
1290 // Test that CanDispatchEvent is set correctly.
1291 wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>(
1292 window_->root_surface()->GetSurfaceId());
1293 wl_pointer_send_enter(server_.seat()->pointer()->resource(), ++serial,
1294 toplevel_surface->resource(), 0, 0);
1295
1296 Sync();
1297
1298 // Only |window_| can dispatch MouseEvents.
1299 VerifyCanDispatchMouseEvents({window_.get()},
1300 {menu_window.get(), nested_menu_window.get()});
1301 VerifyCanDispatchTouchEvents(
1302 {}, {window_.get(), menu_window.get(), nested_menu_window.get()});
1303 VerifyCanDispatchKeyEvents(
1304 {}, {window_.get(), menu_window.get(), nested_menu_window.get()});
1305
1306 struct wl_array empty;
1307 wl_array_init(&empty);
1308 wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), ++serial,
1309 toplevel_surface->resource(), &empty);
1310
1311 Sync();
1312
1313 // Only |window_| can dispatch MouseEvents and KeyEvents.
1314 VerifyCanDispatchMouseEvents({window_.get()},
1315 {menu_window.get(), nested_menu_window.get()});
1316 VerifyCanDispatchTouchEvents(
1317 {}, {window_.get(), menu_window.get(), nested_menu_window.get()});
1318 VerifyCanDispatchKeyEvents({window_.get()},
1319 {menu_window.get(), nested_menu_window.get()});
1320
1321 wl_touch_send_down(server_.seat()->touch()->resource(), ++serial, 0,
1322 toplevel_surface->resource(), 0 /* id */,
1323 wl_fixed_from_int(50), wl_fixed_from_int(100));
1324
1325 Sync();
1326
1327 // Only |window_| can dispatch MouseEvents and KeyEvents.
1328 VerifyCanDispatchMouseEvents({window_.get()},
1329 {menu_window.get(), nested_menu_window.get()});
1330 VerifyCanDispatchTouchEvents({window_.get()},
1331 {menu_window.get(), nested_menu_window.get()});
1332 VerifyCanDispatchKeyEvents({window_.get()},
1333 {menu_window.get(), nested_menu_window.get()});
1334
1335 wl::MockSurface* menu_window_surface = server_.GetObject<wl::MockSurface>(
1336 menu_window->root_surface()->GetSurfaceId());
1337
1338 wl_pointer_send_leave(server_.seat()->pointer()->resource(), ++serial,
1339 toplevel_surface->resource());
1340 wl_pointer_send_enter(server_.seat()->pointer()->resource(), ++serial,
1341 menu_window_surface->resource(), 0, 0);
1342 wl_touch_send_up(server_.seat()->touch()->resource(), ++serial, 1000,
1343 0 /* id */);
1344 wl_keyboard_send_leave(server_.seat()->keyboard()->resource(), ++serial,
1345 toplevel_surface->resource());
1346
1347 Sync();
1348
1349 // Only |menu_window| can dispatch MouseEvents.
1350 VerifyCanDispatchMouseEvents({menu_window.get()},
1351 {window_.get(), nested_menu_window.get()});
1352 VerifyCanDispatchTouchEvents(
1353 {}, {window_.get(), menu_window.get(), nested_menu_window.get()});
1354 VerifyCanDispatchKeyEvents(
1355 {}, {window_.get(), menu_window.get(), nested_menu_window.get()});
1356
1357 wl::MockSurface* nested_menu_window_surface =
1358 server_.GetObject<wl::MockSurface>(
1359 nested_menu_window->root_surface()->GetSurfaceId());
1360
1361 wl_pointer_send_leave(server_.seat()->pointer()->resource(), ++serial,
1362 menu_window_surface->resource());
1363 wl_pointer_send_enter(server_.seat()->pointer()->resource(), ++serial,
1364 nested_menu_window_surface->resource(), 0, 0);
1365
1366 Sync();
1367
1368 // Only |nested_menu_window| can dispatch MouseEvents.
1369 VerifyCanDispatchMouseEvents({nested_menu_window.get()},
1370 {window_.get(), menu_window.get()});
1371 VerifyCanDispatchTouchEvents(
1372 {}, {window_.get(), menu_window.get(), nested_menu_window.get()});
1373 VerifyCanDispatchKeyEvents(
1374 {}, {window_.get(), menu_window.get(), nested_menu_window.get()});
1375 }
1376
TEST_P(WaylandWindowTest,DispatchWindowMove)1377 TEST_P(WaylandWindowTest, DispatchWindowMove) {
1378 EXPECT_CALL(*GetXdgToplevel(), Move(_));
1379 ui::GetWmMoveResizeHandler(*window_)->DispatchHostWindowDragMovement(
1380 HTCAPTION, gfx::Point());
1381 }
1382
1383 // Makes sure hit tests are converted into right edges.
TEST_P(WaylandWindowTest,DispatchWindowResize)1384 TEST_P(WaylandWindowTest, DispatchWindowResize) {
1385 std::vector<int> hit_test_values;
1386 InitializeWithSupportedHitTestValues(&hit_test_values);
1387
1388 auto* wm_move_resize_handler = ui::GetWmMoveResizeHandler(*window_);
1389
1390 for (const int value : hit_test_values) {
1391 {
1392 uint32_t direction = wl::IdentifyDirection(*(connection_.get()), value);
1393 EXPECT_CALL(*GetXdgToplevel(), Resize(_, Eq(direction)));
1394 wm_move_resize_handler->DispatchHostWindowDragMovement(value,
1395 gfx::Point());
1396 }
1397 }
1398 }
1399
1400 // Tests WaylandWindow repositions menu windows to be relative to parent window
1401 // in a right way.
TEST_P(WaylandWindowTest,AdjustPopupBounds)1402 TEST_P(WaylandWindowTest, AdjustPopupBounds) {
1403 PopupPosition menu_window_positioner, nested_menu_window_positioner;
1404
1405 if (GetParam() == kXdgShellV6) {
1406 menu_window_positioner = {
1407 gfx::Rect(439, 46, 1, 30), gfx::Size(287, 409),
1408 ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | ZXDG_POSITIONER_V6_ANCHOR_RIGHT,
1409 ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_RIGHT,
1410 ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X |
1411 ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y};
1412
1413 nested_menu_window_positioner = {
1414 gfx::Rect(4, 80, 279, 1), gfx::Size(305, 99),
1415 ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_RIGHT,
1416 ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_RIGHT,
1417 ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X |
1418 ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y};
1419 } else {
1420 menu_window_positioner = {gfx::Rect(439, 46, 1, 30), gfx::Size(287, 409),
1421 XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT,
1422 XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT,
1423 XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X |
1424 XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y};
1425
1426 nested_menu_window_positioner = {
1427 gfx::Rect(4, 80, 279, 1), gfx::Size(305, 99),
1428 XDG_POSITIONER_ANCHOR_TOP_RIGHT, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT,
1429 XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X |
1430 XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y};
1431 }
1432
1433 auto* toplevel_window = window_.get();
1434 toplevel_window->SetBounds(gfx::Rect(0, 0, 739, 574));
1435
1436 // Case 1: the top menu window is positioned normally.
1437 MockPlatformWindowDelegate menu_window_delegate;
1438 gfx::Rect menu_window_bounds(gfx::Point(440, 76),
1439 menu_window_positioner.size);
1440 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1441 PlatformWindowType::kMenu, toplevel_window->GetWidget(),
1442 menu_window_bounds, &menu_window_delegate);
1443 EXPECT_TRUE(menu_window);
1444
1445 Sync();
1446
1447 VerifyXdgPopupPosition(menu_window.get(), menu_window_positioner);
1448
1449 EXPECT_CALL(menu_window_delegate, OnBoundsChanged(_)).Times(0);
1450 SendConfigureEventPopup(menu_window.get(), menu_window_bounds);
1451
1452 Sync();
1453
1454 EXPECT_EQ(menu_window->GetBounds(), menu_window_bounds);
1455
1456 // Case 2: the nested menu window is positioned normally.
1457 MockPlatformWindowDelegate nested_menu_window_delegate;
1458 gfx::Rect nested_menu_window_bounds(gfx::Point(723, 156),
1459 nested_menu_window_positioner.size);
1460 std::unique_ptr<WaylandWindow> nested_menu_window =
1461 CreateWaylandWindowWithParams(
1462 PlatformWindowType::kMenu, menu_window->GetWidget(),
1463 nested_menu_window_bounds, &nested_menu_window_delegate);
1464 EXPECT_TRUE(nested_menu_window);
1465
1466 Sync();
1467
1468 VerifyXdgPopupPosition(nested_menu_window.get(),
1469 nested_menu_window_positioner);
1470
1471 EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0);
1472 const gfx::Point origin(nested_menu_window_positioner.anchor_rect.x() +
1473 nested_menu_window_positioner.anchor_rect.width(),
1474 nested_menu_window_positioner.anchor_rect.y());
1475 gfx::Rect calculated_nested_bounds = nested_menu_window_bounds;
1476 calculated_nested_bounds.set_origin(origin);
1477 SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
1478
1479 Sync();
1480
1481 EXPECT_EQ(nested_menu_window->GetBounds(), nested_menu_window_bounds);
1482
1483 // Case 3: imagine the menu window was positioned near to the right edge of a
1484 // display. Nothing changes in the way how WaylandWindow calculates bounds,
1485 // because the Wayland compositor does not provide global location of windows.
1486 // Though, the compositor can reposition the window (flip along x or y axis or
1487 // slide along those axis). WaylandWindow just needs to correctly translate
1488 // bounds from relative to parent to be relative to screen. The Wayland
1489 // compositor does not reposition the menu, because it fits the screen, but
1490 // the nested menu window is repositioned to the left.
1491 EXPECT_CALL(
1492 nested_menu_window_delegate,
1493 OnBoundsChanged(gfx::Rect({139, 156}, nested_menu_window_bounds.size())));
1494 calculated_nested_bounds.set_origin({-301, 80});
1495 SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
1496
1497 Sync();
1498
1499 // Case 4: imagine the top level window was moved down to the bottom edge of a
1500 // display and only tab strip with 3-dot menu buttons left visible. In this
1501 // case, Chromium also does not know about that and positions the window
1502 // normally (normal bounds are sent), but the Wayland compositor flips the top
1503 // menu window along y-axis and fixes bounds of a top level window so that it
1504 // is located (from the Chromium point of view) below origin of the menu
1505 // window.
1506 EXPECT_CALL(delegate_, OnBoundsChanged(
1507 gfx::Rect({0, 363}, window_->GetBounds().size())));
1508 EXPECT_CALL(menu_window_delegate,
1509 OnBoundsChanged(gfx::Rect({440, 0}, menu_window_bounds.size())));
1510 SendConfigureEventPopup(menu_window.get(),
1511 gfx::Rect({440, -363}, menu_window_bounds.size()));
1512
1513 Sync();
1514
1515 // The nested menu window is also repositioned accordingly, but it's not
1516 // Wayland compositor reposition, but rather reposition from the Chromium
1517 // side. Thus, we have to check that anchor rect is correct.
1518 nested_menu_window.reset();
1519 nested_menu_window_bounds.set_origin({723, 258});
1520 nested_menu_window = CreateWaylandWindowWithParams(
1521 PlatformWindowType::kMenu, menu_window->GetWidget(),
1522 nested_menu_window_bounds, &nested_menu_window_delegate);
1523 EXPECT_TRUE(nested_menu_window);
1524
1525 Sync();
1526
1527 // We must get the anchor on gfx::Point(4, 258).
1528 nested_menu_window_positioner.anchor_rect.set_origin({4, 258});
1529 VerifyXdgPopupPosition(nested_menu_window.get(),
1530 nested_menu_window_positioner);
1531
1532 Sync();
1533
1534 EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0);
1535 calculated_nested_bounds.set_origin({283, 258});
1536 SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
1537
1538 Sync();
1539
1540 // Case 5: this case involves case 4. Thus, it concerns only the nested menu
1541 // window. imagine that the top menu window is flipped along y-axis and
1542 // positioned near to the right side of a display. The nested menu window is
1543 // flipped along x-axis by the compositor and WaylandWindow must calculate
1544 // bounds back to be relative to display correctly. If the window is near to
1545 // the left edge of a display, nothing is going to change, and the origin will
1546 // be the same as in the previous case.
1547 EXPECT_CALL(
1548 nested_menu_window_delegate,
1549 OnBoundsChanged(gfx::Rect({149, 258}, nested_menu_window_bounds.size())));
1550 calculated_nested_bounds.set_origin({-291, 258});
1551 SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds);
1552
1553 Sync();
1554
1555 // Case 6: imagine the top level window was moved back to normal position. In
1556 // this case, the Wayland compositor positions the menu window normally and
1557 // the WaylandWindow repositions the top level window back to 0,0 (which had
1558 // an offset to compensate the position of the menu window fliped along
1559 // y-axis. It just has had negative y value, which is wrong for Chromium.
1560 EXPECT_CALL(delegate_,
1561 OnBoundsChanged(gfx::Rect({0, 0}, window_->GetBounds().size())));
1562 EXPECT_CALL(menu_window_delegate,
1563 OnBoundsChanged(gfx::Rect({440, 76}, menu_window_bounds.size())));
1564 SendConfigureEventPopup(menu_window.get(),
1565 gfx::Rect({440, 76}, menu_window_bounds.size()));
1566
1567 Sync();
1568
1569 VerifyAndClearExpectations();
1570 }
1571
ACTION_P(VerifyRegion,ptr)1572 ACTION_P(VerifyRegion, ptr) {
1573 wl::TestRegion* region = wl::GetUserDataAs<wl::TestRegion>(arg0);
1574 EXPECT_EQ(*ptr, region->getBounds());
1575 }
1576
TEST_P(WaylandWindowTest,SetOpaqueRegion)1577 TEST_P(WaylandWindowTest, SetOpaqueRegion) {
1578 wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>(
1579 window_->root_surface()->GetSurfaceId());
1580
1581 gfx::Rect new_bounds(0, 0, 500, 600);
1582 auto state_array = MakeStateArray({XDG_TOPLEVEL_STATE_ACTIVATED});
1583 SendConfigureEvent(xdg_surface_, new_bounds.width(), new_bounds.height(), 1,
1584 state_array.get());
1585
1586 SkIRect rect =
1587 SkIRect::MakeXYWH(0, 0, new_bounds.width(), new_bounds.height());
1588 EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)).WillOnce(VerifyRegion(&rect));
1589
1590 Sync();
1591
1592 VerifyAndClearExpectations();
1593
1594 new_bounds.set_size(gfx::Size(1000, 534));
1595 SendConfigureEvent(xdg_surface_, new_bounds.width(), new_bounds.height(), 2,
1596 state_array.get());
1597
1598 rect = SkIRect::MakeXYWH(0, 0, new_bounds.width(), new_bounds.height());
1599 EXPECT_CALL(*mock_surface, SetOpaqueRegion(_)).WillOnce(VerifyRegion(&rect));
1600
1601 Sync();
1602
1603 VerifyAndClearExpectations();
1604 }
1605
TEST_P(WaylandWindowTest,OnCloseRequest)1606 TEST_P(WaylandWindowTest, OnCloseRequest) {
1607 EXPECT_CALL(delegate_, OnCloseRequest());
1608
1609 if (GetParam() == kXdgShellV6)
1610 zxdg_toplevel_v6_send_close(xdg_surface_->xdg_toplevel()->resource());
1611 else
1612 xdg_toplevel_send_close(xdg_surface_->xdg_toplevel()->resource());
1613
1614 Sync();
1615 }
1616
TEST_P(WaylandWindowTest,AuxiliaryWindowSimpleParent)1617 TEST_P(WaylandWindowTest, AuxiliaryWindowSimpleParent) {
1618 VerifyAndClearExpectations();
1619
1620 std::unique_ptr<WaylandWindow> second_window = CreateWaylandWindowWithParams(
1621 PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
1622 gfx::Rect(0, 0, 640, 480), &delegate_);
1623 EXPECT_TRUE(second_window);
1624
1625 // Test case 1: if the subsurface is provided with a parent widget, it must
1626 // always use that as a parent.
1627 gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10));
1628 std::unique_ptr<WaylandWindow> auxiliary_window =
1629 CreateWaylandWindowWithParams(PlatformWindowType::kTooltip,
1630 window_->GetWidget(), subsurface_bounds,
1631 &delegate_);
1632 EXPECT_TRUE(auxiliary_window);
1633
1634 // The subsurface mustn't take the focused window as a parent, but use the
1635 // provided one.
1636 second_window->SetPointerFocus(true);
1637 auxiliary_window->Show(false);
1638
1639 Sync();
1640
1641 auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
1642 auxiliary_window->root_surface()->GetSurfaceId());
1643 auto* test_subsurface = mock_surface_subsurface->sub_surface();
1644
1645 EXPECT_EQ(test_subsurface->position(), subsurface_bounds.origin());
1646 EXPECT_FALSE(test_subsurface->sync());
1647
1648 auto* parent_resource =
1649 server_
1650 .GetObject<wl::MockSurface>(window_->root_surface()->GetSurfaceId())
1651 ->resource();
1652 EXPECT_EQ(parent_resource, test_subsurface->parent_resource());
1653
1654 // Test case 2: the subsurface must use the focused window as its parent.
1655 auxiliary_window = CreateWaylandWindowWithParams(
1656 PlatformWindowType::kTooltip, gfx::kNullAcceleratedWidget,
1657 subsurface_bounds, &delegate_);
1658 EXPECT_TRUE(auxiliary_window);
1659
1660 // The tooltip must take the focused window.
1661 second_window->SetPointerFocus(true);
1662 auxiliary_window->Show(false);
1663
1664 Sync();
1665
1666 // Get new surface after recreating the WaylandAuxiliaryWindow.
1667 mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
1668 auxiliary_window->root_surface()->GetSurfaceId());
1669 test_subsurface = mock_surface_subsurface->sub_surface();
1670
1671 auto* second_parent_resource =
1672 server_
1673 .GetObject<wl::MockSurface>(
1674 second_window->root_surface()->GetSurfaceId())
1675 ->resource();
1676 EXPECT_EQ(second_parent_resource, test_subsurface->parent_resource());
1677
1678 auxiliary_window->Hide();
1679
1680 Sync();
1681
1682 // The subsurface must take the focused window.
1683 second_window->SetPointerFocus(false);
1684 window_->SetPointerFocus(true);
1685 auxiliary_window->Show(false);
1686
1687 Sync();
1688
1689 // The subsurface is invalidated on each Hide call.
1690 test_subsurface = mock_surface_subsurface->sub_surface();
1691
1692 // The |window_|'s resource must be the parent resource.
1693 EXPECT_EQ(parent_resource, test_subsurface->parent_resource());
1694
1695 window_->SetPointerFocus(false);
1696 }
1697
1698 // Case 1: When the menu bounds are positive and there is a positive,
1699 // non-zero anchor width
TEST_P(WaylandWindowTest,NestedPopupMenu)1700 TEST_P(WaylandWindowTest, NestedPopupMenu) {
1701 VerifyAndClearExpectations();
1702
1703 gfx::Rect menu_window_bounds(gfx::Rect(4, 20, 8, 20));
1704 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1705 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds,
1706 &delegate_);
1707 EXPECT_TRUE(menu_window);
1708
1709 VerifyAndClearExpectations();
1710
1711 gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 40, 20));
1712 std::unique_ptr<WaylandWindow> nested_popup_window =
1713 CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
1714 menu_window->GetWidget(),
1715 nestedPopup_bounds, &delegate_);
1716 EXPECT_TRUE(nested_popup_window);
1717
1718 VerifyAndClearExpectations();
1719
1720 nested_popup_window->SetPointerFocus(true);
1721
1722 Sync();
1723
1724 auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
1725
1726 ASSERT_TRUE(mock_surface_nested_popup);
1727
1728 auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width();
1729 EXPECT_EQ(4, anchor_width);
1730
1731 nested_popup_window->SetPointerFocus(false);
1732 }
1733
1734 // Case 2: When the menu bounds are positive and there is a negative or
1735 // zero anchor width
TEST_P(WaylandWindowTest,NestedPopupMenu1)1736 TEST_P(WaylandWindowTest, NestedPopupMenu1) {
1737 VerifyAndClearExpectations();
1738
1739 gfx::Rect menu_window_bounds(gfx::Rect(6, 20, 8, 20));
1740 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1741 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds,
1742 &delegate_);
1743 EXPECT_TRUE(menu_window);
1744
1745 VerifyAndClearExpectations();
1746
1747 gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 10, 20));
1748 std::unique_ptr<WaylandWindow> nested_popup_window =
1749 CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
1750 menu_window->GetWidget(),
1751 nestedPopup_bounds, &delegate_);
1752 EXPECT_TRUE(nested_popup_window);
1753
1754 VerifyAndClearExpectations();
1755
1756 nested_popup_window->SetPointerFocus(true);
1757
1758 Sync();
1759
1760 auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
1761
1762 ASSERT_TRUE(mock_surface_nested_popup);
1763
1764 auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width();
1765 EXPECT_EQ(1, anchor_width);
1766
1767 nested_popup_window->SetPointerFocus(false);
1768 }
1769
1770 // Case 3: When the menu bounds are negative and there is a positive,
1771 // non-zero anchor width
TEST_P(WaylandWindowTest,NestedPopupMenu2)1772 TEST_P(WaylandWindowTest, NestedPopupMenu2) {
1773 VerifyAndClearExpectations();
1774
1775 gfx::Rect menu_window_bounds(gfx::Rect(10, 20, 40, 20));
1776 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1777 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds,
1778 &delegate_);
1779 EXPECT_TRUE(menu_window);
1780
1781 VerifyAndClearExpectations();
1782
1783 gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20));
1784 std::unique_ptr<WaylandWindow> nested_popup_window =
1785 CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
1786 menu_window->GetWidget(),
1787 nestedPopup_bounds, &delegate_);
1788 EXPECT_TRUE(nested_popup_window);
1789
1790 VerifyAndClearExpectations();
1791
1792 nested_popup_window->SetPointerFocus(true);
1793
1794 Sync();
1795
1796 auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
1797
1798 ASSERT_TRUE(mock_surface_nested_popup);
1799
1800 auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width();
1801 EXPECT_EQ(8, anchor_width);
1802
1803 nested_popup_window->SetPointerFocus(false);
1804 }
1805
1806 // Case 4: When the menu bounds are negative and there is a negative,
1807 // zero anchor width
TEST_P(WaylandWindowTest,NestedPopupMenu3)1808 TEST_P(WaylandWindowTest, NestedPopupMenu3) {
1809 VerifyAndClearExpectations();
1810
1811 gfx::Rect menu_window_bounds(gfx::Rect(10, 20, 20, 20));
1812 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1813 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds,
1814 &delegate_);
1815 EXPECT_TRUE(menu_window);
1816
1817 VerifyAndClearExpectations();
1818
1819 gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20));
1820 std::unique_ptr<WaylandWindow> nested_popup_window =
1821 CreateWaylandWindowWithParams(PlatformWindowType::kPopup,
1822 menu_window->GetWidget(),
1823 nestedPopup_bounds, &delegate_);
1824 EXPECT_TRUE(nested_popup_window);
1825
1826 VerifyAndClearExpectations();
1827
1828 nested_popup_window->SetPointerFocus(true);
1829
1830 Sync();
1831
1832 auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get());
1833
1834 ASSERT_TRUE(mock_surface_nested_popup);
1835
1836 auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width();
1837
1838 EXPECT_EQ(1, anchor_width);
1839
1840 nested_popup_window->SetPointerFocus(false);
1841 }
1842
TEST_P(WaylandWindowTest,AuxiliaryWindowNestedParent)1843 TEST_P(WaylandWindowTest, AuxiliaryWindowNestedParent) {
1844 VerifyAndClearExpectations();
1845
1846 gfx::Rect menu_window_bounds(gfx::Point(10, 10), gfx::Size(100, 100));
1847 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
1848 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds,
1849 &delegate_);
1850 EXPECT_TRUE(menu_window);
1851
1852 VerifyAndClearExpectations();
1853
1854 gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10));
1855 std::unique_ptr<WaylandWindow> auxiliary_window =
1856 CreateWaylandWindowWithParams(PlatformWindowType::kTooltip,
1857 menu_window->GetWidget(), subsurface_bounds,
1858 &delegate_);
1859 EXPECT_TRUE(auxiliary_window);
1860
1861 VerifyAndClearExpectations();
1862
1863 menu_window->SetPointerFocus(true);
1864
1865 auxiliary_window->Show(false);
1866
1867 Sync();
1868
1869 auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
1870 auxiliary_window->root_surface()->GetSurfaceId());
1871 auto* test_subsurface = mock_surface_subsurface->sub_surface();
1872
1873 auto new_origin = subsurface_bounds.origin() -
1874 menu_window_bounds.origin().OffsetFromOrigin();
1875 EXPECT_EQ(test_subsurface->position(), new_origin);
1876
1877 menu_window->SetPointerFocus(false);
1878 }
1879
TEST_P(WaylandWindowTest,OnSizeConstraintsChanged)1880 TEST_P(WaylandWindowTest, OnSizeConstraintsChanged) {
1881 const bool kBooleans[] = {false, true};
1882 for (bool has_min_size : kBooleans) {
1883 for (bool has_max_size : kBooleans) {
1884 base::Optional<gfx::Size> min_size =
1885 has_min_size ? base::Optional<gfx::Size>(gfx::Size(100, 200))
1886 : base::nullopt;
1887 base::Optional<gfx::Size> max_size =
1888 has_max_size ? base::Optional<gfx::Size>(gfx::Size(300, 400))
1889 : base::nullopt;
1890 EXPECT_CALL(delegate_, GetMinimumSizeForWindow())
1891 .WillOnce(Return(min_size));
1892 EXPECT_CALL(delegate_, GetMaximumSizeForWindow())
1893 .WillOnce(Return(max_size));
1894
1895 EXPECT_CALL(*GetXdgToplevel(), SetMinSize(100, 200))
1896 .Times(has_min_size ? 1 : 0);
1897 EXPECT_CALL(*GetXdgToplevel(), SetMaxSize(300, 400))
1898 .Times(has_max_size ? 1 : 0);
1899
1900 window_->SizeConstraintsChanged();
1901 Sync();
1902
1903 VerifyAndClearExpectations();
1904 }
1905 }
1906 }
1907
TEST_P(WaylandWindowTest,DestroysCreatesSurfaceOnHideShow)1908 TEST_P(WaylandWindowTest, DestroysCreatesSurfaceOnHideShow) {
1909 MockPlatformWindowDelegate delegate;
1910 auto window = CreateWaylandWindowWithParams(
1911 PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
1912 gfx::Rect(0, 0, 100, 100), &delegate);
1913 ASSERT_TRUE(window);
1914
1915 Sync();
1916
1917 auto* mock_surface = server_.GetObject<wl::MockSurface>(
1918 window->root_surface()->GetSurfaceId());
1919 EXPECT_TRUE(mock_surface->xdg_surface());
1920 EXPECT_TRUE(mock_surface->xdg_surface()->xdg_toplevel());
1921
1922 Sync();
1923
1924 window->Hide();
1925
1926 Sync();
1927
1928 EXPECT_FALSE(mock_surface->xdg_surface());
1929
1930 window->Show(false);
1931
1932 Sync();
1933
1934 EXPECT_TRUE(mock_surface->xdg_surface());
1935 EXPECT_TRUE(mock_surface->xdg_surface()->xdg_toplevel());
1936 }
1937
TEST_P(WaylandWindowTest,DestroysCreatesPopupsOnHideShow)1938 TEST_P(WaylandWindowTest, DestroysCreatesPopupsOnHideShow) {
1939 MockPlatformWindowDelegate delegate;
1940 auto window = CreateWaylandWindowWithParams(
1941 PlatformWindowType::kMenu, window_->GetWidget(), gfx::Rect(0, 0, 50, 50),
1942 &delegate);
1943 ASSERT_TRUE(window);
1944
1945 Sync();
1946
1947 auto* mock_surface = server_.GetObject<wl::MockSurface>(
1948 window->root_surface()->GetSurfaceId());
1949 EXPECT_TRUE(mock_surface->xdg_surface());
1950 EXPECT_TRUE(mock_surface->xdg_surface()->xdg_popup());
1951
1952 Sync();
1953
1954 window->Hide();
1955
1956 Sync();
1957
1958 EXPECT_FALSE(mock_surface->xdg_surface());
1959
1960 window->Show(false);
1961
1962 Sync();
1963
1964 EXPECT_TRUE(mock_surface->xdg_surface());
1965 EXPECT_TRUE(mock_surface->xdg_surface()->xdg_popup());
1966 }
1967
TEST_P(WaylandWindowTest,RemovesReattachesBackgroundOnHideShow)1968 TEST_P(WaylandWindowTest, RemovesReattachesBackgroundOnHideShow) {
1969 EXPECT_TRUE(connection_->buffer_manager_host());
1970
1971 auto interface_ptr = connection_->buffer_manager_host()->BindInterface();
1972 buffer_manager_gpu_->Initialize(std::move(interface_ptr), {}, false);
1973
1974 // Setup wl_buffers.
1975 constexpr uint32_t buffer_id1 = 1;
1976 constexpr uint32_t buffer_id2 = 2;
1977 gfx::Size buffer_size(1024, 768);
1978 auto length = 1024 * 768 * 4;
1979 buffer_manager_gpu_->CreateShmBasedBuffer(MakeFD(), length, buffer_size,
1980 buffer_id1);
1981 buffer_manager_gpu_->CreateShmBasedBuffer(MakeFD(), length, buffer_size,
1982 buffer_id2);
1983
1984 Sync();
1985
1986 // Create window.
1987 MockPlatformWindowDelegate delegate;
1988 auto window = CreateWaylandWindowWithParams(
1989 PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
1990 gfx::Rect(0, 0, 100, 100), &delegate);
1991 ASSERT_TRUE(window);
1992 auto states = InitializeWlArrayWithActivatedState();
1993
1994 Sync();
1995
1996 // Configure window to be ready to attach wl_buffers.
1997 auto* mock_surface = server_.GetObject<wl::MockSurface>(
1998 window->root_surface()->GetSurfaceId());
1999 EXPECT_TRUE(mock_surface->xdg_surface());
2000 EXPECT_TRUE(mock_surface->xdg_surface()->xdg_toplevel());
2001 SendConfigureEvent(mock_surface->xdg_surface(), 100, 100, 1, states.get());
2002
2003 // Commit a frame with only background.
2004 std::vector<ui::ozone::mojom::WaylandOverlayConfigPtr> overlays;
2005 ui::ozone::mojom::WaylandOverlayConfigPtr background{
2006 ui::ozone::mojom::WaylandOverlayConfig::New()};
2007 background->z_order = INT32_MIN;
2008 background->transform = gfx::OVERLAY_TRANSFORM_NONE;
2009 background->buffer_id = buffer_id1;
2010 overlays.push_back(std::move(background));
2011 buffer_manager_gpu_->CommitOverlays(window->GetWidget(), std::move(overlays));
2012 mock_surface->SendFrameCallback();
2013
2014 Sync();
2015
2016 EXPECT_NE(mock_surface->attached_buffer(), nullptr);
2017
2018 // Hiding window attaches a nil wl_buffer as background.
2019 window->Hide();
2020 mock_surface->SendFrameCallback();
2021
2022 Sync();
2023
2024 EXPECT_EQ(mock_surface->attached_buffer(), nullptr);
2025
2026 mock_surface->ReleaseBuffer(mock_surface->prev_attached_buffer());
2027 window->Show(false);
2028
2029 Sync();
2030
2031 SendConfigureEvent(mock_surface->xdg_surface(), 100, 100, 2, states.get());
2032
2033 // Commit a frame with only the primary_plane.
2034 overlays.clear();
2035 ui::ozone::mojom::WaylandOverlayConfigPtr primary{
2036 ui::ozone::mojom::WaylandOverlayConfig::New()};
2037 primary->z_order = 0;
2038 primary->transform = gfx::OVERLAY_TRANSFORM_NONE;
2039 primary->buffer_id = buffer_id2;
2040 overlays.push_back(std::move(primary));
2041 buffer_manager_gpu_->CommitOverlays(window->GetWidget(), std::move(overlays));
2042
2043 Sync();
2044
2045 // WaylandWindow should automatically reattach the background.
2046 EXPECT_NE(mock_surface->attached_buffer(), nullptr);
2047 }
2048
2049 // Tests that if the window gets hidden and shown again, the title, app id and
2050 // size constraints remain the same.
TEST_P(WaylandWindowTest,SetsPropertiesOnShow)2051 TEST_P(WaylandWindowTest, SetsPropertiesOnShow) {
2052 constexpr char kAppId[] = "wayland_test";
2053 const base::string16 kTitle(base::UTF8ToUTF16("WaylandWindowTest"));
2054
2055 PlatformWindowInitProperties properties;
2056 properties.bounds = gfx::Rect(0, 0, 100, 100);
2057 properties.type = PlatformWindowType::kWindow;
2058 properties.wm_class_class = kAppId;
2059
2060 MockPlatformWindowDelegate delegate;
2061 auto window = WaylandWindow::Create(&delegate, connection_.get(),
2062 std::move(properties));
2063 DCHECK(window);
2064 window->Show(false);
2065
2066 Sync();
2067
2068 auto* mock_surface = server_.GetObject<wl::MockSurface>(
2069 window->root_surface()->GetSurfaceId());
2070 auto* mock_xdg_toplevel = mock_surface->xdg_surface()->xdg_toplevel();
2071
2072 // Only app id must be set now.
2073 EXPECT_EQ(std::string(kAppId), mock_xdg_toplevel->app_id());
2074 EXPECT_TRUE(mock_xdg_toplevel->title().empty());
2075 EXPECT_TRUE(mock_xdg_toplevel->min_size().IsEmpty());
2076 EXPECT_TRUE(mock_xdg_toplevel->max_size().IsEmpty());
2077
2078 // Now, propagate size constraints and title.
2079 base::Optional<gfx::Size> min_size(gfx::Size(1, 1));
2080 base::Optional<gfx::Size> max_size(gfx::Size(100, 100));
2081 EXPECT_CALL(delegate, GetMinimumSizeForWindow()).WillOnce(Return(min_size));
2082 EXPECT_CALL(delegate, GetMaximumSizeForWindow()).WillOnce(Return(max_size));
2083
2084 EXPECT_CALL(*mock_xdg_toplevel,
2085 SetMinSize(min_size.value().width(), min_size.value().height()));
2086 EXPECT_CALL(*mock_xdg_toplevel,
2087 SetMaxSize(max_size.value().width(), max_size.value().height()));
2088 EXPECT_CALL(*mock_xdg_toplevel, SetTitle(base::UTF16ToUTF8(kTitle)));
2089
2090 window->SetTitle(kTitle);
2091 window->SizeConstraintsChanged();
2092
2093 Sync();
2094
2095 window->Hide();
2096
2097 Sync();
2098
2099 window->Show(false);
2100
2101 Sync();
2102
2103 mock_xdg_toplevel = mock_surface->xdg_surface()->xdg_toplevel();
2104
2105 // We can't mock all those methods above as long as the xdg_toplevel is
2106 // created and destroyed on each show and hide call. However, it is the same
2107 // WaylandToplevelWindow object that cached the values we set and must restore
2108 // them on Show().
2109 EXPECT_EQ(mock_xdg_toplevel->min_size(), min_size.value());
2110 EXPECT_EQ(mock_xdg_toplevel->max_size(), max_size.value());
2111 EXPECT_EQ(std::string(kAppId), mock_xdg_toplevel->app_id());
2112 EXPECT_EQ(mock_xdg_toplevel->title(), base::UTF16ToUTF8(kTitle));
2113 }
2114
2115 // Tests that a popup window is created using the serial of button press events
2116 // as required by the Wayland protocol spec.
TEST_P(WaylandWindowTest,CreatesPopupOnButtonPressSerial)2117 TEST_P(WaylandWindowTest, CreatesPopupOnButtonPressSerial) {
2118 wl_seat_send_capabilities(
2119 server_.seat()->resource(),
2120 WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD);
2121
2122 Sync();
2123
2124 constexpr uint32_t enter_serial = 1;
2125 constexpr uint32_t button_press_serial = 2;
2126 constexpr uint32_t button_release_serial = 3;
2127
2128 wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>(
2129 window_->root_surface()->GetSurfaceId());
2130 struct wl_array empty;
2131 wl_array_init(&empty);
2132 wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), enter_serial,
2133 toplevel_surface->resource(), &empty);
2134
2135 // Send two events - button down and button up.
2136 wl_pointer_send_button(server_.seat()->pointer()->resource(),
2137 button_press_serial, 1002, BTN_LEFT,
2138 WL_POINTER_BUTTON_STATE_PRESSED);
2139 wl_pointer_send_button(server_.seat()->pointer()->resource(),
2140 button_release_serial, 1004, BTN_LEFT,
2141 WL_POINTER_BUTTON_STATE_RELEASED);
2142 Sync();
2143
2144 // Create a popup window and verify the client used correct serial.
2145 MockPlatformWindowDelegate delegate;
2146 auto popup = CreateWaylandWindowWithParams(
2147 PlatformWindowType::kMenu, window_->GetWidget(), gfx::Rect(0, 0, 50, 50),
2148 &delegate);
2149 ASSERT_TRUE(popup);
2150
2151 Sync();
2152
2153 auto* test_popup = GetPopupByWindow(popup.get());
2154 ASSERT_TRUE(test_popup);
2155 EXPECT_NE(test_popup->grab_serial(), button_release_serial);
2156 EXPECT_EQ(test_popup->grab_serial(), button_press_serial);
2157 }
2158
2159 // Tests that a popup window is created using the serial of touch down events
2160 // as required by the Wayland protocol spec.
TEST_P(WaylandWindowTest,CreatesPopupOnTouchDownSerial)2161 TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) {
2162 wl_seat_send_capabilities(
2163 server_.seat()->resource(),
2164 WL_SEAT_CAPABILITY_TOUCH | WL_SEAT_CAPABILITY_KEYBOARD);
2165
2166 Sync();
2167
2168 constexpr uint32_t enter_serial = 1;
2169 constexpr uint32_t touch_down_serial = 2;
2170 constexpr uint32_t touch_up_serial = 3;
2171
2172 wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>(
2173 window_->root_surface()->GetSurfaceId());
2174 struct wl_array empty;
2175 wl_array_init(&empty);
2176 wl_keyboard_send_enter(server_.seat()->keyboard()->resource(), enter_serial,
2177 toplevel_surface->resource(), &empty);
2178
2179 // Send two events - touch down and touch up.
2180 wl_touch_send_down(server_.seat()->touch()->resource(), touch_down_serial, 0,
2181 surface_->resource(), 0 /* id */, wl_fixed_from_int(50),
2182 wl_fixed_from_int(100));
2183 wl_touch_send_up(server_.seat()->touch()->resource(), touch_up_serial, 1000,
2184 0 /* id */);
2185
2186 Sync();
2187
2188 // Create a popup window and verify the client used correct serial.
2189 MockPlatformWindowDelegate delegate;
2190 auto popup = CreateWaylandWindowWithParams(
2191 PlatformWindowType::kMenu, window_->GetWidget(), gfx::Rect(0, 0, 50, 50),
2192 &delegate);
2193 ASSERT_TRUE(popup);
2194
2195 Sync();
2196
2197 auto* test_popup = GetPopupByWindow(popup.get());
2198 ASSERT_TRUE(test_popup);
2199
2200 // Touch events are the exception. We can't use the serial that was sent
2201 // before the "up" event. Otherwise, some compositors may dismiss popups.
2202 // Thus, no serial must be used.
2203 EXPECT_EQ(test_popup->grab_serial(), 0U);
2204
2205 popup->Hide();
2206
2207 // Send a single down event now.
2208 wl_touch_send_down(server_.seat()->touch()->resource(), touch_down_serial, 0,
2209 surface_->resource(), 0 /* id */, wl_fixed_from_int(50),
2210 wl_fixed_from_int(100));
2211
2212 Sync();
2213
2214 popup->Show(false);
2215
2216 Sync();
2217
2218 test_popup = GetPopupByWindow(popup.get());
2219 ASSERT_TRUE(test_popup);
2220
2221 EXPECT_EQ(test_popup->grab_serial(), touch_down_serial);
2222 }
2223
2224 // Tests nested menu windows get the topmost window in the stack of windows
2225 // within the same family/tree.
TEST_P(WaylandWindowTest,NestedPopupWindowsGetCorrectParent)2226 TEST_P(WaylandWindowTest, NestedPopupWindowsGetCorrectParent) {
2227 VerifyAndClearExpectations();
2228
2229 gfx::Rect menu_window_bounds(gfx::Rect(10, 20, 20, 20));
2230 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
2231 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds,
2232 &delegate_);
2233 EXPECT_TRUE(menu_window);
2234
2235 EXPECT_TRUE(menu_window->parent_window() == window_.get());
2236
2237 gfx::Rect menu_window_bounds2(gfx::Rect(20, 40, 30, 20));
2238 std::unique_ptr<WaylandWindow> menu_window2 = CreateWaylandWindowWithParams(
2239 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds2,
2240 &delegate_);
2241 EXPECT_TRUE(menu_window2);
2242
2243 EXPECT_TRUE(menu_window2->parent_window() == menu_window.get());
2244
2245 gfx::Rect menu_window_bounds3(gfx::Rect(30, 40, 30, 20));
2246 std::unique_ptr<WaylandWindow> menu_window3 = CreateWaylandWindowWithParams(
2247 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds3,
2248 &delegate_);
2249 EXPECT_TRUE(menu_window3);
2250
2251 EXPECT_TRUE(menu_window3->parent_window() == menu_window2.get());
2252
2253 gfx::Rect menu_window_bounds4(gfx::Rect(40, 40, 30, 20));
2254 std::unique_ptr<WaylandWindow> menu_window4 = CreateWaylandWindowWithParams(
2255 PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds4,
2256 &delegate_);
2257 EXPECT_TRUE(menu_window4);
2258
2259 EXPECT_TRUE(menu_window4->parent_window() == menu_window3.get());
2260 }
2261
TEST_P(WaylandWindowTest,DoesNotGrabPopupIfNoSeat)2262 TEST_P(WaylandWindowTest, DoesNotGrabPopupIfNoSeat) {
2263 // Create a popup window and verify the grab serial is not set.
2264 MockPlatformWindowDelegate delegate;
2265 auto popup = CreateWaylandWindowWithParams(
2266 PlatformWindowType::kMenu, window_->GetWidget(), gfx::Rect(0, 0, 50, 50),
2267 &delegate);
2268 ASSERT_TRUE(popup);
2269
2270 Sync();
2271
2272 auto* test_popup = GetPopupByWindow(popup.get());
2273 ASSERT_TRUE(test_popup);
2274 EXPECT_EQ(test_popup->grab_serial(), 0u);
2275 }
2276
TEST_P(WaylandWindowTest,OneWaylandSubsurface)2277 TEST_P(WaylandWindowTest, OneWaylandSubsurface) {
2278 VerifyAndClearExpectations();
2279
2280 std::unique_ptr<WaylandWindow> window = CreateWaylandWindowWithParams(
2281 PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
2282 gfx::Rect(0, 0, 640, 480), &delegate_);
2283 EXPECT_TRUE(window);
2284
2285 gfx::Rect subsurface_bounds(gfx::Point(15, 15), gfx::Size(10, 10));
2286 bool result = window->RequestSubsurface();
2287 EXPECT_TRUE(result);
2288
2289 WaylandSubsurface* wayland_subsurface =
2290 window->wayland_subsurfaces().begin()->get();
2291
2292 Sync();
2293
2294 auto* mock_surface_root_window = server_.GetObject<wl::MockSurface>(
2295 window->root_surface()->GetSurfaceId());
2296 auto* mock_surface_subsurface = server_.GetObject<wl::MockSurface>(
2297 wayland_subsurface->wayland_surface()->GetSurfaceId());
2298 EXPECT_TRUE(mock_surface_subsurface);
2299 wayland_subsurface->ConfigureAndShowSurface(gfx::OVERLAY_TRANSFORM_NONE,
2300 gfx::RectF(), subsurface_bounds,
2301 true, nullptr, nullptr);
2302 connection_->ScheduleFlush();
2303
2304 Sync();
2305
2306 auto* test_subsurface = mock_surface_subsurface->sub_surface();
2307 EXPECT_TRUE(test_subsurface);
2308 auto* parent_resource = mock_surface_root_window->resource();
2309 EXPECT_EQ(parent_resource, test_subsurface->parent_resource());
2310
2311 EXPECT_EQ(test_subsurface->position(), subsurface_bounds.origin());
2312 EXPECT_TRUE(test_subsurface->sync());
2313 }
2314
TEST_P(WaylandWindowTest,UsesCorrectParentForChildrenWindows)2315 TEST_P(WaylandWindowTest, UsesCorrectParentForChildrenWindows) {
2316 uint32_t serial = 0;
2317
2318 MockPlatformWindowDelegate window_delegate;
2319 std::unique_ptr<WaylandWindow> window = CreateWaylandWindowWithParams(
2320 PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
2321 gfx::Rect(10, 10, 100, 100), &window_delegate);
2322 EXPECT_TRUE(window);
2323
2324 window->Show(false);
2325
2326 std::unique_ptr<WaylandWindow> another_window = CreateWaylandWindowWithParams(
2327 PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget,
2328 gfx::Rect(10, 10, 300, 400), &window_delegate);
2329 EXPECT_TRUE(another_window);
2330
2331 another_window->Show(false);
2332
2333 Sync();
2334
2335 auto* window1 = window.get();
2336 auto* window2 = window_.get();
2337 auto* window3 = another_window.get();
2338
2339 // Make sure windows are not "active".
2340 auto empty_state = MakeStateArray({});
2341 SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get());
2342 auto* xdg_surface_window =
2343 server_
2344 .GetObject<wl::MockSurface>(window->root_surface()->GetSurfaceId())
2345 ->xdg_surface();
2346 SendConfigureEvent(xdg_surface_window, 0, 0, ++serial, empty_state.get());
2347 auto* xdg_surface_another_window =
2348 server_
2349 .GetObject<wl::MockSurface>(
2350 another_window->root_surface()->GetSurfaceId())
2351 ->xdg_surface();
2352 SendConfigureEvent(xdg_surface_another_window, 0, 0, ++serial,
2353 empty_state.get());
2354
2355 Sync();
2356
2357 // Case 1: provided parent window's widget..
2358 MockPlatformWindowDelegate menu_window_delegate;
2359 std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
2360 PlatformWindowType::kMenu, window1->GetWidget(),
2361 gfx::Rect(10, 10, 10, 10), &menu_window_delegate);
2362
2363 EXPECT_TRUE(menu_window->parent_window() == window1);
2364
2365 // Case 2: didn't provide parent window's widget - must use current focused.
2366 //
2367 // Subcase 1: pointer focus.
2368 window2->SetPointerFocus(true);
2369 menu_window = CreateWaylandWindowWithParams(
2370 PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget,
2371 gfx::Rect(10, 10, 10, 10), &menu_window_delegate);
2372
2373 EXPECT_TRUE(menu_window->parent_window() == window2);
2374 EXPECT_TRUE(wl::IsMenuType(menu_window->type()));
2375
2376 // Subcase 2: keyboard focus.
2377 window2->SetPointerFocus(false);
2378 window2->set_keyboard_focus(true);
2379 menu_window = CreateWaylandWindowWithParams(
2380 PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget,
2381 gfx::Rect(10, 10, 10, 10), &menu_window_delegate);
2382
2383 // Mustn't be able to create a menu window, but rather creates a toplevel
2384 // window as we must provide at least something.
2385 EXPECT_TRUE(menu_window);
2386 // Make it create xdg objects.
2387 menu_window->Show(false);
2388
2389 Sync();
2390
2391 auto* menu_window_xdg =
2392 server_
2393 .GetObject<wl::MockSurface>(
2394 another_window->root_surface()->GetSurfaceId())
2395 ->xdg_surface();
2396 EXPECT_TRUE(menu_window_xdg);
2397 EXPECT_TRUE(menu_window_xdg->xdg_toplevel());
2398 EXPECT_FALSE(menu_window_xdg->xdg_popup());
2399
2400 // Subcase 3: touch focus.
2401 window2->set_keyboard_focus(false);
2402 window2->set_touch_focus(true);
2403 menu_window = CreateWaylandWindowWithParams(
2404 PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget,
2405 gfx::Rect(10, 10, 10, 10), &menu_window_delegate);
2406
2407 EXPECT_TRUE(menu_window->parent_window() == window2);
2408 EXPECT_TRUE(wl::IsMenuType(menu_window->type()));
2409
2410 // Case 3: neither of the windows are focused. However, there is one that is
2411 // active. Must use that then.
2412 window2->set_touch_focus(false);
2413
2414 auto active = InitializeWlArrayWithActivatedState();
2415 SendConfigureEvent(xdg_surface_another_window, 0, 0, ++serial, active.get());
2416 Sync();
2417
2418 menu_window = CreateWaylandWindowWithParams(
2419 PlatformWindowType::kMenu, gfx::kNullAcceleratedWidget,
2420 gfx::Rect(10, 10, 10, 10), &menu_window_delegate);
2421
2422 EXPECT_TRUE(menu_window->parent_window() == window3);
2423 EXPECT_TRUE(wl::IsMenuType(menu_window->type()));
2424 }
2425
2426 INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
2427 WaylandWindowTest,
2428 ::testing::Values(kXdgShellStable));
2429 INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test,
2430 WaylandWindowTest,
2431 ::testing::Values(kXdgShellV6));
2432
2433 } // namespace ui
2434